1"""BaseHTTPServer that implements the Python WSGI protocol (PEP 3333)
2
3This is both an example of how WSGI can be implemented, and a basis for running
4simple web applications on a local machine, such as might be done when testing
5or debugging an application.  It has not been reviewed for security issues,
6however, and we strongly recommend that you use a "real" web server for
7production use.
8
9For example usage, see the 'if __name__=="__main__"' block at the end of the
10module.  See also the BaseHTTPServer module docs for other API information.
11"""
12
13from http.server import BaseHTTPRequestHandler, HTTPServer
14import sys
15import urllib.parse
16from wsgiref.handlers import SimpleHandler
17from platform import python_implementation
18
19__version__ = "0.2"
20__all__ = ['WSGIServer', 'WSGIRequestHandler', 'demo_app', 'make_server']
21
22
23server_version = "WSGIServer/" + __version__
24sys_version = python_implementation() + "/" + sys.version.split()[0]
25software_version = server_version + ' ' + sys_version
26
27
28class 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
42class 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
71class 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            multithread=False,
132        )
133        handler.request_handler = self      # backpointer for logging
134        handler.run(self.server.get_app())
135
136
137
138def demo_app(environ,start_response):
139    from io import StringIO
140    stdout = StringIO()
141    print("Hello world!", file=stdout)
142    print(file=stdout)
143    h = sorted(environ.items())
144    for k,v in h:
145        print(k,'=',repr(v), file=stdout)
146    start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')])
147    return [stdout.getvalue().encode("utf-8")]
148
149
150def make_server(
151    host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
152):
153    """Create a new WSGI server listening on `host` and `port` for `app`"""
154    server = server_class((host, port), handler_class)
155    server.set_app(app)
156    return server
157
158
159if __name__ == '__main__':
160    with make_server('', 8000, demo_app) as httpd:
161        sa = httpd.socket.getsockname()
162        print("Serving HTTP on", sa[0], "port", sa[1], "...")
163        import webbrowser
164        webbrowser.open('http://localhost:8000/xyz?abc')
165        httpd.handle_request()  # serve one request, then exit
166