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