• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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        env['REMOTE_ADDR'] = self.client_address[0]
88
89        if self.headers.get('content-type') is None:
90            env['CONTENT_TYPE'] = self.headers.get_content_type()
91        else:
92            env['CONTENT_TYPE'] = self.headers['content-type']
93
94        length = self.headers.get('content-length')
95        if length:
96            env['CONTENT_LENGTH'] = length
97
98        for k, v in self.headers.items():
99            k=k.replace('-','_').upper(); v=v.strip()
100            if k in env:
101                continue                    # skip content length, type,etc.
102            if 'HTTP_'+k in env:
103                env['HTTP_'+k] += ','+v     # comma-separate multiple headers
104            else:
105                env['HTTP_'+k] = v
106        return env
107
108    def get_stderr(self):
109        return sys.stderr
110
111    def handle(self):
112        """Handle a single HTTP request"""
113
114        self.raw_requestline = self.rfile.readline(65537)
115        if len(self.raw_requestline) > 65536:
116            self.requestline = ''
117            self.request_version = ''
118            self.command = ''
119            self.send_error(414)
120            return
121
122        if not self.parse_request(): # An error code has been sent, just exit
123            return
124
125        handler = ServerHandler(
126            self.rfile, self.wfile, self.get_stderr(), self.get_environ(),
127            multithread=False,
128        )
129        handler.request_handler = self      # backpointer for logging
130        handler.run(self.server.get_app())
131
132
133
134def demo_app(environ,start_response):
135    from io import StringIO
136    stdout = StringIO()
137    print("Hello world!", file=stdout)
138    print(file=stdout)
139    h = sorted(environ.items())
140    for k,v in h:
141        print(k,'=',repr(v), file=stdout)
142    start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')])
143    return [stdout.getvalue().encode("utf-8")]
144
145
146def make_server(
147    host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
148):
149    """Create a new WSGI server listening on `host` and `port` for `app`"""
150    server = server_class((host, port), handler_class)
151    server.set_app(app)
152    return server
153
154
155if __name__ == '__main__':
156    with make_server('', 8000, demo_app) as httpd:
157        sa = httpd.socket.getsockname()
158        print("Serving HTTP on", sa[0], "port", sa[1], "...")
159        import webbrowser
160        webbrowser.open('http://localhost:8000/xyz?abc')
161        httpd.handle_request()  # serve one request, then exit
162