1  """A high-speed, production ready, thread pooled, generic WSGI 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 these attributes: 
  25       
  26      server.ssl_certificate = <filename> 
  27      server.ssl_private_key = <filename> 
  28       
  29      if __name__ == '__main__': 
  30          try: 
  31              server.start() 
  32          except KeyboardInterrupt: 
  33              server.stop() 
  34   
  35  This won't call the CherryPy engine (application side) at all, only the 
  36  WSGI server, which is independant from the rest of CherryPy. Don't 
  37  let the name "CherryPyWSGIServer" throw you; the name merely reflects 
  38  its origin, not its coupling. 
  39   
  40  For those of you wanting to understand internals of this module, here's the 
  41  basic call flow. The server's listening thread runs a very tight loop, 
  42  sticking incoming connections onto a Queue: 
  43   
  44      server = CherryPyWSGIServer(...) 
  45      server.start() 
  46      while True: 
  47          tick() 
  48          # This blocks until a request comes in: 
  49          child = socket.accept() 
  50          conn = HTTPConnection(child, ...) 
  51          server.requests.put(conn) 
  52   
  53  Worker threads are kept in a pool and poll the Queue, popping off and then 
  54  handling each connection in turn. Each connection can consist of an arbitrary 
  55  number of requests and their responses, so we run a nested loop: 
  56   
  57      while True: 
  58          conn = server.requests.get() 
  59          conn.communicate() 
  60          ->  while True: 
  61                  req = HTTPRequest(...) 
  62                  req.parse_request() 
  63                  ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1" 
  64                      req.rfile.readline() 
  65                      req.read_headers() 
  66                  req.respond() 
  67                  ->  response = wsgi_app(...) 
  68                      try: 
  69                          for chunk in response: 
  70                              if chunk: 
  71                                  req.write(chunk) 
  72                      finally: 
  73                          if hasattr(response, "close"): 
  74                              response.close() 
  75                  if req.close_connection: 
  76                      return 
  77  """ 
  78   
  79   
  80  import base64 
  81  import os 
  82  import Queue 
  83  import re 
  84  quoted_slash = re.compile("(?i)%2F") 
  85  import rfc822 
  86  import socket 
  87  try: 
  88      import cStringIO as StringIO 
  89  except ImportError: 
  90      import StringIO 
  91   
  92  _fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring) 
  93   
  94  import sys 
  95  import threading 
  96  import time 
  97  import traceback 
  98  from urllib import unquote 
  99  from urlparse import urlparse 
 100  import warnings 
 101   
 102  try: 
 103      from OpenSSL import SSL 
 104      from OpenSSL import crypto 
 105  except ImportError: 
 106      SSL = None 
 107   
 108  import errno 
 109   
 111      """Return error numbers for all errors in errnames on this platform. 
 112       
 113      The 'errno' module contains different global constants depending on 
 114      the specific platform (OS). This function will return the list of 
 115      numeric values for a given list of potential names. 
 116      """ 
 117      errno_names = dir(errno) 
 118      nums = [getattr(errno, k) for k in errnames if k in errno_names] 
 119       
 120      return dict.fromkeys(nums).keys() 
  121   
 122  socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR") 
 123   
 124  socket_errors_to_ignore = plat_specific_errors( 
 125      "EPIPE", 
 126      "EBADF", "WSAEBADF", 
 127      "ENOTSOCK", "WSAENOTSOCK", 
 128      "ETIMEDOUT", "WSAETIMEDOUT", 
 129      "ECONNREFUSED", "WSAECONNREFUSED", 
 130      "ECONNRESET", "WSAECONNRESET", 
 131      "ECONNABORTED", "WSAECONNABORTED", 
 132      "ENETRESET", "WSAENETRESET", 
 133      "EHOSTDOWN", "EHOSTUNREACH", 
 134      ) 
 135  socket_errors_to_ignore.append("timed out") 
 136   
 137  socket_errors_nonblocking = plat_specific_errors( 
 138      'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK') 
 139   
 140  comma_separated_headers = ['ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING', 
 141      'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL', 
 142      'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT', 
 143      'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE', 
 144      'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING', 
 145      'WWW-AUTHENTICATE'] 
 146   
 147   
 149      """A WSGI dispatcher for dispatch based on the PATH_INFO. 
 150       
 151      apps: a dict or list of (path_prefix, app) pairs. 
 152      """ 
 153       
 155          try: 
 156              apps = apps.items() 
 157          except AttributeError: 
 158              pass 
 159           
 160           
 161          apps.sort() 
 162          apps.reverse() 
 163           
 164           
 165           
 166          self.apps = [(p.rstrip("/"), a) for p, a in apps] 
  167       
 168 -    def __call__(self, environ, start_response): 
  169          path = environ["PATH_INFO"] or "/" 
 170          for p, app in self.apps: 
 171               
 172              if path.startswith(p + "/") or path == p: 
 173                  environ = environ.copy() 
 174                  environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p 
 175                  environ["PATH_INFO"] = path[len(p):] 
 176                  return app(environ, start_response) 
 177           
 178          start_response('404 Not Found', [('Content-Type', 'text/plain'), 
 179                                           ('Content-Length', '0')]) 
 180          return [''] 
   181   
 182   
 185   
 187      """Wraps a file-like object, raising MaxSizeExceeded if too large.""" 
 188       
 190          self.rfile = rfile 
 191          self.maxlen = maxlen 
 192          self.bytes_read = 0 
  193       
 195          if self.maxlen and self.bytes_read > self.maxlen: 
 196              raise MaxSizeExceeded() 
  197       
 198 -    def read(self, size=None): 
  199          data = self.rfile.read(size) 
 200          self.bytes_read += len(data) 
 201          self._check_length() 
 202          return data 
  203       
 205          if size is not None: 
 206              data = self.rfile.readline(size) 
 207              self.bytes_read += len(data) 
 208              self._check_length() 
 209              return data 
 210           
 211           
 212           
 213          res = [] 
 214          while True: 
 215              data = self.rfile.readline(256) 
 216              self.bytes_read += len(data) 
 217              self._check_length() 
 218              res.append(data) 
 219               
 220              if len(data) < 256 or data[-1:] == "\n": 
 221                  return ''.join(res) 
  222       
 224           
 225          total = 0 
 226          lines = [] 
 227          line = self.readline() 
 228          while line: 
 229              lines.append(line) 
 230              total += len(line) 
 231              if 0 < sizehint <= total: 
 232                  break 
 233              line = self.readline() 
 234          return lines 
  235       
 238       
 241       
 243          data = self.rfile.next() 
 244          self.bytes_read += len(data) 
 245          self._check_length() 
 246          return data 
   247   
 248   
 250      """An HTTP Request (and response). 
 251       
 252      A single HTTP connection may consist of multiple request/response pairs. 
 253       
 254      send: the 'send' method from the connection's socket object. 
 255      wsgi_app: the WSGI application to call. 
 256      environ: a partial WSGI environ (server and connection entries). 
 257          The caller MUST set the following entries: 
 258          * All wsgi.* entries, including .input 
 259          * SERVER_NAME and SERVER_PORT 
 260          * Any SSL_* entries 
 261          * Any custom entries like REMOTE_ADDR and REMOTE_PORT 
 262          * SERVER_SOFTWARE: the value to write in the "Server" response header. 
 263          * ACTUAL_SERVER_PROTOCOL: the value to write in the Status-Line of 
 264              the response. From RFC 2145: "An HTTP server SHOULD send a 
 265              response version equal to the highest version for which the 
 266              server is at least conditionally compliant, and whose major 
 267              version is less than or equal to the one received in the 
 268              request.  An HTTP server MUST NOT send a version for which 
 269              it is not at least conditionally compliant." 
 270       
 271      outheaders: a list of header tuples to write in the response. 
 272      ready: when True, the request has been parsed and is ready to begin 
 273          generating the response. When False, signals the calling Connection 
 274          that the response should not be generated and the connection should 
 275          close. 
 276      close_connection: signals the calling Connection that the request 
 277          should close. This does not imply an error! The client and/or 
 278          server may each request that the connection be closed. 
 279      chunked_write: if True, output will be encoded with the "chunked" 
 280          transfer-coding. This value is set automatically inside 
 281          send_headers. 
 282      """ 
 283       
 284      max_request_header_size = 0 
 285      max_request_body_size = 0 
 286       
 287 -    def __init__(self, wfile, environ, wsgi_app): 
  288          self.rfile = environ['wsgi.input'] 
 289          self.wfile = wfile 
 290          self.environ = environ.copy() 
 291          self.wsgi_app = wsgi_app 
 292           
 293          self.ready = False 
 294          self.started_response = False 
 295          self.status = "" 
 296          self.outheaders = [] 
 297          self.sent_headers = False 
 298          self.close_connection = False 
 299          self.chunked_write = False 
  300       
 302          """Parse the next HTTP request start-line and message-headers.""" 
 303          self.rfile.maxlen = self.max_request_header_size 
 304          self.rfile.bytes_read = 0 
 305           
 306          try: 
 307              self._parse_request() 
 308          except MaxSizeExceeded: 
 309              self.simple_response("413 Request Entity Too Large") 
 310              return 
  311       
 313           
 314           
 315           
 316           
 317           
 318           
 319           
 320          request_line = self.rfile.readline() 
 321          if not request_line: 
 322               
 323              self.ready = False 
 324              return 
 325           
 326          if request_line == "\r\n": 
 327               
 328               
 329               
 330               
 331              request_line = self.rfile.readline() 
 332              if not request_line: 
 333                  self.ready = False 
 334                  return 
 335           
 336          environ = self.environ 
 337           
 338          try: 
 339              method, path, req_protocol = request_line.strip().split(" ", 2) 
 340          except ValueError: 
 341              self.simple_response(400, "Malformed Request-Line") 
 342              return 
 343           
 344          environ["REQUEST_METHOD"] = method 
 345           
 346           
 347          scheme, location, path, params, qs, frag = urlparse(path) 
 348           
 349          if frag: 
 350              self.simple_response("400 Bad Request", 
 351                                   "Illegal #fragment in Request-URI.") 
 352              return 
 353           
 354          if scheme: 
 355              environ["wsgi.url_scheme"] = scheme 
 356          if params: 
 357              path = path + ";" + params 
 358           
 359          environ["SCRIPT_NAME"] = "" 
 360           
 361           
 362           
 363           
 364           
 365           
 366           
 367   
 368           
 369           
 370   
 371   
 372          environ["PATH_INFO"] = path 
 373           
 374           
 375           
 376          environ["QUERY_STRING"] = qs 
 377           
 378           
 379           
 380           
 381           
 382           
 383           
 384           
 385           
 386           
 387           
 388           
 389           
 390          rp = int(req_protocol[5]), int(req_protocol[7]) 
 391          server_protocol = environ["ACTUAL_SERVER_PROTOCOL"] 
 392          sp = int(server_protocol[5]), int(server_protocol[7]) 
 393          if sp[0] != rp[0]: 
 394              self.simple_response("505 HTTP Version Not Supported") 
 395              return 
 396           
 397          environ["SERVER_PROTOCOL"] = req_protocol 
 398          self.response_protocol = "HTTP/%s.%s" % min(rp, sp) 
 399           
 400           
 401          if location: 
 402              environ["SERVER_NAME"] = location 
 403           
 404           
 405          try: 
 406              self.read_headers() 
 407          except ValueError, ex: 
 408              self.simple_response("400 Bad Request", repr(ex.args)) 
 409              return 
 410           
 411          mrbs = self.max_request_body_size 
 412          if mrbs and int(environ.get("CONTENT_LENGTH", 0)) > mrbs: 
 413              self.simple_response("413 Request Entity Too Large") 
 414              return 
 415           
 416           
 417          if self.response_protocol == "HTTP/1.1": 
 418               
 419              if environ.get("HTTP_CONNECTION", "") == "close": 
 420                  self.close_connection = True 
 421          else: 
 422               
 423              if environ.get("HTTP_CONNECTION", "") != "Keep-Alive": 
 424                  self.close_connection = True 
 425           
 426           
 427          te = None 
 428          if self.response_protocol == "HTTP/1.1": 
 429              te = environ.get("HTTP_TRANSFER_ENCODING") 
 430              if te: 
 431                  te = [x.strip().lower() for x in te.split(",") if x.strip()] 
 432           
 433          self.chunked_read = False 
 434           
 435          if te: 
 436              for enc in te: 
 437                  if enc == "chunked": 
 438                      self.chunked_read = True 
 439                  else: 
 440                       
 441                       
 442                      self.simple_response("501 Unimplemented") 
 443                      self.close_connection = True 
 444                      return 
 445           
 446           
 447           
 448           
 449           
 450           
 451           
 452           
 453           
 454           
 455           
 456           
 457           
 458           
 459           
 460           
 461           
 462           
 463          if environ.get("HTTP_EXPECT", "") == "100-continue": 
 464              self.simple_response(100) 
 465           
 466          self.ready = True 
  467       
 469          """Read header lines from the incoming stream.""" 
 470          environ = self.environ 
 471           
 472          while True: 
 473              line = self.rfile.readline() 
 474              if not line: 
 475                   
 476                  raise ValueError("Illegal end of headers.") 
 477               
 478              if line == '\r\n': 
 479                   
 480                  break 
 481               
 482              if line[0] in ' \t': 
 483                   
 484                  v = line.strip() 
 485              else: 
 486                  k, v = line.split(":", 1) 
 487                  k, v = k.strip().upper(), v.strip() 
 488                  envname = "HTTP_" + k.replace("-", "_") 
 489               
 490              if k in comma_separated_headers: 
 491                  existing = environ.get(envname) 
 492                  if existing: 
 493                      v = ", ".join((existing, v)) 
 494              environ[envname] = v 
 495           
 496          ct = environ.pop("HTTP_CONTENT_TYPE", None) 
 497          if ct is not None: 
 498              environ["CONTENT_TYPE"] = ct 
 499          cl = environ.pop("HTTP_CONTENT_LENGTH", None) 
 500          if cl is not None: 
 501              environ["CONTENT_LENGTH"] = cl 
  502       
 504          """Decode the 'chunked' transfer coding.""" 
 505          cl = 0 
 506          data = StringIO.StringIO() 
 507          while True: 
 508              line = self.rfile.readline().strip().split(";", 1) 
 509              chunk_size = int(line.pop(0), 16) 
 510              if chunk_size <= 0: 
 511                  break 
 512   
 513              cl += chunk_size 
 514              data.write(self.rfile.read(chunk_size)) 
 515              crlf = self.rfile.read(2) 
 516              if crlf != "\r\n": 
 517                  self.simple_response("400 Bad Request", 
 518                                       "Bad chunked transfer coding " 
 519                                       "(expected '\\r\\n', got %r)" % crlf) 
 520                  return 
 521           
 522           
 523          self.read_headers() 
 524           
 525          data.seek(0) 
 526          self.environ["wsgi.input"] = data 
 527          self.environ["CONTENT_LENGTH"] = str(cl) or "" 
 528          return True 
  529       
 531          """Call the appropriate WSGI app and write its iterable output.""" 
 532           
 533           
 534           
 535          if self.chunked_read: 
 536               
 537              self.rfile.maxlen = self.max_request_body_size 
 538          else: 
 539              cl = int(self.environ.get("CONTENT_LENGTH", 0)) 
 540              if self.max_request_body_size: 
 541                  self.rfile.maxlen = min(cl, self.max_request_body_size) 
 542              else: 
 543                  self.rfile.maxlen = cl 
 544          self.rfile.bytes_read = 0 
 545           
 546          try: 
 547              self._respond() 
 548          except MaxSizeExceeded: 
 549              if not self.sent_headers: 
 550                  self.simple_response("413 Request Entity Too Large") 
 551              return 
  552       
 554          if self.chunked_read: 
 555              if not self.decode_chunked(): 
 556                  self.close_connection = True 
 557                  return 
 558           
 559          response = self.wsgi_app(self.environ, self.start_response) 
 560          try: 
 561              for chunk in response: 
 562                   
 563                   
 564                   
 565                   
 566                   
 567                   
 568                  if chunk: 
 569                      self.write(chunk) 
 570          finally: 
 571              if hasattr(response, "close"): 
 572                  response.close() 
 573           
 574          if (self.ready and not self.sent_headers): 
 575              self.sent_headers = True 
 576              self.send_headers() 
 577          if self.chunked_write: 
 578              self.wfile.sendall("0\r\n\r\n") 
  579       
 581          """Write a simple response back to the client.""" 
 582          status = str(status) 
 583          buf = ["%s %s\r\n" % (self.environ['ACTUAL_SERVER_PROTOCOL'], status), 
 584                 "Content-Length: %s\r\n" % len(msg), 
 585                 "Content-Type: text/plain\r\n"] 
 586           
 587          if status[:3] == "413" and self.response_protocol == 'HTTP/1.1': 
 588               
 589              self.close_connection = True 
 590              buf.append("Connection: close\r\n") 
 591           
 592          buf.append("\r\n") 
 593          if msg: 
 594              buf.append(msg) 
 595           
 596          try: 
 597              self.wfile.sendall("".join(buf)) 
 598          except socket.error, x: 
 599              if x.args[0] not in socket_errors_to_ignore: 
 600                  raise 
  601       
 603          """WSGI callable to begin the HTTP response.""" 
 604           
 605           
 606          if self.started_response and not exc_info: 
 607              raise AssertionError("WSGI start_response called a second " 
 608                                   "time with no exc_info.") 
 609           
 610           
 611           
 612           
 613          if self.sent_headers: 
 614              try: 
 615                  raise exc_info[0], exc_info[1], exc_info[2] 
 616              finally: 
 617                  exc_info = None 
 618           
 619          self.started_response = True 
 620          self.status = status 
 621          self.outheaders.extend(headers) 
 622          return self.write 
  623       
 625          """WSGI callable to write unbuffered data to the client. 
 626           
 627          This method is also used internally by start_response (to write 
 628          data from the iterable returned by the WSGI application). 
 629          """ 
 630          if not self.started_response: 
 631              raise AssertionError("WSGI write called before start_response.") 
 632           
 633          if not self.sent_headers: 
 634              self.sent_headers = True 
 635              self.send_headers() 
 636           
 637          if self.chunked_write and chunk: 
 638              buf = [hex(len(chunk))[2:], "\r\n", chunk, "\r\n"] 
 639              self.wfile.sendall("".join(buf)) 
 640          else: 
 641              self.wfile.sendall(chunk) 
  642       
 644          """Assert, process, and send the HTTP response message-headers.""" 
 645          hkeys = [key.lower() for key, value in self.outheaders] 
 646          status = int(self.status[:3]) 
 647           
 648          if status == 413: 
 649               
 650              self.close_connection = True 
 651          elif "content-length" not in hkeys: 
 652               
 653               
 654               
 655              if status < 200 or status in (204, 205, 304): 
 656                  pass 
 657              else: 
 658                  if (self.response_protocol == 'HTTP/1.1' 
 659                      and self.environ["REQUEST_METHOD"] != 'HEAD'): 
 660                       
 661                      self.chunked_write = True 
 662                      self.outheaders.append(("Transfer-Encoding", "chunked")) 
 663                  else: 
 664                       
 665                      self.close_connection = True 
 666           
 667          if "connection" not in hkeys: 
 668              if self.response_protocol == 'HTTP/1.1': 
 669                   
 670                  if self.close_connection: 
 671                      self.outheaders.append(("Connection", "close")) 
 672              else: 
 673                   
 674                  if not self.close_connection: 
 675                      self.outheaders.append(("Connection", "Keep-Alive")) 
 676           
 677          if (not self.close_connection) and (not self.chunked_read): 
 678               
 679               
 680               
 681               
 682               
 683               
 684               
 685               
 686               
 687               
 688               
 689               
 690              size = self.rfile.maxlen - self.rfile.bytes_read 
 691              if size > 0: 
 692                  self.rfile.read(size) 
 693           
 694          if "date" not in hkeys: 
 695              self.outheaders.append(("Date", rfc822.formatdate())) 
 696           
 697          if "server" not in hkeys: 
 698              self.outheaders.append(("Server", self.environ['SERVER_SOFTWARE'])) 
 699           
 700          buf = [self.environ['ACTUAL_SERVER_PROTOCOL'], " ", self.status, "\r\n"] 
 701          try: 
 702              buf += [k + ": " + v + "\r\n" for k, v in self.outheaders] 
 703          except TypeError: 
 704              if not isinstance(k, str): 
 705                  raise TypeError("WSGI response header key %r is not a string.") 
 706              if not isinstance(v, str): 
 707                  raise TypeError("WSGI response header value %r is not a string.") 
 708              else: 
 709                  raise 
 710          buf.append("\r\n") 
 711          self.wfile.sendall("".join(buf)) 
   712   
 713   
 715      """Exception raised when a client speaks HTTP to an HTTPS socket.""" 
 716      pass 
  717   
 718   
 720      """Exception raised when the SSL implementation signals a fatal alert.""" 
 721      pass 
  722   
 723   
 724  if not _fileobject_uses_str_type: 
 726          """Faux file object attached to a socket object.""" 
 727   
 729              """Sendall for non-blocking sockets.""" 
 730              while data: 
 731                  try: 
 732                      bytes_sent = self.send(data) 
 733                      data = data[bytes_sent:] 
 734                  except socket.error, e: 
 735                      if e.args[0] not in socket_errors_nonblocking: 
 736                          raise 
  737   
 738 -        def send(self, data): 
  739              return self._sock.send(data) 
  740   
 742              if self._wbuf: 
 743                  buffer = "".join(self._wbuf) 
 744                  self._wbuf = [] 
 745                  self.sendall(buffer) 
  746   
 747 -        def recv(self, size): 
  755   
 756 -        def read(self, size=-1): 
  757               
 758               
 759               
 760              rbufsize = max(self._rbufsize, self.default_bufsize) 
 761               
 762               
 763               
 764              buf = self._rbuf 
 765              buf.seek(0, 2)   
 766              if size < 0: 
 767                   
 768                  self._rbuf = StringIO.StringIO()   
 769                  while True: 
 770                      data = self.recv(rbufsize) 
 771                      if not data: 
 772                          break 
 773                      buf.write(data) 
 774                  return buf.getvalue() 
 775              else: 
 776                   
 777                  buf_len = buf.tell() 
 778                  if buf_len >= size: 
 779                       
 780                      buf.seek(0) 
 781                      rv = buf.read(size) 
 782                      self._rbuf = StringIO.StringIO() 
 783                      self._rbuf.write(buf.read()) 
 784                      return rv 
 785   
 786                  self._rbuf = StringIO.StringIO()   
 787                  while True: 
 788                      left = size - buf_len 
 789                       
 790                       
 791                       
 792                       
 793                       
 794                      data = self.recv(left) 
 795                      if not data: 
 796                          break 
 797                      n = len(data) 
 798                      if n == size and not buf_len: 
 799                           
 800                           
 801                           
 802                           
 803                           
 804                          return data 
 805                      if n == left: 
 806                          buf.write(data) 
 807                          del data   
 808                          break 
 809                      assert n <= left, "recv(%d) returned %d bytes" % (left, n) 
 810                      buf.write(data) 
 811                      buf_len += n 
 812                      del data   
 813                       
 814                  return buf.getvalue() 
  815   
 817              buf = self._rbuf 
 818              buf.seek(0, 2)   
 819              if buf.tell() > 0: 
 820                   
 821                  buf.seek(0) 
 822                  bline = buf.readline(size) 
 823                  if bline.endswith('\n') or len(bline) == size: 
 824                      self._rbuf = StringIO.StringIO() 
 825                      self._rbuf.write(buf.read()) 
 826                      return bline 
 827                  del bline 
 828              if size < 0: 
 829                   
 830                  if self._rbufsize <= 1: 
 831                       
 832                      buf.seek(0) 
 833                      buffers = [buf.read()] 
 834                      self._rbuf = StringIO.StringIO()   
 835                      data = None 
 836                      recv = self.recv 
 837                      while data != "\n": 
 838                          data = recv(1) 
 839                          if not data: 
 840                              break 
 841                          buffers.append(data) 
 842                      return "".join(buffers) 
 843   
 844                  buf.seek(0, 2)   
 845                  self._rbuf = StringIO.StringIO()   
 846                  while True: 
 847                      data = self.recv(self._rbufsize) 
 848                      if not data: 
 849                          break 
 850                      nl = data.find('\n') 
 851                      if nl >= 0: 
 852                          nl += 1 
 853                          buf.write(data[:nl]) 
 854                          self._rbuf.write(data[nl:]) 
 855                          del data 
 856                          break 
 857                      buf.write(data) 
 858                  return buf.getvalue() 
 859              else: 
 860                   
 861                  buf.seek(0, 2)   
 862                  buf_len = buf.tell() 
 863                  if buf_len >= size: 
 864                      buf.seek(0) 
 865                      rv = buf.read(size) 
 866                      self._rbuf = StringIO.StringIO() 
 867                      self._rbuf.write(buf.read()) 
 868                      return rv 
 869                  self._rbuf = StringIO.StringIO()   
 870                  while True: 
 871                      data = self.recv(self._rbufsize) 
 872                      if not data: 
 873                          break 
 874                      left = size - buf_len 
 875                       
 876                      nl = data.find('\n', 0, left) 
 877                      if nl >= 0: 
 878                          nl += 1 
 879                           
 880                          self._rbuf.write(data[nl:]) 
 881                          if buf_len: 
 882                              buf.write(data[:nl]) 
 883                              break 
 884                          else: 
 885                               
 886                               
 887                              return data[:nl] 
 888                      n = len(data) 
 889                      if n == size and not buf_len: 
 890                           
 891                           
 892                          return data 
 893                      if n >= left: 
 894                          buf.write(data[:left]) 
 895                          self._rbuf.write(data[left:]) 
 896                          break 
 897                      buf.write(data) 
 898                      buf_len += n 
 899                       
 900                  return buf.getvalue() 
   901   
 902  else: 
 904          """Faux file object attached to a socket object.""" 
 905   
 907              """Sendall for non-blocking sockets.""" 
 908              while data: 
 909                  try: 
 910                      bytes_sent = self.send(data) 
 911                      data = data[bytes_sent:] 
 912                  except socket.error, e: 
 913                      if e.args[0] not in socket_errors_nonblocking: 
 914                          raise 
  915   
 916 -        def send(self, data): 
  917              return self._sock.send(data) 
  918   
 920              if self._wbuf: 
 921                  buffer = "".join(self._wbuf) 
 922                  self._wbuf = [] 
 923                  self.sendall(buffer) 
  924   
 925 -        def recv(self, size): 
  933   
 934 -        def read(self, size=-1): 
  935              if size < 0: 
 936                   
 937                  buffers = [self._rbuf] 
 938                  self._rbuf = "" 
 939                  if self._rbufsize <= 1: 
 940                      recv_size = self.default_bufsize 
 941                  else: 
 942                      recv_size = self._rbufsize 
 943   
 944                  while True: 
 945                      data = self.recv(recv_size) 
 946                      if not data: 
 947                          break 
 948                      buffers.append(data) 
 949                  return "".join(buffers) 
 950              else: 
 951                   
 952                  data = self._rbuf 
 953                  buf_len = len(data) 
 954                  if buf_len >= size: 
 955                      self._rbuf = data[size:] 
 956                      return data[:size] 
 957                  buffers = [] 
 958                  if data: 
 959                      buffers.append(data) 
 960                  self._rbuf = "" 
 961                  while True: 
 962                      left = size - buf_len 
 963                      recv_size = max(self._rbufsize, left) 
 964                      data = self.recv(recv_size) 
 965                      if not data: 
 966                          break 
 967                      buffers.append(data) 
 968                      n = len(data) 
 969                      if n >= left: 
 970                          self._rbuf = data[left:] 
 971                          buffers[-1] = data[:left] 
 972                          break 
 973                      buf_len += n 
 974                  return "".join(buffers) 
  975   
 977              data = self._rbuf 
 978              if size < 0: 
 979                   
 980                  if self._rbufsize <= 1: 
 981                       
 982                      assert data == "" 
 983                      buffers = [] 
 984                      while data != "\n": 
 985                          data = self.recv(1) 
 986                          if not data: 
 987                              break 
 988                          buffers.append(data) 
 989                      return "".join(buffers) 
 990                  nl = data.find('\n') 
 991                  if nl >= 0: 
 992                      nl += 1 
 993                      self._rbuf = data[nl:] 
 994                      return data[:nl] 
 995                  buffers = [] 
 996                  if data: 
 997                      buffers.append(data) 
 998                  self._rbuf = "" 
 999                  while True: 
1000                      data = self.recv(self._rbufsize) 
1001                      if not data: 
1002                          break 
1003                      buffers.append(data) 
1004                      nl = data.find('\n') 
1005                      if nl >= 0: 
1006                          nl += 1 
1007                          self._rbuf = data[nl:] 
1008                          buffers[-1] = data[:nl] 
1009                          break 
1010                  return "".join(buffers) 
1011              else: 
1012                   
1013                  nl = data.find('\n', 0, size) 
1014                  if nl >= 0: 
1015                      nl += 1 
1016                      self._rbuf = data[nl:] 
1017                      return data[:nl] 
1018                  buf_len = len(data) 
1019                  if buf_len >= size: 
1020                      self._rbuf = data[size:] 
1021                      return data[:size] 
1022                  buffers = [] 
1023                  if data: 
1024                      buffers.append(data) 
1025                  self._rbuf = "" 
1026                  while True: 
1027                      data = self.recv(self._rbufsize) 
1028                      if not data: 
1029                          break 
1030                      buffers.append(data) 
1031                      left = size - buf_len 
1032                      nl = data.find('\n', 0, left) 
1033                      if nl >= 0: 
1034                          nl += 1 
1035                          self._rbuf = data[nl:] 
1036                          buffers[-1] = data[:nl] 
1037                          break 
1038                      n = len(data) 
1039                      if n >= left: 
1040                          self._rbuf = data[left:] 
1041                          buffers[-1] = data[:left] 
1042                          break 
1043                      buf_len += n 
1044                  return "".join(buffers) 
  1045       
1046   
1048      """SSL file object attached to a socket object.""" 
1049       
1050      ssl_timeout = 3 
1051      ssl_retry = .01 
1052       
1053 -    def _safe_call(self, is_reader, call, *args, **kwargs): 
 1054          """Wrap the given call with SSL error-trapping. 
1055           
1056          is_reader: if False EOF errors will be raised. If True, EOF errors 
1057              will return "" (to emulate normal sockets). 
1058          """ 
1059          start = time.time() 
1060          while True: 
1061              try: 
1062                  return call(*args, **kwargs) 
1063              except SSL.WantReadError: 
1064                   
1065                   
1066                   
1067                   
1068                  time.sleep(self.ssl_retry) 
1069              except SSL.WantWriteError: 
1070                  time.sleep(self.ssl_retry) 
1071              except SSL.SysCallError, e: 
1072                  if is_reader and e.args == (-1, 'Unexpected EOF'): 
1073                      return "" 
1074                   
1075                  errnum = e.args[0] 
1076                  if is_reader and errnum in socket_errors_to_ignore: 
1077                      return "" 
1078                  raise socket.error(errnum) 
1079              except SSL.Error, e: 
1080                  if is_reader and e.args == (-1, 'Unexpected EOF'): 
1081                      return "" 
1082                   
1083                  thirdarg = None 
1084                  try: 
1085                      thirdarg = e.args[0][0][2] 
1086                  except IndexError: 
1087                      pass 
1088                   
1089                  if thirdarg == 'http request': 
1090                       
1091                      raise NoSSLError() 
1092                  raise FatalSSLAlert(*e.args) 
1093              except: 
1094                  raise 
1095               
1096              if time.time() - start > self.ssl_timeout: 
1097                  raise socket.timeout("timed out") 
 1098   
1099 -    def recv(self, *args, **kwargs): 
 1100          buf = [] 
1101          r = super(SSL_fileobject, self).recv 
1102          while True: 
1103              data = self._safe_call(True, r, *args, **kwargs) 
1104              buf.append(data) 
1105              p = self._sock.pending() 
1106              if not p: 
1107                  return "".join(buf) 
 1108       
1109 -    def sendall(self, *args, **kwargs): 
 1111   
1112 -    def send(self, *args, **kwargs): 
  1114   
1115   
1117      """An HTTP connection (active socket). 
1118       
1119      socket: the raw socket object (usually TCP) for this connection. 
1120      wsgi_app: the WSGI application for this server/connection. 
1121      environ: a WSGI environ template. This will be copied for each request. 
1122       
1123      rfile: a fileobject for reading from the socket. 
1124      send: a function for writing (+ flush) to the socket. 
1125      """ 
1126       
1127      rbufsize = -1 
1128      RequestHandlerClass = HTTPRequest 
1129      environ = {"wsgi.version": (1, 0), 
1130                 "wsgi.url_scheme": "http", 
1131                 "wsgi.multithread": True, 
1132                 "wsgi.multiprocess": False, 
1133                 "wsgi.run_once": False, 
1134                 "wsgi.errors": sys.stderr, 
1135                 } 
1136       
1137 -    def __init__(self, sock, wsgi_app, environ): 
 1138          self.socket = sock 
1139          self.wsgi_app = wsgi_app 
1140           
1141           
1142          self.environ = self.environ.copy() 
1143          self.environ.update(environ) 
1144           
1145          if SSL and isinstance(sock, SSL.ConnectionType): 
1146              timeout = sock.gettimeout() 
1147              self.rfile = SSL_fileobject(sock, "rb", self.rbufsize) 
1148              self.rfile.ssl_timeout = timeout 
1149              self.wfile = SSL_fileobject(sock, "wb", -1) 
1150              self.wfile.ssl_timeout = timeout 
1151          else: 
1152              self.rfile = CP_fileobject(sock, "rb", self.rbufsize) 
1153              self.wfile = CP_fileobject(sock, "wb", -1) 
1154           
1155           
1156           
1157           
1158           
1159          self.environ["wsgi.input"] = SizeCheckWrapper(self.rfile, 0) 
 1160       
1162          """Read each request and respond appropriately.""" 
1163          try: 
1164              while True: 
1165                   
1166                   
1167                   
1168                  req = None 
1169                  req = self.RequestHandlerClass(self.wfile, self.environ, 
1170                                                 self.wsgi_app) 
1171                   
1172                   
1173                  req.parse_request() 
1174                  if not req.ready: 
1175                      return 
1176                   
1177                  req.respond() 
1178                  if req.close_connection: 
1179                      return 
1180           
1181          except socket.error, e: 
1182              errnum = e.args[0] 
1183              if errnum == 'timed out': 
1184                  if req and not req.sent_headers: 
1185                      req.simple_response("408 Request Timeout") 
1186              elif errnum not in socket_errors_to_ignore: 
1187                  if req and not req.sent_headers: 
1188                      req.simple_response("500 Internal Server Error", 
1189                                          format_exc()) 
1190              return 
1191          except (KeyboardInterrupt, SystemExit): 
1192              raise 
1193          except FatalSSLAlert, e: 
1194               
1195              return 
1196          except NoSSLError: 
1197               
1198              req.wfile = CP_fileobject(self.socket, "wb", -1) 
1199              if req and not req.sent_headers: 
1200                  req.simple_response("400 Bad Request", 
1201                      "The client sent a plain HTTP request, but " 
1202                      "this server only speaks HTTPS on this port.") 
1203          except Exception, e: 
1204              if req and not req.sent_headers: 
1205                  req.simple_response("500 Internal Server Error", format_exc()) 
 1206       
1208          """Close the socket underlying this connection.""" 
1209          self.rfile.close() 
1210           
1211           
1212           
1213           
1214           
1215           
1216          self.socket._sock.close() 
1217           
1218          self.socket.close() 
  1219   
1220   
1228   
1229   
1230  _SHUTDOWNREQUEST = None 
1231   
1233      """Thread which continuously polls a Queue for Connection objects. 
1234       
1235      server: the HTTP Server which spawned this thread, and which owns the 
1236          Queue and is placing active connections into it. 
1237      ready: a simple flag for the calling server to know when this thread 
1238          has begun polling the Queue. 
1239       
1240      Due to the timing issues of polling a Queue, a WorkerThread does not 
1241      check its own 'ready' flag after it has started. To stop the thread, 
1242      it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue 
1243      (one for each running WorkerThread). 
1244      """ 
1245       
1246      conn = None 
1247       
1249          self.ready = False 
1250          self.server = server 
1251          threading.Thread.__init__(self) 
 1252       
 1269   
1270   
1272      """A Request Queue for the CherryPyWSGIServer which pools threads. 
1273       
1274      ThreadPool objects must provide min, get(), put(obj), start() 
1275      and stop(timeout) attributes. 
1276      """ 
1277       
1278 -    def __init__(self, server, min=10, max=-1): 
 1279          self.server = server 
1280          self.min = min 
1281          self.max = max 
1282          self._threads = [] 
1283          self._queue = Queue.Queue() 
1284          self.get = self._queue.get 
 1285       
1287          """Start the pool of threads.""" 
1288          for i in xrange(self.min): 
1289              self._threads.append(WorkerThread(self.server)) 
1290          for worker in self._threads: 
1291              worker.setName("CP WSGIServer " + worker.getName()) 
1292              worker.start() 
1293          for worker in self._threads: 
1294              while not worker.ready: 
1295                  time.sleep(.1) 
 1296       
1298          """Number of worker threads which are idle. Read-only.""" 
1299          return len([t for t in self._threads if t.conn is None]) 
 1300      idle = property(_get_idle, doc=_get_idle.__doc__) 
1301       
1302 -    def put(self, obj): 
 1306       
1307 -    def grow(self, amount): 
 1308          """Spawn new worker threads (not above self.max).""" 
1309          for i in xrange(amount): 
1310              if self.max > 0 and len(self._threads) >= self.max: 
1311                  break 
1312              worker = WorkerThread(self.server) 
1313              worker.setName("CP WSGIServer " + worker.getName()) 
1314              self._threads.append(worker) 
1315              worker.start() 
 1316       
1318          """Kill off worker threads (not below self.min).""" 
1319           
1320           
1321          for t in self._threads: 
1322              if not t.isAlive(): 
1323                  self._threads.remove(t) 
1324                  amount -= 1 
1325           
1326          if amount > 0: 
1327              for i in xrange(min(amount, len(self._threads) - self.min)): 
1328                   
1329                   
1330                   
1331                   
1332                  self._queue.put(_SHUTDOWNREQUEST) 
 1333       
1334 -    def stop(self, timeout=5): 
 1335           
1336           
1337          for worker in self._threads: 
1338              self._queue.put(_SHUTDOWNREQUEST) 
1339           
1340           
1341          current = threading.currentThread() 
1342          while self._threads: 
1343              worker = self._threads.pop() 
1344              if worker is not current and worker.isAlive(): 
1345                  try: 
1346                      if timeout is None or timeout < 0: 
1347                          worker.join() 
1348                      else: 
1349                          worker.join(timeout) 
1350                          if worker.isAlive(): 
1351                               
1352                               
1353                              c = worker.conn 
1354                              if c and not c.rfile.closed: 
1355                                  if SSL and isinstance(c.socket, SSL.ConnectionType): 
1356                                       
1357                                      c.socket.shutdown() 
1358                                  else: 
1359                                      c.socket.shutdown(socket.SHUT_RD) 
1360                              worker.join() 
1361                  except (AssertionError, 
1362                           
1363                           
1364                          KeyboardInterrupt), exc1: 
1365                      pass 
  1366   
1367   
1368   
1370      """A thread-safe wrapper for an SSL.Connection. 
1371       
1372      *args: the arguments to create the wrapped SSL.Connection(*args). 
1373      """ 
1374       
1376          self._ssl_conn = SSL.Connection(*args) 
1377          self._lock = threading.RLock() 
 1378       
1379      for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read', 
1380                'renegotiate', 'bind', 'listen', 'connect', 'accept', 
1381                'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list', 
1382                'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 
1383                'makefile', 'get_app_data', 'set_app_data', 'state_string', 
1384                'sock_shutdown', 'get_peer_certificate', 'want_read', 
1385                'want_write', 'set_connect_state', 'set_accept_state', 
1386                'connect_ex', 'sendall', 'settimeout'): 
1387          exec """def %s(self, *args): 
1388          self._lock.acquire() 
1389          try: 
1390              return self._ssl_conn.%s(*args) 
1391          finally: 
1392              self._lock.release() 
1393  """ % (f, f) 
 1394   
1395   
1396  try: 
1397      import fcntl 
1398  except ImportError: 
1399      try: 
1400          from ctypes import windll, WinError 
1401      except ImportError: 
1403              """Dummy function, since neither fcntl nor ctypes are available.""" 
1404              pass 
 1405      else: 
1407              """Mark the given socket fd as non-inheritable (Windows).""" 
1408              if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0): 
1409                  raise WinError() 
 1410  else: 
1412          """Mark the given socket fd as non-inheritable (POSIX).""" 
1413          fd = sock.fileno() 
1414          old_flags = fcntl.fcntl(fd, fcntl.F_GETFD) 
1415          fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) 
 1416   
1417   
1419      """An HTTP server for WSGI. 
1420       
1421      bind_addr: The interface on which to listen for connections. 
1422          For TCP sockets, a (host, port) tuple. Host values may be any IPv4 
1423          or IPv6 address, or any valid hostname. The string 'localhost' is a 
1424          synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6). 
1425          The string '0.0.0.0' is a special IPv4 entry meaning "any active 
1426          interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for 
1427          IPv6. The empty string or None are not allowed. 
1428           
1429          For UNIX sockets, supply the filename as a string. 
1430      wsgi_app: the WSGI 'application callable'; multiple WSGI applications 
1431          may be passed as (path_prefix, app) pairs. 
1432      numthreads: the number of worker threads to create (default 10). 
1433      server_name: the string to set for WSGI's SERVER_NAME environ entry. 
1434          Defaults to socket.gethostname(). 
1435      max: the maximum number of queued requests (defaults to -1 = no limit). 
1436      request_queue_size: the 'backlog' argument to socket.listen(); 
1437          specifies the maximum number of queued connections (default 5). 
1438      timeout: the timeout in seconds for accepted connections (default 10). 
1439       
1440      nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket 
1441          option. 
1442       
1443      protocol: the version string to write in the Status-Line of all 
1444          HTTP responses. For example, "HTTP/1.1" (the default). This 
1445          also limits the supported features used in the response. 
1446       
1447       
1448      SSL/HTTPS 
1449      --------- 
1450      The OpenSSL module must be importable for SSL functionality. 
1451      You can obtain it from http://pyopenssl.sourceforge.net/ 
1452       
1453      ssl_certificate: the filename of the server SSL certificate. 
1454      ssl_privatekey: the filename of the server's private key file. 
1455       
1456      If either of these is None (both are None by default), this server 
1457      will not use SSL. If both are given and are valid, they will be read 
1458      on server start and used in the SSL context for the listening socket. 
1459      """ 
1460       
1461      protocol = "HTTP/1.1" 
1462      _bind_addr = "127.0.0.1" 
1463      version = "CherryPy/3.1.0" 
1464      ready = False 
1465      _interrupt = None 
1466       
1467      nodelay = True 
1468       
1469      ConnectionClass = HTTPConnection 
1470      environ = {} 
1471       
1472       
1473      ssl_certificate = None 
1474      ssl_private_key = None 
1475       
1476 -    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, 
1477                   max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5): 
 1478          self.requests = ThreadPool(self, min=numthreads or 1, max=max) 
1479           
1480          if callable(wsgi_app): 
1481               
1482               
1483              self.wsgi_app = wsgi_app 
1484          else: 
1485               
1486               
1487               
1488              warnings.warn("The ability to pass multiple apps is deprecated " 
1489                            "and will be removed in 3.2. You should explicitly " 
1490                            "include a WSGIPathInfoDispatcher instead.", 
1491                            DeprecationWarning) 
1492              self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app) 
1493           
1494          self.bind_addr = bind_addr 
1495          if not server_name: 
1496              server_name = socket.gethostname() 
1497          self.server_name = server_name 
1498          self.request_queue_size = request_queue_size 
1499           
1500          self.timeout = timeout 
1501          self.shutdown_timeout = shutdown_timeout 
 1502       
1504          return self.requests.min 
 1506          self.requests.min = value 
 1507      numthreads = property(_get_numthreads, _set_numthreads) 
1508       
1510          return "%s.%s(%r)" % (self.__module__, self.__class__.__name__, 
1511                                self.bind_addr) 
 1512       
1516          if isinstance(value, tuple) and value[0] in ('', None): 
1517               
1518               
1519               
1520               
1521               
1522               
1523               
1524               
1525               
1526               
1527              raise ValueError("Host values of '' or None are not allowed. " 
1528                               "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead " 
1529                               "to listen on all active interfaces.") 
1530          self._bind_addr = value 
 1531      bind_addr = property(_get_bind_addr, _set_bind_addr, 
1532          doc="""The interface on which to listen for connections. 
1533           
1534          For TCP sockets, a (host, port) tuple. Host values may be any IPv4 
1535          or IPv6 address, or any valid hostname. The string 'localhost' is a 
1536          synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6). 
1537          The string '0.0.0.0' is a special IPv4 entry meaning "any active 
1538          interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for 
1539          IPv6. The empty string or None are not allowed. 
1540           
1541          For UNIX sockets, supply the filename as a string.""") 
1542       
1544          """Run the server forever.""" 
1545           
1546           
1547           
1548           
1549          self._interrupt = None 
1550           
1551           
1552          if isinstance(self.bind_addr, basestring): 
1553               
1554               
1555               
1556              try: os.unlink(self.bind_addr) 
1557              except: pass 
1558               
1559               
1560              try: os.chmod(self.bind_addr, 0777) 
1561              except: pass 
1562               
1563              info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)] 
1564          else: 
1565               
1566               
1567              host, port = self.bind_addr 
1568              try: 
1569                  info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, 
1570                                            socket.SOCK_STREAM, 0, socket.AI_PASSIVE) 
1571              except socket.gaierror: 
1572                   
1573                  info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", self.bind_addr)] 
1574           
1575          self.socket = None 
1576          msg = "No socket could be created" 
1577          for res in info: 
1578              af, socktype, proto, canonname, sa = res 
1579              try: 
1580                  self.bind(af, socktype, proto) 
1581              except socket.error, msg: 
1582                  if self.socket: 
1583                      self.socket.close() 
1584                  self.socket = None 
1585                  continue 
1586              break 
1587          if not self.socket: 
1588              raise socket.error, msg 
1589           
1590           
1591          self.socket.settimeout(1) 
1592          self.socket.listen(self.request_queue_size) 
1593           
1594           
1595          self.requests.start() 
1596           
1597          self.ready = True 
1598          while self.ready: 
1599              self.tick() 
1600              if self.interrupt: 
1601                  while self.interrupt is True: 
1602                       
1603                      time.sleep(0.1) 
1604                  if self.interrupt: 
1605                      raise self.interrupt 
 1606       
1607 -    def bind(self, family, type, proto=0): 
 1608          """Create (or recreate) the actual socket object.""" 
1609          self.socket = socket.socket(family, type, proto) 
1610          prevent_socket_inheritance(self.socket) 
1611          self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
1612          if self.nodelay: 
1613              self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 
1614          if self.ssl_certificate and self.ssl_private_key: 
1615              if SSL is None: 
1616                  raise ImportError("You must install pyOpenSSL to use HTTPS.") 
1617               
1618               
1619              ctx = SSL.Context(SSL.SSLv23_METHOD) 
1620              ctx.use_privatekey_file(self.ssl_private_key) 
1621              ctx.use_certificate_file(self.ssl_certificate) 
1622              self.socket = SSLConnection(ctx, self.socket) 
1623              self.populate_ssl_environ() 
1624               
1625               
1626               
1627              if (not isinstance(self.bind_addr, basestring) 
1628                  and self.bind_addr[0] == '::' and family == socket.AF_INET6): 
1629                  try: 
1630                      self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) 
1631                  except (AttributeError, socket.error): 
1632                       
1633                       
1634                      pass 
1635           
1636          self.socket.bind(self.bind_addr) 
 1637       
1639          """Accept a new connection and put it on the Queue.""" 
1640          try: 
1641              s, addr = self.socket.accept() 
1642              prevent_socket_inheritance(s) 
1643              if not self.ready: 
1644                  return 
1645              if hasattr(s, 'settimeout'): 
1646                  s.settimeout(self.timeout) 
1647               
1648              environ = self.environ.copy() 
1649               
1650               
1651              if environ.get("SERVER_SOFTWARE") is None: 
1652                  environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version 
1653               
1654               
1655               
1656              environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol 
1657              environ["SERVER_NAME"] = self.server_name 
1658               
1659              if isinstance(self.bind_addr, basestring): 
1660                   
1661                   
1662                  environ["SERVER_PORT"] = "" 
1663              else: 
1664                  environ["SERVER_PORT"] = str(self.bind_addr[1]) 
1665                   
1666                   
1667                  environ["REMOTE_ADDR"] = addr[0] 
1668                  environ["REMOTE_PORT"] = str(addr[1]) 
1669               
1670              conn = self.ConnectionClass(s, self.wsgi_app, environ) 
1671              self.requests.put(conn) 
1672          except socket.timeout: 
1673               
1674               
1675               
1676              return 
1677          except socket.error, x: 
1678              if x.args[0] in socket_error_eintr: 
1679                   
1680                   
1681                   
1682                   
1683                   
1684                  return 
1685              if x.args[0] in socket_errors_nonblocking: 
1686                   
1687                  return 
1688              if x.args[0] in socket_errors_to_ignore: 
1689                   
1690                   
1691                  return 
1692              raise 
 1693       
1700      interrupt = property(_get_interrupt, _set_interrupt, 
1701                           doc="Set this to an Exception instance to " 
1702                               "interrupt the server.") 
1703       
1705          """Gracefully shutdown a server that is serving forever.""" 
1706          self.ready = False 
1707           
1708          sock = getattr(self, "socket", None) 
1709          if sock: 
1710              if not isinstance(self.bind_addr, basestring): 
1711                   
1712                  try: 
1713                      host, port = sock.getsockname()[:2] 
1714                  except socket.error, x: 
1715                      if x.args[1] != "Bad file descriptor": 
1716                          raise 
1717                  else: 
1718                       
1719                       
1720                       
1721                       
1722                      for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, 
1723                                                    socket.SOCK_STREAM): 
1724                          af, socktype, proto, canonname, sa = res 
1725                          s = None 
1726                          try: 
1727                              s = socket.socket(af, socktype, proto) 
1728                               
1729                               
1730                              s.settimeout(1.0) 
1731                              s.connect((host, port)) 
1732                              s.close() 
1733                          except socket.error: 
1734                              if s: 
1735                                  s.close() 
1736              if hasattr(sock, "close"): 
1737                  sock.close() 
1738              self.socket = None 
1739           
1740          self.requests.stop(self.shutdown_timeout) 
 1741       
1743          """Create WSGI environ entries to be merged into each request.""" 
1744          cert = open(self.ssl_certificate, 'rb').read() 
1745          cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) 
1746          ssl_environ = { 
1747              "wsgi.url_scheme": "https", 
1748              "HTTPS": "on", 
1749               
1750   
1751   
1752   
1753   
1754              } 
1755           
1756           
1757          ssl_environ.update({ 
1758              'SSL_SERVER_M_VERSION': cert.get_version(), 
1759              'SSL_SERVER_M_SERIAL': cert.get_serial_number(), 
1760   
1761   
1762              }) 
1763           
1764          for prefix, dn in [("I", cert.get_issuer()), 
1765                             ("S", cert.get_subject())]: 
1766               
1767               
1768               
1769              dnstr = str(dn)[18:-2] 
1770               
1771              wsgikey = 'SSL_SERVER_%s_DN' % prefix 
1772              ssl_environ[wsgikey] = dnstr 
1773               
1774               
1775               
1776              while dnstr: 
1777                  pos = dnstr.rfind("=") 
1778                  dnstr, value = dnstr[:pos], dnstr[pos + 1:] 
1779                  pos = dnstr.rfind("/") 
1780                  dnstr, key = dnstr[:pos], dnstr[pos + 1:] 
1781                  if key and value: 
1782                      wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) 
1783                      ssl_environ[wsgikey] = value 
1784           
1785          self.environ.update(ssl_environ) 
  1786