"""A WSGI application interface (see PEP 333).""" try: GeneratorExit except NameError: GeneratorExit = None import sys import cherrypy from cherrypy import _cputil, _cpwsgiserver, _cpwsgiserver3 from cherrypy.lib import httptools def requestLine(environ): """Rebuild first line of the request (e.g. "GET /path HTTP/1.0").""" resource = httptools.urljoin(environ.get('SCRIPT_NAME', ''), environ.get('PATH_INFO', '')) if resource == "/*": resource = "*" elif not resource.startswith("/"): resource = "/" + resource qString = environ.get('QUERY_STRING') if qString: resource += '?' + qString resource = resource.replace(" ", "%20") return ('%s %s %s' % (environ['REQUEST_METHOD'], resource or '/', environ['SERVER_PROTOCOL'] ) ) headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization', 'CONTENT_LENGTH': 'Content-Length', 'CONTENT_TYPE': 'Content-Type', 'REMOTE_HOST': 'Remote-Host', 'REMOTE_ADDR': 'Remote-Addr', } def translate_headers(environ): """Translate CGI-environ header names to HTTP header names.""" for cgiName in environ: translatedHeader = headerNames.get(cgiName.upper()) if translatedHeader: yield translatedHeader, environ[cgiName] elif cgiName.upper().startswith("HTTP_"): # Hackish attempt at recovering original header names. translatedHeader = cgiName[5:].replace("_", "-") yield translatedHeader, environ[cgiName] class NullWriter(object): def write(self, data): pass class ResponseIter(object): def __init__(self, request, body): self.body = body self.request = request def __iter__(self): if not self.body: raise StopIteration try: for chunk in self.body: # WSGI requires all data to be of type "str". This coercion should # not take any time at all if chunk is already of type "str". # If it's unicode, it could be a big performance hit (x ~500). if not isinstance(chunk, str): chunk = chunk.encode("ISO-8859-1") yield chunk except (KeyboardInterrupt, SystemExit), ex: raise ex except GeneratorExit: raise except: cherrypy.log(traceback=True) s, h, b = _cputil.bareError() # CherryPy test suite expects bareError body to be output, # so don't call start_response (which, according to PEP 333, # may raise its own error at that point). for chunk in b: # WSGI requires all data to be of type "str". This coercion should # not take any time at all if chunk is already of type "str". # If it's unicode, it could be a big performance hit (x ~500). if not isinstance(chunk, str): chunk = chunk.encode("ISO-8859-1") yield chunk def close(self): try: if self.request: self.request.close() except: cherrypy.log(traceback=True) self.request = None def wsgiApp(environ, start_response): """The WSGI 'application object' for CherryPy.""" # Trap screen output from BaseHTTPRequestHandler.log_message() if not cherrypy.config.get('server.log_to_screen'): sys.stderr = NullWriter() request = None try: env = environ.get clientAddr = (env('REMOTE_ADDR', ''), int(env('REMOTE_PORT', -1))) request = cherrypy.server.request(clientAddr, '', environ['wsgi.url_scheme']) # LOGON_USER is served by IIS, and is the name of the # user after having been mapped to a local account. # Both IIS and Apache set REMOTE_USER, when possible. request.login = (env('LOGON_USER') or env('REMOTE_USER') or None) request.multithread = environ['wsgi.multithread'] request.multiprocess = environ['wsgi.multiprocess'] request.wsgi_environ = environ response = request.run(requestLine(environ), translate_headers(environ), environ['wsgi.input']) s, h, b = response.status, response.header_list, response.body exc = None except (KeyboardInterrupt, SystemExit), ex: try: if request: request.close() except: cherrypy.log(traceback=True) request = None raise ex except: if cherrypy.config.get("server.throw_errors", False): if request: request.close() request = None raise tb = _cputil.formatExc() cherrypy.log(tb) if not cherrypy.config.get("server.show_tracebacks", False): tb = "" s, h, b = _cputil.bareError(tb) exc = sys.exc_info() start_response(s, h, exc) return ResponseIter(request, b) # Server components. class CPHTTPRequest(_cpwsgiserver.HTTPRequest): def __init__(self, socket, addr, server): _cpwsgiserver.HTTPRequest.__init__(self, socket, addr, server) mhs = int(cherrypy.config.get('server.max_request_header_size', 500 * 1024)) if mhs > 0: self.rfile = httptools.SizeCheckWrapper(self.rfile, mhs) def parse_request(self): try: _cpwsgiserver.HTTPRequest.parse_request(self) except httptools.MaxSizeExceeded: msg = "Request Entity Too Large" proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0") self.wfile.write("%s 413 %s\r\n" % (proto, msg)) self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg)) self.wfile.write(msg) self.wfile.flush() self.ready = False cherrypy.log(traceback=True) else: if self.ready: if isinstance(self.rfile, httptools.SizeCheckWrapper): # Unwrap the rfile self.rfile = self.rfile.rfile self.environ["wsgi.input"] = self.rfile class WSGIServer(_cpwsgiserver.CherryPyWSGIServer): """Wrapper for _cpwsgiserver.CherryPyWSGIServer. _cpwsgiserver has been designed to not reference CherryPy in any way, so that it can be used in other frameworks and applications. Therefore, we wrap it here, so we can set our own mount points from cherrypy.tree. """ RequestHandlerClass = CPHTTPRequest def __init__(self): conf = cherrypy.config.get sockFile = cherrypy.config.get('server.socket_file') if sockFile: bind_addr = sockFile else: bind_addr = (conf("server.socket_host"), conf("server.socket_port")) pts = cherrypy.tree.mount_points if pts: apps = [(base, wsgiApp) for base in pts.keys()] else: apps = [("", wsgiApp)] s = _cpwsgiserver.CherryPyWSGIServer s.__init__(self, bind_addr, apps, conf("server.thread_pool"), conf("server.socket_host"), request_queue_size = conf('server.socket_queue_size'), timeout = conf('server.socket_timeout'), ) # Server3 components # class CPHTTPRequest3(_cpwsgiserver3.HTTPRequest): def parse_request(self): mhs = int(cherrypy.config.get('server.max_request_header_size', 500 * 1024)) if mhs > 0: self.rfile = httptools.SizeCheckWrapper(self.rfile, mhs) try: _cpwsgiserver3.HTTPRequest.parse_request(self) except httptools.MaxSizeExceeded: self.simple_response("413 Request Entity Too Large") cherrypy.log(traceback=True) else: if self.ready: if isinstance(self.rfile, httptools.SizeCheckWrapper): # Unwrap the rfile self.rfile = self.rfile.rfile self.environ["wsgi.input"] = self.rfile def decode_chunked(self): """Decode the 'chunked' transfer coding.""" if isinstance(self.rfile, httptools.SizeCheckWrapper): self.rfile = self.rfile.rfile mbs = int(cherrypy.config.get('server.max_request_body_size', 100 * 1024 * 1024)) if mbs > 0: self.rfile = httptools.SizeCheckWrapper(self.rfile, mbs) try: return _cpwsgiserver3.HTTPRequest.decode_chunked(self) except httptools.MaxSizeExceeded: self.simple_response("413 Request Entity Too Large") cherrypy.log(traceback=True) return False class CPHTTPConnection3(_cpwsgiserver3.HTTPConnection): RequestHandlerClass = CPHTTPRequest3 class CPWSGIServer3(_cpwsgiserver3.CherryPyWSGIServer): """Wrapper for _cpwsgiserver3.CherryPyWSGIServer. wsgiserver has been designed to not reference CherryPy in any way, so that it can be used in other frameworks and applications. Therefore, we wrap it here, so we can set our own mount points from cherrypy.tree. """ ConnectionClass = CPHTTPConnection3 def __init__(self): conf = cherrypy.config.get sockFile = cherrypy.config.get('server.socket_file') if sockFile: bind_addr = sockFile else: bind_addr = (conf("server.socket_host"), conf("server.socket_port")) pts = cherrypy.tree.mount_points if pts: apps = [(base, wsgiApp) for base in pts.keys()] else: apps = [("", wsgiApp)] s = _cpwsgiserver3.CherryPyWSGIServer s.__init__(self, bind_addr, apps, conf("server.thread_pool"), conf("server.socket_host"), request_queue_size = conf('server.socket_queue_size'), timeout = conf('server.socket_timeout'), ) self.protocol = conf("server.protocol_version") self.ssl_certificate = conf("server.ssl_certificate") self.ssl_private_key = conf("server.ssl_private_key")