Robert Brewer avatar Robert Brewer committed 50be400

Took out wsgiserver in favor of the cheroot fork.

Comments (0)

Files changed (12)

cherrypy/_cpnative_server.py

 import logging
 import sys
 
+from cheroot import server, ssllib
+
 import cherrypy
 from cherrypy._cpcompat import BytesIO
 from cherrypy._cperror import format_exc, bare_error
 from cherrypy.lib import httputil
-from cherrypy import wsgiserver
 
 
-class NativeGateway(wsgiserver.Gateway):
+class NativeGateway(server.Gateway):
     
     recursive = False
     
             req.write(seg)
 
 
-class CPHTTPServer(wsgiserver.HTTPServer):
-    """Wrapper for wsgiserver.HTTPServer.
-    
-    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 apply some attributes
-    from config -> cherrypy.server -> HTTPServer.
-    """
+class CPHTTPServer(server.HTTPServer):
     
     def __init__(self, server_adapter=cherrypy.server):
         self.server_adapter = server_adapter
                        self.server_adapter.socket_file or
                        None)
         
-        wsgiserver.HTTPServer.__init__(
+        server.HTTPServer.__init__(
             self, server_adapter.bind_addr, NativeGateway,
             minthreads=server_adapter.thread_pool,
             maxthreads=server_adapter.thread_pool_max,
         
         ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
         if self.server_adapter.ssl_context:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
+            adapter_class = ssllib.get_ssl_adapter_class(ssl_module)
             self.ssl_adapter = adapter_class(
                 self.server_adapter.ssl_certificate,
                 self.server_adapter.ssl_private_key,
                 self.server_adapter.ssl_certificate_chain)
             self.ssl_adapter.context = self.server_adapter.ssl_context
         elif self.server_adapter.ssl_certificate:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
+            adapter_class = ssllib.get_ssl_adapter_class(ssl_module)
             self.ssl_adapter = adapter_class(
                 self.server_adapter.ssl_certificate,
                 self.server_adapter.ssl_private_key,

cherrypy/_cprequest.py

         self.output_status = ntob(str(code), 'ascii') + ntob(" ") + headers.encode(reason)
         
         if self.stream:
-            # The upshot: wsgiserver will chunk the response if
+            # The upshot: the cheroot server will chunk the response if
             # you pop Content-Length (or set it explicitly to None).
             # Note that lib.static sets C-L to the file's st_size.
             if dict.get(headers, 'Content-Length') is None:

cherrypy/_cpserver.py

     
     if py3k:
         ssl_module = 'builtin'
-        """The name of a registered SSL adaptation module to use with the builtin
-        WSGI server. Builtin options are: 'builtin' (to use the SSL library built
-        into recent versions of Python). You may also register your
-        own classes in the wsgiserver.ssl_adapters dict."""
+        """The name of a registered SSL adaptation module to use with the
+        WSGI server. Builtin options are: 'builtin' (to use the SSL library
+        built into recent versions of Python). You may also register your
+        own classes in the cheroot.ssllib.ssl_adapters dict."""
     else:
         ssl_module = 'pyopenssl'
-        """The name of a registered SSL adaptation module to use with the builtin
-        WSGI server. Builtin options are 'builtin' (to use the SSL library built
-        into recent versions of Python) and 'pyopenssl' (to use the PyOpenSSL
-        project, which you must install separately). You may also register your
-        own classes in the wsgiserver.ssl_adapters dict."""
+        """The name of a registered SSL adaptation module to use with the
+        WSGI server. Builtin options are 'builtin' (to use the SSL library
+        built into recent versions of Python) and 'pyopenssl' (to use the
+        PyOpenSSL project, which you must install separately). You may also
+        register your own classes in the cheroot.ssllib.ssl_adapters dict."""
     
     statistics = False
     """Turns statistics-gathering on or off for aware HTTP servers."""
     """If True (the default since 3.1), sets the TCP_NODELAY socket option."""
     
     wsgi_version = (1, 0)
-    """The WSGI version tuple to use with the builtin WSGI server.
+    """The WSGI version tuple to use with the WSGI server.
     The provided options are (1, 0) [which includes support for PEP 3333,
     which declares it covers WSGI version 1.0.1 but still mandates the
     wsgi.version (1, 0)] and ('u', 0), an experimental unicode version.
     You may create and register your own experimental versions of the WSGI
-    protocol by adding custom classes to the wsgiserver.wsgi_gateways dict."""
+    protocol by adding custom classes to the cheroot.server.wsgi_gateways dict."""
     
     def __init__(self):
         self.bus = cherrypy.engine

cherrypy/_cpwsgi_server.py

 """WSGI server interface (see PEP 333). This adds some CP-specific bits to
-the framework-agnostic wsgiserver package.
+the framework-agnostic cheroot package.
 """
 import sys
 
 import cherrypy
-from cherrypy import wsgiserver
+from cheroot import wsgi, ssllib
 
 
-class CPWSGIServer(wsgiserver.CherryPyWSGIServer):
-    """Wrapper for wsgiserver.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
-    and apply some attributes from config -> cherrypy.server -> wsgiserver.
-    """
+class CPWSGIServer(wsgi.WSGIServer):
     
     def __init__(self, server_adapter=cherrypy.server):
         self.server_adapter = server_adapter
                        None)
         
         self.wsgi_version = self.server_adapter.wsgi_version
-        s = wsgiserver.CherryPyWSGIServer
-        s.__init__(self, server_adapter.bind_addr, cherrypy.tree,
-                   self.server_adapter.thread_pool,
-                   server_name,
-                   max = self.server_adapter.thread_pool_max,
-                   request_queue_size = self.server_adapter.socket_queue_size,
-                   timeout = self.server_adapter.socket_timeout,
-                   shutdown_timeout = self.server_adapter.shutdown_timeout,
+        s = wsgi.WSGIServer
+        s.__init__(self, server_adapter.bind_addr,
+                   minthreads=self.server_adapter.thread_pool,
+                   maxthreads=self.server_adapter.thread_pool_max,
+                   server_name=server_name,
+                   protocol = self.server_adapter.protocol_version,
                    )
-        self.protocol = self.server_adapter.protocol_version
+        self.wsgi_app = cherrypy.tree
+        self.request_queue_size = self.server_adapter.socket_queue_size
+        self.timeout = self.server_adapter.socket_timeout
+        self.shutdown_timeout = self.server_adapter.shutdown_timeout
         self.nodelay = self.server_adapter.nodelay
 
         if sys.version_info >= (3, 0):
         else:
             ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
         if self.server_adapter.ssl_context:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
+            adapter_class = ssllib.get_ssl_adapter_class(ssl_module)
             self.ssl_adapter = adapter_class(
                 self.server_adapter.ssl_certificate,
                 self.server_adapter.ssl_private_key,
                 self.server_adapter.ssl_certificate_chain)
             self.ssl_adapter.context = self.server_adapter.ssl_context
         elif self.server_adapter.ssl_certificate:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
+            adapter_class = ssllib.get_ssl_adapter_class(ssl_module)
             self.ssl_adapter = adapter_class(
                 self.server_adapter.ssl_certificate,
                 self.server_adapter.ssl_private_key,

cherrypy/test/helper.py

             engine.signal_handler.subscribe()
         if hasattr(engine, "console_control_handler"):
             engine.console_control_handler.subscribe()
-        #engine.subscribe('log', log_to_stderr)
+        engine.subscribe('log', log_to_stderr)
 
     def start(self, modulename=None):
         """Load and start the HTTP server."""

cherrypy/test/test_encoding.py

             self.assertHeader('Content-Encoding', 'gzip')
             self.assertInBody('\x1f\x8b\x08\x00')
         else:
-            # The wsgiserver will simply stop sending data, and the HTTP client
-            # will error due to an incomplete chunk-encoded stream.
+            # The cheroot server will simply stop sending data, and the HTTP
+            # client will error due to an incomplete chunk-encoded stream.
             self.assertRaises((ValueError, IncompleteRead), self.getPage,
                               '/gzip/noshow_stream',
                               headers=[("Accept-Encoding", "gzip")])

cherrypy/wsgiserver/__init__.py

-__all__ = ['HTTPRequest', 'HTTPConnection', 'HTTPServer',
-           'SizeCheckWrapper', 'KnownLengthRFile', 'ChunkedRFile',
-           'MaxSizeExceeded', 'NoSSLError', 'FatalSSLAlert',
-           'WorkerThread', 'ThreadPool', 'SSLAdapter',
-           'CherryPyWSGIServer',
-           'Gateway', 'WSGIGateway', 'WSGIGateway_10', 'WSGIGateway_u0',
-           'WSGIPathInfoDispatcher', 'get_ssl_adapter_class']
-
-import sys
-if sys.version_info < (3, 0):
-    from wsgiserver2 import *
-else:
-    # Le sigh. Boo for backward-incompatible syntax.
-    exec('from .wsgiserver3 import *')

cherrypy/wsgiserver/ssl_builtin.py

-"""A library for integrating Python's builtin ``ssl`` library with CherryPy.
-
-The ssl module must be importable for SSL functionality.
-
-To use this module, set ``CherryPyWSGIServer.ssl_adapter`` to an instance of
-``BuiltinSSLAdapter``.
-"""
-
-try:
-    import ssl
-except ImportError:
-    ssl = None
-
-try:
-    from _pyio import DEFAULT_BUFFER_SIZE
-except ImportError:
-    try:
-        from io import DEFAULT_BUFFER_SIZE
-    except ImportError:
-        DEFAULT_BUFFER_SIZE = -1
-
-import sys
-
-from cherrypy import wsgiserver
-
-
-class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
-    """A wrapper for integrating Python's builtin ssl module with CherryPy."""
-    
-    certificate = None
-    """The filename of the server SSL certificate."""
-    
-    private_key = None
-    """The filename of the server's private key file."""
-    
-    def __init__(self, certificate, private_key, certificate_chain=None):
-        if ssl is None:
-            raise ImportError("You must install the ssl module to use HTTPS.")
-        self.certificate = certificate
-        self.private_key = private_key
-        self.certificate_chain = certificate_chain
-    
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        return sock
-    
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        try:
-            s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
-                    server_side=True, certfile=self.certificate,
-                    keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23)
-        except ssl.SSLError:
-            e = sys.exc_info()[1]
-            if e.errno == ssl.SSL_ERROR_EOF:
-                # This is almost certainly due to the cherrypy engine
-                # 'pinging' the socket to assert it's connectable;
-                # the 'ping' isn't SSL.
-                return None, {}
-            elif e.errno == ssl.SSL_ERROR_SSL:
-                if e.args[1].endswith('http request'):
-                    # The client is speaking HTTP to an HTTPS server.
-                    raise wsgiserver.NoSSLError
-                elif e.args[1].endswith('unknown protocol'):
-                    # The client is speaking some non-HTTP protocol.
-                    # Drop the conn.
-                    return None, {}
-            raise
-        return s, self.get_environ(s)
-    
-    # TODO: fill this out more with mod ssl env
-    def get_environ(self, sock):
-        """Create WSGI environ entries to be merged into each request."""
-        cipher = sock.cipher()
-        ssl_environ = {
-            "wsgi.url_scheme": "https",
-            "HTTPS": "on",
-            'SSL_PROTOCOL': cipher[1],
-            'SSL_CIPHER': cipher[0]
-##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
-##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
-            }
-        return ssl_environ
-    
-    if sys.version_info >= (3, 0):
-        def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
-            return wsgiserver.CP_makefile(sock, mode, bufsize)
-    else:
-        def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
-            return wsgiserver.CP_fileobject(sock, mode, bufsize)
-

cherrypy/wsgiserver/ssl_pyopenssl.py

-"""A library for integrating pyOpenSSL with CherryPy.
-
-The OpenSSL module must be importable for SSL functionality.
-You can obtain it from http://pyopenssl.sourceforge.net/
-
-To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
-SSLAdapter. There are two ways to use SSL:
-
-Method One
-----------
-
- * ``ssl_adapter.context``: an instance of SSL.Context.
-
-If this is not None, it is assumed to be an SSL.Context instance,
-and will be passed to SSL.Connection on bind(). The developer is
-responsible for forming a valid Context object. This approach is
-to be preferred for more flexibility, e.g. if the cert and key are
-streams instead of files, or need decryption, or SSL.SSLv3_METHOD
-is desired instead of the default SSL.SSLv23_METHOD, etc. Consult
-the pyOpenSSL documentation for complete options.
-
-Method Two (shortcut)
----------------------
-
- * ``ssl_adapter.certificate``: the filename of the server SSL certificate.
- * ``ssl_adapter.private_key``: the filename of the server's private key file.
-
-Both are None by default. If ssl_adapter.context is None, but .private_key
-and .certificate are both given and valid, they will be read, and the
-context will be automatically created from them.
-"""
-
-import socket
-import threading
-import time
-
-from cherrypy import wsgiserver
-
-try:
-    from OpenSSL import SSL
-    from OpenSSL import crypto
-except ImportError:
-    SSL = None
-
-
-class SSL_fileobject(wsgiserver.CP_fileobject):
-    """SSL file object attached to a socket object."""
-    
-    ssl_timeout = 3
-    ssl_retry = .01
-    
-    def _safe_call(self, is_reader, call, *args, **kwargs):
-        """Wrap the given call with SSL error-trapping.
-        
-        is_reader: if False EOF errors will be raised. If True, EOF errors
-        will return "" (to emulate normal sockets).
-        """
-        start = time.time()
-        while True:
-            try:
-                return call(*args, **kwargs)
-            except SSL.WantReadError:
-                # Sleep and try again. This is dangerous, because it means
-                # the rest of the stack has no way of differentiating
-                # between a "new handshake" error and "client dropped".
-                # Note this isn't an endless loop: there's a timeout below.
-                time.sleep(self.ssl_retry)
-            except SSL.WantWriteError:
-                time.sleep(self.ssl_retry)
-            except SSL.SysCallError, e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ""
-                
-                errnum = e.args[0]
-                if is_reader and errnum in wsgiserver.socket_errors_to_ignore:
-                    return ""
-                raise socket.error(errnum)
-            except SSL.Error, e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ""
-                
-                thirdarg = None
-                try:
-                    thirdarg = e.args[0][0][2]
-                except IndexError:
-                    pass
-                
-                if thirdarg == 'http request':
-                    # The client is talking HTTP to an HTTPS server.
-                    raise wsgiserver.NoSSLError()
-                
-                raise wsgiserver.FatalSSLAlert(*e.args)
-            except:
-                raise
-            
-            if time.time() - start > self.ssl_timeout:
-                raise socket.timeout("timed out")
-    
-    def recv(self, *args, **kwargs):
-        buf = []
-        r = super(SSL_fileobject, self).recv
-        while True:
-            data = self._safe_call(True, r, *args, **kwargs)
-            buf.append(data)
-            p = self._sock.pending()
-            if not p:
-                return "".join(buf)
-    
-    def sendall(self, *args, **kwargs):
-        return self._safe_call(False, super(SSL_fileobject, self).sendall,
-                               *args, **kwargs)
-
-    def send(self, *args, **kwargs):
-        return self._safe_call(False, super(SSL_fileobject, self).send,
-                               *args, **kwargs)
-
-
-class SSLConnection:
-    """A thread-safe wrapper for an SSL.Connection.
-    
-    ``*args``: the arguments to create the wrapped ``SSL.Connection(*args)``.
-    """
-    
-    def __init__(self, *args):
-        self._ssl_conn = SSL.Connection(*args)
-        self._lock = threading.RLock()
-    
-    for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
-              'renegotiate', 'bind', 'listen', 'connect', 'accept',
-              'setblocking', 'fileno', 'close', 'get_cipher_list',
-              'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
-              'makefile', 'get_app_data', 'set_app_data', 'state_string',
-              'sock_shutdown', 'get_peer_certificate', 'want_read',
-              'want_write', 'set_connect_state', 'set_accept_state',
-              'connect_ex', 'sendall', 'settimeout', 'gettimeout'):
-        exec("""def %s(self, *args):
-        self._lock.acquire()
-        try:
-            return self._ssl_conn.%s(*args)
-        finally:
-            self._lock.release()
-""" % (f, f))
-    
-    def shutdown(self, *args):
-        self._lock.acquire()
-        try:
-            # pyOpenSSL.socket.shutdown takes no args
-            return self._ssl_conn.shutdown()
-        finally:
-            self._lock.release()
-
-
-class pyOpenSSLAdapter(wsgiserver.SSLAdapter):
-    """A wrapper for integrating pyOpenSSL with CherryPy."""
-    
-    context = None
-    """An instance of SSL.Context."""
-    
-    certificate = None
-    """The filename of the server SSL certificate."""
-    
-    private_key = None
-    """The filename of the server's private key file."""
-    
-    certificate_chain = None
-    """Optional. The filename of CA's intermediate certificate bundle.
-    
-    This is needed for cheaper "chained root" SSL certificates, and should be
-    left as None if not required."""
-    
-    def __init__(self, certificate, private_key, certificate_chain=None):
-        if SSL is None:
-            raise ImportError("You must install pyOpenSSL to use HTTPS.")
-        
-        self.context = None
-        self.certificate = certificate
-        self.private_key = private_key
-        self.certificate_chain = certificate_chain
-        self._environ = None
-    
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        if self.context is None:
-            self.context = self.get_context()
-        conn = SSLConnection(self.context, sock)
-        self._environ = self.get_environ()
-        return conn
-    
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        return sock, self._environ.copy()
-    
-    def get_context(self):
-        """Return an SSL.Context from self attributes."""
-        # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
-        c = SSL.Context(SSL.SSLv23_METHOD)
-        c.use_privatekey_file(self.private_key)
-        if self.certificate_chain:
-            c.load_verify_locations(self.certificate_chain)
-        c.use_certificate_file(self.certificate)
-        return c
-    
-    def get_environ(self):
-        """Return WSGI environ entries to be merged into each request."""
-        ssl_environ = {
-            "HTTPS": "on",
-            # pyOpenSSL doesn't provide access to any of these AFAICT
-##            'SSL_PROTOCOL': 'SSLv2',
-##            SSL_CIPHER 	string 	The cipher specification name
-##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
-##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
-            }
-        
-        if self.certificate:
-            # Server certificate attributes
-            cert = open(self.certificate, 'rb').read()
-            cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
-            ssl_environ.update({
-                'SSL_SERVER_M_VERSION': cert.get_version(),
-                'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
-##                'SSL_SERVER_V_START': Validity of server's certificate (start time),
-##                'SSL_SERVER_V_END': Validity of server's certificate (end time),
-                })
-            
-            for prefix, dn in [("I", cert.get_issuer()),
-                               ("S", cert.get_subject())]:
-                # X509Name objects don't seem to have a way to get the
-                # complete DN string. Use str() and slice it instead,
-                # because str(dn) == "<X509Name object '/C=US/ST=...'>"
-                dnstr = str(dn)[18:-2]
-                
-                wsgikey = 'SSL_SERVER_%s_DN' % prefix
-                ssl_environ[wsgikey] = dnstr
-                
-                # The DN should be of the form: /k1=v1/k2=v2, but we must allow
-                # for any value to contain slashes itself (in a URL).
-                while dnstr:
-                    pos = dnstr.rfind("=")
-                    dnstr, value = dnstr[:pos], dnstr[pos + 1:]
-                    pos = dnstr.rfind("/")
-                    dnstr, key = dnstr[:pos], dnstr[pos + 1:]
-                    if key and value:
-                        wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
-                        ssl_environ[wsgikey] = value
-        
-        return ssl_environ
-    
-    def makefile(self, sock, mode='r', bufsize=-1):
-        if SSL and isinstance(sock, SSL.ConnectionType):
-            timeout = sock.gettimeout()
-            f = SSL_fileobject(sock, mode, bufsize)
-            f.ssl_timeout = timeout
-            return f
-        else:
-            return wsgiserver.CP_fileobject(sock, mode, bufsize)
-

cherrypy/wsgiserver/wsgiserver2.py

-"""A high-speed, production ready, thread pooled, generic HTTP server.
-
-Simplest example on how to use this module directly
-(without using CherryPy's application machinery)::
-
-    from cherrypy import wsgiserver
-    
-    def my_crazy_app(environ, start_response):
-        status = '200 OK'
-        response_headers = [('Content-type','text/plain')]
-        start_response(status, response_headers)
-        return ['Hello world!']
-    
-    server = wsgiserver.CherryPyWSGIServer(
-                ('0.0.0.0', 8070), my_crazy_app,
-                server_name='www.cherrypy.example')
-    server.start()
-    
-The CherryPy WSGI server can serve as many WSGI applications 
-as you want in one instance by using a WSGIPathInfoDispatcher::
-    
-    d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
-    server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
-    
-Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
-
-This won't call the CherryPy engine (application side) at all, only the
-HTTP server, which is independent from the rest of CherryPy. Don't
-let the name "CherryPyWSGIServer" throw you; the name merely reflects
-its origin, not its coupling.
-
-For those of you wanting to understand internals of this module, here's the
-basic call flow. The server's listening thread runs a very tight loop,
-sticking incoming connections onto a Queue::
-
-    server = CherryPyWSGIServer(...)
-    server.start()
-    while True:
-        tick()
-        # This blocks until a request comes in:
-        child = socket.accept()
-        conn = HTTPConnection(child, ...)
-        server.requests.put(conn)
-
-Worker threads are kept in a pool and poll the Queue, popping off and then
-handling each connection in turn. Each connection can consist of an arbitrary
-number of requests and their responses, so we run a nested loop::
-
-    while True:
-        conn = server.requests.get()
-        conn.communicate()
-        ->  while True:
-                req = HTTPRequest(...)
-                req.parse_request()
-                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
-                    req.rfile.readline()
-                    read_headers(req.rfile, req.inheaders)
-                req.respond()
-                ->  response = app(...)
-                    try:
-                        for chunk in response:
-                            if chunk:
-                                req.write(chunk)
-                    finally:
-                        if hasattr(response, "close"):
-                            response.close()
-                if req.close_connection:
-                    return
-"""
-
-__all__ = ['HTTPRequest', 'HTTPConnection', 'HTTPServer',
-           'SizeCheckWrapper', 'KnownLengthRFile', 'ChunkedRFile',
-           'CP_fileobject',
-           'MaxSizeExceeded', 'NoSSLError', 'FatalSSLAlert',
-           'WorkerThread', 'ThreadPool', 'SSLAdapter',
-           'CherryPyWSGIServer',
-           'Gateway', 'WSGIGateway', 'WSGIGateway_10', 'WSGIGateway_u0',
-           'WSGIPathInfoDispatcher', 'get_ssl_adapter_class']
-
-import os
-try:
-    import queue
-except:
-    import Queue as queue
-import re
-import rfc822
-import socket
-import sys
-if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
-    socket.IPPROTO_IPV6 = 41
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
-DEFAULT_BUFFER_SIZE = -1
-
-_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
-
-import threading
-import time
-import traceback
-def format_exc(limit=None):
-    """Like print_exc() but return a string. Backport for Python 2.3."""
-    try:
-        etype, value, tb = sys.exc_info()
-        return ''.join(traceback.format_exception(etype, value, tb, limit))
-    finally:
-        etype = value = tb = None
-
-
-from urllib import unquote
-from urlparse import urlparse
-import warnings
-
-if sys.version_info >= (3, 0):
-    bytestr = bytes
-    unicodestr = str
-    basestring = (bytes, str)
-    def ntob(n, encoding='ISO-8859-1'):
-        """Return the given native string as a byte string in the given encoding."""
-        # In Python 3, the native string type is unicode
-        return n.encode(encoding)
-else:
-    bytestr = str
-    unicodestr = unicode
-    basestring = basestring
-    def ntob(n, encoding='ISO-8859-1'):
-        """Return the given native string as a byte string in the given encoding."""
-        # In Python 2, the native string type is bytes. Assume it's already
-        # in the given encoding, which for ISO-8859-1 is almost always what
-        # was intended.
-        return n
-
-LF = ntob('\n')
-CRLF = ntob('\r\n')
-TAB = ntob('\t')
-SPACE = ntob(' ')
-COLON = ntob(':')
-SEMICOLON = ntob(';')
-EMPTY = ntob('')
-NUMBER_SIGN = ntob('#')
-QUESTION_MARK = ntob('?')
-ASTERISK = ntob('*')
-FORWARD_SLASH = ntob('/')
-quoted_slash = re.compile(ntob("(?i)%2F"))
-
-import errno
-
-def plat_specific_errors(*errnames):
-    """Return error numbers for all errors in errnames on this platform.
-    
-    The 'errno' module contains different global constants depending on
-    the specific platform (OS). This function will return the list of
-    numeric values for a given list of potential names.
-    """
-    errno_names = dir(errno)
-    nums = [getattr(errno, k) for k in errnames if k in errno_names]
-    # de-dupe the list
-    return list(dict.fromkeys(nums).keys())
-
-socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
-
-socket_errors_to_ignore = plat_specific_errors(
-    "EPIPE",
-    "EBADF", "WSAEBADF",
-    "ENOTSOCK", "WSAENOTSOCK",
-    "ETIMEDOUT", "WSAETIMEDOUT",
-    "ECONNREFUSED", "WSAECONNREFUSED",
-    "ECONNRESET", "WSAECONNRESET",
-    "ECONNABORTED", "WSAECONNABORTED",
-    "ENETRESET", "WSAENETRESET",
-    "EHOSTDOWN", "EHOSTUNREACH",
-    )
-socket_errors_to_ignore.append("timed out")
-socket_errors_to_ignore.append("The read operation timed out")
-
-socket_errors_nonblocking = plat_specific_errors(
-    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
-
-comma_separated_headers = [ntob(h) for h in
-    ['Accept', 'Accept-Charset', 'Accept-Encoding',
-     'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
-     'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
-     'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
-     'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
-     'WWW-Authenticate']]
-
-
-import logging
-if not hasattr(logging, 'statistics'): logging.statistics = {}
-
-
-def read_headers(rfile, hdict=None):
-    """Read headers from the given stream into the given header dict.
-    
-    If hdict is None, a new header dict is created. Returns the populated
-    header dict.
-    
-    Headers which are repeated are folded together using a comma if their
-    specification so dictates.
-    
-    This function raises ValueError when the read bytes violate the HTTP spec.
-    You should probably return "400 Bad Request" if this happens.
-    """
-    if hdict is None:
-        hdict = {}
-    
-    while True:
-        line = rfile.readline()
-        if not line:
-            # No more data--illegal end of headers
-            raise ValueError("Illegal end of headers.")
-        
-        if line == CRLF:
-            # Normal end of headers
-            break
-        if not line.endswith(CRLF):
-            raise ValueError("HTTP requires CRLF terminators")
-        
-        if line[0] in (SPACE, TAB):
-            # It's a continuation line.
-            v = line.strip()
-        else:
-            try:
-                k, v = line.split(COLON, 1)
-            except ValueError:
-                raise ValueError("Illegal header line.")
-            # TODO: what about TE and WWW-Authenticate?
-            k = k.strip().title()
-            v = v.strip()
-            hname = k
-        
-        if k in comma_separated_headers:
-            existing = hdict.get(hname)
-            if existing:
-                v = ", ".join((existing, v))
-        hdict[hname] = v
-    
-    return hdict
-
-
-class MaxSizeExceeded(Exception):
-    pass
-
-class SizeCheckWrapper(object):
-    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-    
-    def __init__(self, rfile, maxlen):
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-    
-    def _check_length(self):
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise MaxSizeExceeded()
-    
-    def read(self, size=None):
-        data = self.rfile.read(size)
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-    
-    def readline(self, size=None):
-        if size is not None:
-            data = self.rfile.readline(size)
-            self.bytes_read += len(data)
-            self._check_length()
-            return data
-        
-        # User didn't specify a size ...
-        # We read the line in chunks to make sure it's not a 100MB line !
-        res = []
-        while True:
-            data = self.rfile.readline(256)
-            self.bytes_read += len(data)
-            self._check_length()
-            res.append(data)
-            # See http://www.cherrypy.org/ticket/421
-            if len(data) < 256 or data[-1:] == "\n":
-                return EMPTY.join(res)
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline()
-        return lines
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        return self
-    
-    def __next__(self):
-        data = next(self.rfile)
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-    
-    def next(self):
-        data = self.rfile.next()
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-
-
-class KnownLengthRFile(object):
-    """Wraps a file-like object, returning an empty string when exhausted."""
-    
-    def __init__(self, rfile, content_length):
-        self.rfile = rfile
-        self.remaining = content_length
-    
-    def read(self, size=None):
-        if self.remaining == 0:
-            return ''
-        if size is None:
-            size = self.remaining
-        else:
-            size = min(size, self.remaining)
-        
-        data = self.rfile.read(size)
-        self.remaining -= len(data)
-        return data
-    
-    def readline(self, size=None):
-        if self.remaining == 0:
-            return ''
-        if size is None:
-            size = self.remaining
-        else:
-            size = min(size, self.remaining)
-        
-        data = self.rfile.readline(size)
-        self.remaining -= len(data)
-        return data
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        return self
-    
-    def __next__(self):
-        data = next(self.rfile)
-        self.remaining -= len(data)
-        return data
-
-
-class ChunkedRFile(object):
-    """Wraps a file-like object, returning an empty string when exhausted.
-    
-    This class is intended to provide a conforming wsgi.input value for
-    request entities that have been encoded with the 'chunked' transfer
-    encoding.
-    """
-    
-    def __init__(self, rfile, maxlen, bufsize=8192):
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-        self.buffer = EMPTY
-        self.bufsize = bufsize
-        self.closed = False
-    
-    def _fetch(self):
-        if self.closed:
-            return
-        
-        line = self.rfile.readline()
-        self.bytes_read += len(line)
-        
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
-        
-        line = line.strip().split(SEMICOLON, 1)
-        
-        try:
-            chunk_size = line.pop(0)
-            chunk_size = int(chunk_size, 16)
-        except ValueError:
-            raise ValueError("Bad chunked transfer size: " + repr(chunk_size))
-        
-        if chunk_size <= 0:
-            self.closed = True
-            return
-        
-##            if line: chunk_extension = line[0]
-        
-        if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
-            raise IOError("Request Entity Too Large")
-        
-        chunk = self.rfile.read(chunk_size)
-        self.bytes_read += len(chunk)
-        self.buffer += chunk
-        
-        crlf = self.rfile.read(2)
-        if crlf != CRLF:
-            raise ValueError(
-                 "Bad chunked transfer coding (expected '\\r\\n', "
-                 "got " + repr(crlf) + ")")
-    
-    def read(self, size=None):
-        data = EMPTY
-        while True:
-            if size and len(data) >= size:
-                return data
-            
-            if not self.buffer:
-                self._fetch()
-                if not self.buffer:
-                    # EOF
-                    return data
-            
-            if size:
-                remaining = size - len(data)
-                data += self.buffer[:remaining]
-                self.buffer = self.buffer[remaining:]
-            else:
-                data += self.buffer
-    
-    def readline(self, size=None):
-        data = EMPTY
-        while True:
-            if size and len(data) >= size:
-                return data
-            
-            if not self.buffer:
-                self._fetch()
-                if not self.buffer:
-                    # EOF
-                    return data
-            
-            newline_pos = self.buffer.find(LF)
-            if size:
-                if newline_pos == -1:
-                    remaining = size - len(data)
-                    data += self.buffer[:remaining]
-                    self.buffer = self.buffer[remaining:]
-                else:
-                    remaining = min(size - len(data), newline_pos)
-                    data += self.buffer[:remaining]
-                    self.buffer = self.buffer[remaining:]
-            else:
-                if newline_pos == -1:
-                    data += self.buffer
-                else:
-                    data += self.buffer[:newline_pos]
-                    self.buffer = self.buffer[newline_pos:]
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-    
-    def read_trailer_lines(self):
-        if not self.closed:
-            raise ValueError(
-                "Cannot read trailers until the request body has been read.")
-        
-        while True:
-            line = self.rfile.readline()
-            if not line:
-                # No more data--illegal end of headers
-                raise ValueError("Illegal end of headers.")
-            
-            self.bytes_read += len(line)
-            if self.maxlen and self.bytes_read > self.maxlen:
-                raise IOError("Request Entity Too Large")
-            
-            if line == CRLF:
-                # Normal end of headers
-                break
-            if not line.endswith(CRLF):
-                raise ValueError("HTTP requires CRLF terminators")
-            
-            yield line
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        # Shamelessly stolen from StringIO
-        total = 0
-        line = self.readline(sizehint)
-        while line:
-            yield line
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-
-
-class HTTPRequest(object):
-    """An HTTP Request (and response).
-    
-    A single HTTP connection may consist of multiple request/response pairs.
-    """
-    
-    server = None
-    """The HTTPServer object which is receiving this request."""
-    
-    conn = None
-    """The HTTPConnection object on which this request connected."""
-    
-    inheaders = {}
-    """A dict of request headers."""
-    
-    outheaders = []
-    """A list of header tuples to write in the response."""
-    
-    ready = False
-    """When True, the request has been parsed and is ready to begin generating
-    the response. When False, signals the calling Connection that the response
-    should not be generated and the connection should close."""
-    
-    close_connection = False
-    """Signals the calling Connection that the request should close. This does
-    not imply an error! The client and/or server may each request that the
-    connection be closed."""
-    
-    chunked_write = False
-    """If True, output will be encoded with the "chunked" transfer-coding.
-    
-    This value is set automatically inside send_headers."""
-    
-    def __init__(self, server, conn):
-        self.server= server
-        self.conn = conn
-        
-        self.ready = False
-        self.started_request = False
-        self.scheme = ntob("http")
-        if self.server.ssl_adapter is not None:
-            self.scheme = ntob("https")
-        # Use the lowest-common protocol in case read_request_line errors.
-        self.response_protocol = 'HTTP/1.0'
-        self.inheaders = {}
-        
-        self.status = ""
-        self.outheaders = []
-        self.sent_headers = False
-        self.close_connection = self.__class__.close_connection
-        self.chunked_read = False
-        self.chunked_write = self.__class__.chunked_write
-    
-    def parse_request(self):
-        """Parse the next HTTP request start-line and message-headers."""
-        self.rfile = SizeCheckWrapper(self.conn.rfile,
-                                      self.server.max_request_header_size)
-        try:
-            success = self.read_request_line()
-        except MaxSizeExceeded:
-            self.simple_response("414 Request-URI Too Long",
-                "The Request-URI sent with the request exceeds the maximum "
-                "allowed bytes.")
-            return
-        else:
-            if not success:
-                return
-        
-        try:
-            success = self.read_request_headers()
-        except MaxSizeExceeded:
-            self.simple_response("413 Request Entity Too Large",
-                "The headers sent with the request exceed the maximum "
-                "allowed bytes.")
-            return
-        else:
-            if not success:
-                return
-        
-        self.ready = True
-    
-    def read_request_line(self):
-        # HTTP/1.1 connections are persistent by default. If a client
-        # requests a page, then idles (leaves the connection open),
-        # then rfile.readline() will raise socket.error("timed out").
-        # Note that it does this based on the value given to settimeout(),
-        # and doesn't need the client to request or acknowledge the close
-        # (although your TCP stack might suffer for it: cf Apache's history
-        # with FIN_WAIT_2).
-        request_line = self.rfile.readline()
-        
-        # Set started_request to True so communicate() knows to send 408
-        # from here on out.
-        self.started_request = True
-        if not request_line:
-            return False
-        
-        if request_line == CRLF:
-            # RFC 2616 sec 4.1: "...if the server is reading the protocol
-            # stream at the beginning of a message and receives a CRLF
-            # first, it should ignore the CRLF."
-            # But only ignore one leading line! else we enable a DoS.
-            request_line = self.rfile.readline()
-            if not request_line:
-                return False
-        
-        if not request_line.endswith(CRLF):
-            self.simple_response("400 Bad Request", "HTTP requires CRLF terminators")
-            return False
-        
-        try:
-            method, uri, req_protocol = request_line.strip().split(SPACE, 2)
-            rp = int(req_protocol[5]), int(req_protocol[7])
-        except (ValueError, IndexError):
-            self.simple_response("400 Bad Request", "Malformed Request-Line")
-            return False
-        
-        self.uri = uri
-        self.method = method
-        
-        # uri may be an abs_path (including "http://host.domain.tld");
-        scheme, authority, path = self.parse_request_uri(uri)
-        if NUMBER_SIGN in path:
-            self.simple_response("400 Bad Request",
-                                 "Illegal #fragment in Request-URI.")
-            return False
-        
-        if scheme:
-            self.scheme = scheme
-        
-        qs = EMPTY
-        if QUESTION_MARK in path:
-            path, qs = path.split(QUESTION_MARK, 1)
-        
-        # Unquote the path+params (e.g. "/this%20path" -> "/this path").
-        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
-        #
-        # But note that "...a URI must be separated into its components
-        # before the escaped characters within those components can be
-        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
-        # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
-        try:
-            atoms = [unquote(x) for x in quoted_slash.split(path)]
-        except ValueError:
-            ex = sys.exc_info()[1]
-            self.simple_response("400 Bad Request", ex.args[0])
-            return False
-        path = "%2F".join(atoms)
-        self.path = path
-        
-        # Note that, like wsgiref and most other HTTP servers,
-        # we "% HEX HEX"-unquote the path but not the query string.
-        self.qs = qs
-        
-        # Compare request and server HTTP protocol versions, in case our
-        # server does not support the requested protocol. Limit our output
-        # to min(req, server). We want the following output:
-        #     request    server     actual written   supported response
-        #     protocol   protocol  response protocol    feature set
-        # a     1.0        1.0           1.0                1.0
-        # b     1.0        1.1           1.1                1.0
-        # c     1.1        1.0           1.0                1.0
-        # d     1.1        1.1           1.1                1.1
-        # Notice that, in (b), the response will be "HTTP/1.1" even though
-        # the client only understands 1.0. RFC 2616 10.5.6 says we should
-        # only return 505 if the _major_ version is different.
-        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
-        
-        if sp[0] != rp[0]:
-            self.simple_response("505 HTTP Version Not Supported")
-            return False
-
-        self.request_protocol = req_protocol
-        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
-
-        return True
-
-    def read_request_headers(self):
-        """Read self.rfile into self.inheaders. Return success."""
-        
-        # then all the http headers
-        try:
-            read_headers(self.rfile, self.inheaders)
-        except ValueError:
-            ex = sys.exc_info()[1]
-            self.simple_response("400 Bad Request", ex.args[0])
-            return False
-        
-        mrbs = self.server.max_request_body_size
-        if mrbs and int(self.inheaders.get("Content-Length", 0)) > mrbs:
-            self.simple_response("413 Request Entity Too Large",
-                "The entity sent with the request exceeds the maximum "
-                "allowed bytes.")
-            return False
-        
-        # Persistent connection support
-        if self.response_protocol == "HTTP/1.1":
-            # Both server and client are HTTP/1.1
-            if self.inheaders.get("Connection", "") == "close":
-                self.close_connection = True
-        else:
-            # Either the server or client (or both) are HTTP/1.0
-            if self.inheaders.get("Connection", "") != "Keep-Alive":
-                self.close_connection = True
-        
-        # Transfer-Encoding support
-        te = None
-        if self.response_protocol == "HTTP/1.1":
-            te = self.inheaders.get("Transfer-Encoding")
-            if te:
-                te = [x.strip().lower() for x in te.split(",") if x.strip()]
-        
-        self.chunked_read = False
-        
-        if te:
-            for enc in te:
-                if enc == "chunked":
-                    self.chunked_read = True
-                else:
-                    # Note that, even if we see "chunked", we must reject
-                    # if there is an extension we don't recognize.
-                    self.simple_response("501 Unimplemented")
-                    self.close_connection = True
-                    return False
-        
-        # From PEP 333:
-        # "Servers and gateways that implement HTTP 1.1 must provide
-        # transparent support for HTTP 1.1's "expect/continue" mechanism.
-        # This may be done in any of several ways:
-        #   1. Respond to requests containing an Expect: 100-continue request
-        #      with an immediate "100 Continue" response, and proceed normally.
-        #   2. Proceed with the request normally, but provide the application
-        #      with a wsgi.input stream that will send the "100 Continue"
-        #      response if/when the application first attempts to read from
-        #      the input stream. The read request must then remain blocked
-        #      until the client responds.
-        #   3. Wait until the client decides that the server does not support
-        #      expect/continue, and sends the request body on its own.
-        #      (This is suboptimal, and is not recommended.)
-        #
-        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
-        # but it seems like it would be a big slowdown for such a rare case.
-        if self.inheaders.get("Expect", "") == "100-continue":
-            # Don't use simple_response here, because it emits headers
-            # we don't want. See http://www.cherrypy.org/ticket/951
-            msg = self.server.protocol + " 100 Continue\r\n\r\n"
-            try:
-                self.conn.wfile.sendall(msg)
-            except socket.error:
-                x = sys.exc_info()[1]
-                if x.args[0] not in socket_errors_to_ignore:
-                    raise
-        return True
-    
-    def parse_request_uri(self, uri):
-        """Parse a Request-URI into (scheme, authority, path).
-        
-        Note that Request-URI's must be one of::
-            
-            Request-URI    = "*" | absoluteURI | abs_path | authority
-        
-        Therefore, a Request-URI which starts with a double forward-slash
-        cannot be a "net_path"::
-        
-            net_path      = "//" authority [ abs_path ]
-        
-        Instead, it must be interpreted as an "abs_path" with an empty first
-        path segment::
-        
-            abs_path      = "/"  path_segments
-            path_segments = segment *( "/" segment )
-            segment       = *pchar *( ";" param )
-            param         = *pchar
-        """
-        if uri == ASTERISK:
-            return None, None, uri
-        
-        i = uri.find('://')
-        if i > 0 and QUESTION_MARK not in uri[:i]:
-            # An absoluteURI.
-            # If there's a scheme (and it must be http or https), then:
-            # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
-            scheme, remainder = uri[:i].lower(), uri[i + 3:]
-            authority, path = remainder.split(FORWARD_SLASH, 1)
-            path = FORWARD_SLASH + path
-            return scheme, authority, path
-        
-        if uri.startswith(FORWARD_SLASH):
-            # An abs_path.
-            return None, None, uri
-        else:
-            # An authority.
-            return None, uri, None
-    
-    def respond(self):
-        """Call the gateway and write its iterable output."""
-        mrbs = self.server.max_request_body_size
-        if self.chunked_read:
-            self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
-        else:
-            cl = int(self.inheaders.get("Content-Length", 0))
-            if mrbs and mrbs < cl:
-                if not self.sent_headers:
-                    self.simple_response("413 Request Entity Too Large",
-                        "The entity sent with the request exceeds the maximum "
-                        "allowed bytes.")
-                return
-            self.rfile = KnownLengthRFile(self.conn.rfile, cl)
-        
-        self.server.gateway(self).respond()
-        
-        if (self.ready and not self.sent_headers):
-            self.sent_headers = True
-            self.send_headers()
-        if self.chunked_write:
-            self.conn.wfile.sendall("0\r\n\r\n")
-    
-    def simple_response(self, status, msg=""):
-        """Write a simple response back to the client."""
-        status = str(status)
-        buf = [self.server.protocol + SPACE +
-               status + CRLF,
-               "Content-Length: %s\r\n" % len(msg),
-               "Content-Type: text/plain\r\n"]
-        
-        if status[:3] in ("413", "414"):
-            # Request Entity Too Large / Request-URI Too Long
-            self.close_connection = True
-            if self.response_protocol == 'HTTP/1.1':
-                # This will not be true for 414, since read_request_line
-                # usually raises 414 before reading the whole line, and we
-                # therefore cannot know the proper response_protocol.
-                buf.append("Connection: close\r\n")
-            else:
-                # HTTP/1.0 had no 413/414 status nor Connection header.
-                # Emit 400 instead and trust the message body is enough.
-                status = "400 Bad Request"
-        
-        buf.append(CRLF)
-        if msg:
-            if isinstance(msg, unicodestr):
-                msg = msg.encode("ISO-8859-1")
-            buf.append(msg)
-        
-        try:
-            self.conn.wfile.sendall("".join(buf))
-        except socket.error:
-            x = sys.exc_info()[1]
-            if x.args[0] not in socket_errors_to_ignore:
-                raise
-    
-    def write(self, chunk):
-        """Write unbuffered data to the client."""
-        if self.chunked_write and chunk:
-            buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
-            self.conn.wfile.sendall(EMPTY.join(buf))
-        else:
-            self.conn.wfile.sendall(chunk)
-    
-    def send_headers(self):
-        """Assert, process, and send the HTTP response message-headers.
-        
-        You must set self.status, and self.outheaders before calling this.
-        """
-        hkeys = [key.lower() for key, value in self.outheaders]
-        status = int(self.status[:3])
-        
-        if status == 413:
-            # Request Entity Too Large. Close conn to avoid garbage.
-            self.close_connection = True
-        elif "content-length" not in hkeys:
-            # "All 1xx (informational), 204 (no content),
-            # and 304 (not modified) responses MUST NOT
-            # include a message-body." So no point chunking.
-            if status < 200 or status in (204, 205, 304):
-                pass
-            else:
-                if (self.response_protocol == 'HTTP/1.1'
-                    and self.method != 'HEAD'):
-                    # Use the chunked transfer-coding
-                    self.chunked_write = True
-                    self.outheaders.append(("Transfer-Encoding", "chunked"))
-                else:
-                    # Closing the conn is the only way to determine len.
-                    self.close_connection = True
-        
-        if "connection" not in hkeys:
-            if self.response_protocol == 'HTTP/1.1':
-                # Both server and client are HTTP/1.1 or better
-                if self.close_connection:
-                    self.outheaders.append(("Connection", "close"))
-            else:
-                # Server and/or client are HTTP/1.0
-                if not self.close_connection:
-                    self.outheaders.append(("Connection", "Keep-Alive"))
-        
-        if (not self.close_connection) and (not self.chunked_read):
-            # Read any remaining request body data on the socket.
-            # "If an origin server receives a request that does not include an
-            # Expect request-header field with the "100-continue" expectation,
-            # the request includes a request body, and the server responds
-            # with a final status code before reading the entire request body
-            # from the transport connection, then the server SHOULD NOT close
-            # the transport connection until it has read the entire request,
-            # or until the client closes the connection. Otherwise, the client
-            # might not reliably receive the response message. However, this
-            # requirement is not be construed as preventing a server from
-            # defending itself against denial-of-service attacks, or from
-            # badly broken client implementations."
-            remaining = getattr(self.rfile, 'remaining', 0)
-            if remaining > 0:
-                self.rfile.read(remaining)
-        
-        if "date" not in hkeys:
-            self.outheaders.append(("Date", rfc822.formatdate()))
-        
-        if "server" not in hkeys:
-            self.outheaders.append(("Server", self.server.server_name))
-        
-        buf = [self.server.protocol + SPACE + self.status + CRLF]
-        for k, v in self.outheaders:
-            buf.append(k + COLON + SPACE + v + CRLF)
-        buf.append(CRLF)
-        self.conn.wfile.sendall(EMPTY.join(buf))
-
-
-class NoSSLError(Exception):
-    """Exception raised when a client speaks HTTP to an HTTPS socket."""
-    pass
-
-
-class FatalSSLAlert(Exception):
-    """Exception raised when the SSL implementation signals a fatal alert."""
-    pass
-
-
-class CP_fileobject(socket._fileobject):
-    """Faux file object attached to a socket object."""
-
-    def __init__(self, *args, **kwargs):
-        self.bytes_read = 0
-        self.bytes_written = 0
-        socket._fileobject.__init__(self, *args, **kwargs)
-    
-    def sendall(self, data):
-        """Sendall for non-blocking sockets."""
-        while data:
-            try:
-                bytes_sent = self.send(data)
-                data = data[bytes_sent:]
-            except socket.error, e:
-                if e.args[0] not in socket_errors_nonblocking:
-                    raise
-
-    def send(self, data):
-        bytes_sent = self._sock.send(data)
-        self.bytes_written += bytes_sent
-        return bytes_sent
-
-    def flush(self):
-        if self._wbuf:
-            buffer = "".join(self._wbuf)
-            self._wbuf = []
-            self.sendall(buffer)
-
-    def recv(self, size):
-        while True:
-            try:
-                data = self._sock.recv(size)
-                self.bytes_read += len(data)
-                return data
-            except socket.error, e:
-                if (e.args[0] not in socket_errors_nonblocking
-                    and e.args[0] not in socket_error_eintr):
-                    raise
-
-    if not _fileobject_uses_str_type:
-        def read(self, size=-1):
-            # Use max, disallow tiny reads in a loop as they are very inefficient.
-            # We never leave read() with any leftover data from a new recv() call
-            # in our internal buffer.
-            rbufsize = max(self._rbufsize, self.default_bufsize)
-            # Our use of StringIO rather than lists of string objects returned by
-            # recv() minimizes memory usage and fragmentation that occurs when
-            # rbufsize is large compared to the typical return value of recv().
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if size < 0:
-                # Read until EOF
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(rbufsize)
-                    if not data:
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    # Already have size bytes in our buffer?  Extract and return.
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    left = size - buf_len
-                    # recv() will malloc the amount of memory given as its
-                    # parameter even though it often returns much less data
-                    # than that.  The returned data string is short lived
-                    # as we copy it into a StringIO and free it.  This avoids
-                    # fragmentation issues on many platforms.
-                    data = self.recv(left)
-                    if not data:
-                        break
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid buffer data copies when:
-                        # - We have no data in our buffer.
-                        # AND
-                        # - Our call to recv returned exactly the
-                        #   number of bytes we were asked to read.
-                        return data
-                    if n == left:
-                        buf.write(data)
-                        del data  # explicit free
-                        break
-                    assert n <= left, "recv(%d) returned %d bytes" % (left, n)
-                    buf.write(data)
-                    buf_len += n
-                    del data  # explicit free
-                    #assert buf_len == buf.tell()
-                return buf.getvalue()
-
-        def readline(self, size=-1):
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if buf.tell() > 0:
-                # check if we already have it in our buffer
-                buf.seek(0)
-                bline = buf.readline(size)
-                if bline.endswith('\n') or len(bline) == size:
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return bline
-                del bline
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    buf.seek(0)
-                    buffers = [buf.read()]
-                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                    data = None
-                    recv = self.recv
-                    while data != "\n":
-                        data = recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return "".join(buffers)
-
-                buf.seek(0, 2)  # seek end
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        buf.write(data[:nl])
-                        self._rbuf.write(data[nl:])
-                        del data
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes first
-                buf.seek(0, 2)  # seek end
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    left = size - buf_len
-                    # did we just receive a newline?
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        # save the excess data to _rbuf
-                        self._rbuf.write(data[nl:])
-                        if buf_len:
-                            buf.write(data[:nl])
-                            break
-                        else:
-                            # Shortcut.  Avoid data copy through buf when returning
-                            # a substring of our first recv().
-                            return data[:nl]
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid data copy through buf when
-                        # returning exactly all of our first recv().
-                        return data
-                    if n >= left:
-                        buf.write(data[:left])
-                        self._rbuf.write(data[left:])
-                        break
-                    buf.write(data)
-                    buf_len += n
-                    #assert buf_len == buf.tell()
-                return buf.getvalue()
-    else:
-        def read(self, size=-1):
-            if size < 0:
-                # Read until EOF
-                buffers = [self._rbuf]
-                self._rbuf = ""
-                if self._rbufsize <= 1:
-                    recv_size = self.default_bufsize
-                else:
-                    recv_size = self._rbufsize
-
-                while True:
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                return "".join(buffers)
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                data = self._rbuf
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    left = size - buf_len
-                    recv_size = max(self._rbufsize, left)
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return "".join(buffers)
-
-        def readline(self, size=-1):
-            data = self._rbuf
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    assert data == ""
-                    buffers = []
-                    while data != "\n":
-                        data = self.recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return "".join(buffers)
-                nl = data.find('\n')
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                return "".join(buffers)
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes first
-                nl = data.find('\n', 0, size)
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    left = size - buf_len
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return "".join(buffers)
-
-
-class HTTPConnection(object):
-    """An HTTP connection (active socket).
-    
-    server: the Server object which received this connection.
-    socket: the raw socket object (usually TCP) for this connection.
-    makefile: a fileobject class for reading from the socket.
-    """
-    
-    remote_addr = None
-    remote_port = None
-    ssl_env = None
-    rbufsize = DEFAULT_BUFFER_SIZE
-    wbufsize = DEFAULT_BUFFER_SIZE
-    RequestHandlerClass = HTTPRequest
-    
-    def __init__(self, server, sock, makefile=CP_fileobject):
-        self.server = server
-        self.socket = sock
-        self.rfile = makefile(sock, "rb", self.rbufsize)
-        self.wfile = makefile(sock, "wb", self.wbufsize)
-        self.requests_seen = 0
-    
-    def communicate(self):
-        """Read each request and respond appropriately."""
-        request_seen = False
-        try:
-            while True:
-                # (re)set req to None so that if something goes wrong in
-                # the RequestHandlerClass constructor, the error doesn't
-                # get written to the previous request.
-                req = None
-                req = self.RequestHandlerClass(self.server, self)
-                
-                # This order of operations should guarantee correct pipelining.
-                req.parse_request()
-                if self.server.stats['Enabled']:
-                    self.requests_seen += 1
-                if not req.ready:
-                    # Something went wrong in the parsing (and the server has
-                    # probably already made a simple_response). Return and
-                    # let the conn close.
-                    return
-                
-                request_seen = True
-                req.respond()
-                if req.close_connection:
-                    return
-        except socket.error:
-            e = sys.exc_info()[1]
-            errnum = e.args[0]
-            # sadly SSL sockets return a different (longer) time out string
-            if errnum == 'timed out' or errnum == 'The read operation timed out':
-                # Don't error if we're between requests; only error
-                # if 1) no request has been started at all, or 2) we're
-                # in the middle of a request.
-                # See http://www.cherrypy.org/ticket/853
-                if (not request_seen) or (req and req.started_request):
-                    # Don't bother writing the 408 if the response
-                    # has already started being written.
-                    if req and not req.sent_headers:
-                        try:
-                            req.simple_response("408 Request Timeout")
-                        except FatalSSLAlert:
-                            # Close the connection.
-                            return
-            elif errnum not in socket_errors_to_ignore:
-                self.server.error_log("socket.error %s" % repr(errnum),
-                                      level=logging.WARNING, traceback=True)
-                if req and not req.sent_headers:
-                    try:
-                        req.simple_response("500 Internal Server Error")
-                    except FatalSSLAlert:
-                        # Close the connection.
-                        return
-            return
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except FatalSSLAlert:
-            # Close the connection.
-            return
-        except NoSSLError:
-            if req and not req.sent_headers:
-                # Unwrap our wfile
-                self.wfile = CP_fileobject(self.socket._sock, "wb", self.wbufsize)
-                req.simple_response("400 Bad Request",
-                    "The client sent a plain HTTP request, but "
-                    "this server only speaks HTTPS on this port.")
-                self.linger = True
-        except Exception:
-            e = sys.exc_info()[1]
-            self.server.error_log(repr(e), level=logging.ERROR, traceback=True)
-            if req and not req.sent_headers:
-                try:
-                    req.simple_response("500 Internal Server Error")
-                except FatalSSLAlert:
-                    # Close the connection.
-                    return
-    
-    linger = False
-    
-    def close(self):
-        """Close the socket underlying this connection."""
-        self.rfile.close()
-        
-        if not self.linger:
-            # Python's socket module does NOT call close on the kernel socket
-            # when you call socket.close(). We do so manually here because we
-            # want this server to send a FIN TCP segment immediately. Note this
-            # must be called *before* calling socket.close(), because the latter
-            # drops its reference to the kernel socket.
-            if hasattr(self.socket, '_sock'):
-                self.socket._sock.close()
-            self.socket.close()
-        else:
-            # On the other hand, sometimes we want to hang around for a bit
-            # to make sure the client has a chance to read our entire
-            # response. Skipping the close() calls here delays the FIN
-            # packet until the socket object is garbage-collected later.
-            # Someday, perhaps, we'll do the full lingering_close that
-            # Apache does, but not today.
-            pass
-
-
-class TrueyZero(object):
-    """An object which equals and does math like the integer '0' but evals True."""
-    def __add__(self, other):
-        return other
-    def __radd__(self, other):
-        return other
-trueyzero = TrueyZero()
-
-
-_SHUTDOWNREQUEST = None
-
-class WorkerThread(threading.Thread):
-    """Thread which continuously polls a Queue for Connection objects.
-    
-    Due to the timing issues of polling a Queue, a WorkerThread does not
-    check its own 'ready' flag after it has started. To stop the thread,
-    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
-    (one for each running WorkerThread).
-    """
-    
-    conn = None
-    """The current connection pulled off the Queue, or None."""
-    
-    server = None
-    """The HTTP Server which spawned this thread, and which owns the
-    Queue and is placing active connections into it."""
-    
-    ready = False
-    """A simple flag for the calling server to know when this thread
-    has begun polling the Queue."""
-    
-    
-    def __init__(self, server):
-        self.ready = False
-        self.server = server
-        
-        self.requests_seen = 0
-        self.bytes_read = 0
-        self.bytes_written = 0
-        self.start_time = None
-        self.work_time = 0
-        self.stats = {
-            'Requests': lambda s: self.requests_seen + ((self.start_time is None) and trueyzero or self.conn.requests_seen),
-            'Bytes Read': lambda s: self.bytes_read + ((self.start_time is None) and trueyzero or self.conn.rfile.bytes_read),
-            'Bytes Written': lambda s: self.bytes_written + ((self.start_time is None) and trueyzero or self.conn.wfile.bytes_written),
-            'Work Time': lambda s: self.work_time + ((self.start_time is None) and trueyzero or time.time() - self.start_time),
-            'Read Throughput': lambda s: s['Bytes Read'](s) / (s['Work Time'](s) or 1e-6),
-            'Write Throughput': lambda s: s['Bytes Written'](s) / (s['Work Time'](s) or 1e-6),
-        }
-        threading.Thread.__init__(self)
-    
-    def run(self):
-        self.server.stats['Worker Threads'][self.getName()] = self.stats
-        try:
-            self.ready = True
-            while True:
-                conn = self.server.requests.get()
-                if conn is _SHUTDOWNREQUEST:
-                    return
-                
-                self.conn = conn
-                if self.server.stats['Enabled']:
-                    self.start_time = time.time()
-                try:
-                    conn.communicate()
-                finally:
-                    conn.close()
-                    if self.server.stats['Enabled']:
-                        self.requests_seen += self.conn.requests_seen
-                        self.bytes_read += self.conn.rfile.bytes_read
-                        self.bytes_written += self.conn.wfile.bytes_written
-                        self.work_time += time.time() - self.start_time
-                        self.start_time = None
-                    self.conn = None
-        except (KeyboardInterrupt, SystemExit):
-            exc = sys.exc_info()[1]
-            self.server.interrupt = exc
-
-
-class ThreadPool(object):
-    """A Request Queue for an HTTPServer which pools threads.
-    
-    ThreadPool objects must provide min, get(), put(obj), start()
-    and stop(timeout) attributes.
-    """
-    
-    def __init__(self, server, min=10, max=-1):
-        self.server = server
-        self.min = min
-        self.max = max
-        self._threads = []
-        self._queue = queue.Queue()
-        self.get = self._queue.get
-    
-    def start(self):
-        """Start the pool of threads."""
-        for i in range(self.min):
-            self._threads.append(WorkerThread(self.server))
-        for worker in self._threads:
-            worker.setName("CP Server " + worker.getName())
-            worker.start()
-        for worker in self._threads:
-            while not worker.ready:
-                time.sleep(.1)
-    
-    def _get_idle(self):
-        """Number of worker threads which are idle. Read-only."""
-        return len([t for t in self._threads if t.conn is None])
-    idle = property(_get_idle, doc=_get_idle.__doc__)
-    
-    def put(self, obj):
-        self._queue.put(obj)
-        if obj is _SHUTDOWNREQUEST:
-            return
-    
-    def grow(self, amount):
-        """Spawn new worker threads (not above self.max)."""
-        for i in range(amount):
-            if self.max > 0 and len(self._threads) >= self.max:
-                break
-            worker = WorkerThread(self.server)
-            worker.setName("CP Server " + worker.getName())
-            self._threads.append(worker)
-            worker.start()
-    
-    def shrink(self, amount):
-        """Kill off worker threads (not below self.min)."""
-        # Grow/shrink the pool if necessary.
-        # Remove any dead threads from our list
-        for t in self._threads:
-            if not t.isAlive():
-                self._threads.remove(t)
-                amount -= 1
-        
-        if amount > 0:
-            for i in range(min(amount, len(self._threads) - self.min)):
-                # Put a number of shutdown requests on the queue equal
-                # to 'amount'. Once each of those is processed by a worker,
-                # that worker will terminate and be culled from our list
-                # in self.put.
-                self._queue.put(_SHUTDOWNREQUEST)
-    
-    def stop(self, timeout=5):
-        # Must shut down threads here so the code that calls
-        # this method can know when all threads are stopped.
-        for worker in self._threads:
-            self._queue.put(_SHUTDOWNREQUEST)
-        
-        # Don't join currentThread (when stop is called inside a request).
-        current = threading.currentThread()
-        if timeout and timeout >= 0:
-            endtime = time.time() + timeout
-        while self._threads:
-            worker = self._threads.pop()
-            if worker is not current and worker.isAlive():
-                try: