OpenSecurity/install/web.py-0.37/web/wsgiserver/__init__.py
changeset 3 65432e6c6042
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/OpenSecurity/install/web.py-0.37/web/wsgiserver/__init__.py	Mon Dec 02 14:02:05 2013 +0100
     1.3 @@ -0,0 +1,2219 @@
     1.4 +"""A high-speed, production ready, thread pooled, generic HTTP server.
     1.5 +
     1.6 +Simplest example on how to use this module directly
     1.7 +(without using CherryPy's application machinery)::
     1.8 +
     1.9 +    from cherrypy import wsgiserver
    1.10 +    
    1.11 +    def my_crazy_app(environ, start_response):
    1.12 +        status = '200 OK'
    1.13 +        response_headers = [('Content-type','text/plain')]
    1.14 +        start_response(status, response_headers)
    1.15 +        return ['Hello world!']
    1.16 +    
    1.17 +    server = wsgiserver.CherryPyWSGIServer(
    1.18 +                ('0.0.0.0', 8070), my_crazy_app,
    1.19 +                server_name='www.cherrypy.example')
    1.20 +    server.start()
    1.21 +    
    1.22 +The CherryPy WSGI server can serve as many WSGI applications 
    1.23 +as you want in one instance by using a WSGIPathInfoDispatcher::
    1.24 +    
    1.25 +    d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
    1.26 +    server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
    1.27 +    
    1.28 +Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
    1.29 +
    1.30 +This won't call the CherryPy engine (application side) at all, only the
    1.31 +HTTP server, which is independent from the rest of CherryPy. Don't
    1.32 +let the name "CherryPyWSGIServer" throw you; the name merely reflects
    1.33 +its origin, not its coupling.
    1.34 +
    1.35 +For those of you wanting to understand internals of this module, here's the
    1.36 +basic call flow. The server's listening thread runs a very tight loop,
    1.37 +sticking incoming connections onto a Queue::
    1.38 +
    1.39 +    server = CherryPyWSGIServer(...)
    1.40 +    server.start()
    1.41 +    while True:
    1.42 +        tick()
    1.43 +        # This blocks until a request comes in:
    1.44 +        child = socket.accept()
    1.45 +        conn = HTTPConnection(child, ...)
    1.46 +        server.requests.put(conn)
    1.47 +
    1.48 +Worker threads are kept in a pool and poll the Queue, popping off and then
    1.49 +handling each connection in turn. Each connection can consist of an arbitrary
    1.50 +number of requests and their responses, so we run a nested loop::
    1.51 +
    1.52 +    while True:
    1.53 +        conn = server.requests.get()
    1.54 +        conn.communicate()
    1.55 +        ->  while True:
    1.56 +                req = HTTPRequest(...)
    1.57 +                req.parse_request()
    1.58 +                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
    1.59 +                    req.rfile.readline()
    1.60 +                    read_headers(req.rfile, req.inheaders)
    1.61 +                req.respond()
    1.62 +                ->  response = app(...)
    1.63 +                    try:
    1.64 +                        for chunk in response:
    1.65 +                            if chunk:
    1.66 +                                req.write(chunk)
    1.67 +                    finally:
    1.68 +                        if hasattr(response, "close"):
    1.69 +                            response.close()
    1.70 +                if req.close_connection:
    1.71 +                    return
    1.72 +"""
    1.73 +
    1.74 +CRLF = '\r\n'
    1.75 +import os
    1.76 +import Queue
    1.77 +import re
    1.78 +quoted_slash = re.compile("(?i)%2F")
    1.79 +import rfc822
    1.80 +import socket
    1.81 +import sys
    1.82 +if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
    1.83 +    socket.IPPROTO_IPV6 = 41
    1.84 +try:
    1.85 +    import cStringIO as StringIO
    1.86 +except ImportError:
    1.87 +    import StringIO
    1.88 +DEFAULT_BUFFER_SIZE = -1
    1.89 +
    1.90 +_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
    1.91 +
    1.92 +import threading
    1.93 +import time
    1.94 +import traceback
    1.95 +def format_exc(limit=None):
    1.96 +    """Like print_exc() but return a string. Backport for Python 2.3."""
    1.97 +    try:
    1.98 +        etype, value, tb = sys.exc_info()
    1.99 +        return ''.join(traceback.format_exception(etype, value, tb, limit))
   1.100 +    finally:
   1.101 +        etype = value = tb = None
   1.102 +
   1.103 +
   1.104 +from urllib import unquote
   1.105 +from urlparse import urlparse
   1.106 +import warnings
   1.107 +
   1.108 +import errno
   1.109 +
   1.110 +def plat_specific_errors(*errnames):
   1.111 +    """Return error numbers for all errors in errnames on this platform.
   1.112 +    
   1.113 +    The 'errno' module contains different global constants depending on
   1.114 +    the specific platform (OS). This function will return the list of
   1.115 +    numeric values for a given list of potential names.
   1.116 +    """
   1.117 +    errno_names = dir(errno)
   1.118 +    nums = [getattr(errno, k) for k in errnames if k in errno_names]
   1.119 +    # de-dupe the list
   1.120 +    return dict.fromkeys(nums).keys()
   1.121 +
   1.122 +socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
   1.123 +
   1.124 +socket_errors_to_ignore = plat_specific_errors(
   1.125 +    "EPIPE",
   1.126 +    "EBADF", "WSAEBADF",
   1.127 +    "ENOTSOCK", "WSAENOTSOCK",
   1.128 +    "ETIMEDOUT", "WSAETIMEDOUT",
   1.129 +    "ECONNREFUSED", "WSAECONNREFUSED",
   1.130 +    "ECONNRESET", "WSAECONNRESET",
   1.131 +    "ECONNABORTED", "WSAECONNABORTED",
   1.132 +    "ENETRESET", "WSAENETRESET",
   1.133 +    "EHOSTDOWN", "EHOSTUNREACH",
   1.134 +    )
   1.135 +socket_errors_to_ignore.append("timed out")
   1.136 +socket_errors_to_ignore.append("The read operation timed out")
   1.137 +
   1.138 +socket_errors_nonblocking = plat_specific_errors(
   1.139 +    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
   1.140 +
   1.141 +comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
   1.142 +    'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
   1.143 +    'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
   1.144 +    'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
   1.145 +    'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
   1.146 +    'WWW-Authenticate']
   1.147 +
   1.148 +
   1.149 +import logging
   1.150 +if not hasattr(logging, 'statistics'): logging.statistics = {}
   1.151 +
   1.152 +
   1.153 +def read_headers(rfile, hdict=None):
   1.154 +    """Read headers from the given stream into the given header dict.
   1.155 +    
   1.156 +    If hdict is None, a new header dict is created. Returns the populated
   1.157 +    header dict.
   1.158 +    
   1.159 +    Headers which are repeated are folded together using a comma if their
   1.160 +    specification so dictates.
   1.161 +    
   1.162 +    This function raises ValueError when the read bytes violate the HTTP spec.
   1.163 +    You should probably return "400 Bad Request" if this happens.
   1.164 +    """
   1.165 +    if hdict is None:
   1.166 +        hdict = {}
   1.167 +    
   1.168 +    while True:
   1.169 +        line = rfile.readline()
   1.170 +        if not line:
   1.171 +            # No more data--illegal end of headers
   1.172 +            raise ValueError("Illegal end of headers.")
   1.173 +        
   1.174 +        if line == CRLF:
   1.175 +            # Normal end of headers
   1.176 +            break
   1.177 +        if not line.endswith(CRLF):
   1.178 +            raise ValueError("HTTP requires CRLF terminators")
   1.179 +        
   1.180 +        if line[0] in ' \t':
   1.181 +            # It's a continuation line.
   1.182 +            v = line.strip()
   1.183 +        else:
   1.184 +            try:
   1.185 +                k, v = line.split(":", 1)
   1.186 +            except ValueError:
   1.187 +                raise ValueError("Illegal header line.")
   1.188 +            # TODO: what about TE and WWW-Authenticate?
   1.189 +            k = k.strip().title()
   1.190 +            v = v.strip()
   1.191 +            hname = k
   1.192 +        
   1.193 +        if k in comma_separated_headers:
   1.194 +            existing = hdict.get(hname)
   1.195 +            if existing:
   1.196 +                v = ", ".join((existing, v))
   1.197 +        hdict[hname] = v
   1.198 +    
   1.199 +    return hdict
   1.200 +
   1.201 +
   1.202 +class MaxSizeExceeded(Exception):
   1.203 +    pass
   1.204 +
   1.205 +class SizeCheckWrapper(object):
   1.206 +    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
   1.207 +    
   1.208 +    def __init__(self, rfile, maxlen):
   1.209 +        self.rfile = rfile
   1.210 +        self.maxlen = maxlen
   1.211 +        self.bytes_read = 0
   1.212 +    
   1.213 +    def _check_length(self):
   1.214 +        if self.maxlen and self.bytes_read > self.maxlen:
   1.215 +            raise MaxSizeExceeded()
   1.216 +    
   1.217 +    def read(self, size=None):
   1.218 +        data = self.rfile.read(size)
   1.219 +        self.bytes_read += len(data)
   1.220 +        self._check_length()
   1.221 +        return data
   1.222 +    
   1.223 +    def readline(self, size=None):
   1.224 +        if size is not None:
   1.225 +            data = self.rfile.readline(size)
   1.226 +            self.bytes_read += len(data)
   1.227 +            self._check_length()
   1.228 +            return data
   1.229 +        
   1.230 +        # User didn't specify a size ...
   1.231 +        # We read the line in chunks to make sure it's not a 100MB line !
   1.232 +        res = []
   1.233 +        while True:
   1.234 +            data = self.rfile.readline(256)
   1.235 +            self.bytes_read += len(data)
   1.236 +            self._check_length()
   1.237 +            res.append(data)
   1.238 +            # See http://www.cherrypy.org/ticket/421
   1.239 +            if len(data) < 256 or data[-1:] == "\n":
   1.240 +                return ''.join(res)
   1.241 +    
   1.242 +    def readlines(self, sizehint=0):
   1.243 +        # Shamelessly stolen from StringIO
   1.244 +        total = 0
   1.245 +        lines = []
   1.246 +        line = self.readline()
   1.247 +        while line:
   1.248 +            lines.append(line)
   1.249 +            total += len(line)
   1.250 +            if 0 < sizehint <= total:
   1.251 +                break
   1.252 +            line = self.readline()
   1.253 +        return lines
   1.254 +    
   1.255 +    def close(self):
   1.256 +        self.rfile.close()
   1.257 +    
   1.258 +    def __iter__(self):
   1.259 +        return self
   1.260 +    
   1.261 +    def next(self):
   1.262 +        data = self.rfile.next()
   1.263 +        self.bytes_read += len(data)
   1.264 +        self._check_length()
   1.265 +        return data
   1.266 +
   1.267 +
   1.268 +class KnownLengthRFile(object):
   1.269 +    """Wraps a file-like object, returning an empty string when exhausted."""
   1.270 +    
   1.271 +    def __init__(self, rfile, content_length):
   1.272 +        self.rfile = rfile
   1.273 +        self.remaining = content_length
   1.274 +    
   1.275 +    def read(self, size=None):
   1.276 +        if self.remaining == 0:
   1.277 +            return ''
   1.278 +        if size is None:
   1.279 +            size = self.remaining
   1.280 +        else:
   1.281 +            size = min(size, self.remaining)
   1.282 +        
   1.283 +        data = self.rfile.read(size)
   1.284 +        self.remaining -= len(data)
   1.285 +        return data
   1.286 +    
   1.287 +    def readline(self, size=None):
   1.288 +        if self.remaining == 0:
   1.289 +            return ''
   1.290 +        if size is None:
   1.291 +            size = self.remaining
   1.292 +        else:
   1.293 +            size = min(size, self.remaining)
   1.294 +        
   1.295 +        data = self.rfile.readline(size)
   1.296 +        self.remaining -= len(data)
   1.297 +        return data
   1.298 +    
   1.299 +    def readlines(self, sizehint=0):
   1.300 +        # Shamelessly stolen from StringIO
   1.301 +        total = 0
   1.302 +        lines = []
   1.303 +        line = self.readline(sizehint)
   1.304 +        while line:
   1.305 +            lines.append(line)
   1.306 +            total += len(line)
   1.307 +            if 0 < sizehint <= total:
   1.308 +                break
   1.309 +            line = self.readline(sizehint)
   1.310 +        return lines
   1.311 +    
   1.312 +    def close(self):
   1.313 +        self.rfile.close()
   1.314 +    
   1.315 +    def __iter__(self):
   1.316 +        return self
   1.317 +    
   1.318 +    def __next__(self):
   1.319 +        data = next(self.rfile)
   1.320 +        self.remaining -= len(data)
   1.321 +        return data
   1.322 +
   1.323 +
   1.324 +class ChunkedRFile(object):
   1.325 +    """Wraps a file-like object, returning an empty string when exhausted.
   1.326 +    
   1.327 +    This class is intended to provide a conforming wsgi.input value for
   1.328 +    request entities that have been encoded with the 'chunked' transfer
   1.329 +    encoding.
   1.330 +    """
   1.331 +    
   1.332 +    def __init__(self, rfile, maxlen, bufsize=8192):
   1.333 +        self.rfile = rfile
   1.334 +        self.maxlen = maxlen
   1.335 +        self.bytes_read = 0
   1.336 +        self.buffer = ''
   1.337 +        self.bufsize = bufsize
   1.338 +        self.closed = False
   1.339 +    
   1.340 +    def _fetch(self):
   1.341 +        if self.closed:
   1.342 +            return
   1.343 +        
   1.344 +        line = self.rfile.readline()
   1.345 +        self.bytes_read += len(line)
   1.346 +        
   1.347 +        if self.maxlen and self.bytes_read > self.maxlen:
   1.348 +            raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
   1.349 +        
   1.350 +        line = line.strip().split(";", 1)
   1.351 +        
   1.352 +        try:
   1.353 +            chunk_size = line.pop(0)
   1.354 +            chunk_size = int(chunk_size, 16)
   1.355 +        except ValueError:
   1.356 +            raise ValueError("Bad chunked transfer size: " + repr(chunk_size))
   1.357 +        
   1.358 +        if chunk_size <= 0:
   1.359 +            self.closed = True
   1.360 +            return
   1.361 +        
   1.362 +##            if line: chunk_extension = line[0]
   1.363 +        
   1.364 +        if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
   1.365 +            raise IOError("Request Entity Too Large")
   1.366 +        
   1.367 +        chunk = self.rfile.read(chunk_size)
   1.368 +        self.bytes_read += len(chunk)
   1.369 +        self.buffer += chunk
   1.370 +        
   1.371 +        crlf = self.rfile.read(2)
   1.372 +        if crlf != CRLF:
   1.373 +            raise ValueError(
   1.374 +                 "Bad chunked transfer coding (expected '\\r\\n', "
   1.375 +                 "got " + repr(crlf) + ")")
   1.376 +    
   1.377 +    def read(self, size=None):
   1.378 +        data = ''
   1.379 +        while True:
   1.380 +            if size and len(data) >= size:
   1.381 +                return data
   1.382 +            
   1.383 +            if not self.buffer:
   1.384 +                self._fetch()
   1.385 +                if not self.buffer:
   1.386 +                    # EOF
   1.387 +                    return data
   1.388 +            
   1.389 +            if size:
   1.390 +                remaining = size - len(data)
   1.391 +                data += self.buffer[:remaining]
   1.392 +                self.buffer = self.buffer[remaining:]
   1.393 +            else:
   1.394 +                data += self.buffer
   1.395 +    
   1.396 +    def readline(self, size=None):
   1.397 +        data = ''
   1.398 +        while True:
   1.399 +            if size and len(data) >= size:
   1.400 +                return data
   1.401 +            
   1.402 +            if not self.buffer:
   1.403 +                self._fetch()
   1.404 +                if not self.buffer:
   1.405 +                    # EOF
   1.406 +                    return data
   1.407 +            
   1.408 +            newline_pos = self.buffer.find('\n')
   1.409 +            if size:
   1.410 +                if newline_pos == -1:
   1.411 +                    remaining = size - len(data)
   1.412 +                    data += self.buffer[:remaining]
   1.413 +                    self.buffer = self.buffer[remaining:]
   1.414 +                else:
   1.415 +                    remaining = min(size - len(data), newline_pos)
   1.416 +                    data += self.buffer[:remaining]
   1.417 +                    self.buffer = self.buffer[remaining:]
   1.418 +            else:
   1.419 +                if newline_pos == -1:
   1.420 +                    data += self.buffer
   1.421 +                else:
   1.422 +                    data += self.buffer[:newline_pos]
   1.423 +                    self.buffer = self.buffer[newline_pos:]
   1.424 +    
   1.425 +    def readlines(self, sizehint=0):
   1.426 +        # Shamelessly stolen from StringIO
   1.427 +        total = 0
   1.428 +        lines = []
   1.429 +        line = self.readline(sizehint)
   1.430 +        while line:
   1.431 +            lines.append(line)
   1.432 +            total += len(line)
   1.433 +            if 0 < sizehint <= total:
   1.434 +                break
   1.435 +            line = self.readline(sizehint)
   1.436 +        return lines
   1.437 +    
   1.438 +    def read_trailer_lines(self):
   1.439 +        if not self.closed:
   1.440 +            raise ValueError(
   1.441 +                "Cannot read trailers until the request body has been read.")
   1.442 +        
   1.443 +        while True:
   1.444 +            line = self.rfile.readline()
   1.445 +            if not line:
   1.446 +                # No more data--illegal end of headers
   1.447 +                raise ValueError("Illegal end of headers.")
   1.448 +            
   1.449 +            self.bytes_read += len(line)
   1.450 +            if self.maxlen and self.bytes_read > self.maxlen:
   1.451 +                raise IOError("Request Entity Too Large")
   1.452 +            
   1.453 +            if line == CRLF:
   1.454 +                # Normal end of headers
   1.455 +                break
   1.456 +            if not line.endswith(CRLF):
   1.457 +                raise ValueError("HTTP requires CRLF terminators")
   1.458 +            
   1.459 +            yield line
   1.460 +    
   1.461 +    def close(self):
   1.462 +        self.rfile.close()
   1.463 +    
   1.464 +    def __iter__(self):
   1.465 +        # Shamelessly stolen from StringIO
   1.466 +        total = 0
   1.467 +        line = self.readline(sizehint)
   1.468 +        while line:
   1.469 +            yield line
   1.470 +            total += len(line)
   1.471 +            if 0 < sizehint <= total:
   1.472 +                break
   1.473 +            line = self.readline(sizehint)
   1.474 +
   1.475 +
   1.476 +class HTTPRequest(object):
   1.477 +    """An HTTP Request (and response).
   1.478 +    
   1.479 +    A single HTTP connection may consist of multiple request/response pairs.
   1.480 +    """
   1.481 +    
   1.482 +    server = None
   1.483 +    """The HTTPServer object which is receiving this request."""
   1.484 +    
   1.485 +    conn = None
   1.486 +    """The HTTPConnection object on which this request connected."""
   1.487 +    
   1.488 +    inheaders = {}
   1.489 +    """A dict of request headers."""
   1.490 +    
   1.491 +    outheaders = []
   1.492 +    """A list of header tuples to write in the response."""
   1.493 +    
   1.494 +    ready = False
   1.495 +    """When True, the request has been parsed and is ready to begin generating
   1.496 +    the response. When False, signals the calling Connection that the response
   1.497 +    should not be generated and the connection should close."""
   1.498 +    
   1.499 +    close_connection = False
   1.500 +    """Signals the calling Connection that the request should close. This does
   1.501 +    not imply an error! The client and/or server may each request that the
   1.502 +    connection be closed."""
   1.503 +    
   1.504 +    chunked_write = False
   1.505 +    """If True, output will be encoded with the "chunked" transfer-coding.
   1.506 +    
   1.507 +    This value is set automatically inside send_headers."""
   1.508 +    
   1.509 +    def __init__(self, server, conn):
   1.510 +        self.server= server
   1.511 +        self.conn = conn
   1.512 +        
   1.513 +        self.ready = False
   1.514 +        self.started_request = False
   1.515 +        self.scheme = "http"
   1.516 +        if self.server.ssl_adapter is not None:
   1.517 +            self.scheme = "https"
   1.518 +        # Use the lowest-common protocol in case read_request_line errors.
   1.519 +        self.response_protocol = 'HTTP/1.0'
   1.520 +        self.inheaders = {}
   1.521 +        
   1.522 +        self.status = ""
   1.523 +        self.outheaders = []
   1.524 +        self.sent_headers = False
   1.525 +        self.close_connection = self.__class__.close_connection
   1.526 +        self.chunked_read = False
   1.527 +        self.chunked_write = self.__class__.chunked_write
   1.528 +    
   1.529 +    def parse_request(self):
   1.530 +        """Parse the next HTTP request start-line and message-headers."""
   1.531 +        self.rfile = SizeCheckWrapper(self.conn.rfile,
   1.532 +                                      self.server.max_request_header_size)
   1.533 +        try:
   1.534 +            self.read_request_line()
   1.535 +        except MaxSizeExceeded:
   1.536 +            self.simple_response("414 Request-URI Too Long",
   1.537 +                "The Request-URI sent with the request exceeds the maximum "
   1.538 +                "allowed bytes.")
   1.539 +            return
   1.540 +        
   1.541 +        try:
   1.542 +            success = self.read_request_headers()
   1.543 +        except MaxSizeExceeded:
   1.544 +            self.simple_response("413 Request Entity Too Large",
   1.545 +                "The headers sent with the request exceed the maximum "
   1.546 +                "allowed bytes.")
   1.547 +            return
   1.548 +        else:
   1.549 +            if not success:
   1.550 +                return
   1.551 +        
   1.552 +        self.ready = True
   1.553 +    
   1.554 +    def read_request_line(self):
   1.555 +        # HTTP/1.1 connections are persistent by default. If a client
   1.556 +        # requests a page, then idles (leaves the connection open),
   1.557 +        # then rfile.readline() will raise socket.error("timed out").
   1.558 +        # Note that it does this based on the value given to settimeout(),
   1.559 +        # and doesn't need the client to request or acknowledge the close
   1.560 +        # (although your TCP stack might suffer for it: cf Apache's history
   1.561 +        # with FIN_WAIT_2).
   1.562 +        request_line = self.rfile.readline()
   1.563 +        
   1.564 +        # Set started_request to True so communicate() knows to send 408
   1.565 +        # from here on out.
   1.566 +        self.started_request = True
   1.567 +        if not request_line:
   1.568 +            # Force self.ready = False so the connection will close.
   1.569 +            self.ready = False
   1.570 +            return
   1.571 +        
   1.572 +        if request_line == CRLF:
   1.573 +            # RFC 2616 sec 4.1: "...if the server is reading the protocol
   1.574 +            # stream at the beginning of a message and receives a CRLF
   1.575 +            # first, it should ignore the CRLF."
   1.576 +            # But only ignore one leading line! else we enable a DoS.
   1.577 +            request_line = self.rfile.readline()
   1.578 +            if not request_line:
   1.579 +                self.ready = False
   1.580 +                return
   1.581 +        
   1.582 +        if not request_line.endswith(CRLF):
   1.583 +            self.simple_response("400 Bad Request", "HTTP requires CRLF terminators")
   1.584 +            return
   1.585 +        
   1.586 +        try:
   1.587 +            method, uri, req_protocol = request_line.strip().split(" ", 2)
   1.588 +            rp = int(req_protocol[5]), int(req_protocol[7])
   1.589 +        except (ValueError, IndexError):
   1.590 +            self.simple_response("400 Bad Request", "Malformed Request-Line")
   1.591 +            return
   1.592 +        
   1.593 +        self.uri = uri
   1.594 +        self.method = method
   1.595 +        
   1.596 +        # uri may be an abs_path (including "http://host.domain.tld");
   1.597 +        scheme, authority, path = self.parse_request_uri(uri)
   1.598 +        if '#' in path:
   1.599 +            self.simple_response("400 Bad Request",
   1.600 +                                 "Illegal #fragment in Request-URI.")
   1.601 +            return
   1.602 +        
   1.603 +        if scheme:
   1.604 +            self.scheme = scheme
   1.605 +        
   1.606 +        qs = ''
   1.607 +        if '?' in path:
   1.608 +            path, qs = path.split('?', 1)
   1.609 +        
   1.610 +        # Unquote the path+params (e.g. "/this%20path" -> "/this path").
   1.611 +        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
   1.612 +        #
   1.613 +        # But note that "...a URI must be separated into its components
   1.614 +        # before the escaped characters within those components can be
   1.615 +        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
   1.616 +        # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
   1.617 +        try:
   1.618 +            atoms = [unquote(x) for x in quoted_slash.split(path)]
   1.619 +        except ValueError, ex:
   1.620 +            self.simple_response("400 Bad Request", ex.args[0])
   1.621 +            return
   1.622 +        path = "%2F".join(atoms)
   1.623 +        self.path = path
   1.624 +        
   1.625 +        # Note that, like wsgiref and most other HTTP servers,
   1.626 +        # we "% HEX HEX"-unquote the path but not the query string.
   1.627 +        self.qs = qs
   1.628 +        
   1.629 +        # Compare request and server HTTP protocol versions, in case our
   1.630 +        # server does not support the requested protocol. Limit our output
   1.631 +        # to min(req, server). We want the following output:
   1.632 +        #     request    server     actual written   supported response
   1.633 +        #     protocol   protocol  response protocol    feature set
   1.634 +        # a     1.0        1.0           1.0                1.0
   1.635 +        # b     1.0        1.1           1.1                1.0
   1.636 +        # c     1.1        1.0           1.0                1.0
   1.637 +        # d     1.1        1.1           1.1                1.1
   1.638 +        # Notice that, in (b), the response will be "HTTP/1.1" even though
   1.639 +        # the client only understands 1.0. RFC 2616 10.5.6 says we should
   1.640 +        # only return 505 if the _major_ version is different.
   1.641 +        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
   1.642 +        
   1.643 +        if sp[0] != rp[0]:
   1.644 +            self.simple_response("505 HTTP Version Not Supported")
   1.645 +            return
   1.646 +        self.request_protocol = req_protocol
   1.647 +        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
   1.648 +    
   1.649 +    def read_request_headers(self):
   1.650 +        """Read self.rfile into self.inheaders. Return success."""
   1.651 +        
   1.652 +        # then all the http headers
   1.653 +        try:
   1.654 +            read_headers(self.rfile, self.inheaders)
   1.655 +        except ValueError, ex:
   1.656 +            self.simple_response("400 Bad Request", ex.args[0])
   1.657 +            return False
   1.658 +        
   1.659 +        mrbs = self.server.max_request_body_size
   1.660 +        if mrbs and int(self.inheaders.get("Content-Length", 0)) > mrbs:
   1.661 +            self.simple_response("413 Request Entity Too Large",
   1.662 +                "The entity sent with the request exceeds the maximum "
   1.663 +                "allowed bytes.")
   1.664 +            return False
   1.665 +        
   1.666 +        # Persistent connection support
   1.667 +        if self.response_protocol == "HTTP/1.1":
   1.668 +            # Both server and client are HTTP/1.1
   1.669 +            if self.inheaders.get("Connection", "") == "close":
   1.670 +                self.close_connection = True
   1.671 +        else:
   1.672 +            # Either the server or client (or both) are HTTP/1.0
   1.673 +            if self.inheaders.get("Connection", "") != "Keep-Alive":
   1.674 +                self.close_connection = True
   1.675 +        
   1.676 +        # Transfer-Encoding support
   1.677 +        te = None
   1.678 +        if self.response_protocol == "HTTP/1.1":
   1.679 +            te = self.inheaders.get("Transfer-Encoding")
   1.680 +            if te:
   1.681 +                te = [x.strip().lower() for x in te.split(",") if x.strip()]
   1.682 +        
   1.683 +        self.chunked_read = False
   1.684 +        
   1.685 +        if te:
   1.686 +            for enc in te:
   1.687 +                if enc == "chunked":
   1.688 +                    self.chunked_read = True
   1.689 +                else:
   1.690 +                    # Note that, even if we see "chunked", we must reject
   1.691 +                    # if there is an extension we don't recognize.
   1.692 +                    self.simple_response("501 Unimplemented")
   1.693 +                    self.close_connection = True
   1.694 +                    return False
   1.695 +        
   1.696 +        # From PEP 333:
   1.697 +        # "Servers and gateways that implement HTTP 1.1 must provide
   1.698 +        # transparent support for HTTP 1.1's "expect/continue" mechanism.
   1.699 +        # This may be done in any of several ways:
   1.700 +        #   1. Respond to requests containing an Expect: 100-continue request
   1.701 +        #      with an immediate "100 Continue" response, and proceed normally.
   1.702 +        #   2. Proceed with the request normally, but provide the application
   1.703 +        #      with a wsgi.input stream that will send the "100 Continue"
   1.704 +        #      response if/when the application first attempts to read from
   1.705 +        #      the input stream. The read request must then remain blocked
   1.706 +        #      until the client responds.
   1.707 +        #   3. Wait until the client decides that the server does not support
   1.708 +        #      expect/continue, and sends the request body on its own.
   1.709 +        #      (This is suboptimal, and is not recommended.)
   1.710 +        #
   1.711 +        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
   1.712 +        # but it seems like it would be a big slowdown for such a rare case.
   1.713 +        if self.inheaders.get("Expect", "") == "100-continue":
   1.714 +            # Don't use simple_response here, because it emits headers
   1.715 +            # we don't want. See http://www.cherrypy.org/ticket/951
   1.716 +            msg = self.server.protocol + " 100 Continue\r\n\r\n"
   1.717 +            try:
   1.718 +                self.conn.wfile.sendall(msg)
   1.719 +            except socket.error, x:
   1.720 +                if x.args[0] not in socket_errors_to_ignore:
   1.721 +                    raise
   1.722 +        return True
   1.723 +    
   1.724 +    def parse_request_uri(self, uri):
   1.725 +        """Parse a Request-URI into (scheme, authority, path).
   1.726 +        
   1.727 +        Note that Request-URI's must be one of::
   1.728 +            
   1.729 +            Request-URI    = "*" | absoluteURI | abs_path | authority
   1.730 +        
   1.731 +        Therefore, a Request-URI which starts with a double forward-slash
   1.732 +        cannot be a "net_path"::
   1.733 +        
   1.734 +            net_path      = "//" authority [ abs_path ]
   1.735 +        
   1.736 +        Instead, it must be interpreted as an "abs_path" with an empty first
   1.737 +        path segment::
   1.738 +        
   1.739 +            abs_path      = "/"  path_segments
   1.740 +            path_segments = segment *( "/" segment )
   1.741 +            segment       = *pchar *( ";" param )
   1.742 +            param         = *pchar
   1.743 +        """
   1.744 +        if uri == "*":
   1.745 +            return None, None, uri
   1.746 +        
   1.747 +        i = uri.find('://')
   1.748 +        if i > 0 and '?' not in uri[:i]:
   1.749 +            # An absoluteURI.
   1.750 +            # If there's a scheme (and it must be http or https), then:
   1.751 +            # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
   1.752 +            scheme, remainder = uri[:i].lower(), uri[i + 3:]
   1.753 +            authority, path = remainder.split("/", 1)
   1.754 +            return scheme, authority, path
   1.755 +        
   1.756 +        if uri.startswith('/'):
   1.757 +            # An abs_path.
   1.758 +            return None, None, uri
   1.759 +        else:
   1.760 +            # An authority.
   1.761 +            return None, uri, None
   1.762 +    
   1.763 +    def respond(self):
   1.764 +        """Call the gateway and write its iterable output."""
   1.765 +        mrbs = self.server.max_request_body_size
   1.766 +        if self.chunked_read:
   1.767 +            self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
   1.768 +        else:
   1.769 +            cl = int(self.inheaders.get("Content-Length", 0))
   1.770 +            if mrbs and mrbs < cl:
   1.771 +                if not self.sent_headers:
   1.772 +                    self.simple_response("413 Request Entity Too Large",
   1.773 +                        "The entity sent with the request exceeds the maximum "
   1.774 +                        "allowed bytes.")
   1.775 +                return
   1.776 +            self.rfile = KnownLengthRFile(self.conn.rfile, cl)
   1.777 +        
   1.778 +        self.server.gateway(self).respond()
   1.779 +        
   1.780 +        if (self.ready and not self.sent_headers):
   1.781 +            self.sent_headers = True
   1.782 +            self.send_headers()
   1.783 +        if self.chunked_write:
   1.784 +            self.conn.wfile.sendall("0\r\n\r\n")
   1.785 +    
   1.786 +    def simple_response(self, status, msg=""):
   1.787 +        """Write a simple response back to the client."""
   1.788 +        status = str(status)
   1.789 +        buf = [self.server.protocol + " " +
   1.790 +               status + CRLF,
   1.791 +               "Content-Length: %s\r\n" % len(msg),
   1.792 +               "Content-Type: text/plain\r\n"]
   1.793 +        
   1.794 +        if status[:3] in ("413", "414"):
   1.795 +            # Request Entity Too Large / Request-URI Too Long
   1.796 +            self.close_connection = True
   1.797 +            if self.response_protocol == 'HTTP/1.1':
   1.798 +                # This will not be true for 414, since read_request_line
   1.799 +                # usually raises 414 before reading the whole line, and we
   1.800 +                # therefore cannot know the proper response_protocol.
   1.801 +                buf.append("Connection: close\r\n")
   1.802 +            else:
   1.803 +                # HTTP/1.0 had no 413/414 status nor Connection header.
   1.804 +                # Emit 400 instead and trust the message body is enough.
   1.805 +                status = "400 Bad Request"
   1.806 +        
   1.807 +        buf.append(CRLF)
   1.808 +        if msg:
   1.809 +            if isinstance(msg, unicode):
   1.810 +                msg = msg.encode("ISO-8859-1")
   1.811 +            buf.append(msg)
   1.812 +        
   1.813 +        try:
   1.814 +            self.conn.wfile.sendall("".join(buf))
   1.815 +        except socket.error, x:
   1.816 +            if x.args[0] not in socket_errors_to_ignore:
   1.817 +                raise
   1.818 +    
   1.819 +    def write(self, chunk):
   1.820 +        """Write unbuffered data to the client."""
   1.821 +        if self.chunked_write and chunk:
   1.822 +            buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
   1.823 +            self.conn.wfile.sendall("".join(buf))
   1.824 +        else:
   1.825 +            self.conn.wfile.sendall(chunk)
   1.826 +    
   1.827 +    def send_headers(self):
   1.828 +        """Assert, process, and send the HTTP response message-headers.
   1.829 +        
   1.830 +        You must set self.status, and self.outheaders before calling this.
   1.831 +        """
   1.832 +        hkeys = [key.lower() for key, value in self.outheaders]
   1.833 +        status = int(self.status[:3])
   1.834 +        
   1.835 +        if status == 413:
   1.836 +            # Request Entity Too Large. Close conn to avoid garbage.
   1.837 +            self.close_connection = True
   1.838 +        elif "content-length" not in hkeys:
   1.839 +            # "All 1xx (informational), 204 (no content),
   1.840 +            # and 304 (not modified) responses MUST NOT
   1.841 +            # include a message-body." So no point chunking.
   1.842 +            if status < 200 or status in (204, 205, 304):
   1.843 +                pass
   1.844 +            else:
   1.845 +                if (self.response_protocol == 'HTTP/1.1'
   1.846 +                    and self.method != 'HEAD'):
   1.847 +                    # Use the chunked transfer-coding
   1.848 +                    self.chunked_write = True
   1.849 +                    self.outheaders.append(("Transfer-Encoding", "chunked"))
   1.850 +                else:
   1.851 +                    # Closing the conn is the only way to determine len.
   1.852 +                    self.close_connection = True
   1.853 +        
   1.854 +        if "connection" not in hkeys:
   1.855 +            if self.response_protocol == 'HTTP/1.1':
   1.856 +                # Both server and client are HTTP/1.1 or better
   1.857 +                if self.close_connection:
   1.858 +                    self.outheaders.append(("Connection", "close"))
   1.859 +            else:
   1.860 +                # Server and/or client are HTTP/1.0
   1.861 +                if not self.close_connection:
   1.862 +                    self.outheaders.append(("Connection", "Keep-Alive"))
   1.863 +        
   1.864 +        if (not self.close_connection) and (not self.chunked_read):
   1.865 +            # Read any remaining request body data on the socket.
   1.866 +            # "If an origin server receives a request that does not include an
   1.867 +            # Expect request-header field with the "100-continue" expectation,
   1.868 +            # the request includes a request body, and the server responds
   1.869 +            # with a final status code before reading the entire request body
   1.870 +            # from the transport connection, then the server SHOULD NOT close
   1.871 +            # the transport connection until it has read the entire request,
   1.872 +            # or until the client closes the connection. Otherwise, the client
   1.873 +            # might not reliably receive the response message. However, this
   1.874 +            # requirement is not be construed as preventing a server from
   1.875 +            # defending itself against denial-of-service attacks, or from
   1.876 +            # badly broken client implementations."
   1.877 +            remaining = getattr(self.rfile, 'remaining', 0)
   1.878 +            if remaining > 0:
   1.879 +                self.rfile.read(remaining)
   1.880 +        
   1.881 +        if "date" not in hkeys:
   1.882 +            self.outheaders.append(("Date", rfc822.formatdate()))
   1.883 +        
   1.884 +        if "server" not in hkeys:
   1.885 +            self.outheaders.append(("Server", self.server.server_name))
   1.886 +        
   1.887 +        buf = [self.server.protocol + " " + self.status + CRLF]
   1.888 +        for k, v in self.outheaders:
   1.889 +            buf.append(k + ": " + v + CRLF)
   1.890 +        buf.append(CRLF)
   1.891 +        self.conn.wfile.sendall("".join(buf))
   1.892 +
   1.893 +
   1.894 +class NoSSLError(Exception):
   1.895 +    """Exception raised when a client speaks HTTP to an HTTPS socket."""
   1.896 +    pass
   1.897 +
   1.898 +
   1.899 +class FatalSSLAlert(Exception):
   1.900 +    """Exception raised when the SSL implementation signals a fatal alert."""
   1.901 +    pass
   1.902 +
   1.903 +
   1.904 +class CP_fileobject(socket._fileobject):
   1.905 +    """Faux file object attached to a socket object."""
   1.906 +
   1.907 +    def __init__(self, *args, **kwargs):
   1.908 +        self.bytes_read = 0
   1.909 +        self.bytes_written = 0
   1.910 +        socket._fileobject.__init__(self, *args, **kwargs)
   1.911 +    
   1.912 +    def sendall(self, data):
   1.913 +        """Sendall for non-blocking sockets."""
   1.914 +        while data:
   1.915 +            try:
   1.916 +                bytes_sent = self.send(data)
   1.917 +                data = data[bytes_sent:]
   1.918 +            except socket.error, e:
   1.919 +                if e.args[0] not in socket_errors_nonblocking:
   1.920 +                    raise
   1.921 +
   1.922 +    def send(self, data):
   1.923 +        bytes_sent = self._sock.send(data)
   1.924 +        self.bytes_written += bytes_sent
   1.925 +        return bytes_sent
   1.926 +
   1.927 +    def flush(self):
   1.928 +        if self._wbuf:
   1.929 +            buffer = "".join(self._wbuf)
   1.930 +            self._wbuf = []
   1.931 +            self.sendall(buffer)
   1.932 +
   1.933 +    def recv(self, size):
   1.934 +        while True:
   1.935 +            try:
   1.936 +                data = self._sock.recv(size)
   1.937 +                self.bytes_read += len(data)
   1.938 +                return data
   1.939 +            except socket.error, e:
   1.940 +                if (e.args[0] not in socket_errors_nonblocking
   1.941 +                    and e.args[0] not in socket_error_eintr):
   1.942 +                    raise
   1.943 +
   1.944 +    if not _fileobject_uses_str_type:
   1.945 +        def read(self, size=-1):
   1.946 +            # Use max, disallow tiny reads in a loop as they are very inefficient.
   1.947 +            # We never leave read() with any leftover data from a new recv() call
   1.948 +            # in our internal buffer.
   1.949 +            rbufsize = max(self._rbufsize, self.default_bufsize)
   1.950 +            # Our use of StringIO rather than lists of string objects returned by
   1.951 +            # recv() minimizes memory usage and fragmentation that occurs when
   1.952 +            # rbufsize is large compared to the typical return value of recv().
   1.953 +            buf = self._rbuf
   1.954 +            buf.seek(0, 2)  # seek end
   1.955 +            if size < 0:
   1.956 +                # Read until EOF
   1.957 +                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
   1.958 +                while True:
   1.959 +                    data = self.recv(rbufsize)
   1.960 +                    if not data:
   1.961 +                        break
   1.962 +                    buf.write(data)
   1.963 +                return buf.getvalue()
   1.964 +            else:
   1.965 +                # Read until size bytes or EOF seen, whichever comes first
   1.966 +                buf_len = buf.tell()
   1.967 +                if buf_len >= size:
   1.968 +                    # Already have size bytes in our buffer?  Extract and return.
   1.969 +                    buf.seek(0)
   1.970 +                    rv = buf.read(size)
   1.971 +                    self._rbuf = StringIO.StringIO()
   1.972 +                    self._rbuf.write(buf.read())
   1.973 +                    return rv
   1.974 +
   1.975 +                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
   1.976 +                while True:
   1.977 +                    left = size - buf_len
   1.978 +                    # recv() will malloc the amount of memory given as its
   1.979 +                    # parameter even though it often returns much less data
   1.980 +                    # than that.  The returned data string is short lived
   1.981 +                    # as we copy it into a StringIO and free it.  This avoids
   1.982 +                    # fragmentation issues on many platforms.
   1.983 +                    data = self.recv(left)
   1.984 +                    if not data:
   1.985 +                        break
   1.986 +                    n = len(data)
   1.987 +                    if n == size and not buf_len:
   1.988 +                        # Shortcut.  Avoid buffer data copies when:
   1.989 +                        # - We have no data in our buffer.
   1.990 +                        # AND
   1.991 +                        # - Our call to recv returned exactly the
   1.992 +                        #   number of bytes we were asked to read.
   1.993 +                        return data
   1.994 +                    if n == left:
   1.995 +                        buf.write(data)
   1.996 +                        del data  # explicit free
   1.997 +                        break
   1.998 +                    assert n <= left, "recv(%d) returned %d bytes" % (left, n)
   1.999 +                    buf.write(data)
  1.1000 +                    buf_len += n
  1.1001 +                    del data  # explicit free
  1.1002 +                    #assert buf_len == buf.tell()
  1.1003 +                return buf.getvalue()
  1.1004 +
  1.1005 +        def readline(self, size=-1):
  1.1006 +            buf = self._rbuf
  1.1007 +            buf.seek(0, 2)  # seek end
  1.1008 +            if buf.tell() > 0:
  1.1009 +                # check if we already have it in our buffer
  1.1010 +                buf.seek(0)
  1.1011 +                bline = buf.readline(size)
  1.1012 +                if bline.endswith('\n') or len(bline) == size:
  1.1013 +                    self._rbuf = StringIO.StringIO()
  1.1014 +                    self._rbuf.write(buf.read())
  1.1015 +                    return bline
  1.1016 +                del bline
  1.1017 +            if size < 0:
  1.1018 +                # Read until \n or EOF, whichever comes first
  1.1019 +                if self._rbufsize <= 1:
  1.1020 +                    # Speed up unbuffered case
  1.1021 +                    buf.seek(0)
  1.1022 +                    buffers = [buf.read()]
  1.1023 +                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  1.1024 +                    data = None
  1.1025 +                    recv = self.recv
  1.1026 +                    while data != "\n":
  1.1027 +                        data = recv(1)
  1.1028 +                        if not data:
  1.1029 +                            break
  1.1030 +                        buffers.append(data)
  1.1031 +                    return "".join(buffers)
  1.1032 +
  1.1033 +                buf.seek(0, 2)  # seek end
  1.1034 +                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  1.1035 +                while True:
  1.1036 +                    data = self.recv(self._rbufsize)
  1.1037 +                    if not data:
  1.1038 +                        break
  1.1039 +                    nl = data.find('\n')
  1.1040 +                    if nl >= 0:
  1.1041 +                        nl += 1
  1.1042 +                        buf.write(data[:nl])
  1.1043 +                        self._rbuf.write(data[nl:])
  1.1044 +                        del data
  1.1045 +                        break
  1.1046 +                    buf.write(data)
  1.1047 +                return buf.getvalue()
  1.1048 +            else:
  1.1049 +                # Read until size bytes or \n or EOF seen, whichever comes first
  1.1050 +                buf.seek(0, 2)  # seek end
  1.1051 +                buf_len = buf.tell()
  1.1052 +                if buf_len >= size:
  1.1053 +                    buf.seek(0)
  1.1054 +                    rv = buf.read(size)
  1.1055 +                    self._rbuf = StringIO.StringIO()
  1.1056 +                    self._rbuf.write(buf.read())
  1.1057 +                    return rv
  1.1058 +                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  1.1059 +                while True:
  1.1060 +                    data = self.recv(self._rbufsize)
  1.1061 +                    if not data:
  1.1062 +                        break
  1.1063 +                    left = size - buf_len
  1.1064 +                    # did we just receive a newline?
  1.1065 +                    nl = data.find('\n', 0, left)
  1.1066 +                    if nl >= 0:
  1.1067 +                        nl += 1
  1.1068 +                        # save the excess data to _rbuf
  1.1069 +                        self._rbuf.write(data[nl:])
  1.1070 +                        if buf_len:
  1.1071 +                            buf.write(data[:nl])
  1.1072 +                            break
  1.1073 +                        else:
  1.1074 +                            # Shortcut.  Avoid data copy through buf when returning
  1.1075 +                            # a substring of our first recv().
  1.1076 +                            return data[:nl]
  1.1077 +                    n = len(data)
  1.1078 +                    if n == size and not buf_len:
  1.1079 +                        # Shortcut.  Avoid data copy through buf when
  1.1080 +                        # returning exactly all of our first recv().
  1.1081 +                        return data
  1.1082 +                    if n >= left:
  1.1083 +                        buf.write(data[:left])
  1.1084 +                        self._rbuf.write(data[left:])
  1.1085 +                        break
  1.1086 +                    buf.write(data)
  1.1087 +                    buf_len += n
  1.1088 +                    #assert buf_len == buf.tell()
  1.1089 +                return buf.getvalue()
  1.1090 +    else:
  1.1091 +        def read(self, size=-1):
  1.1092 +            if size < 0:
  1.1093 +                # Read until EOF
  1.1094 +                buffers = [self._rbuf]
  1.1095 +                self._rbuf = ""
  1.1096 +                if self._rbufsize <= 1:
  1.1097 +                    recv_size = self.default_bufsize
  1.1098 +                else:
  1.1099 +                    recv_size = self._rbufsize
  1.1100 +
  1.1101 +                while True:
  1.1102 +                    data = self.recv(recv_size)
  1.1103 +                    if not data:
  1.1104 +                        break
  1.1105 +                    buffers.append(data)
  1.1106 +                return "".join(buffers)
  1.1107 +            else:
  1.1108 +                # Read until size bytes or EOF seen, whichever comes first
  1.1109 +                data = self._rbuf
  1.1110 +                buf_len = len(data)
  1.1111 +                if buf_len >= size:
  1.1112 +                    self._rbuf = data[size:]
  1.1113 +                    return data[:size]
  1.1114 +                buffers = []
  1.1115 +                if data:
  1.1116 +                    buffers.append(data)
  1.1117 +                self._rbuf = ""
  1.1118 +                while True:
  1.1119 +                    left = size - buf_len
  1.1120 +                    recv_size = max(self._rbufsize, left)
  1.1121 +                    data = self.recv(recv_size)
  1.1122 +                    if not data:
  1.1123 +                        break
  1.1124 +                    buffers.append(data)
  1.1125 +                    n = len(data)
  1.1126 +                    if n >= left:
  1.1127 +                        self._rbuf = data[left:]
  1.1128 +                        buffers[-1] = data[:left]
  1.1129 +                        break
  1.1130 +                    buf_len += n
  1.1131 +                return "".join(buffers)
  1.1132 +
  1.1133 +        def readline(self, size=-1):
  1.1134 +            data = self._rbuf
  1.1135 +            if size < 0:
  1.1136 +                # Read until \n or EOF, whichever comes first
  1.1137 +                if self._rbufsize <= 1:
  1.1138 +                    # Speed up unbuffered case
  1.1139 +                    assert data == ""
  1.1140 +                    buffers = []
  1.1141 +                    while data != "\n":
  1.1142 +                        data = self.recv(1)
  1.1143 +                        if not data:
  1.1144 +                            break
  1.1145 +                        buffers.append(data)
  1.1146 +                    return "".join(buffers)
  1.1147 +                nl = data.find('\n')
  1.1148 +                if nl >= 0:
  1.1149 +                    nl += 1
  1.1150 +                    self._rbuf = data[nl:]
  1.1151 +                    return data[:nl]
  1.1152 +                buffers = []
  1.1153 +                if data:
  1.1154 +                    buffers.append(data)
  1.1155 +                self._rbuf = ""
  1.1156 +                while True:
  1.1157 +                    data = self.recv(self._rbufsize)
  1.1158 +                    if not data:
  1.1159 +                        break
  1.1160 +                    buffers.append(data)
  1.1161 +                    nl = data.find('\n')
  1.1162 +                    if nl >= 0:
  1.1163 +                        nl += 1
  1.1164 +                        self._rbuf = data[nl:]
  1.1165 +                        buffers[-1] = data[:nl]
  1.1166 +                        break
  1.1167 +                return "".join(buffers)
  1.1168 +            else:
  1.1169 +                # Read until size bytes or \n or EOF seen, whichever comes first
  1.1170 +                nl = data.find('\n', 0, size)
  1.1171 +                if nl >= 0:
  1.1172 +                    nl += 1
  1.1173 +                    self._rbuf = data[nl:]
  1.1174 +                    return data[:nl]
  1.1175 +                buf_len = len(data)
  1.1176 +                if buf_len >= size:
  1.1177 +                    self._rbuf = data[size:]
  1.1178 +                    return data[:size]
  1.1179 +                buffers = []
  1.1180 +                if data:
  1.1181 +                    buffers.append(data)
  1.1182 +                self._rbuf = ""
  1.1183 +                while True:
  1.1184 +                    data = self.recv(self._rbufsize)
  1.1185 +                    if not data:
  1.1186 +                        break
  1.1187 +                    buffers.append(data)
  1.1188 +                    left = size - buf_len
  1.1189 +                    nl = data.find('\n', 0, left)
  1.1190 +                    if nl >= 0:
  1.1191 +                        nl += 1
  1.1192 +                        self._rbuf = data[nl:]
  1.1193 +                        buffers[-1] = data[:nl]
  1.1194 +                        break
  1.1195 +                    n = len(data)
  1.1196 +                    if n >= left:
  1.1197 +                        self._rbuf = data[left:]
  1.1198 +                        buffers[-1] = data[:left]
  1.1199 +                        break
  1.1200 +                    buf_len += n
  1.1201 +                return "".join(buffers)
  1.1202 +
  1.1203 +
  1.1204 +class HTTPConnection(object):
  1.1205 +    """An HTTP connection (active socket).
  1.1206 +    
  1.1207 +    server: the Server object which received this connection.
  1.1208 +    socket: the raw socket object (usually TCP) for this connection.
  1.1209 +    makefile: a fileobject class for reading from the socket.
  1.1210 +    """
  1.1211 +    
  1.1212 +    remote_addr = None
  1.1213 +    remote_port = None
  1.1214 +    ssl_env = None
  1.1215 +    rbufsize = DEFAULT_BUFFER_SIZE
  1.1216 +    wbufsize = DEFAULT_BUFFER_SIZE
  1.1217 +    RequestHandlerClass = HTTPRequest
  1.1218 +    
  1.1219 +    def __init__(self, server, sock, makefile=CP_fileobject):
  1.1220 +        self.server = server
  1.1221 +        self.socket = sock
  1.1222 +        self.rfile = makefile(sock, "rb", self.rbufsize)
  1.1223 +        self.wfile = makefile(sock, "wb", self.wbufsize)
  1.1224 +        self.requests_seen = 0
  1.1225 +    
  1.1226 +    def communicate(self):
  1.1227 +        """Read each request and respond appropriately."""
  1.1228 +        request_seen = False
  1.1229 +        try:
  1.1230 +            while True:
  1.1231 +                # (re)set req to None so that if something goes wrong in
  1.1232 +                # the RequestHandlerClass constructor, the error doesn't
  1.1233 +                # get written to the previous request.
  1.1234 +                req = None
  1.1235 +                req = self.RequestHandlerClass(self.server, self)
  1.1236 +                
  1.1237 +                # This order of operations should guarantee correct pipelining.
  1.1238 +                req.parse_request()
  1.1239 +                if self.server.stats['Enabled']:
  1.1240 +                    self.requests_seen += 1
  1.1241 +                if not req.ready:
  1.1242 +                    # Something went wrong in the parsing (and the server has
  1.1243 +                    # probably already made a simple_response). Return and
  1.1244 +                    # let the conn close.
  1.1245 +                    return
  1.1246 +                
  1.1247 +                request_seen = True
  1.1248 +                req.respond()
  1.1249 +                if req.close_connection:
  1.1250 +                    return
  1.1251 +        except socket.error, e:
  1.1252 +            errnum = e.args[0]
  1.1253 +            # sadly SSL sockets return a different (longer) time out string
  1.1254 +            if errnum == 'timed out' or errnum == 'The read operation timed out':
  1.1255 +                # Don't error if we're between requests; only error
  1.1256 +                # if 1) no request has been started at all, or 2) we're
  1.1257 +                # in the middle of a request.
  1.1258 +                # See http://www.cherrypy.org/ticket/853
  1.1259 +                if (not request_seen) or (req and req.started_request):
  1.1260 +                    # Don't bother writing the 408 if the response
  1.1261 +                    # has already started being written.
  1.1262 +                    if req and not req.sent_headers:
  1.1263 +                        try:
  1.1264 +                            req.simple_response("408 Request Timeout")
  1.1265 +                        except FatalSSLAlert:
  1.1266 +                            # Close the connection.
  1.1267 +                            return
  1.1268 +            elif errnum not in socket_errors_to_ignore:
  1.1269 +                if req and not req.sent_headers:
  1.1270 +                    try:
  1.1271 +                        req.simple_response("500 Internal Server Error",
  1.1272 +                                            format_exc())
  1.1273 +                    except FatalSSLAlert:
  1.1274 +                        # Close the connection.
  1.1275 +                        return
  1.1276 +            return
  1.1277 +        except (KeyboardInterrupt, SystemExit):
  1.1278 +            raise
  1.1279 +        except FatalSSLAlert:
  1.1280 +            # Close the connection.
  1.1281 +            return
  1.1282 +        except NoSSLError:
  1.1283 +            if req and not req.sent_headers:
  1.1284 +                # Unwrap our wfile
  1.1285 +                self.wfile = CP_fileobject(self.socket._sock, "wb", self.wbufsize)
  1.1286 +                req.simple_response("400 Bad Request",
  1.1287 +                    "The client sent a plain HTTP request, but "
  1.1288 +                    "this server only speaks HTTPS on this port.")
  1.1289 +                self.linger = True
  1.1290 +        except Exception:
  1.1291 +            if req and not req.sent_headers:
  1.1292 +                try:
  1.1293 +                    req.simple_response("500 Internal Server Error", format_exc())
  1.1294 +                except FatalSSLAlert:
  1.1295 +                    # Close the connection.
  1.1296 +                    return
  1.1297 +    
  1.1298 +    linger = False
  1.1299 +    
  1.1300 +    def close(self):
  1.1301 +        """Close the socket underlying this connection."""
  1.1302 +        self.rfile.close()
  1.1303 +        
  1.1304 +        if not self.linger:
  1.1305 +            # Python's socket module does NOT call close on the kernel socket
  1.1306 +            # when you call socket.close(). We do so manually here because we
  1.1307 +            # want this server to send a FIN TCP segment immediately. Note this
  1.1308 +            # must be called *before* calling socket.close(), because the latter
  1.1309 +            # drops its reference to the kernel socket.
  1.1310 +            if hasattr(self.socket, '_sock'):
  1.1311 +                self.socket._sock.close()
  1.1312 +            self.socket.close()
  1.1313 +        else:
  1.1314 +            # On the other hand, sometimes we want to hang around for a bit
  1.1315 +            # to make sure the client has a chance to read our entire
  1.1316 +            # response. Skipping the close() calls here delays the FIN
  1.1317 +            # packet until the socket object is garbage-collected later.
  1.1318 +            # Someday, perhaps, we'll do the full lingering_close that
  1.1319 +            # Apache does, but not today.
  1.1320 +            pass
  1.1321 +
  1.1322 +
  1.1323 +_SHUTDOWNREQUEST = None
  1.1324 +
  1.1325 +class WorkerThread(threading.Thread):
  1.1326 +    """Thread which continuously polls a Queue for Connection objects.
  1.1327 +    
  1.1328 +    Due to the timing issues of polling a Queue, a WorkerThread does not
  1.1329 +    check its own 'ready' flag after it has started. To stop the thread,
  1.1330 +    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
  1.1331 +    (one for each running WorkerThread).
  1.1332 +    """
  1.1333 +    
  1.1334 +    conn = None
  1.1335 +    """The current connection pulled off the Queue, or None."""
  1.1336 +    
  1.1337 +    server = None
  1.1338 +    """The HTTP Server which spawned this thread, and which owns the
  1.1339 +    Queue and is placing active connections into it."""
  1.1340 +    
  1.1341 +    ready = False
  1.1342 +    """A simple flag for the calling server to know when this thread
  1.1343 +    has begun polling the Queue."""
  1.1344 +    
  1.1345 +    
  1.1346 +    def __init__(self, server):
  1.1347 +        self.ready = False
  1.1348 +        self.server = server
  1.1349 +        
  1.1350 +        self.requests_seen = 0
  1.1351 +        self.bytes_read = 0
  1.1352 +        self.bytes_written = 0
  1.1353 +        self.start_time = None
  1.1354 +        self.work_time = 0
  1.1355 +        self.stats = {
  1.1356 +            'Requests': lambda s: self.requests_seen + ((self.start_time is None) and 0 or self.conn.requests_seen),
  1.1357 +            'Bytes Read': lambda s: self.bytes_read + ((self.start_time is None) and 0 or self.conn.rfile.bytes_read),
  1.1358 +            'Bytes Written': lambda s: self.bytes_written + ((self.start_time is None) and 0 or self.conn.wfile.bytes_written),
  1.1359 +            'Work Time': lambda s: self.work_time + ((self.start_time is None) and 0 or time.time() - self.start_time),
  1.1360 +            'Read Throughput': lambda s: s['Bytes Read'](s) / (s['Work Time'](s) or 1e-6),
  1.1361 +            'Write Throughput': lambda s: s['Bytes Written'](s) / (s['Work Time'](s) or 1e-6),
  1.1362 +        }
  1.1363 +        threading.Thread.__init__(self)
  1.1364 +    
  1.1365 +    def run(self):
  1.1366 +        self.server.stats['Worker Threads'][self.getName()] = self.stats
  1.1367 +        try:
  1.1368 +            self.ready = True
  1.1369 +            while True:
  1.1370 +                conn = self.server.requests.get()
  1.1371 +                if conn is _SHUTDOWNREQUEST:
  1.1372 +                    return
  1.1373 +                
  1.1374 +                self.conn = conn
  1.1375 +                if self.server.stats['Enabled']:
  1.1376 +                    self.start_time = time.time()
  1.1377 +                try:
  1.1378 +                    conn.communicate()
  1.1379 +                finally:
  1.1380 +                    conn.close()
  1.1381 +                    if self.server.stats['Enabled']:
  1.1382 +                        self.requests_seen += self.conn.requests_seen
  1.1383 +                        self.bytes_read += self.conn.rfile.bytes_read
  1.1384 +                        self.bytes_written += self.conn.wfile.bytes_written
  1.1385 +                        self.work_time += time.time() - self.start_time
  1.1386 +                        self.start_time = None
  1.1387 +                    self.conn = None
  1.1388 +        except (KeyboardInterrupt, SystemExit), exc:
  1.1389 +            self.server.interrupt = exc
  1.1390 +
  1.1391 +
  1.1392 +class ThreadPool(object):
  1.1393 +    """A Request Queue for the CherryPyWSGIServer which pools threads.
  1.1394 +    
  1.1395 +    ThreadPool objects must provide min, get(), put(obj), start()
  1.1396 +    and stop(timeout) attributes.
  1.1397 +    """
  1.1398 +    
  1.1399 +    def __init__(self, server, min=10, max=-1):
  1.1400 +        self.server = server
  1.1401 +        self.min = min
  1.1402 +        self.max = max
  1.1403 +        self._threads = []
  1.1404 +        self._queue = Queue.Queue()
  1.1405 +        self.get = self._queue.get
  1.1406 +    
  1.1407 +    def start(self):
  1.1408 +        """Start the pool of threads."""
  1.1409 +        for i in range(self.min):
  1.1410 +            self._threads.append(WorkerThread(self.server))
  1.1411 +        for worker in self._threads:
  1.1412 +            worker.setName("CP Server " + worker.getName())
  1.1413 +            worker.start()
  1.1414 +        for worker in self._threads:
  1.1415 +            while not worker.ready:
  1.1416 +                time.sleep(.1)
  1.1417 +    
  1.1418 +    def _get_idle(self):
  1.1419 +        """Number of worker threads which are idle. Read-only."""
  1.1420 +        return len([t for t in self._threads if t.conn is None])
  1.1421 +    idle = property(_get_idle, doc=_get_idle.__doc__)
  1.1422 +    
  1.1423 +    def put(self, obj):
  1.1424 +        self._queue.put(obj)
  1.1425 +        if obj is _SHUTDOWNREQUEST:
  1.1426 +            return
  1.1427 +    
  1.1428 +    def grow(self, amount):
  1.1429 +        """Spawn new worker threads (not above self.max)."""
  1.1430 +        for i in range(amount):
  1.1431 +            if self.max > 0 and len(self._threads) >= self.max:
  1.1432 +                break
  1.1433 +            worker = WorkerThread(self.server)
  1.1434 +            worker.setName("CP Server " + worker.getName())
  1.1435 +            self._threads.append(worker)
  1.1436 +            worker.start()
  1.1437 +    
  1.1438 +    def shrink(self, amount):
  1.1439 +        """Kill off worker threads (not below self.min)."""
  1.1440 +        # Grow/shrink the pool if necessary.
  1.1441 +        # Remove any dead threads from our list
  1.1442 +        for t in self._threads:
  1.1443 +            if not t.isAlive():
  1.1444 +                self._threads.remove(t)
  1.1445 +                amount -= 1
  1.1446 +        
  1.1447 +        if amount > 0:
  1.1448 +            for i in range(min(amount, len(self._threads) - self.min)):
  1.1449 +                # Put a number of shutdown requests on the queue equal
  1.1450 +                # to 'amount'. Once each of those is processed by a worker,
  1.1451 +                # that worker will terminate and be culled from our list
  1.1452 +                # in self.put.
  1.1453 +                self._queue.put(_SHUTDOWNREQUEST)
  1.1454 +    
  1.1455 +    def stop(self, timeout=5):
  1.1456 +        # Must shut down threads here so the code that calls
  1.1457 +        # this method can know when all threads are stopped.
  1.1458 +        for worker in self._threads:
  1.1459 +            self._queue.put(_SHUTDOWNREQUEST)
  1.1460 +        
  1.1461 +        # Don't join currentThread (when stop is called inside a request).
  1.1462 +        current = threading.currentThread()
  1.1463 +        if timeout and timeout >= 0:
  1.1464 +            endtime = time.time() + timeout
  1.1465 +        while self._threads:
  1.1466 +            worker = self._threads.pop()
  1.1467 +            if worker is not current and worker.isAlive():
  1.1468 +                try:
  1.1469 +                    if timeout is None or timeout < 0:
  1.1470 +                        worker.join()
  1.1471 +                    else:
  1.1472 +                        remaining_time = endtime - time.time()
  1.1473 +                        if remaining_time > 0:
  1.1474 +                            worker.join(remaining_time)
  1.1475 +                        if worker.isAlive():
  1.1476 +                            # We exhausted the timeout.
  1.1477 +                            # Forcibly shut down the socket.
  1.1478 +                            c = worker.conn
  1.1479 +                            if c and not c.rfile.closed:
  1.1480 +                                try:
  1.1481 +                                    c.socket.shutdown(socket.SHUT_RD)
  1.1482 +                                except TypeError:
  1.1483 +                                    # pyOpenSSL sockets don't take an arg
  1.1484 +                                    c.socket.shutdown()
  1.1485 +                            worker.join()
  1.1486 +                except (AssertionError,
  1.1487 +                        # Ignore repeated Ctrl-C.
  1.1488 +                        # See http://www.cherrypy.org/ticket/691.
  1.1489 +                        KeyboardInterrupt), exc1:
  1.1490 +                    pass
  1.1491 +    
  1.1492 +    def _get_qsize(self):
  1.1493 +        return self._queue.qsize()
  1.1494 +    qsize = property(_get_qsize)
  1.1495 +
  1.1496 +
  1.1497 +
  1.1498 +try:
  1.1499 +    import fcntl
  1.1500 +except ImportError:
  1.1501 +    try:
  1.1502 +        from ctypes import windll, WinError
  1.1503 +    except ImportError:
  1.1504 +        def prevent_socket_inheritance(sock):
  1.1505 +            """Dummy function, since neither fcntl nor ctypes are available."""
  1.1506 +            pass
  1.1507 +    else:
  1.1508 +        def prevent_socket_inheritance(sock):
  1.1509 +            """Mark the given socket fd as non-inheritable (Windows)."""
  1.1510 +            if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
  1.1511 +                raise WinError()
  1.1512 +else:
  1.1513 +    def prevent_socket_inheritance(sock):
  1.1514 +        """Mark the given socket fd as non-inheritable (POSIX)."""
  1.1515 +        fd = sock.fileno()
  1.1516 +        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
  1.1517 +        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
  1.1518 +
  1.1519 +
  1.1520 +class SSLAdapter(object):
  1.1521 +    """Base class for SSL driver library adapters.
  1.1522 +    
  1.1523 +    Required methods:
  1.1524 +    
  1.1525 +        * ``wrap(sock) -> (wrapped socket, ssl environ dict)``
  1.1526 +        * ``makefile(sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE) -> socket file object``
  1.1527 +    """
  1.1528 +    
  1.1529 +    def __init__(self, certificate, private_key, certificate_chain=None):
  1.1530 +        self.certificate = certificate
  1.1531 +        self.private_key = private_key
  1.1532 +        self.certificate_chain = certificate_chain
  1.1533 +    
  1.1534 +    def wrap(self, sock):
  1.1535 +        raise NotImplemented
  1.1536 +    
  1.1537 +    def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
  1.1538 +        raise NotImplemented
  1.1539 +
  1.1540 +
  1.1541 +class HTTPServer(object):
  1.1542 +    """An HTTP server."""
  1.1543 +    
  1.1544 +    _bind_addr = "127.0.0.1"
  1.1545 +    _interrupt = None
  1.1546 +    
  1.1547 +    gateway = None
  1.1548 +    """A Gateway instance."""
  1.1549 +    
  1.1550 +    minthreads = None
  1.1551 +    """The minimum number of worker threads to create (default 10)."""
  1.1552 +    
  1.1553 +    maxthreads = None
  1.1554 +    """The maximum number of worker threads to create (default -1 = no limit)."""
  1.1555 +    
  1.1556 +    server_name = None
  1.1557 +    """The name of the server; defaults to socket.gethostname()."""
  1.1558 +    
  1.1559 +    protocol = "HTTP/1.1"
  1.1560 +    """The version string to write in the Status-Line of all HTTP responses.
  1.1561 +    
  1.1562 +    For example, "HTTP/1.1" is the default. This also limits the supported
  1.1563 +    features used in the response."""
  1.1564 +    
  1.1565 +    request_queue_size = 5
  1.1566 +    """The 'backlog' arg to socket.listen(); max queued connections (default 5)."""
  1.1567 +    
  1.1568 +    shutdown_timeout = 5
  1.1569 +    """The total time, in seconds, to wait for worker threads to cleanly exit."""
  1.1570 +    
  1.1571 +    timeout = 10
  1.1572 +    """The timeout in seconds for accepted connections (default 10)."""
  1.1573 +    
  1.1574 +    version = "CherryPy/3.2.0"
  1.1575 +    """A version string for the HTTPServer."""
  1.1576 +    
  1.1577 +    software = None
  1.1578 +    """The value to set for the SERVER_SOFTWARE entry in the WSGI environ.
  1.1579 +    
  1.1580 +    If None, this defaults to ``'%s Server' % self.version``."""
  1.1581 +    
  1.1582 +    ready = False
  1.1583 +    """An internal flag which marks whether the socket is accepting connections."""
  1.1584 +    
  1.1585 +    max_request_header_size = 0
  1.1586 +    """The maximum size, in bytes, for request headers, or 0 for no limit."""
  1.1587 +    
  1.1588 +    max_request_body_size = 0
  1.1589 +    """The maximum size, in bytes, for request bodies, or 0 for no limit."""
  1.1590 +    
  1.1591 +    nodelay = True
  1.1592 +    """If True (the default since 3.1), sets the TCP_NODELAY socket option."""
  1.1593 +    
  1.1594 +    ConnectionClass = HTTPConnection
  1.1595 +    """The class to use for handling HTTP connections."""
  1.1596 +    
  1.1597 +    ssl_adapter = None
  1.1598 +    """An instance of SSLAdapter (or a subclass).
  1.1599 +    
  1.1600 +    You must have the corresponding SSL driver library installed."""
  1.1601 +    
  1.1602 +    def __init__(self, bind_addr, gateway, minthreads=10, maxthreads=-1,
  1.1603 +                 server_name=None):
  1.1604 +        self.bind_addr = bind_addr
  1.1605 +        self.gateway = gateway
  1.1606 +        
  1.1607 +        self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads)
  1.1608 +        
  1.1609 +        if not server_name:
  1.1610 +            server_name = socket.gethostname()
  1.1611 +        self.server_name = server_name
  1.1612 +        self.clear_stats()
  1.1613 +    
  1.1614 +    def clear_stats(self):
  1.1615 +        self._start_time = None
  1.1616 +        self._run_time = 0
  1.1617 +        self.stats = {
  1.1618 +            'Enabled': False,
  1.1619 +            'Bind Address': lambda s: repr(self.bind_addr),
  1.1620 +            'Run time': lambda s: (not s['Enabled']) and 0 or self.runtime(),
  1.1621 +            'Accepts': 0,
  1.1622 +            'Accepts/sec': lambda s: s['Accepts'] / self.runtime(),
  1.1623 +            'Queue': lambda s: getattr(self.requests, "qsize", None),
  1.1624 +            'Threads': lambda s: len(getattr(self.requests, "_threads", [])),
  1.1625 +            'Threads Idle': lambda s: getattr(self.requests, "idle", None),
  1.1626 +            'Socket Errors': 0,
  1.1627 +            'Requests': lambda s: (not s['Enabled']) and 0 or sum([w['Requests'](w) for w
  1.1628 +                                       in s['Worker Threads'].values()], 0),
  1.1629 +            'Bytes Read': lambda s: (not s['Enabled']) and 0 or sum([w['Bytes Read'](w) for w
  1.1630 +                                         in s['Worker Threads'].values()], 0),
  1.1631 +            'Bytes Written': lambda s: (not s['Enabled']) and 0 or sum([w['Bytes Written'](w) for w
  1.1632 +                                            in s['Worker Threads'].values()], 0),
  1.1633 +            'Work Time': lambda s: (not s['Enabled']) and 0 or sum([w['Work Time'](w) for w
  1.1634 +                                         in s['Worker Threads'].values()], 0),
  1.1635 +            'Read Throughput': lambda s: (not s['Enabled']) and 0 or sum(
  1.1636 +                [w['Bytes Read'](w) / (w['Work Time'](w) or 1e-6)
  1.1637 +                 for w in s['Worker Threads'].values()], 0),
  1.1638 +            'Write Throughput': lambda s: (not s['Enabled']) and 0 or sum(
  1.1639 +                [w['Bytes Written'](w) / (w['Work Time'](w) or 1e-6)
  1.1640 +                 for w in s['Worker Threads'].values()], 0),
  1.1641 +            'Worker Threads': {},
  1.1642 +            }
  1.1643 +        logging.statistics["CherryPy HTTPServer %d" % id(self)] = self.stats
  1.1644 +    
  1.1645 +    def runtime(self):
  1.1646 +        if self._start_time is None:
  1.1647 +            return self._run_time
  1.1648 +        else:
  1.1649 +            return self._run_time + (time.time() - self._start_time)
  1.1650 +    
  1.1651 +    def __str__(self):
  1.1652 +        return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
  1.1653 +                              self.bind_addr)
  1.1654 +    
  1.1655 +    def _get_bind_addr(self):
  1.1656 +        return self._bind_addr
  1.1657 +    def _set_bind_addr(self, value):
  1.1658 +        if isinstance(value, tuple) and value[0] in ('', None):
  1.1659 +            # Despite the socket module docs, using '' does not
  1.1660 +            # allow AI_PASSIVE to work. Passing None instead
  1.1661 +            # returns '0.0.0.0' like we want. In other words:
  1.1662 +            #     host    AI_PASSIVE     result
  1.1663 +            #      ''         Y         192.168.x.y
  1.1664 +            #      ''         N         192.168.x.y
  1.1665 +            #     None        Y         0.0.0.0
  1.1666 +            #     None        N         127.0.0.1
  1.1667 +            # But since you can get the same effect with an explicit
  1.1668 +            # '0.0.0.0', we deny both the empty string and None as values.
  1.1669 +            raise ValueError("Host values of '' or None are not allowed. "
  1.1670 +                             "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
  1.1671 +                             "to listen on all active interfaces.")
  1.1672 +        self._bind_addr = value
  1.1673 +    bind_addr = property(_get_bind_addr, _set_bind_addr,
  1.1674 +        doc="""The interface on which to listen for connections.
  1.1675 +        
  1.1676 +        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
  1.1677 +        or IPv6 address, or any valid hostname. The string 'localhost' is a
  1.1678 +        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
  1.1679 +        The string '0.0.0.0' is a special IPv4 entry meaning "any active
  1.1680 +        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
  1.1681 +        IPv6. The empty string or None are not allowed.
  1.1682 +        
  1.1683 +        For UNIX sockets, supply the filename as a string.""")
  1.1684 +    
  1.1685 +    def start(self):
  1.1686 +        """Run the server forever."""
  1.1687 +        # We don't have to trap KeyboardInterrupt or SystemExit here,
  1.1688 +        # because cherrpy.server already does so, calling self.stop() for us.
  1.1689 +        # If you're using this server with another framework, you should
  1.1690 +        # trap those exceptions in whatever code block calls start().
  1.1691 +        self._interrupt = None
  1.1692 +        
  1.1693 +        if self.software is None:
  1.1694 +            self.software = "%s Server" % self.version
  1.1695 +        
  1.1696 +        # SSL backward compatibility
  1.1697 +        if (self.ssl_adapter is None and
  1.1698 +            getattr(self, 'ssl_certificate', None) and
  1.1699 +            getattr(self, 'ssl_private_key', None)):
  1.1700 +            warnings.warn(
  1.1701 +                    "SSL attributes are deprecated in CherryPy 3.2, and will "
  1.1702 +                    "be removed in CherryPy 3.3. Use an ssl_adapter attribute "
  1.1703 +                    "instead.",
  1.1704 +                    DeprecationWarning
  1.1705 +                )
  1.1706 +            try:
  1.1707 +                from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
  1.1708 +            except ImportError:
  1.1709 +                pass
  1.1710 +            else:
  1.1711 +                self.ssl_adapter = pyOpenSSLAdapter(
  1.1712 +                    self.ssl_certificate, self.ssl_private_key,
  1.1713 +                    getattr(self, 'ssl_certificate_chain', None))
  1.1714 +        
  1.1715 +        # Select the appropriate socket
  1.1716 +        if isinstance(self.bind_addr, basestring):
  1.1717 +            # AF_UNIX socket
  1.1718 +            
  1.1719 +            # So we can reuse the socket...
  1.1720 +            try: os.unlink(self.bind_addr)
  1.1721 +            except: pass
  1.1722 +            
  1.1723 +            # So everyone can access the socket...
  1.1724 +            try: os.chmod(self.bind_addr, 0777)
  1.1725 +            except: pass
  1.1726 +            
  1.1727 +            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
  1.1728 +        else:
  1.1729 +            # AF_INET or AF_INET6 socket
  1.1730 +            # Get the correct address family for our host (allows IPv6 addresses)
  1.1731 +            host, port = self.bind_addr
  1.1732 +            try:
  1.1733 +                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
  1.1734 +                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
  1.1735 +            except socket.gaierror:
  1.1736 +                if ':' in self.bind_addr[0]:
  1.1737 +                    info = [(socket.AF_INET6, socket.SOCK_STREAM,
  1.1738 +                             0, "", self.bind_addr + (0, 0))]
  1.1739 +                else:
  1.1740 +                    info = [(socket.AF_INET, socket.SOCK_STREAM,
  1.1741 +                             0, "", self.bind_addr)]
  1.1742 +        
  1.1743 +        self.socket = None
  1.1744 +        msg = "No socket could be created"
  1.1745 +        for res in info:
  1.1746 +            af, socktype, proto, canonname, sa = res
  1.1747 +            try:
  1.1748 +                self.bind(af, socktype, proto)
  1.1749 +            except socket.error:
  1.1750 +                if self.socket:
  1.1751 +                    self.socket.close()
  1.1752 +                self.socket = None
  1.1753 +                continue
  1.1754 +            break
  1.1755 +        if not self.socket:
  1.1756 +            raise socket.error(msg)
  1.1757 +        
  1.1758 +        # Timeout so KeyboardInterrupt can be caught on Win32
  1.1759 +        self.socket.settimeout(1)
  1.1760 +        self.socket.listen(self.request_queue_size)
  1.1761 +        
  1.1762 +        # Create worker threads
  1.1763 +        self.requests.start()
  1.1764 +        
  1.1765 +        self.ready = True
  1.1766 +        self._start_time = time.time()
  1.1767 +        while self.ready:
  1.1768 +            self.tick()
  1.1769 +            if self.interrupt:
  1.1770 +                while self.interrupt is True:
  1.1771 +                    # Wait for self.stop() to complete. See _set_interrupt.
  1.1772 +                    time.sleep(0.1)
  1.1773 +                if self.interrupt:
  1.1774 +                    raise self.interrupt
  1.1775 +    
  1.1776 +    def bind(self, family, type, proto=0):
  1.1777 +        """Create (or recreate) the actual socket object."""
  1.1778 +        self.socket = socket.socket(family, type, proto)
  1.1779 +        prevent_socket_inheritance(self.socket)
  1.1780 +        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  1.1781 +        if self.nodelay and not isinstance(self.bind_addr, str):
  1.1782 +            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  1.1783 +        
  1.1784 +        if self.ssl_adapter is not None:
  1.1785 +            self.socket = self.ssl_adapter.bind(self.socket)
  1.1786 +        
  1.1787 +        # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
  1.1788 +        # activate dual-stack. See http://www.cherrypy.org/ticket/871.
  1.1789 +        if (hasattr(socket, 'AF_INET6') and family == socket.AF_INET6
  1.1790 +            and self.bind_addr[0] in ('::', '::0', '::0.0.0.0')):
  1.1791 +            try:
  1.1792 +                self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
  1.1793 +            except (AttributeError, socket.error):
  1.1794 +                # Apparently, the socket option is not available in
  1.1795 +                # this machine's TCP stack
  1.1796 +                pass
  1.1797 +        
  1.1798 +        self.socket.bind(self.bind_addr)
  1.1799 +    
  1.1800 +    def tick(self):
  1.1801 +        """Accept a new connection and put it on the Queue."""
  1.1802 +        try:
  1.1803 +            s, addr = self.socket.accept()
  1.1804 +            if self.stats['Enabled']:
  1.1805 +                self.stats['Accepts'] += 1
  1.1806 +            if not self.ready:
  1.1807 +                return
  1.1808 +            
  1.1809 +            prevent_socket_inheritance(s)
  1.1810 +            if hasattr(s, 'settimeout'):
  1.1811 +                s.settimeout(self.timeout)
  1.1812 +            
  1.1813 +            makefile = CP_fileobject
  1.1814 +            ssl_env = {}
  1.1815 +            # if ssl cert and key are set, we try to be a secure HTTP server
  1.1816 +            if self.ssl_adapter is not None:
  1.1817 +                try:
  1.1818 +                    s, ssl_env = self.ssl_adapter.wrap(s)
  1.1819 +                except NoSSLError:
  1.1820 +                    msg = ("The client sent a plain HTTP request, but "
  1.1821 +                           "this server only speaks HTTPS on this port.")
  1.1822 +                    buf = ["%s 400 Bad Request\r\n" % self.protocol,
  1.1823 +                           "Content-Length: %s\r\n" % len(msg),
  1.1824 +                           "Content-Type: text/plain\r\n\r\n",
  1.1825 +                           msg]
  1.1826 +                    
  1.1827 +                    wfile = CP_fileobject(s, "wb", DEFAULT_BUFFER_SIZE)
  1.1828 +                    try:
  1.1829 +                        wfile.sendall("".join(buf))
  1.1830 +                    except socket.error, x:
  1.1831 +                        if x.args[0] not in socket_errors_to_ignore:
  1.1832 +                            raise
  1.1833 +                    return
  1.1834 +                if not s:
  1.1835 +                    return
  1.1836 +                makefile = self.ssl_adapter.makefile
  1.1837 +                # Re-apply our timeout since we may have a new socket object
  1.1838 +                if hasattr(s, 'settimeout'):
  1.1839 +                    s.settimeout(self.timeout)
  1.1840 +            
  1.1841 +            conn = self.ConnectionClass(self, s, makefile)
  1.1842 +            
  1.1843 +            if not isinstance(self.bind_addr, basestring):
  1.1844 +                # optional values
  1.1845 +                # Until we do DNS lookups, omit REMOTE_HOST
  1.1846 +                if addr is None: # sometimes this can happen
  1.1847 +                    # figure out if AF_INET or AF_INET6.
  1.1848 +                    if len(s.getsockname()) == 2:
  1.1849 +                        # AF_INET
  1.1850 +                        addr = ('0.0.0.0', 0)
  1.1851 +                    else:
  1.1852 +                        # AF_INET6
  1.1853 +                        addr = ('::', 0)
  1.1854 +                conn.remote_addr = addr[0]
  1.1855 +                conn.remote_port = addr[1]
  1.1856 +            
  1.1857 +            conn.ssl_env = ssl_env
  1.1858 +            
  1.1859 +            self.requests.put(conn)
  1.1860 +        except socket.timeout:
  1.1861 +            # The only reason for the timeout in start() is so we can
  1.1862 +            # notice keyboard interrupts on Win32, which don't interrupt
  1.1863 +            # accept() by default
  1.1864 +            return
  1.1865 +        except socket.error, x:
  1.1866 +            if self.stats['Enabled']:
  1.1867 +                self.stats['Socket Errors'] += 1
  1.1868 +            if x.args[0] in socket_error_eintr:
  1.1869 +                # I *think* this is right. EINTR should occur when a signal
  1.1870 +                # is received during the accept() call; all docs say retry
  1.1871 +                # the call, and I *think* I'm reading it right that Python
  1.1872 +                # will then go ahead and poll for and handle the signal
  1.1873 +                # elsewhere. See http://www.cherrypy.org/ticket/707.
  1.1874 +                return
  1.1875 +            if x.args[0] in socket_errors_nonblocking:
  1.1876 +                # Just try again. See http://www.cherrypy.org/ticket/479.
  1.1877 +                return
  1.1878 +            if x.args[0] in socket_errors_to_ignore:
  1.1879 +                # Our socket was closed.
  1.1880 +                # See http://www.cherrypy.org/ticket/686.
  1.1881 +                return
  1.1882 +            raise
  1.1883 +    
  1.1884 +    def _get_interrupt(self):
  1.1885 +        return self._interrupt
  1.1886 +    def _set_interrupt(self, interrupt):
  1.1887 +        self._interrupt = True
  1.1888 +        self.stop()
  1.1889 +        self._interrupt = interrupt
  1.1890 +    interrupt = property(_get_interrupt, _set_interrupt,
  1.1891 +                         doc="Set this to an Exception instance to "
  1.1892 +                             "interrupt the server.")
  1.1893 +    
  1.1894 +    def stop(self):
  1.1895 +        """Gracefully shutdown a server that is serving forever."""
  1.1896 +        self.ready = False
  1.1897 +        if self._start_time is not None:
  1.1898 +            self._run_time += (time.time() - self._start_time)
  1.1899 +        self._start_time = None
  1.1900 +        
  1.1901 +        sock = getattr(self, "socket", None)
  1.1902 +        if sock:
  1.1903 +            if not isinstance(self.bind_addr, basestring):
  1.1904 +                # Touch our own socket to make accept() return immediately.
  1.1905 +                try:
  1.1906 +                    host, port = sock.getsockname()[:2]
  1.1907 +                except socket.error, x:
  1.1908 +                    if x.args[0] not in socket_errors_to_ignore:
  1.1909 +                        # Changed to use error code and not message
  1.1910 +                        # See http://www.cherrypy.org/ticket/860.
  1.1911 +                        raise
  1.1912 +                else:
  1.1913 +                    # Note that we're explicitly NOT using AI_PASSIVE,
  1.1914 +                    # here, because we want an actual IP to touch.
  1.1915 +                    # localhost won't work if we've bound to a public IP,
  1.1916 +                    # but it will if we bound to '0.0.0.0' (INADDR_ANY).
  1.1917 +                    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
  1.1918 +                                                  socket.SOCK_STREAM):
  1.1919 +                        af, socktype, proto, canonname, sa = res
  1.1920 +                        s = None
  1.1921 +                        try:
  1.1922 +                            s = socket.socket(af, socktype, proto)
  1.1923 +                            # See http://groups.google.com/group/cherrypy-users/
  1.1924 +                            #        browse_frm/thread/bbfe5eb39c904fe0
  1.1925 +                            s.settimeout(1.0)
  1.1926 +                            s.connect((host, port))
  1.1927 +                            s.close()
  1.1928 +                        except socket.error:
  1.1929 +                            if s:
  1.1930 +                                s.close()
  1.1931 +            if hasattr(sock, "close"):
  1.1932 +                sock.close()
  1.1933 +            self.socket = None
  1.1934 +        
  1.1935 +        self.requests.stop(self.shutdown_timeout)
  1.1936 +
  1.1937 +
  1.1938 +class Gateway(object):
  1.1939 +    
  1.1940 +    def __init__(self, req):
  1.1941 +        self.req = req
  1.1942 +    
  1.1943 +    def respond(self):
  1.1944 +        raise NotImplemented
  1.1945 +
  1.1946 +
  1.1947 +# These may either be wsgiserver.SSLAdapter subclasses or the string names
  1.1948 +# of such classes (in which case they will be lazily loaded).
  1.1949 +ssl_adapters = {
  1.1950 +    'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
  1.1951 +    'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
  1.1952 +    }
  1.1953 +
  1.1954 +def get_ssl_adapter_class(name='pyopenssl'):
  1.1955 +    adapter = ssl_adapters[name.lower()]
  1.1956 +    if isinstance(adapter, basestring):
  1.1957 +        last_dot = adapter.rfind(".")
  1.1958 +        attr_name = adapter[last_dot + 1:]
  1.1959 +        mod_path = adapter[:last_dot]
  1.1960 +        
  1.1961 +        try:
  1.1962 +            mod = sys.modules[mod_path]
  1.1963 +            if mod is None:
  1.1964 +                raise KeyError()
  1.1965 +        except KeyError:
  1.1966 +            # The last [''] is important.
  1.1967 +            mod = __import__(mod_path, globals(), locals(), [''])
  1.1968 +        
  1.1969 +        # Let an AttributeError propagate outward.
  1.1970 +        try:
  1.1971 +            adapter = getattr(mod, attr_name)
  1.1972 +        except AttributeError:
  1.1973 +            raise AttributeError("'%s' object has no attribute '%s'"
  1.1974 +                                 % (mod_path, attr_name))
  1.1975 +    
  1.1976 +    return adapter
  1.1977 +
  1.1978 +# -------------------------------- WSGI Stuff -------------------------------- #
  1.1979 +
  1.1980 +
  1.1981 +class CherryPyWSGIServer(HTTPServer):
  1.1982 +    
  1.1983 +    wsgi_version = (1, 0)
  1.1984 +    
  1.1985 +    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
  1.1986 +                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
  1.1987 +        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
  1.1988 +        self.wsgi_app = wsgi_app
  1.1989 +        self.gateway = wsgi_gateways[self.wsgi_version]
  1.1990 +        
  1.1991 +        self.bind_addr = bind_addr
  1.1992 +        if not server_name:
  1.1993 +            server_name = socket.gethostname()
  1.1994 +        self.server_name = server_name
  1.1995 +        self.request_queue_size = request_queue_size
  1.1996 +        
  1.1997 +        self.timeout = timeout
  1.1998 +        self.shutdown_timeout = shutdown_timeout
  1.1999 +        self.clear_stats()
  1.2000 +    
  1.2001 +    def _get_numthreads(self):
  1.2002 +        return self.requests.min
  1.2003 +    def _set_numthreads(self, value):
  1.2004 +        self.requests.min = value
  1.2005 +    numthreads = property(_get_numthreads, _set_numthreads)
  1.2006 +
  1.2007 +
  1.2008 +class WSGIGateway(Gateway):
  1.2009 +    
  1.2010 +    def __init__(self, req):
  1.2011 +        self.req = req
  1.2012 +        self.started_response = False
  1.2013 +        self.env = self.get_environ()
  1.2014 +        self.remaining_bytes_out = None
  1.2015 +    
  1.2016 +    def get_environ(self):
  1.2017 +        """Return a new environ dict targeting the given wsgi.version"""
  1.2018 +        raise NotImplemented
  1.2019 +    
  1.2020 +    def respond(self):
  1.2021 +        response = self.req.server.wsgi_app(self.env, self.start_response)
  1.2022 +        try:
  1.2023 +            for chunk in response:
  1.2024 +                # "The start_response callable must not actually transmit
  1.2025 +                # the response headers. Instead, it must store them for the
  1.2026 +                # server or gateway to transmit only after the first
  1.2027 +                # iteration of the application return value that yields
  1.2028 +                # a NON-EMPTY string, or upon the application's first
  1.2029 +                # invocation of the write() callable." (PEP 333)
  1.2030 +                if chunk:
  1.2031 +                    if isinstance(chunk, unicode):
  1.2032 +                        chunk = chunk.encode('ISO-8859-1')
  1.2033 +                    self.write(chunk)
  1.2034 +        finally:
  1.2035 +            if hasattr(response, "close"):
  1.2036 +                response.close()
  1.2037 +    
  1.2038 +    def start_response(self, status, headers, exc_info = None):
  1.2039 +        """WSGI callable to begin the HTTP response."""
  1.2040 +        # "The application may call start_response more than once,
  1.2041 +        # if and only if the exc_info argument is provided."
  1.2042 +        if self.started_response and not exc_info:
  1.2043 +            raise AssertionError("WSGI start_response called a second "
  1.2044 +                                 "time with no exc_info.")
  1.2045 +        self.started_response = True
  1.2046 +        
  1.2047 +        # "if exc_info is provided, and the HTTP headers have already been
  1.2048 +        # sent, start_response must raise an error, and should raise the
  1.2049 +        # exc_info tuple."
  1.2050 +        if self.req.sent_headers:
  1.2051 +            try:
  1.2052 +                raise exc_info[0], exc_info[1], exc_info[2]
  1.2053 +            finally:
  1.2054 +                exc_info = None
  1.2055 +        
  1.2056 +        self.req.status = status
  1.2057 +        for k, v in headers:
  1.2058 +            if not isinstance(k, str):
  1.2059 +                raise TypeError("WSGI response header key %r is not a byte string." % k)
  1.2060 +            if not isinstance(v, str):
  1.2061 +                raise TypeError("WSGI response header value %r is not a byte string." % v)
  1.2062 +            if k.lower() == 'content-length':
  1.2063 +                self.remaining_bytes_out = int(v)
  1.2064 +        self.req.outheaders.extend(headers)
  1.2065 +        
  1.2066 +        return self.write
  1.2067 +    
  1.2068 +    def write(self, chunk):
  1.2069 +        """WSGI callable to write unbuffered data to the client.
  1.2070 +        
  1.2071 +        This method is also used internally by start_response (to write
  1.2072 +        data from the iterable returned by the WSGI application).
  1.2073 +        """
  1.2074 +        if not self.started_response:
  1.2075 +            raise AssertionError("WSGI write called before start_response.")
  1.2076 +        
  1.2077 +        chunklen = len(chunk)
  1.2078 +        rbo = self.remaining_bytes_out
  1.2079 +        if rbo is not None and chunklen > rbo:
  1.2080 +            if not self.req.sent_headers:
  1.2081 +                # Whew. We can send a 500 to the client.
  1.2082 +                self.req.simple_response("500 Internal Server Error",
  1.2083 +                    "The requested resource returned more bytes than the "
  1.2084 +                    "declared Content-Length.")
  1.2085 +            else:
  1.2086 +                # Dang. We have probably already sent data. Truncate the chunk
  1.2087 +                # to fit (so the client doesn't hang) and raise an error later.
  1.2088 +                chunk = chunk[:rbo]
  1.2089 +        
  1.2090 +        if not self.req.sent_headers:
  1.2091 +            self.req.sent_headers = True
  1.2092 +            self.req.send_headers()
  1.2093 +        
  1.2094 +        self.req.write(chunk)
  1.2095 +        
  1.2096 +        if rbo is not None:
  1.2097 +            rbo -= chunklen
  1.2098 +            if rbo < 0:
  1.2099 +                raise ValueError(
  1.2100 +                    "Response body exceeds the declared Content-Length.")
  1.2101 +
  1.2102 +
  1.2103 +class WSGIGateway_10(WSGIGateway):
  1.2104 +    
  1.2105 +    def get_environ(self):
  1.2106 +        """Return a new environ dict targeting the given wsgi.version"""
  1.2107 +        req = self.req
  1.2108 +        env = {
  1.2109 +            # set a non-standard environ entry so the WSGI app can know what
  1.2110 +            # the *real* server protocol is (and what features to support).
  1.2111 +            # See http://www.faqs.org/rfcs/rfc2145.html.
  1.2112 +            'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
  1.2113 +            'PATH_INFO': req.path,
  1.2114 +            'QUERY_STRING': req.qs,
  1.2115 +            'REMOTE_ADDR': req.conn.remote_addr or '',
  1.2116 +            'REMOTE_PORT': str(req.conn.remote_port or ''),
  1.2117 +            'REQUEST_METHOD': req.method,
  1.2118 +            'REQUEST_URI': req.uri,
  1.2119 +            'SCRIPT_NAME': '',
  1.2120 +            'SERVER_NAME': req.server.server_name,
  1.2121 +            # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
  1.2122 +            'SERVER_PROTOCOL': req.request_protocol,
  1.2123 +            'SERVER_SOFTWARE': req.server.software,
  1.2124 +            'wsgi.errors': sys.stderr,
  1.2125 +            'wsgi.input': req.rfile,
  1.2126 +            'wsgi.multiprocess': False,
  1.2127 +            'wsgi.multithread': True,
  1.2128 +            'wsgi.run_once': False,
  1.2129 +            'wsgi.url_scheme': req.scheme,
  1.2130 +            'wsgi.version': (1, 0),
  1.2131 +            }
  1.2132 +        
  1.2133 +        if isinstance(req.server.bind_addr, basestring):
  1.2134 +            # AF_UNIX. This isn't really allowed by WSGI, which doesn't
  1.2135 +            # address unix domain sockets. But it's better than nothing.
  1.2136 +            env["SERVER_PORT"] = ""
  1.2137 +        else:
  1.2138 +            env["SERVER_PORT"] = str(req.server.bind_addr[1])
  1.2139 +        
  1.2140 +        # Request headers
  1.2141 +        for k, v in req.inheaders.iteritems():
  1.2142 +            env["HTTP_" + k.upper().replace("-", "_")] = v
  1.2143 +        
  1.2144 +        # CONTENT_TYPE/CONTENT_LENGTH
  1.2145 +        ct = env.pop("HTTP_CONTENT_TYPE", None)
  1.2146 +        if ct is not None:
  1.2147 +            env["CONTENT_TYPE"] = ct
  1.2148 +        cl = env.pop("HTTP_CONTENT_LENGTH", None)
  1.2149 +        if cl is not None:
  1.2150 +            env["CONTENT_LENGTH"] = cl
  1.2151 +        
  1.2152 +        if req.conn.ssl_env:
  1.2153 +            env.update(req.conn.ssl_env)
  1.2154 +        
  1.2155 +        return env
  1.2156 +
  1.2157 +
  1.2158 +class WSGIGateway_u0(WSGIGateway_10):
  1.2159 +    
  1.2160 +    def get_environ(self):
  1.2161 +        """Return a new environ dict targeting the given wsgi.version"""
  1.2162 +        req = self.req
  1.2163 +        env_10 = WSGIGateway_10.get_environ(self)
  1.2164 +        env = dict([(k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems()])
  1.2165 +        env[u'wsgi.version'] = ('u', 0)
  1.2166 +        
  1.2167 +        # Request-URI
  1.2168 +        env.setdefault(u'wsgi.url_encoding', u'utf-8')
  1.2169 +        try:
  1.2170 +            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
  1.2171 +                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
  1.2172 +        except UnicodeDecodeError:
  1.2173 +            # Fall back to latin 1 so apps can transcode if needed.
  1.2174 +            env[u'wsgi.url_encoding'] = u'ISO-8859-1'
  1.2175 +            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
  1.2176 +                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
  1.2177 +        
  1.2178 +        for k, v in sorted(env.items()):
  1.2179 +            if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'):
  1.2180 +                env[k] = v.decode('ISO-8859-1')
  1.2181 +        
  1.2182 +        return env
  1.2183 +
  1.2184 +wsgi_gateways = {
  1.2185 +    (1, 0): WSGIGateway_10,
  1.2186 +    ('u', 0): WSGIGateway_u0,
  1.2187 +}
  1.2188 +
  1.2189 +class WSGIPathInfoDispatcher(object):
  1.2190 +    """A WSGI dispatcher for dispatch based on the PATH_INFO.
  1.2191 +    
  1.2192 +    apps: a dict or list of (path_prefix, app) pairs.
  1.2193 +    """
  1.2194 +    
  1.2195 +    def __init__(self, apps):
  1.2196 +        try:
  1.2197 +            apps = apps.items()
  1.2198 +        except AttributeError:
  1.2199 +            pass
  1.2200 +        
  1.2201 +        # Sort the apps by len(path), descending
  1.2202 +        apps.sort(cmp=lambda x,y: cmp(len(x[0]), len(y[0])))
  1.2203 +        apps.reverse()
  1.2204 +        
  1.2205 +        # The path_prefix strings must start, but not end, with a slash.
  1.2206 +        # Use "" instead of "/".
  1.2207 +        self.apps = [(p.rstrip("/"), a) for p, a in apps]
  1.2208 +    
  1.2209 +    def __call__(self, environ, start_response):
  1.2210 +        path = environ["PATH_INFO"] or "/"
  1.2211 +        for p, app in self.apps:
  1.2212 +            # The apps list should be sorted by length, descending.
  1.2213 +            if path.startswith(p + "/") or path == p:
  1.2214 +                environ = environ.copy()
  1.2215 +                environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
  1.2216 +                environ["PATH_INFO"] = path[len(p):]
  1.2217 +                return app(environ, start_response)
  1.2218 +        
  1.2219 +        start_response('404 Not Found', [('Content-Type', 'text/plain'),
  1.2220 +                                         ('Content-Length', '0')])
  1.2221 +        return ['']
  1.2222 +