simple_server.py 5.3 KB

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