Home | History | Annotate | Download | only in wsgiref
      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