NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

__init__.py (55203B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\wsgiserver\__init__.py
      2 CRLF = '\r\n'
      3 import os
      4 import Queue
      5 import re
      6 quoted_slash = re.compile('(?i)%2F')
      7 import rfc822
      8 import socket
      9 import sys
     10 if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
     11     socket.IPPROTO_IPV6 = 41
     12 try:
     13     import cStringIO as StringIO
     14 except ImportError:
     15     import StringIO
     16 
     17 DEFAULT_BUFFER_SIZE = -1
     18 _fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
     19 import threading
     20 import time
     21 import traceback
     22 
     23 def format_exc(limit = None):
     24     try:
     25         etype, value, tb = sys.exc_info()
     26         return ''.join(traceback.format_exception(etype, value, tb, limit))
     27     finally:
     28         etype = value = tb = None
     29 
     30 
     31 from urllib import unquote
     32 from urlparse import urlparse
     33 import warnings
     34 import errno
     35 
     36 def plat_specific_errors(*errnames):
     37     errno_names = dir(errno)
     38     nums = [ getattr(errno, k) for k in errnames if k in errno_names ]
     39     return dict.fromkeys(nums).keys()
     40 
     41 
     42 socket_error_eintr = plat_specific_errors('EINTR', 'WSAEINTR')
     43 socket_errors_to_ignore = plat_specific_errors('EPIPE', 'EBADF', 'WSAEBADF', 'ENOTSOCK', 'WSAENOTSOCK', 'ETIMEDOUT', 'WSAETIMEDOUT', 'ECONNREFUSED', 'WSAECONNREFUSED', 'ECONNRESET', 'WSAECONNRESET', 'ECONNABORTED', 'WSAECONNABORTED', 'ENETRESET', 'WSAENETRESET', 'EHOSTDOWN', 'EHOSTUNREACH')
     44 socket_errors_to_ignore.append('timed out')
     45 socket_errors_to_ignore.append('The read operation timed out')
     46 socket_errors_nonblocking = plat_specific_errors('EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
     47 comma_separated_headers = ['Accept',
     48  'Accept-Charset',
     49  'Accept-Encoding',
     50  'Accept-Language',
     51  'Accept-Ranges',
     52  'Allow',
     53  'Cache-Control',
     54  'Connection',
     55  'Content-Encoding',
     56  'Content-Language',
     57  'Expect',
     58  'If-Match',
     59  'If-None-Match',
     60  'Pragma',
     61  'Proxy-Authenticate',
     62  'TE',
     63  'Trailer',
     64  'Transfer-Encoding',
     65  'Upgrade',
     66  'Vary',
     67  'Via',
     68  'Warning',
     69  'WWW-Authenticate']
     70 import logging
     71 if not hasattr(logging, 'statistics'):
     72     logging.statistics = {}
     73 
     74 def read_headers(rfile, hdict = None):
     75     if hdict is None:
     76         hdict = {}
     77     while True:
     78         line = rfile.readline()
     79         if not line:
     80             raise ValueError('Illegal end of headers.')
     81         if line == CRLF:
     82             break
     83         if not line.endswith(CRLF):
     84             raise ValueError('HTTP requires CRLF terminators')
     85         if line[0] in ' \t':
     86             v = line.strip()
     87         else:
     88             try:
     89                 k, v = line.split(':', 1)
     90             except ValueError:
     91                 raise ValueError('Illegal header line.')
     92 
     93             k = k.strip().title()
     94             v = v.strip()
     95             hname = k
     96         if k in comma_separated_headers:
     97             existing = hdict.get(hname)
     98             if existing:
     99                 v = ', '.join((existing, v))
    100         hdict[hname] = v
    101 
    102     return hdict
    103 
    104 
    105 class MaxSizeExceeded(Exception):
    106     pass
    107 
    108 
    109 class SizeCheckWrapper(object):
    110 
    111     def __init__(self, rfile, maxlen):
    112         self.rfile = rfile
    113         self.maxlen = maxlen
    114         self.bytes_read = 0
    115 
    116     def _check_length(self):
    117         if self.maxlen and self.bytes_read > self.maxlen:
    118             raise MaxSizeExceeded()
    119 
    120     def read(self, size = None):
    121         data = self.rfile.read(size)
    122         self.bytes_read += len(data)
    123         self._check_length()
    124         return data
    125 
    126     def readline(self, size = None):
    127         if size is not None:
    128             data = self.rfile.readline(size)
    129             self.bytes_read += len(data)
    130             self._check_length()
    131             return data
    132         res = []
    133         while True:
    134             data = self.rfile.readline(256)
    135             self.bytes_read += len(data)
    136             self._check_length()
    137             res.append(data)
    138             if len(data) < 256 or data[-1:] == '\n':
    139                 return ''.join(res)
    140 
    141     def readlines(self, sizehint = 0):
    142         total = 0
    143         lines = []
    144         line = self.readline()
    145         while line:
    146             lines.append(line)
    147             total += len(line)
    148             if 0 < sizehint <= total:
    149                 break
    150             line = self.readline()
    151 
    152         return lines
    153 
    154     def close(self):
    155         self.rfile.close()
    156 
    157     def __iter__(self):
    158         return self
    159 
    160     def next(self):
    161         data = self.rfile.next()
    162         self.bytes_read += len(data)
    163         self._check_length()
    164         return data
    165 
    166 
    167 class KnownLengthRFile(object):
    168 
    169     def __init__(self, rfile, content_length):
    170         self.rfile = rfile
    171         self.remaining = content_length
    172 
    173     def read(self, size = None):
    174         if self.remaining == 0:
    175             return ''
    176         if size is None:
    177             size = self.remaining
    178         else:
    179             size = min(size, self.remaining)
    180         data = self.rfile.read(size)
    181         self.remaining -= len(data)
    182         return data
    183 
    184     def readline(self, size = None):
    185         if self.remaining == 0:
    186             return ''
    187         if size is None:
    188             size = self.remaining
    189         else:
    190             size = min(size, self.remaining)
    191         data = self.rfile.readline(size)
    192         self.remaining -= len(data)
    193         return data
    194 
    195     def readlines(self, sizehint = 0):
    196         total = 0
    197         lines = []
    198         line = self.readline(sizehint)
    199         while line:
    200             lines.append(line)
    201             total += len(line)
    202             if 0 < sizehint <= total:
    203                 break
    204             line = self.readline(sizehint)
    205 
    206         return lines
    207 
    208     def close(self):
    209         self.rfile.close()
    210 
    211     def __iter__(self):
    212         return self
    213 
    214     def __next__(self):
    215         data = next(self.rfile)
    216         self.remaining -= len(data)
    217         return data
    218 
    219 
    220 class ChunkedRFile(object):
    221 
    222     def __init__(self, rfile, maxlen, bufsize = 8192):
    223         self.rfile = rfile
    224         self.maxlen = maxlen
    225         self.bytes_read = 0
    226         self.buffer = ''
    227         self.bufsize = bufsize
    228         self.closed = False
    229 
    230     def _fetch(self):
    231         if self.closed:
    232             return
    233         line = self.rfile.readline()
    234         self.bytes_read += len(line)
    235         if self.maxlen and self.bytes_read > self.maxlen:
    236             raise MaxSizeExceeded('Request Entity Too Large', self.maxlen)
    237         line = line.strip().split(';', 1)
    238         try:
    239             chunk_size = line.pop(0)
    240             chunk_size = int(chunk_size, 16)
    241         except ValueError:
    242             raise ValueError('Bad chunked transfer size: ' + repr(chunk_size))
    243 
    244         if chunk_size <= 0:
    245             self.closed = True
    246             return
    247         if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
    248             raise IOError('Request Entity Too Large')
    249         chunk = self.rfile.read(chunk_size)
    250         self.bytes_read += len(chunk)
    251         self.buffer += chunk
    252         crlf = self.rfile.read(2)
    253         if crlf != CRLF:
    254             raise ValueError("Bad chunked transfer coding (expected '\\r\\n', got " + repr(crlf) + ')')
    255 
    256     def read(self, size = None):
    257         data = ''
    258         while True:
    259             if size and len(data) >= size:
    260                 return data
    261             if not self.buffer:
    262                 self._fetch()
    263                 if not self.buffer:
    264                     return data
    265             if size:
    266                 remaining = size - len(data)
    267                 data += self.buffer[:remaining]
    268                 self.buffer = self.buffer[remaining:]
    269             else:
    270                 data += self.buffer
    271 
    272     def readline(self, size = None):
    273         data = ''
    274         while True:
    275             if size and len(data) >= size:
    276                 return data
    277             if not self.buffer:
    278                 self._fetch()
    279                 if not self.buffer:
    280                     return data
    281             newline_pos = self.buffer.find('\n')
    282             if size:
    283                 if newline_pos == -1:
    284                     remaining = size - len(data)
    285                     data += self.buffer[:remaining]
    286                     self.buffer = self.buffer[remaining:]
    287                 else:
    288                     remaining = min(size - len(data), newline_pos)
    289                     data += self.buffer[:remaining]
    290                     self.buffer = self.buffer[remaining:]
    291             elif newline_pos == -1:
    292                 data += self.buffer
    293             else:
    294                 data += self.buffer[:newline_pos]
    295                 self.buffer = self.buffer[newline_pos:]
    296 
    297     def readlines(self, sizehint = 0):
    298         total = 0
    299         lines = []
    300         line = self.readline(sizehint)
    301         while line:
    302             lines.append(line)
    303             total += len(line)
    304             if 0 < sizehint <= total:
    305                 break
    306             line = self.readline(sizehint)
    307 
    308         return lines
    309 
    310     def read_trailer_lines(self):
    311         if not self.closed:
    312             raise ValueError('Cannot read trailers until the request body has been read.')
    313         while True:
    314             line = self.rfile.readline()
    315             if not line:
    316                 raise ValueError('Illegal end of headers.')
    317             self.bytes_read += len(line)
    318             if self.maxlen and self.bytes_read > self.maxlen:
    319                 raise IOError('Request Entity Too Large')
    320             if line == CRLF:
    321                 break
    322             if not line.endswith(CRLF):
    323                 raise ValueError('HTTP requires CRLF terminators')
    324             yield line
    325 
    326     def close(self):
    327         self.rfile.close()
    328 
    329     def __iter__(self):
    330         total = 0
    331         line = self.readline(sizehint)
    332         while line:
    333             yield line
    334             total += len(line)
    335             if 0 < sizehint <= total:
    336                 break
    337             line = self.readline(sizehint)
    338 
    339 
    340 class HTTPRequest(object):
    341     server = None
    342     conn = None
    343     inheaders = {}
    344     outheaders = []
    345     ready = False
    346     close_connection = False
    347     chunked_write = False
    348 
    349     def __init__(self, server, conn):
    350         self.server = server
    351         self.conn = conn
    352         self.ready = False
    353         self.started_request = False
    354         self.scheme = 'http'
    355         if self.server.ssl_adapter is not None:
    356             self.scheme = 'https'
    357         self.response_protocol = 'HTTP/1.0'
    358         self.inheaders = {}
    359         self.status = ''
    360         self.outheaders = []
    361         self.sent_headers = False
    362         self.close_connection = self.__class__.close_connection
    363         self.chunked_read = False
    364         self.chunked_write = self.__class__.chunked_write
    365 
    366     def parse_request(self):
    367         self.rfile = SizeCheckWrapper(self.conn.rfile, self.server.max_request_header_size)
    368         try:
    369             self.read_request_line()
    370         except MaxSizeExceeded:
    371             self.simple_response('414 Request-URI Too Long', 'The Request-URI sent with the request exceeds the maximum allowed bytes.')
    372             return
    373 
    374         try:
    375             success = self.read_request_headers()
    376         except MaxSizeExceeded:
    377             self.simple_response('413 Request Entity Too Large', 'The headers sent with the request exceed the maximum allowed bytes.')
    378             return
    379 
    380         if not success:
    381             return
    382         self.ready = True
    383 
    384     def read_request_line(self):
    385         request_line = self.rfile.readline()
    386         self.started_request = True
    387         if not request_line:
    388             self.ready = False
    389             return
    390         if request_line == CRLF:
    391             request_line = self.rfile.readline()
    392             if not request_line:
    393                 self.ready = False
    394                 return
    395         if not request_line.endswith(CRLF):
    396             self.simple_response('400 Bad Request', 'HTTP requires CRLF terminators')
    397             return
    398         try:
    399             method, uri, req_protocol = request_line.strip().split(' ', 2)
    400             rp = (int(req_protocol[5]), int(req_protocol[7]))
    401         except (ValueError, IndexError):
    402             self.simple_response('400 Bad Request', 'Malformed Request-Line')
    403             return
    404 
    405         self.uri = uri
    406         self.method = method
    407         scheme, authority, path = self.parse_request_uri(uri)
    408         if '#' in path:
    409             self.simple_response('400 Bad Request', 'Illegal #fragment in Request-URI.')
    410             return
    411         if scheme:
    412             self.scheme = scheme
    413         qs = ''
    414         if '?' in path:
    415             path, qs = path.split('?', 1)
    416         try:
    417             atoms = [ unquote(x) for x in quoted_slash.split(path) ]
    418         except ValueError as ex:
    419             self.simple_response('400 Bad Request', ex.args[0])
    420             return
    421 
    422         path = '%2F'.join(atoms)
    423         self.path = path
    424         self.qs = qs
    425         sp = (int(self.server.protocol[5]), int(self.server.protocol[7]))
    426         if sp[0] != rp[0]:
    427             self.simple_response('505 HTTP Version Not Supported')
    428             return
    429         self.request_protocol = req_protocol
    430         self.response_protocol = 'HTTP/%s.%s' % min(rp, sp)
    431 
    432     def read_request_headers(self):
    433         try:
    434             read_headers(self.rfile, self.inheaders)
    435         except ValueError as ex:
    436             self.simple_response('400 Bad Request', ex.args[0])
    437             return False
    438 
    439         mrbs = self.server.max_request_body_size
    440         if mrbs and int(self.inheaders.get('Content-Length', 0)) > mrbs:
    441             self.simple_response('413 Request Entity Too Large', 'The entity sent with the request exceeds the maximum allowed bytes.')
    442             return False
    443         if self.response_protocol == 'HTTP/1.1':
    444             if self.inheaders.get('Connection', '') == 'close':
    445                 self.close_connection = True
    446         elif self.inheaders.get('Connection', '') != 'Keep-Alive':
    447             self.close_connection = True
    448         te = None
    449         if self.response_protocol == 'HTTP/1.1':
    450             te = self.inheaders.get('Transfer-Encoding')
    451             if te:
    452                 te = [ x.strip().lower() for x in te.split(',') if x.strip() ]
    453         self.chunked_read = False
    454         if te:
    455             for enc in te:
    456                 if enc == 'chunked':
    457                     self.chunked_read = True
    458                 else:
    459                     self.simple_response('501 Unimplemented')
    460                     self.close_connection = True
    461                     return False
    462 
    463         if self.inheaders.get('Expect', '') == '100-continue':
    464             msg = self.server.protocol + ' 100 Continue\r\n\r\n'
    465             try:
    466                 self.conn.wfile.sendall(msg)
    467             except socket.error as x:
    468                 if x.args[0] not in socket_errors_to_ignore:
    469                     raise
    470 
    471         return True
    472 
    473     def parse_request_uri(self, uri):
    474         if uri == '*':
    475             return (None, None, uri)
    476         i = uri.find('://')
    477         if i > 0 and '?' not in uri[:i]:
    478             scheme, remainder = uri[:i].lower(), uri[i + 3:]
    479             authority, path = remainder.split('/', 1)
    480             return (scheme, authority, path)
    481         elif uri.startswith('/'):
    482             return (None, None, uri)
    483         else:
    484             return (None, uri, None)
    485 
    486     def respond(self):
    487         mrbs = self.server.max_request_body_size
    488         if self.chunked_read:
    489             self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
    490         else:
    491             cl = int(self.inheaders.get('Content-Length', 0))
    492             if mrbs and mrbs < cl:
    493                 if not self.sent_headers:
    494                     self.simple_response('413 Request Entity Too Large', 'The entity sent with the request exceeds the maximum allowed bytes.')
    495                 return
    496             self.rfile = KnownLengthRFile(self.conn.rfile, cl)
    497         self.server.gateway(self).respond()
    498         if self.ready and not self.sent_headers:
    499             self.sent_headers = True
    500             self.send_headers()
    501         if self.chunked_write:
    502             self.conn.wfile.sendall('0\r\n\r\n')
    503 
    504     def simple_response(self, status, msg = ''):
    505         status = str(status)
    506         buf = [self.server.protocol + ' ' + status + CRLF, 'Content-Length: %s\r\n' % len(msg), 'Content-Type: text/plain\r\n']
    507         if status[:3] in ('413', '414'):
    508             self.close_connection = True
    509             if self.response_protocol == 'HTTP/1.1':
    510                 buf.append('Connection: close\r\n')
    511             else:
    512                 status = '400 Bad Request'
    513         buf.append(CRLF)
    514         if msg:
    515             if isinstance(msg, unicode):
    516                 msg = msg.encode('ISO-8859-1')
    517             buf.append(msg)
    518         try:
    519             self.conn.wfile.sendall(''.join(buf))
    520         except socket.error as x:
    521             if x.args[0] not in socket_errors_to_ignore:
    522                 raise
    523 
    524     def write(self, chunk):
    525         if self.chunked_write and chunk:
    526             buf = [hex(len(chunk))[2:],
    527              CRLF,
    528              chunk,
    529              CRLF]
    530             self.conn.wfile.sendall(''.join(buf))
    531         else:
    532             self.conn.wfile.sendall(chunk)
    533 
    534     def send_headers(self):
    535         hkeys = [ key.lower() for key, value in self.outheaders ]
    536         status = int(self.status[:3])
    537         if status == 413:
    538             self.close_connection = True
    539         elif 'content-length' not in hkeys:
    540             if status < 200 or status in (204, 205, 304):
    541                 pass
    542             elif self.response_protocol == 'HTTP/1.1' and self.method != 'HEAD':
    543                 self.chunked_write = True
    544                 self.outheaders.append(('Transfer-Encoding', 'chunked'))
    545             else:
    546                 self.close_connection = True
    547         if 'connection' not in hkeys:
    548             if self.response_protocol == 'HTTP/1.1':
    549                 if self.close_connection:
    550                     self.outheaders.append(('Connection', 'close'))
    551             elif not self.close_connection:
    552                 self.outheaders.append(('Connection', 'Keep-Alive'))
    553         if not self.close_connection and not self.chunked_read:
    554             remaining = getattr(self.rfile, 'remaining', 0)
    555             if remaining > 0:
    556                 self.rfile.read(remaining)
    557         if 'date' not in hkeys:
    558             self.outheaders.append(('Date', rfc822.formatdate()))
    559         if 'server' not in hkeys:
    560             self.outheaders.append(('Server', self.server.server_name))
    561         buf = [self.server.protocol + ' ' + self.status + CRLF]
    562         for k, v in self.outheaders:
    563             buf.append(k + ': ' + v + CRLF)
    564 
    565         buf.append(CRLF)
    566         self.conn.wfile.sendall(''.join(buf))
    567 
    568 
    569 class NoSSLError(Exception):
    570     pass
    571 
    572 
    573 class FatalSSLAlert(Exception):
    574     pass
    575 
    576 
    577 class CP_fileobject(socket._fileobject):
    578 
    579     def __init__(self, *args, **kwargs):
    580         self.bytes_read = 0
    581         self.bytes_written = 0
    582         socket._fileobject.__init__(self, *args, **kwargs)
    583 
    584     def sendall(self, data):
    585         while data:
    586             try:
    587                 bytes_sent = self.send(data)
    588                 data = data[bytes_sent:]
    589             except socket.error as e:
    590                 if e.args[0] not in socket_errors_nonblocking:
    591                     raise
    592 
    593     def send(self, data):
    594         bytes_sent = self._sock.send(data)
    595         self.bytes_written += bytes_sent
    596         return bytes_sent
    597 
    598     def flush(self):
    599         if self._wbuf:
    600             buffer = ''.join(self._wbuf)
    601             self._wbuf = []
    602             self.sendall(buffer)
    603 
    604     def recv(self, size):
    605         while True:
    606             try:
    607                 data = self._sock.recv(size)
    608                 self.bytes_read += len(data)
    609                 return data
    610             except socket.error as e:
    611                 if e.args[0] not in socket_errors_nonblocking and e.args[0] not in socket_error_eintr:
    612                     raise
    613 
    614     if not _fileobject_uses_str_type:
    615 
    616         def read(self, size = -1):
    617             rbufsize = max(self._rbufsize, self.default_bufsize)
    618             buf = self._rbuf
    619             buf.seek(0, 2)
    620             if size < 0:
    621                 self._rbuf = StringIO.StringIO()
    622                 while True:
    623                     data = self.recv(rbufsize)
    624                     if not data:
    625                         break
    626                     buf.write(data)
    627 
    628                 return buf.getvalue()
    629             else:
    630                 buf_len = buf.tell()
    631                 if buf_len >= size:
    632                     buf.seek(0)
    633                     rv = buf.read(size)
    634                     self._rbuf = StringIO.StringIO()
    635                     self._rbuf.write(buf.read())
    636                     return rv
    637                 self._rbuf = StringIO.StringIO()
    638                 while True:
    639                     left = size - buf_len
    640                     data = self.recv(left)
    641                     if not data:
    642                         break
    643                     n = len(data)
    644                     if n == size and not buf_len:
    645                         return data
    646                     if n == left:
    647                         buf.write(data)
    648                         del data
    649                         break
    650                     buf.write(data)
    651                     buf_len += n
    652                     del data
    653 
    654                 return buf.getvalue()
    655 
    656         def readline(self, size = -1):
    657             buf = self._rbuf
    658             buf.seek(0, 2)
    659             if buf.tell() > 0:
    660                 buf.seek(0)
    661                 bline = buf.readline(size)
    662                 if bline.endswith('\n') or len(bline) == size:
    663                     self._rbuf = StringIO.StringIO()
    664                     self._rbuf.write(buf.read())
    665                     return bline
    666                 del bline
    667             if size < 0:
    668                 if self._rbufsize <= 1:
    669                     buf.seek(0)
    670                     buffers = [buf.read()]
    671                     self._rbuf = StringIO.StringIO()
    672                     data = None
    673                     recv = self.recv
    674                     while data != '\n':
    675                         data = recv(1)
    676                         if not data:
    677                             break
    678                         buffers.append(data)
    679 
    680                     return ''.join(buffers)
    681                 buf.seek(0, 2)
    682                 self._rbuf = StringIO.StringIO()
    683                 while True:
    684                     data = self.recv(self._rbufsize)
    685                     if not data:
    686                         break
    687                     nl = data.find('\n')
    688                     if nl >= 0:
    689                         nl += 1
    690                         buf.write(data[:nl])
    691                         self._rbuf.write(data[nl:])
    692                         del data
    693                         break
    694                     buf.write(data)
    695 
    696                 return buf.getvalue()
    697             else:
    698                 buf.seek(0, 2)
    699                 buf_len = buf.tell()
    700                 if buf_len >= size:
    701                     buf.seek(0)
    702                     rv = buf.read(size)
    703                     self._rbuf = StringIO.StringIO()
    704                     self._rbuf.write(buf.read())
    705                     return rv
    706                 self._rbuf = StringIO.StringIO()
    707                 while True:
    708                     data = self.recv(self._rbufsize)
    709                     if not data:
    710                         break
    711                     left = size - buf_len
    712                     nl = data.find('\n', 0, left)
    713                     if nl >= 0:
    714                         nl += 1
    715                         self._rbuf.write(data[nl:])
    716                         if buf_len:
    717                             buf.write(data[:nl])
    718                             break
    719                         else:
    720                             return data[:nl]
    721                     n = len(data)
    722                     if n == size and not buf_len:
    723                         return data
    724                     if n >= left:
    725                         buf.write(data[:left])
    726                         self._rbuf.write(data[left:])
    727                         break
    728                     buf.write(data)
    729                     buf_len += n
    730 
    731                 return buf.getvalue()
    732 
    733     else:
    734 
    735         def read(self, size = -1):
    736             if size < 0:
    737                 buffers = [self._rbuf]
    738                 self._rbuf = ''
    739                 if self._rbufsize <= 1:
    740                     recv_size = self.default_bufsize
    741                 else:
    742                     recv_size = self._rbufsize
    743                 while True:
    744                     data = self.recv(recv_size)
    745                     if not data:
    746                         break
    747                     buffers.append(data)
    748 
    749                 return ''.join(buffers)
    750             else:
    751                 data = self._rbuf
    752                 buf_len = len(data)
    753                 if buf_len >= size:
    754                     self._rbuf = data[size:]
    755                     return data[:size]
    756                 buffers = []
    757                 if data:
    758                     buffers.append(data)
    759                 self._rbuf = ''
    760                 while True:
    761                     left = size - buf_len
    762                     recv_size = max(self._rbufsize, left)
    763                     data = self.recv(recv_size)
    764                     if not data:
    765                         break
    766                     buffers.append(data)
    767                     n = len(data)
    768                     if n >= left:
    769                         self._rbuf = data[left:]
    770                         buffers[-1] = data[:left]
    771                         break
    772                     buf_len += n
    773 
    774                 return ''.join(buffers)
    775 
    776         def readline(self, size = -1):
    777             data = self._rbuf
    778             if size < 0:
    779                 if self._rbufsize <= 1:
    780                     buffers = []
    781                     while data != '\n':
    782                         data = self.recv(1)
    783                         if not data:
    784                             break
    785                         buffers.append(data)
    786 
    787                     return ''.join(buffers)
    788                 nl = data.find('\n')
    789                 if nl >= 0:
    790                     nl += 1
    791                     self._rbuf = data[nl:]
    792                     return data[:nl]
    793                 buffers = []
    794                 if data:
    795                     buffers.append(data)
    796                 self._rbuf = ''
    797                 while True:
    798                     data = self.recv(self._rbufsize)
    799                     if not data:
    800                         break
    801                     buffers.append(data)
    802                     nl = data.find('\n')
    803                     if nl >= 0:
    804                         nl += 1
    805                         self._rbuf = data[nl:]
    806                         buffers[-1] = data[:nl]
    807                         break
    808 
    809                 return ''.join(buffers)
    810             else:
    811                 nl = data.find('\n', 0, size)
    812                 if nl >= 0:
    813                     nl += 1
    814                     self._rbuf = data[nl:]
    815                     return data[:nl]
    816                 buf_len = len(data)
    817                 if buf_len >= size:
    818                     self._rbuf = data[size:]
    819                     return data[:size]
    820                 buffers = []
    821                 if data:
    822                     buffers.append(data)
    823                 self._rbuf = ''
    824                 while True:
    825                     data = self.recv(self._rbufsize)
    826                     if not data:
    827                         break
    828                     buffers.append(data)
    829                     left = size - buf_len
    830                     nl = data.find('\n', 0, left)
    831                     if nl >= 0:
    832                         nl += 1
    833                         self._rbuf = data[nl:]
    834                         buffers[-1] = data[:nl]
    835                         break
    836                     n = len(data)
    837                     if n >= left:
    838                         self._rbuf = data[left:]
    839                         buffers[-1] = data[:left]
    840                         break
    841                     buf_len += n
    842 
    843                 return ''.join(buffers)
    844 
    845 
    846 class HTTPConnection(object):
    847     remote_addr = None
    848     remote_port = None
    849     ssl_env = None
    850     rbufsize = DEFAULT_BUFFER_SIZE
    851     wbufsize = DEFAULT_BUFFER_SIZE
    852     RequestHandlerClass = HTTPRequest
    853 
    854     def __init__(self, server, sock, makefile = CP_fileobject):
    855         self.server = server
    856         self.socket = sock
    857         self.rfile = makefile(sock, 'rb', self.rbufsize)
    858         self.wfile = makefile(sock, 'wb', self.wbufsize)
    859         self.requests_seen = 0
    860 
    861     def communicate(self):
    862         request_seen = False
    863         try:
    864             while True:
    865                 req = None
    866                 req = self.RequestHandlerClass(self.server, self)
    867                 req.parse_request()
    868                 if self.server.stats['Enabled']:
    869                     self.requests_seen += 1
    870                 if not req.ready:
    871                     return
    872                 request_seen = True
    873                 req.respond()
    874                 if req.close_connection:
    875                     return
    876 
    877         except socket.error as e:
    878             errnum = e.args[0]
    879             if errnum == 'timed out' or errnum == 'The read operation timed out':
    880                 if not request_seen or req and req.started_request:
    881                     if req and not req.sent_headers:
    882                         try:
    883                             req.simple_response('408 Request Timeout')
    884                         except FatalSSLAlert:
    885                             return
    886 
    887             elif errnum not in socket_errors_to_ignore:
    888                 LogError()
    889                 if req and not req.sent_headers:
    890                     try:
    891                         req.simple_response('500 Internal Server Error', format_exc())
    892                     except FatalSSLAlert:
    893                         return
    894 
    895             return
    896         except (KeyboardInterrupt, SystemExit):
    897             raise
    898         except FatalSSLAlert:
    899             return
    900         except NoSSLError:
    901             if req and not req.sent_headers:
    902                 self.wfile = CP_fileobject(self.socket._sock, 'wb', self.wbufsize)
    903                 req.simple_response('400 Bad Request', 'The client sent a plain HTTP request, but this server only speaks HTTPS on this port.')
    904                 self.linger = True
    905         except Exception:
    906             LogError()
    907             if req and not req.sent_headers:
    908                 try:
    909                     req.simple_response('500 Internal Server Error', format_exc())
    910                 except FatalSSLAlert:
    911                     return
    912 
    913     linger = False
    914 
    915     def close(self):
    916         self.rfile.close()
    917         if not self.linger:
    918             if hasattr(self.socket, '_sock'):
    919                 self.socket._sock.close()
    920             self.socket.close()
    921 
    922 
    923 def LogError():
    924     import traceback
    925     traceback.print_exc()
    926 
    927 
    928 _SHUTDOWNREQUEST = None
    929 
    930 class WorkerThread(threading.Thread):
    931     conn = None
    932     server = None
    933     ready = False
    934 
    935     def __init__(self, server):
    936         self.ready = False
    937         self.server = server
    938         self.requests_seen = 0
    939         self.bytes_read = 0
    940         self.bytes_written = 0
    941         self.start_time = None
    942         self.work_time = 0
    943         self.stats = {'Requests': lambda s: self.requests_seen + (self.start_time is None and 0 or self.conn.requests_seen),
    944          'Bytes Read': lambda s: self.bytes_read + (self.start_time is None and 0 or self.conn.rfile.bytes_read),
    945          'Bytes Written': lambda s: self.bytes_written + (self.start_time is None and 0 or self.conn.wfile.bytes_written),
    946          'Work Time': lambda s: self.work_time + (self.start_time is None and 0 or time.time() - self.start_time),
    947          'Read Throughput': lambda s: s['Bytes Read'](s) / (s['Work Time'](s) or 1e-06),
    948          'Write Throughput': lambda s: s['Bytes Written'](s) / (s['Work Time'](s) or 1e-06)}
    949         threading.Thread.__init__(self)
    950 
    951     def run(self):
    952         self.server.stats['Worker Threads'][self.getName()] = self.stats
    953         try:
    954             self.ready = True
    955             while True:
    956                 conn = self.server.requests.get()
    957                 if conn is _SHUTDOWNREQUEST:
    958                     return
    959                 self.conn = conn
    960                 if self.server.stats['Enabled']:
    961                     self.start_time = time.time()
    962                 try:
    963                     conn.communicate()
    964                 finally:
    965                     conn.close()
    966                     if self.server.stats['Enabled']:
    967                         self.requests_seen += self.conn.requests_seen
    968                         self.bytes_read += self.conn.rfile.bytes_read
    969                         self.bytes_written += self.conn.wfile.bytes_written
    970                         self.work_time += time.time() - self.start_time
    971                         self.start_time = None
    972                     self.conn = None
    973 
    974         except (KeyboardInterrupt, SystemExit) as exc:
    975             self.server.interrupt = exc
    976 
    977 
    978 class ThreadPool(object):
    979 
    980     def __init__(self, server, min = 10, max = -1):
    981         self.server = server
    982         self.min = min
    983         self.max = max
    984         self._threads = []
    985         self._queue = Queue.Queue()
    986         self.get = self._queue.get
    987 
    988     def start(self):
    989         for i in range(self.min):
    990             self._threads.append(WorkerThread(self.server))
    991 
    992         for worker in self._threads:
    993             worker.setName('CP Server ' + worker.getName())
    994             worker.start()
    995 
    996         for worker in self._threads:
    997             while not worker.ready:
    998                 time.sleep(0.1)
    999 
   1000     def _get_idle(self):
   1001         return len([ t for t in self._threads if t.conn is None ])
   1002 
   1003     idle = property(_get_idle, doc=_get_idle.__doc__)
   1004 
   1005     def put(self, obj):
   1006         self._queue.put(obj)
   1007         if obj is _SHUTDOWNREQUEST:
   1008             return
   1009 
   1010     def grow(self, amount):
   1011         for i in range(amount):
   1012             if self.max > 0 and len(self._threads) >= self.max:
   1013                 break
   1014             worker = WorkerThread(self.server)
   1015             worker.setName('CP Server ' + worker.getName())
   1016             self._threads.append(worker)
   1017             worker.start()
   1018 
   1019     def shrink(self, amount):
   1020         for t in self._threads:
   1021             if not t.isAlive():
   1022                 self._threads.remove(t)
   1023                 amount -= 1
   1024 
   1025         if amount > 0:
   1026             for i in range(min(amount, len(self._threads) - self.min)):
   1027                 self._queue.put(_SHUTDOWNREQUEST)
   1028 
   1029     def stop(self, timeout = 5):
   1030         for worker in self._threads:
   1031             self._queue.put(_SHUTDOWNREQUEST)
   1032 
   1033         current = threading.currentThread()
   1034         if timeout and timeout >= 0:
   1035             endtime = time.time() + timeout
   1036         while self._threads:
   1037             worker = self._threads.pop()
   1038             if worker is not current and worker.isAlive():
   1039                 try:
   1040                     if timeout is None or timeout < 0:
   1041                         worker.join()
   1042                     else:
   1043                         remaining_time = endtime - time.time()
   1044                         if remaining_time > 0:
   1045                             worker.join(remaining_time)
   1046                         if worker.isAlive():
   1047                             c = worker.conn
   1048                             if c and not c.rfile.closed:
   1049                                 try:
   1050                                     c.socket.shutdown(socket.SHUT_RD)
   1051                                 except TypeError:
   1052                                     c.socket.shutdown()
   1053 
   1054                             worker.join()
   1055                 except (AssertionError, KeyboardInterrupt) as exc1:
   1056                     pass
   1057 
   1058     def _get_qsize(self):
   1059         return self._queue.qsize()
   1060 
   1061     qsize = property(_get_qsize)
   1062 
   1063 
   1064 try:
   1065     import fcntl
   1066 except ImportError:
   1067     try:
   1068         from ctypes import windll, WinError
   1069         import ctypes
   1070         import ctypes.wintypes
   1071     except ImportError:
   1072 
   1073         def prevent_socket_inheritance(sock):
   1074             pass
   1075 
   1076 
   1077     else:
   1078         kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
   1079         GetLastError = ctypes.get_last_error
   1080         _SetHandleInformation = kernel32.SetHandleInformation
   1081         _SetHandleInformation.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD]
   1082         _SetHandleInformation.restype = ctypes.wintypes.BOOL
   1083 
   1084         def WinError(code = None, descr = None):
   1085             if code is None:
   1086                 code = GetLastError()
   1087             return ctypes.WinError(code, descr)
   1088 
   1089 
   1090         def prevent_socket_inheritance(sock):
   1091             try:
   1092                 if not kernel32.SetHandleInformation(sock.fileno(), 1, 0):
   1093                     raise WinError()
   1094             except Exception:
   1095                 logging.exception('WSGI main ticker got an error')
   1096 
   1097 
   1098 else:
   1099 
   1100     def prevent_socket_inheritance(sock):
   1101         fd = sock.fileno()
   1102         old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
   1103         fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
   1104 
   1105 
   1106 class SSLAdapter(object):
   1107 
   1108     def __init__(self, certificate, private_key, certificate_chain = None):
   1109         self.certificate = certificate
   1110         self.private_key = private_key
   1111         self.certificate_chain = certificate_chain
   1112 
   1113     def wrap(self, sock):
   1114         raise NotImplemented
   1115 
   1116     def makefile(self, sock, mode = 'r', bufsize = DEFAULT_BUFFER_SIZE):
   1117         raise NotImplemented
   1118 
   1119 
   1120 class HTTPServer(object):
   1121     _bind_addr = '127.0.0.1'
   1122     _interrupt = None
   1123     gateway = None
   1124     minthreads = None
   1125     maxthreads = None
   1126     server_name = None
   1127     protocol = 'HTTP/1.1'
   1128     request_queue_size = 5
   1129     shutdown_timeout = 5
   1130     timeout = 10
   1131     version = 'CherryPy/3.2.0'
   1132     software = None
   1133     ready = False
   1134     max_request_header_size = 0
   1135     max_request_body_size = 0
   1136     nodelay = True
   1137     ConnectionClass = HTTPConnection
   1138     ssl_adapter = None
   1139 
   1140     def __init__(self, bind_addr, gateway, minthreads = 10, maxthreads = -1, server_name = None):
   1141         self.bind_addr = bind_addr
   1142         self.gateway = gateway
   1143         self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads)
   1144         if not server_name:
   1145             server_name = socket.gethostname()
   1146         self.server_name = server_name
   1147         self.clear_stats()
   1148 
   1149     def clear_stats(self):
   1150         self._start_time = None
   1151         self._run_time = 0
   1152         self.stats = {'Enabled': False,
   1153          'Bind Address': lambda s: repr(self.bind_addr),
   1154          'Run time': lambda s: not s['Enabled'] and 0 or self.runtime(),
   1155          'Accepts': 0,
   1156          'Accepts/sec': lambda s: s['Accepts'] / self.runtime(),
   1157          'Queue': lambda s: getattr(self.requests, 'qsize', None),
   1158          'Threads': lambda s: len(getattr(self.requests, '_threads', [])),
   1159          'Threads Idle': lambda s: getattr(self.requests, 'idle', None),
   1160          'Socket Errors': 0,
   1161          'Requests': lambda s: not s['Enabled'] and 0 or sum([ w['Requests'](w) for w in s['Worker Threads'].values() ], 0),
   1162          'Bytes Read': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Read'](w) for w in s['Worker Threads'].values() ], 0),
   1163          'Bytes Written': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Written'](w) for w in s['Worker Threads'].values() ], 0),
   1164          'Work Time': lambda s: not s['Enabled'] and 0 or sum([ w['Work Time'](w) for w in s['Worker Threads'].values() ], 0),
   1165          'Read Throughput': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Read'](w) / (w['Work Time'](w) or 1e-06) for w in s['Worker Threads'].values() ], 0),
   1166          'Write Throughput': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Written'](w) / (w['Work Time'](w) or 1e-06) for w in s['Worker Threads'].values() ], 0),
   1167          'Worker Threads': {}}
   1168         logging.statistics['CherryPy HTTPServer %d' % id(self)] = self.stats
   1169 
   1170     def runtime(self):
   1171         if self._start_time is None:
   1172             return self._run_time
   1173         else:
   1174             return self._run_time + (time.time() - self._start_time)
   1175 
   1176     def __str__(self):
   1177         return '%s.%s(%r)' % (self.__module__, self.__class__.__name__, self.bind_addr)
   1178 
   1179     def _get_bind_addr(self):
   1180         return self._bind_addr
   1181 
   1182     def _set_bind_addr(self, value):
   1183         if isinstance(value, tuple) and value[0] in ('', None):
   1184             raise ValueError("Host values of '' or None are not allowed. Use '0.0.0.0' (IPv4) or '::' (IPv6) instead to listen on all active interfaces.")
   1185         self._bind_addr = value
   1186 
   1187     bind_addr = property(_get_bind_addr, _set_bind_addr, doc='The interface on which to listen for connections.\n\n        For TCP sockets, a (host, port) tuple. Host values may be any IPv4\n        or IPv6 address, or any valid hostname. The string \'localhost\' is a\n        synonym for \'127.0.0.1\' (or \'::1\', if your hosts file prefers IPv6).\n        The string \'0.0.0.0\' is a special IPv4 entry meaning "any active\n        interface" (INADDR_ANY), and \'::\' is the similar IN6ADDR_ANY for\n        IPv6. The empty string or None are not allowed.\n\n        For UNIX sockets, supply the filename as a string.')
   1188 
   1189     def start(self):
   1190         self._interrupt = None
   1191         if self.software is None:
   1192             self.software = '%s Server' % self.version
   1193         if self.ssl_adapter is None and getattr(self, 'ssl_certificate', None) and getattr(self, 'ssl_private_key', None):
   1194             warnings.warn('SSL attributes are deprecated in CherryPy 3.2, and will be removed in CherryPy 3.3. Use an ssl_adapter attribute instead.', DeprecationWarning)
   1195             try:
   1196                 from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
   1197             except ImportError:
   1198                 pass
   1199             else:
   1200                 self.ssl_adapter = pyOpenSSLAdapter(self.ssl_certificate, self.ssl_private_key, getattr(self, 'ssl_certificate_chain', None))
   1201 
   1202         if isinstance(self.bind_addr, basestring):
   1203             try:
   1204                 os.unlink(self.bind_addr)
   1205             except:
   1206                 pass
   1207 
   1208             try:
   1209                 os.chmod(self.bind_addr, 511)
   1210             except:
   1211                 pass
   1212 
   1213             info = [(socket.AF_UNIX,
   1214               socket.SOCK_STREAM,
   1215               0,
   1216               '',
   1217               self.bind_addr)]
   1218         else:
   1219             host, port = self.bind_addr
   1220             try:
   1221                 info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
   1222             except socket.gaierror:
   1223                 if ':' in self.bind_addr[0]:
   1224                     info = [(socket.AF_INET6,
   1225                       socket.SOCK_STREAM,
   1226                       0,
   1227                       '',
   1228                       self.bind_addr + (0, 0))]
   1229                 else:
   1230                     info = [(socket.AF_INET,
   1231                       socket.SOCK_STREAM,
   1232                       0,
   1233                       '',
   1234                       self.bind_addr)]
   1235 
   1236         self.socket = None
   1237         msg = 'No socket could be created'
   1238         for res in info:
   1239             af, socktype, proto, canonname, sa = res
   1240             try:
   1241                 self.bind(af, socktype, proto)
   1242             except socket.error:
   1243                 if self.socket:
   1244                     self.socket.close()
   1245                 self.socket = None
   1246                 continue
   1247 
   1248             break
   1249 
   1250         if not self.socket:
   1251             raise socket.error(msg)
   1252         self.socket.settimeout(1)
   1253         self.socket.listen(self.request_queue_size)
   1254         self.requests.start()
   1255         self.ready = True
   1256         self._start_time = time.time()
   1257         while self.ready:
   1258             self.tick()
   1259             if self.interrupt:
   1260                 while self.interrupt is True:
   1261                     time.sleep(0.1)
   1262 
   1263                 if self.interrupt:
   1264                     raise self.interrupt
   1265 
   1266     def bind(self, family, type, proto = 0):
   1267         self.socket = socket.socket(family, type, proto)
   1268         prevent_socket_inheritance(self.socket)
   1269         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
   1270         if self.nodelay and not isinstance(self.bind_addr, str):
   1271             self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
   1272         if self.ssl_adapter is not None:
   1273             self.socket = self.ssl_adapter.bind(self.socket)
   1274         if hasattr(socket, 'AF_INET6') and family == socket.AF_INET6 and self.bind_addr[0] in ('::', '::0', '::0.0.0.0'):
   1275             try:
   1276                 self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
   1277             except (AttributeError, socket.error):
   1278                 pass
   1279 
   1280         self.socket.bind(self.bind_addr)
   1281 
   1282     def tick(self):
   1283         try:
   1284             s, addr = self.socket.accept()
   1285             if self.stats['Enabled']:
   1286                 self.stats['Accepts'] += 1
   1287             if not self.ready:
   1288                 return
   1289             prevent_socket_inheritance(s)
   1290             if hasattr(s, 'settimeout'):
   1291                 s.settimeout(self.timeout)
   1292             makefile = CP_fileobject
   1293             ssl_env = {}
   1294             if self.ssl_adapter is not None:
   1295                 try:
   1296                     s, ssl_env = self.ssl_adapter.wrap(s)
   1297                 except NoSSLError:
   1298                     msg = 'The client sent a plain HTTP request, but this server only speaks HTTPS on this port.'
   1299                     buf = ['%s 400 Bad Request\r\n' % self.protocol,
   1300                      'Content-Length: %s\r\n' % len(msg),
   1301                      'Content-Type: text/plain\r\n\r\n',
   1302                      msg]
   1303                     wfile = CP_fileobject(s, 'wb', DEFAULT_BUFFER_SIZE)
   1304                     try:
   1305                         wfile.sendall(''.join(buf))
   1306                     except socket.error as x:
   1307                         if x.args[0] not in socket_errors_to_ignore:
   1308                             raise
   1309 
   1310                     return
   1311 
   1312                 if not s:
   1313                     return
   1314                 makefile = self.ssl_adapter.makefile
   1315                 if hasattr(s, 'settimeout'):
   1316                     s.settimeout(self.timeout)
   1317             conn = self.ConnectionClass(self, s, makefile)
   1318             if not isinstance(self.bind_addr, basestring):
   1319                 if addr is None:
   1320                     if len(s.getsockname()) == 2:
   1321                         addr = ('0.0.0.0', 0)
   1322                     else:
   1323                         addr = ('::', 0)
   1324                 conn.remote_addr = addr[0]
   1325                 conn.remote_port = addr[1]
   1326             conn.ssl_env = ssl_env
   1327             self.requests.put(conn)
   1328         except socket.timeout:
   1329             return
   1330         except socket.error as x:
   1331             if self.stats['Enabled']:
   1332                 self.stats['Socket Errors'] += 1
   1333             if x.args[0] in socket_error_eintr:
   1334                 return
   1335             if x.args[0] in socket_errors_nonblocking:
   1336                 return
   1337             if x.args[0] in socket_errors_to_ignore:
   1338                 return
   1339             raise
   1340 
   1341     def _get_interrupt(self):
   1342         return self._interrupt
   1343 
   1344     def _set_interrupt(self, interrupt):
   1345         self._interrupt = True
   1346         self.stop()
   1347         self._interrupt = interrupt
   1348 
   1349     interrupt = property(_get_interrupt, _set_interrupt, doc='Set this to an Exception instance to interrupt the server.')
   1350 
   1351     def stop(self):
   1352         self.ready = False
   1353         if self._start_time is not None:
   1354             self._run_time += time.time() - self._start_time
   1355         self._start_time = None
   1356         sock = getattr(self, 'socket', None)
   1357         if sock:
   1358             if not isinstance(self.bind_addr, basestring):
   1359                 try:
   1360                     host, port = sock.getsockname()[:2]
   1361                 except socket.error as x:
   1362                     if x.args[0] not in socket_errors_to_ignore:
   1363                         raise
   1364                 else:
   1365                     for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
   1366                         af, socktype, proto, canonname, sa = res
   1367                         s = None
   1368                         try:
   1369                             s = socket.socket(af, socktype, proto)
   1370                             s.settimeout(1.0)
   1371                             s.connect((host, port))
   1372                             s.close()
   1373                         except socket.error:
   1374                             if s:
   1375                                 s.close()
   1376 
   1377             if hasattr(sock, 'close'):
   1378                 sock.close()
   1379             self.socket = None
   1380         self.requests.stop(self.shutdown_timeout)
   1381 
   1382 
   1383 class Gateway(object):
   1384 
   1385     def __init__(self, req):
   1386         self.req = req
   1387 
   1388     def respond(self):
   1389         raise NotImplemented
   1390 
   1391 
   1392 ssl_adapters = {'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
   1393  'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter'}
   1394 
   1395 def get_ssl_adapter_class(name = 'pyopenssl'):
   1396     adapter = ssl_adapters[name.lower()]
   1397     if isinstance(adapter, basestring):
   1398         last_dot = adapter.rfind('.')
   1399         attr_name = adapter[last_dot + 1:]
   1400         mod_path = adapter[:last_dot]
   1401         try:
   1402             mod = sys.modules[mod_path]
   1403             if mod is None:
   1404                 raise KeyError()
   1405         except KeyError:
   1406             mod = __import__(mod_path, globals(), locals(), [''])
   1407 
   1408         try:
   1409             adapter = getattr(mod, attr_name)
   1410         except AttributeError:
   1411             raise AttributeError("'%s' object has no attribute '%s'" % (mod_path, attr_name))
   1412 
   1413     return adapter
   1414 
   1415 
   1416 class CherryPyWSGIServer(HTTPServer):
   1417     wsgi_version = (1, 0)
   1418 
   1419     def __init__(self, bind_addr, wsgi_app, numthreads = 10, server_name = None, max = -1, request_queue_size = 5, timeout = 10, shutdown_timeout = 5):
   1420         self.requests = ThreadPool(self, min=numthreads or 1, max=max)
   1421         self.wsgi_app = wsgi_app
   1422         self.gateway = wsgi_gateways[self.wsgi_version]
   1423         self.bind_addr = bind_addr
   1424         if not server_name:
   1425             server_name = socket.gethostname()
   1426         self.server_name = server_name
   1427         self.request_queue_size = request_queue_size
   1428         self.timeout = timeout
   1429         self.shutdown_timeout = shutdown_timeout
   1430         self.clear_stats()
   1431 
   1432     def _get_numthreads(self):
   1433         return self.requests.min
   1434 
   1435     def _set_numthreads(self, value):
   1436         self.requests.min = value
   1437 
   1438     numthreads = property(_get_numthreads, _set_numthreads)
   1439 
   1440 
   1441 class WSGIGateway(Gateway):
   1442 
   1443     def __init__(self, req):
   1444         self.req = req
   1445         self.started_response = False
   1446         self.env = self.get_environ()
   1447         self.remaining_bytes_out = None
   1448 
   1449     def get_environ(self):
   1450         raise NotImplemented
   1451 
   1452     def respond(self):
   1453         response = self.req.server.wsgi_app(self.env, self.start_response)
   1454         try:
   1455             for chunk in response:
   1456                 if chunk:
   1457                     if isinstance(chunk, unicode):
   1458                         chunk = chunk.encode('ISO-8859-1')
   1459                     self.write(chunk)
   1460 
   1461         finally:
   1462             if hasattr(response, 'close'):
   1463                 response.close()
   1464 
   1465     def start_response(self, status, headers, exc_info = None):
   1466         if self.started_response and not exc_info:
   1467             raise AssertionError('WSGI start_response called a second time with no exc_info.')
   1468         self.started_response = True
   1469         if self.req.sent_headers:
   1470             try:
   1471                 raise exc_info[0], exc_info[1], exc_info[2]
   1472             finally:
   1473                 exc_info = None
   1474 
   1475         self.req.status = status
   1476         for k, v in headers:
   1477             if not isinstance(k, str):
   1478                 raise TypeError('WSGI response header key %r is not a byte string.' % k)
   1479             if not isinstance(v, str):
   1480                 raise TypeError('WSGI response header value %r is not a byte string.' % v)
   1481             if k.lower() == 'content-length':
   1482                 self.remaining_bytes_out = int(v)
   1483 
   1484         self.req.outheaders.extend(headers)
   1485         return self.write
   1486 
   1487     def write(self, chunk):
   1488         if not self.started_response:
   1489             raise AssertionError('WSGI write called before start_response.')
   1490         chunklen = len(chunk)
   1491         rbo = self.remaining_bytes_out
   1492         if rbo is not None and chunklen > rbo:
   1493             if not self.req.sent_headers:
   1494                 self.req.simple_response('500 Internal Server Error', 'The requested resource returned more bytes than the declared Content-Length.')
   1495             else:
   1496                 chunk = chunk[:rbo]
   1497         if not self.req.sent_headers:
   1498             self.req.sent_headers = True
   1499             self.req.send_headers()
   1500         self.req.write(chunk)
   1501         if rbo is not None:
   1502             rbo -= chunklen
   1503             if rbo < 0:
   1504                 raise ValueError('Response body exceeds the declared Content-Length.')
   1505 
   1506 
   1507 class WSGIGateway_10(WSGIGateway):
   1508 
   1509     def get_environ(self):
   1510         req = self.req
   1511         env = {'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
   1512          'PATH_INFO': req.path,
   1513          'QUERY_STRING': req.qs,
   1514          'REMOTE_ADDR': req.conn.remote_addr or '',
   1515          'REMOTE_PORT': str(req.conn.remote_port or ''),
   1516          'REQUEST_METHOD': req.method,
   1517          'REQUEST_URI': req.uri,
   1518          'SCRIPT_NAME': '',
   1519          'SERVER_NAME': req.server.server_name,
   1520          'SERVER_PROTOCOL': req.request_protocol,
   1521          'SERVER_SOFTWARE': req.server.software,
   1522          'wsgi.errors': sys.stderr,
   1523          'wsgi.input': req.rfile,
   1524          'wsgi.multiprocess': False,
   1525          'wsgi.multithread': True,
   1526          'wsgi.run_once': False,
   1527          'wsgi.url_scheme': req.scheme,
   1528          'wsgi.version': (1, 0)}
   1529         if isinstance(req.server.bind_addr, basestring):
   1530             env['SERVER_PORT'] = ''
   1531         else:
   1532             env['SERVER_PORT'] = str(req.server.bind_addr[1])
   1533         for k, v in req.inheaders.iteritems():
   1534             env['HTTP_' + k.upper().replace('-', '_')] = v
   1535 
   1536         ct = env.pop('HTTP_CONTENT_TYPE', None)
   1537         if ct is not None:
   1538             env['CONTENT_TYPE'] = ct
   1539         cl = env.pop('HTTP_CONTENT_LENGTH', None)
   1540         if cl is not None:
   1541             env['CONTENT_LENGTH'] = cl
   1542         if req.conn.ssl_env:
   1543             env.update(req.conn.ssl_env)
   1544         return env
   1545 
   1546 
   1547 class WSGIGateway_u0(WSGIGateway_10):
   1548 
   1549     def get_environ(self):
   1550         req = self.req
   1551         env_10 = WSGIGateway_10.get_environ(self)
   1552         env = dict([ (k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems() ])
   1553         env[u'wsgi.version'] = ('u', 0)
   1554         env.setdefault(u'wsgi.url_encoding', u'utf-8')
   1555         try:
   1556             for key in [u'PATH_INFO', u'SCRIPT_NAME', u'QUERY_STRING']:
   1557                 env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
   1558 
   1559         except UnicodeDecodeError:
   1560             env[u'wsgi.url_encoding'] = u'ISO-8859-1'
   1561             for key in [u'PATH_INFO', u'SCRIPT_NAME', u'QUERY_STRING']:
   1562                 env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
   1563 
   1564         for k, v in sorted(env.items()):
   1565             if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'):
   1566                 env[k] = v.decode('ISO-8859-1')
   1567 
   1568         return env
   1569 
   1570 
   1571 wsgi_gateways = {(1, 0): WSGIGateway_10,
   1572  ('u', 0): WSGIGateway_u0}
   1573 
   1574 class WSGIPathInfoDispatcher(object):
   1575 
   1576     def __init__(self, apps):
   1577         try:
   1578             apps = apps.items()
   1579         except AttributeError:
   1580             pass
   1581 
   1582         apps.sort(cmp=lambda x, y: cmp(len(x[0]), len(y[0])))
   1583         apps.reverse()
   1584         self.apps = [ (p.rstrip('/'), a) for p, a in apps ]
   1585 
   1586     def __call__(self, environ, start_response):
   1587         path = environ['PATH_INFO'] or '/'
   1588         for p, app in self.apps:
   1589             if path.startswith(p + '/') or path == p:
   1590                 environ = environ.copy()
   1591                 environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'] + p
   1592                 environ['PATH_INFO'] = path[len(p):]
   1593                 return app(environ, start_response)
   1594 
   1595         start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', '0')])
   1596         return ['']