1 """BaseHTTPServer that implements the Python WSGI protocol (PEP 3333) 2 3 This is both an example of how WSGI can be implemented, and a basis for running 4 simple web applications on a local machine, such as might be done when testing 5 or debugging an application. It has not been reviewed for security issues, 6 however, and we strongly recommend that you use a "real" web server for 7 production use. 8 9 For example usage, see the 'if __name__=="__main__"' block at the end of the 10 module. See also the BaseHTTPServer module docs for other API information. 11 """ 12 13 from http.server import BaseHTTPRequestHandler, HTTPServer 14 import sys 15 import urllib.parse 16 from wsgiref.handlers import SimpleHandler 17 from platform import python_implementation 18 19 __version__ = "0.2" 20 __all__ = ['WSGIServer', 'WSGIRequestHandler', 'demo_app', 'make_server'] 21 22 23 server_version = "WSGIServer/" + __version__ 24 sys_version = python_implementation() + "/" + sys.version.split()[0] 25 software_version = server_version + ' ' + sys_version 26 27 28 class ServerHandler(SimpleHandler): 29 30 server_software = software_version 31 32 def close(self): 33 try: 34 self.request_handler.log_request( 35 self.status.split(' ',1)[0], self.bytes_sent 36 ) 37 finally: 38 SimpleHandler.close(self) 39 40 41 42 class WSGIServer(HTTPServer): 43 44 """BaseHTTPServer that implements the Python WSGI protocol""" 45 46 application = None 47 48 def server_bind(self): 49 """Override server_bind to store the server name.""" 50 HTTPServer.server_bind(self) 51 self.setup_environ() 52 53 def setup_environ(self): 54 # Set up base environment 55 env = self.base_environ = {} 56 env['SERVER_NAME'] = self.server_name 57 env['GATEWAY_INTERFACE'] = 'CGI/1.1' 58 env['SERVER_PORT'] = str(self.server_port) 59 env['REMOTE_HOST']='' 60 env['CONTENT_LENGTH']='' 61 env['SCRIPT_NAME'] = '' 62 63 def get_app(self): 64 return self.application 65 66 def set_app(self,application): 67 self.application = application 68 69 70 71 class WSGIRequestHandler(BaseHTTPRequestHandler): 72 73 server_version = "WSGIServer/" + __version__ 74 75 def get_environ(self): 76 env = self.server.base_environ.copy() 77 env['SERVER_PROTOCOL'] = self.request_version 78 env['SERVER_SOFTWARE'] = self.server_version 79 env['REQUEST_METHOD'] = self.command 80 if '?' in self.path: 81 path,query = self.path.split('?',1) 82 else: 83 path,query = self.path,'' 84 85 env['PATH_INFO'] = urllib.parse.unquote(path, 'iso-8859-1') 86 env['QUERY_STRING'] = query 87 88 host = self.address_string() 89 if host != self.client_address[0]: 90 env['REMOTE_HOST'] = host 91 env['REMOTE_ADDR'] = self.client_address[0] 92 93 if self.headers.get('content-type') is None: 94 env['CONTENT_TYPE'] = self.headers.get_content_type() 95 else: 96 env['CONTENT_TYPE'] = self.headers['content-type'] 97 98 length = self.headers.get('content-length') 99 if length: 100 env['CONTENT_LENGTH'] = length 101 102 for k, v in self.headers.items(): 103 k=k.replace('-','_').upper(); v=v.strip() 104 if k in env: 105 continue # skip content length, type,etc. 106 if 'HTTP_'+k in env: 107 env['HTTP_'+k] += ','+v # comma-separate multiple headers 108 else: 109 env['HTTP_'+k] = v 110 return env 111 112 def get_stderr(self): 113 return sys.stderr 114 115 def handle(self): 116 """Handle a single HTTP request""" 117 118 self.raw_requestline = self.rfile.readline(65537) 119 if len(self.raw_requestline) > 65536: 120 self.requestline = '' 121 self.request_version = '' 122 self.command = '' 123 self.send_error(414) 124 return 125 126 if not self.parse_request(): # An error code has been sent, just exit 127 return 128 129 handler = ServerHandler( 130 self.rfile, self.wfile, self.get_stderr(), self.get_environ() 131 ) 132 handler.request_handler = self # backpointer for logging 133 handler.run(self.server.get_app()) 134 135 136 137 def demo_app(environ,start_response): 138 from io import StringIO 139 stdout = StringIO() 140 print("Hello world!", file=stdout) 141 print(file=stdout) 142 h = sorted(environ.items()) 143 for k,v in h: 144 print(k,'=',repr(v), file=stdout) 145 start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')]) 146 return [stdout.getvalue().encode("utf-8")] 147 148 149 def make_server( 150 host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler 151 ): 152 """Create a new WSGI server listening on `host` and `port` for `app`""" 153 server = server_class((host, port), handler_class) 154 server.set_app(app) 155 return server 156 157 158 if __name__ == '__main__': 159 with make_server('', 8000, demo_app) as httpd: 160 sa = httpd.socket.getsockname() 161 print("Serving HTTP on", sa[0], "port", sa[1], "...") 162 import webbrowser 163 webbrowser.open('http://localhost:8000/xyz?abc') 164 httpd.handle_request() # serve one request, then exit 165