web.web.wsgiserver

Package web :: Package web :: Package wsgiserver |
[hide private]
[frames] | no frames]

# Source Code for Package web.web.wsgiserver

       1  """A high-speed, production ready, thread pooled, generic HTTP server.
       2
       3  Simplest example on how to use this module directly
       4  (without using CherryPy's application machinery):
       5
       6      from cherrypy import wsgiserver
       7
       8      def my_crazy_app(environ, start_response):
       9          status = '200 OK'
      10          response_headers = [('Content-type','text/plain')]
      11          start_response(status, response_headers)
      12          return ['Hello world!\n']
      13
      14      server = wsgiserver.CherryPyWSGIServer(
      15                  ('0.0.0.0', 8070), my_crazy_app,
      16                  server_name='www.cherrypy.example')
      17
      18  The CherryPy WSGI server can serve as many WSGI applications
      19  as you want in one instance by using a WSGIPathInfoDispatcher:
      20
      21      d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
      22      server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
      23
      24  Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
      25
      26  This won't call the CherryPy engine (application side) at all, only the
      27  HTTP server, which is independent from the rest of CherryPy. Don't
      28  let the name "CherryPyWSGIServer" throw you; the name merely reflects
      29  its origin, not its coupling.
      30
      31  For those of you wanting to understand internals of this module, here's the
      32  basic call flow. The server's listening thread runs a very tight loop,
      33  sticking incoming connections onto a Queue:
      34
      35      server = CherryPyWSGIServer(...)
      36      server.start()
      37      while True:
      38          tick()
      39          # This blocks until a request comes in:
      40          child = socket.accept()
      41          conn = HTTPConnection(child, ...)
      42          server.requests.put(conn)
      43
      44  Worker threads are kept in a pool and poll the Queue, popping off and then
      45  handling each connection in turn. Each connection can consist of an arbitrary
      46  number of requests and their responses, so we run a nested loop:
      47
      48      while True:
      49          conn = server.requests.get()
      50          conn.communicate()
      51          ->  while True:
      52                  req = HTTPRequest(...)
      53                  req.parse_request()
      54                  ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
      55                      req.rfile.readline()
      56                      read_headers(req.rfile, req.inheaders)
      57                  req.respond()
      58                  ->  response = app(...)
      59                      try:
      60                          for chunk in response:
      61                              if chunk:
      62                                  req.write(chunk)
      63                      finally:
      64                          if hasattr(response, "close"):
      65                              response.close()
      66                  if req.close_connection:
      67                      return
      68  """
      69
      70  CRLF = '\r\n'
      71  import os
      72  import Queue
      73  import re
      74  quoted_slash = re.compile("(?i)%2F")
      75  import rfc822
      76  import socket
      77  import sys
      78  if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
      79      socket.IPPROTO_IPV6 = 41
      80  try:
      81      import cStringIO as StringIO
      82  except ImportError:
      83      import StringIO
      84
      85  _fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
      86
      87  import threading
      88  import time
      89  import traceback
      90  from urllib import unquote
      91  from urlparse import urlparse
      92  import warnings
      93
      94  import errno
      95
      96 -def plat_specific_errors(*errnames):
      97      """Return error numbers for all errors in errnames on this platform.
      98
      99      The 'errno' module contains different global constants depending on
     100      the specific platform (OS). This function will return the list of
     101      numeric values for a given list of potential names.
     102      """
     103      errno_names = dir(errno)
     104      nums = [getattr(errno, k) for k in errnames if k in errno_names]
     105      # de-dupe the list
     106      return dict.fromkeys(nums).keys()
     107
     108  socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
     109
     110  socket_errors_to_ignore = plat_specific_errors(
     111      "EPIPE",
     112      "EBADF", "WSAEBADF",
     113      "ENOTSOCK", "WSAENOTSOCK",
     114      "ETIMEDOUT", "WSAETIMEDOUT",
     115      "ECONNREFUSED", "WSAECONNREFUSED",
     116      "ECONNRESET", "WSAECONNRESET",
     117      "ECONNABORTED", "WSAECONNABORTED",
     118      "ENETRESET", "WSAENETRESET",
     119      "EHOSTDOWN", "EHOSTUNREACH",
     120      )
     121  socket_errors_to_ignore.append("timed out")
     122  socket_errors_to_ignore.append("The read operation timed out")
     123
     124  socket_errors_nonblocking = plat_specific_errors(
     125      'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
     126
     127  comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
     128      'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
     129      'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
     130      'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
     131      'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
     132      'WWW-Authenticate']
     133
     134
     135 -def read_headers(rfile, hdict=None):
     136      """Read headers from the given stream into the given header dict.
     137
     138      If hdict is None, a new header dict is created. Returns the populated
     139      header dict.
     140
     141      Headers which are repeated are folded together using a comma if their
     142      specification so dictates.
     143
     144      This function raises ValueError when the read bytes violate the HTTP spec.
     145      You should probably return "400 Bad Request" if this happens.
     146      """
     147      if hdict is None:
     148          hdict = {}
     149
     150      while True:
     151          line = rfile.readline()
     152          if not line:
     153              # No more data--illegal end of headers
     154              raise ValueError("Illegal end of headers.")
     155
     156          if line == CRLF:
     157              # Normal end of headers
     158              break
     159          if not line.endswith(CRLF):
     160              raise ValueError("HTTP requires CRLF terminators")
     161
     162          if line[0] in ' \t':
     163              # It's a continuation line.
     164              v = line.strip()
     165          else:
     166              try:
     167                  k, v = line.split(":", 1)
     168              except ValueError:
     169                  raise ValueError("Illegal header line.")
     170              # TODO: what about TE and WWW-Authenticate?
     171              k = k.strip().title()
     172              v = v.strip()
     173              hname = k
     174
     175          if k in comma_separated_headers:
     176              existing = hdict.get(hname)
     177              if existing:
     178                  v = ", ".join((existing, v))
     179          hdict[hname] = v
     180
     181      return hdict
     182
     183
     184 -class MaxSizeExceeded(Exception):
     185      pass
     186
     187 -class SizeCheckWrapper(object):
     188      """Wraps a file-like object, raising MaxSizeExceeded if too large."""
     189
     190 -    def __init__(self, rfile, maxlen):
     191          self.rfile = rfile
     192          self.maxlen = maxlen
     193          self.bytes_read = 0
     194
     195 -    def _check_length(self):
     196          if self.maxlen and self.bytes_read > self.maxlen:
     197              raise MaxSizeExceeded()
     198
     199 -    def read(self, size=None):
     200          data = self.rfile.read(size)
     201          self.bytes_read += len(data)
     202          self._check_length()
     203          return data
     204
     205 -    def readline(self, size=None):
     206          if size is not None:
     207              data = self.rfile.readline(size)
     208              self.bytes_read += len(data)
     209              self._check_length()
     210              return data
     211
     212          # User didn't specify a size ...
     213          # We read the line in chunks to make sure it's not a 100MB line !
     214          res = []
     215          while True:
     216              data = self.rfile.readline(256)
     217              self.bytes_read += len(data)
     218              self._check_length()
     219              res.append(data)
     220              # See http://www.cherrypy.org/ticket/421
     221              if len(data) < 256 or data[-1:] == "\n":
     222                  return ''.join(res)
     223
     224 -    def readlines(self, sizehint=0):
     225          # Shamelessly stolen from StringIO
     226          total = 0
     227          lines = []
     228          line = self.readline()
     229          while line:
     230              lines.append(line)
     231              total += len(line)
     232              if 0 < sizehint <= total:
     233                  break
     234              line = self.readline()
     235          return lines
     236
     237 -    def close(self):
     238          self.rfile.close()
     239
     240 -    def __iter__(self):
     241          return self
     242
     243 -    def next(self):
     244          data = self.rfile.next()
     245          self.bytes_read += len(data)
     246          self._check_length()
     247          return data
     248
     249
     250 -class KnownLengthRFile(object):
     251      """Wraps a file-like object, returning an empty string when exhausted."""
     252
     253 -    def __init__(self, rfile, content_length):
     254          self.rfile = rfile
     255          self.remaining = content_length
     256
     257 -    def read(self, size=None):
     258          if self.remaining == 0:
     259              return ''
     260          if size is None:
     261              size = self.remaining
     262          else:
     263              size = min(size, self.remaining)
     264
     265          data = self.rfile.read(size)
     266          self.remaining -= len(data)
     267          return data
     268
     269 -    def readline(self, size=None):
     270          if self.remaining == 0:
     271              return ''
     272          if size is None:
     273              size = self.remaining
     274          else:
     275              size = min(size, self.remaining)
     276
     277          data = self.rfile.readline(size)
     278          self.remaining -= len(data)
     279          return data
     280
     281 -    def readlines(self, sizehint=0):
     282          # Shamelessly stolen from StringIO
     283          total = 0
     284          lines = []
     285          line = self.readline(sizehint)
     286          while line:
     287              lines.append(line)
     288              total += len(line)
     289              if 0 < sizehint <= total:
     290                  break
     291              line = self.readline(sizehint)
     292          return lines
     293
     294 -    def close(self):
     295          self.rfile.close()
     296
     297 -    def __iter__(self):
     298          return self
     299
     300 -    def __next__(self):
     301          data = next(self.rfile)
     302          self.remaining -= len(data)
     303          return data
     304
     305
     306 -class MaxSizeExceeded(Exception):
     307      pass
     308
     309
     310 -class ChunkedRFile(object):
     311      """Wraps a file-like object, returning an empty string when exhausted.
     312
     313      This class is intended to provide a conforming wsgi.input value for
     314      request entities that have been encoded with the 'chunked' transfer
     315      encoding.
     316      """
     317
     318 -    def __init__(self, rfile, maxlen, bufsize=8192):
     319          self.rfile = rfile
     320          self.maxlen = maxlen
     321          self.bytes_read = 0
     322          self.buffer = ''
     323          self.bufsize = bufsize
     324          self.closed = False
     325
     326 -    def _fetch(self):
     327          if self.closed:
     328              return
     329
     330          line = self.rfile.readline()
     331          self.bytes_read += len(line)
     332
     333          if self.maxlen and self.bytes_read > self.maxlen:
     334              raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
     335
     336          line = line.strip().split(";", 1)
     337
     338          try:
     339              chunk_size = line.pop(0)
     340              chunk_size = int(chunk_size, 16)
     341          except ValueError:
     342              raise ValueError("Bad chunked transfer size: " + repr(chunk_size))
     343
     344          if chunk_size <= 0:
     345              self.closed = True
     346              return
     347
     348  ##            if line: chunk_extension = line[0]
     349
     350          if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
     351              raise IOError("Request Entity Too Large")
     352
     353          chunk = self.rfile.read(chunk_size)
     354          self.bytes_read += len(chunk)
     355          self.buffer += chunk
     356
     357          crlf = self.rfile.read(2)
     358          if crlf != CRLF:
     359              raise ValueError(
     360                   "Bad chunked transfer coding (expected '\\r\\n', "
     361                   "got " + repr(crlf) + ")")
     362
     363 -    def read(self, size=None):
     364          data = ''
     365          while True:
     366              if size and len(data) >= size:
     367                  return data
     368
     369              if not self.buffer:
     370                  self._fetch()
     371                  if not self.buffer:
     372                      # EOF
     373                      return data
     374
     375              if size:
     376                  remaining = size - len(data)
     377                  data += self.buffer[:remaining]
     378                  self.buffer = self.buffer[remaining:]
     379              else:
     380                  data += self.buffer
     381
     382 -    def readline(self, size=None):
     383          data = ''
     384          while True:
     385              if size and len(data) >= size:
     386                  return data
     387
     388              if not self.buffer:
     389                  self._fetch()
     390                  if not self.buffer:
     391                      # EOF
     392                      return data
     393
     394              newline_pos = self.buffer.find('\n')
     395              if size:
     396                  if newline_pos == -1:
     397                      remaining = size - len(data)
     398                      data += self.buffer[:remaining]
     399                      self.buffer = self.buffer[remaining:]
     400                  else:
     401                      remaining = min(size - len(data), newline_pos)
     402                      data += self.buffer[:remaining]
     403                      self.buffer = self.buffer[remaining:]
     404              else:
     405                  if newline_pos == -1:
     406                      data += self.buffer
     407                  else:
     408                      data += self.buffer[:newline_pos]
     409                      self.buffer = self.buffer[newline_pos:]
     410
     411 -    def readlines(self, sizehint=0):
     412          # Shamelessly stolen from StringIO
     413          total = 0
     414          lines = []
     415          line = self.readline(sizehint)
     416          while line:
     417              lines.append(line)
     418              total += len(line)
     419              if 0 < sizehint <= total:
     420                  break
     421              line = self.readline(sizehint)
     422          return lines
     423
     424 -    def read_trailer_lines(self):
     425          if not self.closed:
     426              raise ValueError(
     427                  "Cannot read trailers until the request body has been read.")
     428
     429          while True:
     430              line = self.rfile.readline()
     431              if not line:
     432                  # No more data--illegal end of headers
     433                  raise ValueError("Illegal end of headers.")
     434
     435              self.bytes_read += len(line)
     436              if self.maxlen and self.bytes_read > self.maxlen:
     437                  raise IOError("Request Entity Too Large")
     438
     439              if line == CRLF:
     440                  # Normal end of headers
     441                  break
     442              if not line.endswith(CRLF):
     443                  raise ValueError("HTTP requires CRLF terminators")
     444
     445              yield line
     446
     447 -    def close(self):
     448          self.rfile.close()
     449
     450 -    def __iter__(self):
     451          # Shamelessly stolen from StringIO
     452          total = 0
     453          line = self.readline(sizehint)
     454          while line:
     455              yield line
     456              total += len(line)
     457              if 0 < sizehint <= total:
     458                  break
     459              line = self.readline(sizehint)
     460
     461
     462 -class HTTPRequest(object):
     463      """An HTTP Request (and response).
     464
     465      A single HTTP connection may consist of multiple request/response pairs.
     466
     467      server: the Server object which is receiving this request.
     468      conn: the HTTPConnection object on which this request connected.
     469
     470      inheaders: a dict of request headers.
     471      outheaders: a list of header tuples to write in the response.
     472      ready: when True, the request has been parsed and is ready to begin
     473          generating the response. When False, signals the calling Connection
     474          that the response should not be generated and the connection should
     475          close.
     476      close_connection: signals the calling Connection that the request
     477          should close. This does not imply an error! The client and/or
     478          server may each request that the connection be closed.
     479      chunked_write: if True, output will be encoded with the "chunked"
     480          transfer-coding. This value is set automatically inside
     481          send_headers.
     482      """
     483
     484 -    def __init__(self, server, conn):
     485          self.server= server
     486          self.conn = conn
     487
     488          self.ready = False
     489          self.started_request = False
     490          self.scheme = "http"
     491          if self.server.ssl_adapter is not None:
     492              self.scheme = "https"
     493          # Use the lowest-common protocol in case read_request_line errors.
     494          self.response_protocol = 'HTTP/1.0'
     495          self.inheaders = {}
     496
     497          self.status = ""
     498          self.outheaders = []
     499          self.sent_headers = False
     500          self.close_connection = False
     501          self.chunked_read = False
     502          self.chunked_write = False
     503
     504 -    def parse_request(self):
     505          """Parse the next HTTP request start-line and message-headers."""
     506          self.rfile = SizeCheckWrapper(self.conn.rfile,
     507                                        self.server.max_request_header_size)
     508          try:
     509              self.read_request_line()
     510          except MaxSizeExceeded:
     511              self.simple_response("414 Request-URI Too Long",
     512                  "The Request-URI sent with the request exceeds the maximum "
     513                  "allowed bytes.")
     514              return
     515
     516          try:
     517              success = self.read_request_headers()
     518          except MaxSizeExceeded:
     519              self.simple_response("413 Request Entity Too Large",
     520                  "The headers sent with the request exceed the maximum "
     521                  "allowed bytes.")
     522              return
     523          else:
     524              if not success:
     525                  return
     526
     527          self.ready = True
     528
     529 -    def read_request_line(self):
     530          # HTTP/1.1 connections are persistent by default. If a client
     531          # requests a page, then idles (leaves the connection open),
     532          # then rfile.readline() will raise socket.error("timed out").
     533          # Note that it does this based on the value given to settimeout(),
     534          # and doesn't need the client to request or acknowledge the close
     535          # (although your TCP stack might suffer for it: cf Apache's history
     536          # with FIN_WAIT_2).
     537          request_line = self.rfile.readline()
     538
     539          # Set started_request to True so communicate() knows to send 408
     540          # from here on out.
     541          self.started_request = True
     542          if not request_line:
     543              # Force self.ready = False so the connection will close.
     544              self.ready = False
     545              return
     546
     547          if request_line == CRLF:
     548              # RFC 2616 sec 4.1: "...if the server is reading the protocol
     549              # stream at the beginning of a message and receives a CRLF
     550              # first, it should ignore the CRLF."
     551              # But only ignore one leading line! else we enable a DoS.
     552              request_line = self.rfile.readline()
     553              if not request_line:
     554                  self.ready = False
     555                  return
     556
     557          if not request_line.endswith(CRLF):
     558              self.simple_response("400 Bad Request", "HTTP requires CRLF terminators")
     559              return
     560
     561          try:
     562              method, uri, req_protocol = request_line.strip().split(" ", 2)
     563          except ValueError:
     564              self.simple_response("400 Bad Request", "Malformed Request-Line")
     565              return
     566
     567          self.uri = uri
     568          self.method = method
     569
     570          # uri may be an abs_path (including "http://host.domain.tld");
     571          scheme, authority, path = self.parse_request_uri(uri)
     572          if '#' in path:
     573              self.simple_response("400 Bad Request",
     574                                   "Illegal #fragment in Request-URI.")
     575              return
     576
     577          if scheme:
     578              self.scheme = scheme
     579
     580          qs = ''
     581          if '?' in path:
     582              path, qs = path.split('?', 1)
     583
     584          # Unquote the path+params (e.g. "/this%20path" -> "/this path").
     585          # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
     586          #
     587          # But note that "...a URI must be separated into its components
     588          # before the escaped characters within those components can be
     589          # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
     590          # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
     591          try:
     592              atoms = [unquote(x) for x in quoted_slash.split(path)]
     593          except ValueError, ex:
     594              self.simple_response("400 Bad Request", ex.args[0])
     595              return
     596          path = "%2F".join(atoms)
     597          self.path = path
     598
     599          # Note that, like wsgiref and most other HTTP servers,
     600          # we "% HEX HEX"-unquote the path but not the query string.
     601          self.qs = qs
     602
     603          # Compare request and server HTTP protocol versions, in case our
     604          # server does not support the requested protocol. Limit our output
     605          # to min(req, server). We want the following output:
     606          #     request    server     actual written   supported response
     607          #     protocol   protocol  response protocol    feature set
     608          # a     1.0        1.0           1.0                1.0
     609          # b     1.0        1.1           1.1                1.0
     610          # c     1.1        1.0           1.0                1.0
     611          # d     1.1        1.1           1.1                1.1
     612          # Notice that, in (b), the response will be "HTTP/1.1" even though
     613          # the client only understands 1.0. RFC 2616 10.5.6 says we should
     614          # only return 505 if the _major_ version is different.
     615          rp = int(req_protocol[5]), int(req_protocol[7])
     616          sp = int(self.server.protocol[5]), int(self.server.protocol[7])
     617
     618          if sp[0] != rp[0]:
     619              self.simple_response("505 HTTP Version Not Supported")
     620              return
     621          self.request_protocol = req_protocol
     622          self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
     623
     624 -    def read_request_headers(self):
     625          """Read self.rfile into self.inheaders. Return success."""
     626
     627          # then all the http headers
     628          try:
     629              read_headers(self.rfile, self.inheaders)
     630          except ValueError, ex:
     631              self.simple_response("400 Bad Request", ex.args[0])
     632              return False
     633
     634          mrbs = self.server.max_request_body_size
     635          if mrbs and int(self.inheaders.get("Content-Length", 0)) > mrbs:
     636              self.simple_response("413 Request Entity Too Large",
     637                  "The entity sent with the request exceeds the maximum "
     638                  "allowed bytes.")
     639              return False
     640
     641          # Persistent connection support
     642          if self.response_protocol == "HTTP/1.1":
     643              # Both server and client are HTTP/1.1
     644              if self.inheaders.get("Connection", "") == "close":
     645                  self.close_connection = True
     646          else:
     647              # Either the server or client (or both) are HTTP/1.0
     648              if self.inheaders.get("Connection", "") != "Keep-Alive":
     649                  self.close_connection = True
     650
     651          # Transfer-Encoding support
     652          te = None
     653          if self.response_protocol == "HTTP/1.1":
     654              te = self.inheaders.get("Transfer-Encoding")
     655              if te:
     656                  te = [x.strip().lower() for x in te.split(",") if x.strip()]
     657
     658          if te:
     659              for enc in te:
     660                  if enc == "chunked":
     661                      self.chunked_read = True
     662                  else:
     663                      # Note that, even if we see "chunked", we must reject
     664                      # if there is an extension we don't recognize.
     665                      self.simple_response("501 Unimplemented")
     666                      self.close_connection = True
     667                      return False
     668
     669          # From PEP 333:
     670          # "Servers and gateways that implement HTTP 1.1 must provide
     671          # transparent support for HTTP 1.1's "expect/continue" mechanism.
     672          # This may be done in any of several ways:
     673          #   1. Respond to requests containing an Expect: 100-continue request
     674          #      with an immediate "100 Continue" response, and proceed normally.
     675          #   2. Proceed with the request normally, but provide the application
     676          #      with a wsgi.input stream that will send the "100 Continue"
     677          #      response if/when the application first attempts to read from
     678          #      the input stream. The read request must then remain blocked
     679          #      until the client responds.
     680          #   3. Wait until the client decides that the server does not support
     681          #      expect/continue, and sends the request body on its own.
     682          #      (This is suboptimal, and is not recommended.)
     683          #
     684          # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
     685          # but it seems like it would be a big slowdown for such a rare case.
     686          if self.inheaders.get("Expect", "") == "100-continue":
     687              # Don't use simple_response here, because it emits headers
     688              # we don't want. See http://www.cherrypy.org/ticket/951
     689              msg = self.server.protocol + " 100 Continue\r\n\r\n"
     690              try:
     691                  self.conn.wfile.sendall(msg)
     692              except socket.error, x:
     693                  if x.args[0] not in socket_errors_to_ignore:
     694                      raise
     695          return True
     696
     697 -    def parse_request_uri(self, uri):
     698          """Parse a Request-URI into (scheme, authority, path).
     699
     700          Note that Request-URI's must be one of:
     701
     702              Request-URI    = "*" | absoluteURI | abs_path | authority
     703
     704          Therefore, a Request-URI which starts with a double forward-slash
     705          cannot be a "net_path":
     706
     707              net_path      = "//" authority [ abs_path ]
     708
     709          Instead, it must be interpreted as an "abs_path" with an empty first
     710          path segment:
     711
     712              abs_path      = "/"  path_segments
     713              path_segments = segment *( "/" segment )
     714              segment       = *pchar *( ";" param )
     715              param         = *pchar
     716          """
     717          if uri == "*":
     718              return None, None, uri
     719
     720          i = uri.find('://')
     721          if i > 0 and '?' not in uri[:i]:
     722              # An absoluteURI.
     723              # If there's a scheme (and it must be http or https), then:
     724              # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
     725              scheme, remainder = uri[:i].lower(), uri[i + 3:]
     726              authority, path = remainder.split("/", 1)
     727              return scheme, authority, path
     728
     729          if uri.startswith('/'):
     730              # An abs_path.
     731              return None, None, uri
     732          else:
     733              # An authority.
     734              return None, uri, None
     735
     736 -    def respond(self):
     737          """Call the gateway and write its iterable output."""
     738          mrbs = self.server.max_request_body_size
     739          if self.chunked_read:
     740              self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
     741          else:
     742              cl = int(self.inheaders.get("Content-Length", 0))
     743              if mrbs and mrbs < cl:
     744                  if not self.sent_headers:
     745                      self.simple_response("413 Request Entity Too Large",
     746                          "The entity sent with the request exceeds the maximum "
     747                          "allowed bytes.")
     748                  return
     749              self.rfile = KnownLengthRFile(self.conn.rfile, cl)
     750
     751          self.server.gateway(self).respond()
     752
     753          if (self.ready and not self.sent_headers):
     754              self.sent_headers = True
     755              self.send_headers()
     756          if self.chunked_write:
     757              self.conn.wfile.sendall("0\r\n\r\n")
     758
     759 -    def simple_response(self, status, msg=""):
     760          """Write a simple response back to the client."""
     761          status = str(status)
     762          buf = ["Content-Length: %s\r\n" % len(msg),
     763                 "Content-Type: text/plain\r\n"]
     764
     765          if status[:3] in ("413", "414"):
     766              # Request Entity Too Large / Request-URI Too Long
     767              self.close_connection = True
     768              if self.response_protocol == 'HTTP/1.1':
     769                  # This will not be true for 414, since read_request_line
     770                  # usually raises 414 before reading the whole line, and we
     771                  # therefore cannot know the proper response_protocol.
     772                  buf.append("Connection: close\r\n")
     773              else:
     774                  # HTTP/1.0 had no 413/414 status nor Connection header.
     775                  # Emit 400 instead and trust the message body is enough.
     776                  status = "400 Bad Request"
     777
     778          buf.append(CRLF)
     779          if msg:
     780              if isinstance(msg, unicode):
     781                  msg = msg.encode("ISO-8859-1")
     782              buf.append(msg)
     783
     784          status_line = self.server.protocol + " " + status + CRLF
     785          try:
     786              self.conn.wfile.sendall(status_line + "".join(buf))
     787          except socket.error, x:
     788              if x.args[0] not in socket_errors_to_ignore:
     789                  raise
     790
     791 -    def write(self, chunk):
     792          """Write unbuffered data to the client."""
     793          if self.chunked_write and chunk:
     794              buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
     795              self.conn.wfile.sendall("".join(buf))
     796          else:
     797              self.conn.wfile.sendall(chunk)
     798
     799 -    def send_headers(self):
     800          """Assert, process, and send the HTTP response message-headers.
     801
     802          You must set self.status, and self.outheaders before calling this.
     803          """
     804          hkeys = [key.lower() for key, value in self.outheaders]
     805          status = int(self.status[:3])
     806
     807          if status == 413:
     808              # Request Entity Too Large. Close conn to avoid garbage.
     809              self.close_connection = True
     810          elif "content-length" not in hkeys:
     811              # "All 1xx (informational), 204 (no content),
     812              # and 304 (not modified) responses MUST NOT
     813              # include a message-body." So no point chunking.
     814              if status < 200 or status in (204, 205, 304):
     815                  pass
     816              else:
     817                  if (self.response_protocol == 'HTTP/1.1'
     818                      and self.method != 'HEAD'):
     819                      # Use the chunked transfer-coding
     820                      self.chunked_write = True
     821                      self.outheaders.append(("Transfer-Encoding", "chunked"))
     822                  else:
     823                      # Closing the conn is the only way to determine len.
     824                      self.close_connection = True
     825
     826          if "connection" not in hkeys:
     827              if self.response_protocol == 'HTTP/1.1':
     828                  # Both server and client are HTTP/1.1 or better
     829                  if self.close_connection:
     830                      self.outheaders.append(("Connection", "close"))
     831              else:
     832                  # Server and/or client are HTTP/1.0
     833                  if not self.close_connection:
     834                      self.outheaders.append(("Connection", "Keep-Alive"))
     835
     836          if (not self.close_connection) and (not self.chunked_read):
     837              # Read any remaining request body data on the socket.
     838              # "If an origin server receives a request that does not include an
     839              # Expect request-header field with the "100-continue" expectation,
     840              # the request includes a request body, and the server responds
     841              # with a final status code before reading the entire request body
     842              # from the transport connection, then the server SHOULD NOT close
     843              # the transport connection until it has read the entire request,
     844              # or until the client closes the connection. Otherwise, the client
     845              # might not reliably receive the response message. However, this
     846              # requirement is not be construed as preventing a server from
     847              # defending itself against denial-of-service attacks, or from
     848              # badly broken client implementations."
     849              remaining = getattr(self.rfile, 'remaining', 0)
     850              if remaining > 0:
     851                  self.rfile.read(remaining)
     852
     853          if "date" not in hkeys:
     854              self.outheaders.append(("Date", rfc822.formatdate()))
     855
     856          if "server" not in hkeys:
     857              self.outheaders.append(("Server", self.server.server_name))
     858
     859          buf = [self.server.protocol + " " + self.status + CRLF]
     860          for k, v in self.outheaders:
     861              buf.append(k + ": " + v + CRLF)
     862          buf.append(CRLF)
     863          self.conn.wfile.sendall("".join(buf))
     864
     865
     866 -class NoSSLError(Exception):
     867      """Exception raised when a client speaks HTTP to an HTTPS socket."""
     868      pass
     869
     870
     871 -class FatalSSLAlert(Exception):
     872      """Exception raised when the SSL implementation signals a fatal alert."""
     873      pass
     874
     875
     876  if not _fileobject_uses_str_type:
     877 -    class CP_fileobject(socket._fileobject):
     878          """Faux file object attached to a socket object."""
     879
     880 -        def sendall(self, data):
     881              """Sendall for non-blocking sockets."""
     882              while data:
     883                  try:
     884                      bytes_sent = self.send(data)
     885                      data = data[bytes_sent:]
     886                  except socket.error, e:
     887                      if e.args[0] not in socket_errors_nonblocking:
     888                          raise
     889
     890 -        def send(self, data):
     891              return self._sock.send(data)
     892
     893 -        def flush(self):
     894              if self._wbuf:
     895                  buffer = "".join(self._wbuf)
     896                  self._wbuf = []
     897                  self.sendall(buffer)
     898
     899 -        def recv(self, size):
     900              while True:
     901                  try:
     902                      return self._sock.recv(size)
     903                  except socket.error, e:
     904                      if (e.args[0] not in socket_errors_nonblocking
     905                          and e.args[0] not in socket_error_eintr):
     906                          raise
     907
     908 -        def read(self, size=-1):
     909              # Use max, disallow tiny reads in a loop as they are very inefficient.
     910              # We never leave read() with any leftover data from a new recv() call
     911              # in our internal buffer.
     912              rbufsize = max(self._rbufsize, self.default_bufsize)
     913              # Our use of StringIO rather than lists of string objects returned by
     914              # recv() minimizes memory usage and fragmentation that occurs when
     915              # rbufsize is large compared to the typical return value of recv().
     916              buf = self._rbuf
     917              buf.seek(0, 2)  # seek end
     918              if size < 0:
     919                  # Read until EOF
     920                  self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
     921                  while True:
     922                      data = self.recv(rbufsize)
     923                      if not data:
     924                          break
     925                      buf.write(data)
     926                  return buf.getvalue()
     927              else:
     928                  # Read until size bytes or EOF seen, whichever comes first
     929                  buf_len = buf.tell()
     930                  if buf_len >= size:
     931                      # Already have size bytes in our buffer?  Extract and return.
     932                      buf.seek(0)
     933                      rv = buf.read(size)
     934                      self._rbuf = StringIO.StringIO()
     935                      self._rbuf.write(buf.read())
     936                      return rv
     937
     938                  self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
     939                  while True:
     940                      left = size - buf_len
     941                      # recv() will malloc the amount of memory given as its
     942                      # parameter even though it often returns much less data
     943                      # than that.  The returned data string is short lived
     944                      # as we copy it into a StringIO and free it.  This avoids
     945                      # fragmentation issues on many platforms.
     946                      data = self.recv(left)
     947                      if not data:
     948                          break
     949                      n = len(data)
     950                      if n == size and not buf_len:
     951                          # Shortcut.  Avoid buffer data copies when:
     952                          # - We have no data in our buffer.
     953                          # AND
     954                          # - Our call to recv returned exactly the
     955                          #   number of bytes we were asked to read.
     956                          return data
     957                      if n == left:
     958                          buf.write(data)
     959                          del data  # explicit free
     960                          break
     961                      assert n <= left, "recv(%d) returned %d bytes" % (left, n)
     962                      buf.write(data)
     963                      buf_len += n
     964                      del data  # explicit free
     965                      #assert buf_len == buf.tell()
     966                  return buf.getvalue()
     967
     968 -        def readline(self, size=-1):
     969              buf = self._rbuf
     970              buf.seek(0, 2)  # seek end
     971              if buf.tell() > 0:
     972                  # check if we already have it in our buffer
     973                  buf.seek(0)
     974                  bline = buf.readline(size)
     975                  if bline.endswith('\n') or len(bline) == size:
     976                      self._rbuf = StringIO.StringIO()
     977                      self._rbuf.write(buf.read())
     978                      return bline
     979                  del bline
     980              if size < 0:
     981                  # Read until \n or EOF, whichever comes first
     982                  if self._rbufsize <= 1:
     983                      # Speed up unbuffered case
     984                      buf.seek(0)
     985                      buffers = [buf.read()]
     986                      self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
     987                      data = None
     988                      recv = self.recv
     989                      while data != "\n":
     990                          data = recv(1)
     991                          if not data:
     992                              break
     993                          buffers.append(data)
     994                      return "".join(buffers)
     995
     996                  buf.seek(0, 2)  # seek end
     997                  self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
     998                  while True:
     999                      data = self.recv(self._rbufsize)
    1000                      if not data:
    1001                          break
    1002                      nl = data.find('\n')
    1003                      if nl >= 0:
    1004                          nl += 1
    1005                          buf.write(data[:nl])
    1006                          self._rbuf.write(data[nl:])
    1007                          del data
    1008                          break
    1009                      buf.write(data)
    1010                  return buf.getvalue()
    1011              else:
    1012                  # Read until size bytes or \n or EOF seen, whichever comes first
    1013                  buf.seek(0, 2)  # seek end
    1014                  buf_len = buf.tell()
    1015                  if buf_len >= size:
    1016                      buf.seek(0)
    1017                      rv = buf.read(size)
    1018                      self._rbuf = StringIO.StringIO()
    1019                      self._rbuf.write(buf.read())
    1020                      return rv
    1021                  self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
    1022                  while True:
    1023                      data = self.recv(self._rbufsize)
    1024                      if not data:
    1025                          break
    1026                      left = size - buf_len
    1027                      # did we just receive a newline?
    1028                      nl = data.find('\n', 0, left)
    1029                      if nl >= 0:
    1030                          nl += 1
    1031                          # save the excess data to _rbuf
    1032                          self._rbuf.write(data[nl:])
    1033                          if buf_len:
    1034                              buf.write(data[:nl])
    1035                              break
    1036                          else:
    1037                              # Shortcut.  Avoid data copy through buf when returning
    1038                              # a substring of our first recv().
    1039                              return data[:nl]
    1040                      n = len(data)
    1041                      if n == size and not buf_len:
    1042                          # Shortcut.  Avoid data copy through buf when
    1043                          # returning exactly all of our first recv().
    1044                          return data
    1045                      if n >= left:
    1046                          buf.write(data[:left])
    1047                          self._rbuf.write(data[left:])
    1048                          break
    1049                      buf.write(data)
    1050                      buf_len += n
    1051                      #assert buf_len == buf.tell()
    1052                  return buf.getvalue()
    1053
    1054  else:
    1055 -    class CP_fileobject(socket._fileobject):
    1056          """Faux file object attached to a socket object."""
    1057
    1058 -        def sendall(self, data):
    1059              """Sendall for non-blocking sockets."""
    1060              while data:
    1061                  try:
    1062                      bytes_sent = self.send(data)
    1063                      data = data[bytes_sent:]
    1064                  except socket.error, e:
    1065                      if e.args[0] not in socket_errors_nonblocking:
    1066                          raise
    1067
    1068 -        def send(self, data):
    1069              return self._sock.send(data)
    1070
    1071 -        def flush(self):
    1072              if self._wbuf:
    1073                  buffer = "".join(self._wbuf)
    1074                  self._wbuf = []
    1075                  self.sendall(buffer)
    1076
    1077 -        def recv(self, size):
    1078              while True:
    1079                  try:
    1080                      return self._sock.recv(size)
    1081                  except socket.error, e:
    1082                      if (e.args[0] not in socket_errors_nonblocking
    1083                          and e.args[0] not in socket_error_eintr):
    1084                          raise
    1085
    1086 -        def read(self, size=-1):
    1087              if size < 0:
    1088                  # Read until EOF
    1089                  buffers = [self._rbuf]
    1090                  self._rbuf = ""
    1091                  if self._rbufsize <= 1:
    1092                      recv_size = self.default_bufsize
    1093                  else:
    1094                      recv_size = self._rbufsize
    1095
    1096                  while True:
    1097                      data = self.recv(recv_size)
    1098                      if not data:
    1099                          break
    1100                      buffers.append(data)
    1101                  return "".join(buffers)
    1102              else:
    1103                  # Read until size bytes or EOF seen, whichever comes first
    1104                  data = self._rbuf
    1105                  buf_len = len(data)
    1106                  if buf_len >= size:
    1107                      self._rbuf = data[size:]
    1108                      return data[:size]
    1109                  buffers = []
    1110                  if data:
    1111                      buffers.append(data)
    1112                  self._rbuf = ""
    1113                  while True:
    1114                      left = size - buf_len
    1115                      recv_size = max(self._rbufsize, left)
    1116                      data = self.recv(recv_size)
    1117                      if not data:
    1118                          break
    1119                      buffers.append(data)
    1120                      n = len(data)
    1121                      if n >= left:
    1122                          self._rbuf = data[left:]
    1123                          buffers[-1] = data[:left]
    1124                          break
    1125                      buf_len += n
    1126                  return "".join(buffers)
    1127
    1128 -        def readline(self, size=-1):
    1129              data = self._rbuf
    1130              if size < 0:
    1131                  # Read until \n or EOF, whichever comes first
    1132                  if self._rbufsize <= 1:
    1133                      # Speed up unbuffered case
    1134                      assert data == ""
    1135                      buffers = []
    1136                      while data != "\n":
    1137                          data = self.recv(1)
    1138                          if not data:
    1139                              break
    1140                          buffers.append(data)
    1141                      return "".join(buffers)
    1142                  nl = data.find('\n')
    1143                  if nl >= 0:
    1144                      nl += 1
    1145                      self._rbuf = data[nl:]
    1146                      return data[:nl]
    1147                  buffers = []
    1148                  if data:
    1149                      buffers.append(data)
    1150                  self._rbuf = ""
    1151                  while True:
    1152                      data = self.recv(self._rbufsize)
    1153                      if not data:
    1154                          break
    1155                      buffers.append(data)
    1156                      nl = data.find('\n')
    1157                      if nl >= 0:
    1158                          nl += 1
    1159                          self._rbuf = data[nl:]
    1160                          buffers[-1] = data[:nl]
    1161                          break
    1162                  return "".join(buffers)
    1163              else:
    1164                  # Read until size bytes or \n or EOF seen, whichever comes first
    1165                  nl = data.find('\n', 0, size)
    1166                  if nl >= 0:
    1167                      nl += 1
    1168                      self._rbuf = data[nl:]
    1169                      return data[:nl]
    1170                  buf_len = len(data)
    1171                  if buf_len >= size:
    1172                      self._rbuf = data[size:]
    1173                      return data[:size]
    1174                  buffers = []
    1175                  if data:
    1176                      buffers.append(data)
    1177                  self._rbuf = ""
    1178                  while True:
    1179                      data = self.recv(self._rbufsize)
    1180                      if not data:
    1181                          break
    1182                      buffers.append(data)
    1183                      left = size - buf_len
    1184                      nl = data.find('\n', 0, left)
    1185                      if nl >= 0:
    1186                          nl += 1
    1187                          self._rbuf = data[nl:]
    1188                          buffers[-1] = data[:nl]
    1189                          break
    1190                      n = len(data)
    1191                      if n >= left:
    1192                          self._rbuf = data[left:]
    1193                          buffers[-1] = data[:left]
    1194                          break
    1195                      buf_len += n
    1196                  return "".join(buffers)
    1197
    1198
    1199 -class HTTPConnection(object):
    1200      """An HTTP connection (active socket).
    1201
    1202      server: the Server object which received this connection.
    1203      socket: the raw socket object (usually TCP) for this connection.
    1204      makefile: a fileobject class for reading from the socket.
    1205      """
    1206
    1207      remote_addr = None
    1208      remote_port = None
    1209      ssl_env = None
    1210      rbufsize = -1
    1211      RequestHandlerClass = HTTPRequest
    1212
    1213 -    def __init__(self, server, sock, makefile=CP_fileobject):
    1214          self.server = server
    1215          self.socket = sock
    1216          self.rfile = makefile(sock, "rb", self.rbufsize)
    1217          self.wfile = makefile(sock, "wb", -1)
    1218
    1219 -    def communicate(self):
    1220          """Read each request and respond appropriately."""
    1221          request_seen = False
    1222          try:
    1223              while True:
    1224                  # (re)set req to None so that if something goes wrong in
    1225                  # the RequestHandlerClass constructor, the error doesn't
    1226                  # get written to the previous request.
    1227                  req = None
    1228                  req = self.RequestHandlerClass(self.server, self)
    1229
    1230                  # This order of operations should guarantee correct pipelining.
    1231                  req.parse_request()
    1232                  if not req.ready:
    1233                      # Something went wrong in the parsing (and the server has
    1234                      # probably already made a simple_response). Return and
    1235                      # let the conn close.
    1236                      return
    1237
    1238                  request_seen = True
    1239                  req.respond()
    1240                  if req.close_connection:
    1241                      return
    1242          except socket.error, e:
    1243              errnum = e.args[0]
    1244              if errnum == 'timed out':
    1245                  # Don't error if we're between requests; only error
    1246                  # if 1) no request has been started at all, or 2) we're
    1247                  # in the middle of a request.
    1248                  # See http://www.cherrypy.org/ticket/853
    1249                  if (not request_seen) or (req and req.started_request):
    1250                      # Don't bother writing the 408 if the response
    1251                      # has already started being written.
    1252                      if req and not req.sent_headers:
    1253                          try:
    1254                              req.simple_response("408 Request Timeout")
    1255                          except FatalSSLAlert:
    1256                              # Close the connection.
    1257                              return
    1258              elif errnum not in socket_errors_to_ignore:
    1259                  if req and not req.sent_headers:
    1260                      try:
    1261                          req.simple_response("500 Internal Server Error",
    1262                                              format_exc())
    1263                      except FatalSSLAlert:
    1264                          # Close the connection.
    1265                          return
    1266              return
    1267          except (KeyboardInterrupt, SystemExit):
    1268              raise
    1269          except FatalSSLAlert:
    1270              # Close the connection.
    1271              return
    1272          except NoSSLError:
    1273              if req and not req.sent_headers:
    1274                  # Unwrap our wfile
    1275                  self.wfile = CP_fileobject(self.socket._sock, "wb", -1)
    1276                  req.simple_response("400 Bad Request",
    1277                      "The client sent a plain HTTP request, but "
    1278                      "this server only speaks HTTPS on this port.")
    1279                  self.linger = True
    1280          except Exception:
    1281              if req and not req.sent_headers:
    1282                  try:
    1283                      req.simple_response("500 Internal Server Error", format_exc())
    1284                  except FatalSSLAlert:
    1285                      # Close the connection.
    1286                      return
    1287
    1288      linger = False
    1289
    1290 -    def close(self):
    1291          """Close the socket underlying this connection."""
    1292          self.rfile.close()
    1293
    1294          if not self.linger:
    1295              # Python's socket module does NOT call close on the kernel socket
    1296              # when you call socket.close(). We do so manually here because we
    1297              # want this server to send a FIN TCP segment immediately. Note this
    1298              # must be called *before* calling socket.close(), because the latter
    1299              # drops its reference to the kernel socket.
    1300              if hasattr(self.socket, '_sock'):
    1301                  self.socket._sock.close()
    1302              self.socket.close()
    1303          else:
    1304              # On the other hand, sometimes we want to hang around for a bit
    1305              # to make sure the client has a chance to read our entire
    1306              # response. Skipping the close() calls here delays the FIN
    1307              # packet until the socket object is garbage-collected later.
    1308              # Someday, perhaps, we'll do the full lingering_close that
    1309              # Apache does, but not today.
    1310              pass
    1311
    1312
    1313 -def format_exc(limit=None):
    1314      """Like print_exc() but return a string. Backport for Python 2.3."""
    1315      try:
    1316          etype, value, tb = sys.exc_info()
    1317          return ''.join(traceback.format_exception(etype, value, tb, limit))
    1318      finally:
    1319          etype = value = tb = None
    1320
    1321
    1322  _SHUTDOWNREQUEST = None
    1323
    1324 -class WorkerThread(threading.Thread):
    1325      """Thread which continuously polls a Queue for Connection objects.
    1326
    1327      server: the HTTP Server which spawned this thread, and which owns the
    1328          Queue and is placing active connections into it.
    1329      ready: a simple flag for the calling server to know when this thread
    1330          has begun polling the Queue.
    1331
    1332      Due to the timing issues of polling a Queue, a WorkerThread does not
    1333      check its own 'ready' flag after it has started. To stop the thread,
    1334      it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
    1335      (one for each running WorkerThread).
    1336      """
    1337
    1338      conn = None
    1339
    1340 -    def __init__(self, server):
    1341          self.ready = False
    1342          self.server = server
    1343          threading.Thread.__init__(self)
    1344
    1345 -    def run(self):
    1346          try:
    1347              self.ready = True
    1348              while True:
    1349                  conn = self.server.requests.get()
    1350                  if conn is _SHUTDOWNREQUEST:
    1351                      return
    1352
    1353                  self.conn = conn
    1354                  try:
    1355                      conn.communicate()
    1356                  finally:
    1357                      conn.close()
    1358                      self.conn = None
    1359          except (KeyboardInterrupt, SystemExit), exc:
    1360              self.server.interrupt = exc
    1361
    1362
    1363 -class ThreadPool(object):
    1364      """A Request Queue for the CherryPyWSGIServer which pools threads.
    1365
    1366      ThreadPool objects must provide min, get(), put(obj), start()
    1367      and stop(timeout) attributes.
    1368      """
    1369
    1370 -    def __init__(self, server, min=10, max=-1):
    1371          self.server = server
    1372          self.min = min
    1373          self.max = max
    1374          self._threads = []
    1375          self._queue = Queue.Queue()
    1376          self.get = self._queue.get
    1377
    1378 -    def start(self):
    1379          """Start the pool of threads."""
    1380          for i in range(self.min):
    1381              self._threads.append(WorkerThread(self.server))
    1382          for worker in self._threads:
    1383              worker.setName("CP Server " + worker.getName())
    1384              worker.start()
    1385          for worker in self._threads:
    1386              while not worker.ready:
    1387                  time.sleep(.1)
    1388
    1389 -    def _get_idle(self):
    1390          """Number of worker threads which are idle. Read-only."""
    1391          return len([t for t in self._threads if t.conn is None])
    1392      idle = property(_get_idle, doc=_get_idle.__doc__)
    1393
    1394 -    def put(self, obj):
    1395          self._queue.put(obj)
    1396          if obj is _SHUTDOWNREQUEST:
    1397              return
    1398
    1399 -    def grow(self, amount):
    1400          """Spawn new worker threads (not above self.max)."""
    1401          for i in range(amount):
    1402              if self.max > 0 and len(self._threads) >= self.max:
    1403                  break
    1404              worker = WorkerThread(self.server)
    1405              worker.setName("CP Server " + worker.getName())
    1406              self._threads.append(worker)
    1407              worker.start()
    1408
    1409 -    def shrink(self, amount):
    1410          """Kill off worker threads (not below self.min)."""
    1411          # Grow/shrink the pool if necessary.
    1412          # Remove any dead threads from our list
    1413          for t in self._threads:
    1414              if not t.isAlive():
    1415                  self._threads.remove(t)
    1416                  amount -= 1
    1417
    1418          if amount > 0:
    1419              for i in range(min(amount, len(self._threads) - self.min)):
    1420                  # Put a number of shutdown requests on the queue equal
    1421                  # to 'amount'. Once each of those is processed by a worker,
    1422                  # that worker will terminate and be culled from our list
    1423                  # in self.put.
    1424                  self._queue.put(_SHUTDOWNREQUEST)
    1425
    1426 -    def stop(self, timeout=5):
    1427          # Must shut down threads here so the code that calls
    1428          # this method can know when all threads are stopped.
    1429          for worker in self._threads:
    1430              self._queue.put(_SHUTDOWNREQUEST)
    1431
    1432          # Don't join currentThread (when stop is called inside a request).
    1433          current = threading.currentThread()
    1434          if timeout and timeout >= 0:
    1435              endtime = time.time() + timeout
    1436          while self._threads:
    1437              worker = self._threads.pop()
    1438              if worker is not current and worker.isAlive():
    1439                  try:
    1440                      if timeout is None or timeout < 0:
    1441                          worker.join()
    1442                      else:
    1443                          remaining_time = endtime - time.time()
    1444                          if remaining_time > 0:
    1445                              worker.join(remaining_time)
    1446                          if worker.isAlive():
    1447                              # We exhausted the timeout.
    1448                              # Forcibly shut down the socket.
    1449                              c = worker.conn
    1450                              if c and not c.rfile.closed:
    1451                                  try:
    1452                                      c.socket.shutdown(socket.SHUT_RD)
    1453                                  except TypeError:
    1454                                      # pyOpenSSL sockets don't take an arg
    1455                                      c.socket.shutdown()
    1456                              worker.join()
    1457                  except (AssertionError,
    1458                          # Ignore repeated Ctrl-C.
    1459                          # See http://www.cherrypy.org/ticket/691.
    1460                          KeyboardInterrupt), exc1:
    1461                      pass
    1462
    1463
    1464
    1465  try:
    1466      import fcntl
    1467  except ImportError:
    1468      try:
    1469          from ctypes import windll, WinError
    1470      except ImportError:
    1471 -        def prevent_socket_inheritance(sock):
    1472              """Dummy function, since neither fcntl nor ctypes are available."""
    1473              pass
    1474      else:
    1475 -        def prevent_socket_inheritance(sock):
    1476              """Mark the given socket fd as non-inheritable (Windows)."""
    1477              if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
    1478                  raise WinError()
    1479  else:
    1480 -    def prevent_socket_inheritance(sock):
    1481          """Mark the given socket fd as non-inheritable (POSIX)."""
    1482          fd = sock.fileno()
    1483          old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
    1484          fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
    1485
    1486
    1487 -class SSLAdapter(object):
    1488
    1489 -    def __init__(self, certificate, private_key, certificate_chain=None, client_CA=None):
    1490          self.certificate = certificate
    1491          self.private_key = private_key
    1492          self.certificate_chain = certificate_chain
    1493          self.client_CA = client_CA
    1494
    1495 -    def wrap(self, sock):
    1496          raise NotImplemented
    1497
    1498 -    def makefile(self, sock, mode='r', bufsize=-1):
    1499          raise NotImplemented
    1500
    1501
    1502 -class HTTPServer(object):
    1503      """An HTTP server.
    1504
    1505      bind_addr: The interface on which to listen for connections.
    1506          For TCP sockets, a (host, port) tuple. Host values may be any IPv4
    1507          or IPv6 address, or any valid hostname. The string 'localhost' is a
    1508          synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
    1509          The string '0.0.0.0' is a special IPv4 entry meaning "any active
    1510          interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
    1511          IPv6. The empty string or None are not allowed.
    1512
    1513          For UNIX sockets, supply the filename as a string.
    1514      gateway: a Gateway instance.
    1515      minthreads: the minimum number of worker threads to create (default 10).
    1516      maxthreads: the maximum number of worker threads to create (default -1 = no limit).
    1517      server_name: defaults to socket.gethostname().
    1518
    1519      request_queue_size: the 'backlog' argument to socket.listen();
    1520          specifies the maximum number of queued connections (default 5).
    1521      timeout: the timeout in seconds for accepted connections (default 10).
    1522      nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket
    1523          option.
    1524      protocol: the version string to write in the Status-Line of all
    1525          HTTP responses. For example, "HTTP/1.1" (the default). This
    1526          also limits the supported features used in the response.
    1527
    1528
    1529      SSL/HTTPS
    1530      ---------
    1531      You must have an ssl library installed and set self.ssl_adapter to an
    1532      instance of SSLAdapter (or a subclass) which provides the methods:
    1533          wrap(sock) -> wrapped socket, ssl environ dict
    1534          makefile(sock, mode='r', bufsize=-1) -> socket file object
    1535      """
    1536
    1537      protocol = "HTTP/1.1"
    1538      _bind_addr = "127.0.0.1"
    1539      version = "CherryPy/3.2.0rc1"
    1540      response_header = None
    1541      ready = False
    1542      _interrupt = None
    1543      max_request_header_size = 0
    1544      max_request_body_size = 0
    1545      nodelay = True
    1546
    1547      ConnectionClass = HTTPConnection
    1548
    1549      ssl_adapter = None
    1550
    1551 -    def __init__(self, bind_addr, gateway, minthreads=10, maxthreads=-1,
    1552                   server_name=None):
    1553          self.bind_addr = bind_addr
    1554          self.gateway = gateway
    1555
    1556          self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads)
    1557
    1558          if not server_name:
    1559              server_name = socket.gethostname()
    1560          self.server_name = server_name
    1561
    1562 -    def __str__(self):
    1563          return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
    1564                                self.bind_addr)
    1565
    1566 -    def _get_bind_addr(self):
    1567          return self._bind_addr
    1568 -    def _set_bind_addr(self, value):
    1569          if isinstance(value, tuple) and value[0] in ('', None):
    1570              # Despite the socket module docs, using '' does not
    1571              # allow AI_PASSIVE to work. Passing None instead
    1572              # returns '0.0.0.0' like we want. In other words:
    1573              #     host    AI_PASSIVE     result
    1574              #      ''         Y         192.168.x.y
    1575              #      ''         N         192.168.x.y
    1576              #     None        Y         0.0.0.0
    1577              #     None        N         127.0.0.1
    1578              # But since you can get the same effect with an explicit
    1579              # '0.0.0.0', we deny both the empty string and None as values.
    1580              raise ValueError("Host values of '' or None are not allowed. "
    1581                               "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
    1582                               "to listen on all active interfaces.")
    1583          self._bind_addr = value
    1584      bind_addr = property(_get_bind_addr, _set_bind_addr,
    1585          doc="""The interface on which to listen for connections.
    1586
    1587          For TCP sockets, a (host, port) tuple. Host values may be any IPv4
    1588          or IPv6 address, or any valid hostname. The string 'localhost' is a
    1589          synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
    1590          The string '0.0.0.0' is a special IPv4 entry meaning "any active
    1591          interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
    1592          IPv6. The empty string or None are not allowed.
    1593
    1594          For UNIX sockets, supply the filename as a string.""")
    1595
    1596 -    def start(self):
    1597          """Run the server forever."""
    1598          # We don't have to trap KeyboardInterrupt or SystemExit here,
    1599          # because cherrpy.server already does so, calling self.stop() for us.
    1600          # If you're using this server with another framework, you should
    1601          # trap those exceptions in whatever code block calls start().
    1602          self._interrupt = None
    1603
    1604          # SSL backward compatibility
    1605          if (self.ssl_adapter is None and
    1606              getattr(self, 'ssl_certificate', None) and
    1607              getattr(self, 'ssl_private_key', None)):
    1608              warnings.warn(
    1609                      "SSL attributes are deprecated in CherryPy 3.2, and will "
    1610                      "be removed in CherryPy 3.3. Use an ssl_adapter attribute "
    1611                      "instead.",
    1612                      DeprecationWarning
    1613                  )
    1614              try:
    1615                  from web.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
    1616              except ImportError:
    1617                  pass
    1618              else:
    1619                  self.ssl_adapter = pyOpenSSLAdapter(
    1620                      self.ssl_certificate, self.ssl_private_key,
    1621                      getattr(self, 'ssl_certificate_chain', None))
    1622
    1623          # Select the appropriate socket
    1624          if isinstance(self.bind_addr, basestring):
    1625              # AF_UNIX socket
    1626
    1627              # So we can reuse the socket...
    1628              try: os.unlink(self.bind_addr)
    1629              except: pass
    1630
    1631              # So everyone can access the socket...
    1632              try: os.chmod(self.bind_addr, 0777)
    1633              except: pass
    1634
    1635              info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
    1636          else:
    1637              # AF_INET or AF_INET6 socket
    1638              # Get the correct address family for our host (allows IPv6 addresses)
    1639              host, port = self.bind_addr
    1640              try:
    1641                  info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
    1642                                            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
    1643              except socket.gaierror:
    1644                  if ':' in self.bind_addr[0]:
    1645                      info = [(socket.AF_INET6, socket.SOCK_STREAM,
    1646                               0, "", self.bind_addr + (0, 0))]
    1647                  else:
    1648                      info = [(socket.AF_INET, socket.SOCK_STREAM,
    1649                               0, "", self.bind_addr)]
    1650
    1651          self.socket = None
    1652          msg = "No socket could be created"
    1653          for res in info:
    1654              af, socktype, proto, canonname, sa = res
    1655              try:
    1656                  self.bind(af, socktype, proto)
    1657              except socket.error, msg:
    1658                  if self.socket:
    1659                      self.socket.close()
    1660                  self.socket = None
    1661                  continue
    1662              break
    1663          if not self.socket:
    1664              raise socket.error(msg)
    1665
    1666          # Timeout so KeyboardInterrupt can be caught on Win32
    1667          self.socket.settimeout(1)
    1668          self.socket.listen(self.request_queue_size)
    1669
    1670          # Create worker threads
    1671          self.requests.start()
    1672
    1673          self.ready = True
    1674          while self.ready:
    1675              self.tick()
    1676              if self.interrupt:
    1677                  while self.interrupt is True:
    1678                      # Wait for self.stop() to complete. See _set_interrupt.
    1679                      time.sleep(0.1)
    1680                  if self.interrupt:
    1681                      raise self.interrupt
    1682
    1683 -    def bind(self, family, type, proto=0):
    1684          """Create (or recreate) the actual socket object."""
    1685          self.socket = socket.socket(family, type, proto)
    1686          prevent_socket_inheritance(self.socket)
    1687          self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    1688          if self.nodelay and not isinstance(self.bind_addr, str):
    1689              self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    1690
    1691          if self.ssl_adapter is not None:
    1692              self.socket = self.ssl_adapter.bind(self.socket)
    1693
    1694          # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
    1695          # activate dual-stack. See http://www.cherrypy.org/ticket/871.
    1696          if (family == socket.AF_INET6
    1697              and self.bind_addr[0] in ('::', '::0', '::0.0.0.0')):
    1698              try:
    1699                  self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
    1700              except (AttributeError, socket.error):
    1701                  # Apparently, the socket option is not available in
    1702                  # this machine's TCP stack
    1703                  pass
    1704
    1705          self.socket.bind(self.bind_addr)
    1706
    1707 -    def tick(self):
    1708          """Accept a new connection and put it on the Queue."""
    1709          try:
    1710              s, addr = self.socket.accept()
    1711              if not self.ready:
    1712                  return
    1713
    1714              prevent_socket_inheritance(s)
    1715              if hasattr(s, 'settimeout'):
    1716                  s.settimeout(self.timeout)
    1717
    1718              if self.response_header is None:
    1719                  self.response_header = "%s Server" % self.version
    1720
    1721              makefile = CP_fileobject
    1722              ssl_env = {}
    1723              # if ssl cert and key are set, we try to be a secure HTTP server
    1724              if self.ssl_adapter is not None:
    1725                  try:
    1726                      s, ssl_env = self.ssl_adapter.wrap(s)
    1727                  except NoSSLError:
    1728                      msg = ("The client sent a plain HTTP request, but "
    1729                             "this server only speaks HTTPS on this port.")
    1730                      buf = ["%s 400 Bad Request\r\n" % self.protocol,
    1731                             "Content-Length: %s\r\n" % len(msg),
    1732                             "Content-Type: text/plain\r\n\r\n",
    1733                             msg]
    1734
    1735                      wfile = CP_fileobject(s, "wb", -1)
    1736                      try:
    1737                          wfile.sendall("".join(buf))
    1738                      except socket.error, x:
    1739                          if x.args[0] not in socket_errors_to_ignore:
    1740                              raise
    1741                      return
    1742                  if not s:
    1743                      return
    1744                  makefile = self.ssl_adapter.makefile
    1745
    1746              conn = self.ConnectionClass(self, s, makefile)
    1747
    1748              if not isinstance(self.bind_addr, basestring):
    1749                  # optional values
    1750                  # Until we do DNS lookups, omit REMOTE_HOST
    1751                  if addr is None: # sometimes this can happen
    1752                      # figure out if AF_INET or AF_INET6.
    1753                      if len(s.getsockname()) == 2:
    1754                          # AF_INET
    1755                          addr = ('0.0.0.0', 0)
    1756                      else:
    1757                          # AF_INET6
    1758                          addr = ('::', 0)
    1759                  conn.remote_addr = addr[0]
    1760                  conn.remote_port = addr[1]
    1761
    1762              conn.ssl_env = ssl_env
    1763
    1764              self.requests.put(conn)
    1765          except socket.timeout:
    1766              # The only reason for the timeout in start() is so we can
    1767              # notice keyboard interrupts on Win32, which don't interrupt
    1768              # accept() by default
    1769              return
    1770          except socket.error, x:
    1771              if x.args[0] in socket_error_eintr:
    1772                  # I *think* this is right. EINTR should occur when a signal
    1773                  # is received during the accept() call; all docs say retry
    1774                  # the call, and I *think* I'm reading it right that Python
    1775                  # will then go ahead and poll for and handle the signal
    1776                  # elsewhere. See http://www.cherrypy.org/ticket/707.
    1777                  return
    1778              if x.args[0] in socket_errors_nonblocking:
    1779                  # Just try again. See http://www.cherrypy.org/ticket/479.
    1780                  return
    1781              if x.args[0] in socket_errors_to_ignore:
    1782                  # Our socket was closed.
    1783                  # See http://www.cherrypy.org/ticket/686.
    1784                  return
    1785              raise
    1786
    1787 -    def _get_interrupt(self):
    1788          return self._interrupt
    1789 -    def _set_interrupt(self, interrupt):
    1790          self._interrupt = True
    1791          self.stop()
    1792          self._interrupt = interrupt
    1793      interrupt = property(_get_interrupt, _set_interrupt,
    1794                           doc="Set this to an Exception instance to "
    1795                               "interrupt the server.")
    1796
    1797 -    def stop(self):
    1798          """Gracefully shutdown a server that is serving forever."""
    1799          self.ready = False
    1800
    1801          sock = getattr(self, "socket", None)
    1802          if sock:
    1803              if not isinstance(self.bind_addr, basestring):
    1804                  # Touch our own socket to make accept() return immediately.
    1805                  try:
    1806                      host, port = sock.getsockname()[:2]
    1807                  except socket.error, x:
    1808                      if x.args[0] not in socket_errors_to_ignore:
    1809                          # Changed to use error code and not message
    1810                          # See http://www.cherrypy.org/ticket/860.
    1811                          raise
    1812                  else:
    1813                      # Note that we're explicitly NOT using AI_PASSIVE,
    1814                      # here, because we want an actual IP to touch.
    1815                      # localhost won't work if we've bound to a public IP,
    1816                      # but it will if we bound to '0.0.0.0' (INADDR_ANY).
    1817                      for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
    1818                                                    socket.SOCK_STREAM):
    1819                          af, socktype, proto, canonname, sa = res
    1820                          s = None
    1821                          try:
    1822                              s = socket.socket(af, socktype, proto)
    1823                              # See http://groups.google.com/group/cherrypy-users/
    1824                              #        browse_frm/thread/bbfe5eb39c904fe0
    1825                              s.settimeout(1.0)
    1826                              s.connect((host, port))
    1827                              s.close()
    1828                          except socket.error:
    1829                              if s:
    1830                                  s.close()
    1831              if hasattr(sock, "close"):
    1832                  sock.close()
    1833              self.socket = None
    1834
    1835          self.requests.stop(self.shutdown_timeout)
    1836
    1837
    1838 -class Gateway(object):
    1839
    1840 -    def __init__(self, req):
    1841          self.req = req
    1842
    1843 -    def respond(self):
    1844          raise NotImplemented
    1845
    1846
    1847  # These may either be wsgiserver.SSLAdapter subclasses or the string names
    1848  # of such classes (in which case they will be lazily loaded).
    1849  ssl_adapters = {
    1850      'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
    1851      'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
    1852      }
    1853
    1854 -def get_ssl_adapter_class(name='pyopenssl'):
    1855      adapter = ssl_adapters[name.lower()]
    1856      if isinstance(adapter, basestring):
    1857          last_dot = adapter.rfind(".")
    1858          attr_name = adapter[last_dot + 1:]
    1859          mod_path = adapter[:last_dot]
    1860
    1861          try:
    1862              mod = sys.modules[mod_path]
    1863              if mod is None:
    1864                  raise KeyError()
    1865          except KeyError:
    1866              # The last [''] is important.
    1867              mod = __import__(mod_path, globals(), locals(), [''])
    1868
    1869          # Let an AttributeError propagate outward.
    1870          try:
    1871              adapter = getattr(mod, attr_name)
    1872          except AttributeError:
    1873              raise AttributeError("'%s' object has no attribute '%s'"
    1874                                   % (mod_path, attr_name))
    1875
    1876      return adapter
    1877
    1878  # -------------------------------- WSGI Stuff -------------------------------- #
    1879
    1880
    1881 -class CherryPyWSGIServer(HTTPServer):
    1882
    1883      wsgi_version = (1, 1)
    1884
    1885 -    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
    1886                   max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
    1887          self.requests = ThreadPool(self, min=numthreads or 1, max=max)
    1888          self.wsgi_app = wsgi_app
    1889          self.gateway = wsgi_gateways[self.wsgi_version]
    1890
    1891          self.bind_addr = bind_addr
    1892          if not server_name:
    1893              server_name = socket.gethostname()
    1894          self.server_name = server_name
    1895          self.request_queue_size = request_queue_size
    1896
    1897          self.timeout = timeout
    1898          self.shutdown_timeout = shutdown_timeout
    1899
    1900 -    def _get_numthreads(self):
    1901          return self.requests.min
    1902 -    def _set_numthreads(self, value):
    1903          self.requests.min = value
    1904      numthreads = property(_get_numthreads, _set_numthreads)
    1905
    1906
    1907 -class WSGIGateway(Gateway):
    1908
    1909 -    def __init__(self, req):
    1910          self.req = req
    1911          self.started_response = False
    1912          self.env = self.get_environ()
    1913
    1914 -    def get_environ(self):
    1915          """Return a new environ dict targeting the given wsgi.version"""
    1916          raise NotImplemented
    1917
    1918 -    def respond(self):
    1919          response = self.req.server.wsgi_app(self.env, self.start_response)
    1920          try:
    1921              for chunk in response:
    1922                  # "The start_response callable must not actually transmit
    1923                  # the response headers. Instead, it must store them for the
    1924                  # server or gateway to transmit only after the first
    1925                  # iteration of the application return value that yields
    1926                  # a NON-EMPTY string, or upon the application's first
    1927                  # invocation of the write() callable." (PEP 333)
    1928                  if chunk:
    1929                      if isinstance(chunk, unicode):
    1930                          chunk = chunk.encode('ISO-8859-1')
    1931                      self.write(chunk)
    1932          finally:
    1933              if hasattr(response, "close"):
    1934                  response.close()
    1935
    1936 -    def start_response(self, status, headers, exc_info = None):
    1937          """WSGI callable to begin the HTTP response."""
    1938          # "The application may call start_response more than once,
    1939          # if and only if the exc_info argument is provided."
    1940          if self.started_response and not exc_info:
    1941              raise AssertionError("WSGI start_response called a second "
    1942                                   "time with no exc_info.")
    1943          self.started_response = True
    1944
    1945          # "if exc_info is provided, and the HTTP headers have already been
    1946          # sent, start_response must raise an error, and should raise the
    1947          # exc_info tuple."
    1948          if self.req.sent_headers:
    1949              try:
    1950                  raise exc_info[0], exc_info[1], exc_info[2]
    1951              finally:
    1952                  exc_info = None
    1953
    1954          self.req.status = status
    1955          for k, v in headers:
    1956              if not isinstance(k, str):
    1957                  raise TypeError("WSGI response header key %r is not a byte string." % k)
    1958              if not isinstance(v, str):
    1959                  raise TypeError("WSGI response header value %r is not a byte string." % v)
    1960          self.req.outheaders.extend(headers)
    1961
    1962          return self.write
    1963
    1964 -    def write(self, chunk):
    1965          """WSGI callable to write unbuffered data to the client.
    1966
    1967          This method is also used internally by start_response (to write
    1968          data from the iterable returned by the WSGI application).
    1969          """
    1970          if not self.started_response:
    1971              raise AssertionError("WSGI write called before start_response.")
    1972
    1973          if not self.req.sent_headers:
    1974              self.req.sent_headers = True
    1975              self.req.send_headers()
    1976
    1977          self.req.write(chunk)
    1978
    1979
    1980 -class WSGIGateway_10(WSGIGateway):
    1981
    1982 -    def get_environ(self):
    1983          """Return a new environ dict targeting the given wsgi.version"""
    1984          req = self.req
    1985          env = {
    1986              # set a non-standard environ entry so the WSGI app can know what
    1987              # the *real* server protocol is (and what features to support).
    1988              # See http://www.faqs.org/rfcs/rfc2145.html.
    1989              'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
    1990              'PATH_INFO': req.path,
    1991              'QUERY_STRING': req.qs,
    1992              'REMOTE_ADDR': req.conn.remote_addr or '',
    1993              'REMOTE_PORT': str(req.conn.remote_port or ''),
    1994              'REQUEST_METHOD': req.method,
    1995              'REQUEST_URI': req.uri,
    1996              'SCRIPT_NAME': '',
    1997              'SERVER_NAME': req.server.server_name,
    1998              # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
    1999              'SERVER_PROTOCOL': req.request_protocol,
    2000              'SERVER_SOFTWARE': "%s WSGI Server" % req.server.version,
    2001              'wsgi.errors': sys.stderr,
    2002              'wsgi.input': req.rfile,
    2003              'wsgi.multiprocess': False,
    2004              'wsgi.multithread': True,
    2005              'wsgi.run_once': False,
    2006              'wsgi.url_scheme': req.scheme,
    2007              'wsgi.version': (1, 0),
    2008              }
    2009
    2010          if isinstance(req.server.bind_addr, basestring):
    2011              # AF_UNIX. This isn't really allowed by WSGI, which doesn't
    2012              # address unix domain sockets. But it's better than nothing.
    2013              env["SERVER_PORT"] = ""
    2014          else:
    2015              env["SERVER_PORT"] = str(req.server.bind_addr[1])
    2016
    2017          # CONTENT_TYPE/CONTENT_LENGTH
    2018          for k, v in req.inheaders.iteritems():
    2019              env["HTTP_" + k.upper().replace("-", "_")] = v
    2020          ct = env.pop("HTTP_CONTENT_TYPE", None)
    2021          if ct is not None:
    2022              env["CONTENT_TYPE"] = ct
    2023          cl = env.pop("HTTP_CONTENT_LENGTH", None)
    2024          if cl is not None:
    2025              env["CONTENT_LENGTH"] = cl
    2026
    2027          if req.conn.ssl_env:
    2028              env.update(req.conn.ssl_env)
    2029
    2030          return env
    2031
    2032
    2033 -class WSGIGateway_11(WSGIGateway_10):
    2034
    2035 -    def get_environ(self):
    2036          env = WSGIGateway_10.get_environ(self)
    2037          env['wsgi.version'] = (1, 1)
    2038          return env
    2039
    2040
    2041 -class WSGIGateway_u0(WSGIGateway_10):
    2042
    2043 -    def get_environ(self):
    2044          """Return a new environ dict targeting the given wsgi.version"""
    2045          req = self.req
    2046          env_10 = WSGIGateway_10.get_environ(self)
    2047          env = dict([(k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems()])
    2048          env[u'wsgi.version'] = ('u', 0)
    2049
    2050          # Request-URI
    2051          env.setdefault(u'wsgi.url_encoding', u'utf-8')
    2052          try:
    2053              for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
    2054                  env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
    2055          except UnicodeDecodeError:
    2056              # Fall back to latin 1 so apps can transcode if needed.
    2057              env[u'wsgi.url_encoding'] = u'ISO-8859-1'
    2058              for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
    2059                  env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
    2060
    2061          for k, v in sorted(env.items()):
    2062              if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'):
    2063                  env[k] = v.decode('ISO-8859-1')
    2064
    2065          return env
    2066
    2067  wsgi_gateways = {
    2068      (1, 0): WSGIGateway_10,
    2069      (1, 1): WSGIGateway_11,
    2070      ('u', 0): WSGIGateway_u0,
    2071  }
    2072
    2073 -class WSGIPathInfoDispatcher(object):
    2074      """A WSGI dispatcher for dispatch based on the PATH_INFO.
    2075
    2076      apps: a dict or list of (path_prefix, app) pairs.
    2077      """
    2078
    2079 -    def __init__(self, apps):
    2080          try:
    2081              apps = apps.items()
    2082          except AttributeError:
    2083              pass
    2084
    2085          # Sort the apps by len(path), descending
    2086          apps.sort(cmp=lambda x,y: cmp(len(x[0]), len(y[0])))
    2087          apps.reverse()
    2088
    2089          # The path_prefix strings must start, but not end, with a slash.
    2090          # Use "" instead of "/".
    2091          self.apps = [(p.rstrip("/"), a) for p, a in apps]
    2092
    2093 -    def __call__(self, environ, start_response):
    2094          path = environ["PATH_INFO"] or "/"
    2095          for p, app in self.apps:
    2096              # The apps list should be sorted by length, descending.
    2097              if path.startswith(p + "/") or path == p:
    2098                  environ = environ.copy()
    2099                  environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
    2100                  environ["PATH_INFO"] = path[len(p):]
    2101                  return app(environ, start_response)
    2102
    2103          start_response('404 Not Found', [('Content-Type', 'text/plain'),
    2104                                           ('Content-Length', '0')])
    2105          return ['']
    2106