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

Source Code for Package cherrypy.wsgiserver

   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   
110 -def plat_specific_errors(*errnames):
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 # de-dupe the list 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
148 -class WSGIPathInfoDispatcher(object):
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
154 - def __init__(self, apps):
155 try: 156 apps = apps.items() 157 except AttributeError: 158 pass 159 160 # Sort the apps by len(path), descending 161 apps.sort() 162 apps.reverse() 163 164 # The path_prefix strings must start, but not end, with a slash. 165 # Use "" instead of "/". 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 # The apps list should be sorted by length, descending. 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
183 -class MaxSizeExceeded(Exception):
184 pass
185
186 -class SizeCheckWrapper(object):
187 """Wraps a file-like object, raising MaxSizeExceeded if too large.""" 188
189 - def __init__(self, rfile, maxlen):
190 self.rfile = rfile 191 self.maxlen = maxlen 192 self.bytes_read = 0
193
194 - def _check_length(self):
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
204 - def readline(self, size=None):
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 # User didn't specify a size ... 212 # We read the line in chunks to make sure it's not a 100MB line ! 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 # See http://www.cherrypy.org/ticket/421 220 if len(data) < 256 or data[-1:] == "\n": 221 return ''.join(res)
222
223 - def readlines(self, sizehint=0):
224 # Shamelessly stolen from StringIO 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
236 - def close(self):
237 self.rfile.close()
238
239 - def __iter__(self):
240 return self
241
242 - def next(self):
243 data = self.rfile.next() 244 self.bytes_read += len(data) 245 self._check_length() 246 return data
247 248
249 -class HTTPRequest(object):
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
301 - def parse_request(self):
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
312 - def _parse_request(self):
313 # HTTP/1.1 connections are persistent by default. If a client 314 # requests a page, then idles (leaves the connection open), 315 # then rfile.readline() will raise socket.error("timed out"). 316 # Note that it does this based on the value given to settimeout(), 317 # and doesn't need the client to request or acknowledge the close 318 # (although your TCP stack might suffer for it: cf Apache's history 319 # with FIN_WAIT_2). 320 request_line = self.rfile.readline() 321 if not request_line: 322 # Force self.ready = False so the connection will close. 323 self.ready = False 324 return 325 326 if request_line == "\r\n": 327 # RFC 2616 sec 4.1: "...if the server is reading the protocol 328 # stream at the beginning of a message and receives a CRLF 329 # first, it should ignore the CRLF." 330 # But only ignore one leading line! else we enable a DoS. 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 # path may be an abs_path (including "http://host.domain.tld"); 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 # Unquote the path+params (e.g. "/this%20path" -> "this path"). 362 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 363 # 364 # But note that "...a URI must be separated into its components 365 # before the escaped characters within those components can be 366 # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2 367 atoms = [unquote(x) for x in quoted_slash.split(path)] 368 path = "%2F".join(atoms) 369 environ["PATH_INFO"] = path 370 371 # Note that, like wsgiref and most other WSGI servers, 372 # we unquote the path but not the query string. 373 environ["QUERY_STRING"] = qs 374 375 # Compare request and server HTTP protocol versions, in case our 376 # server does not support the requested protocol. Limit our output 377 # to min(req, server). We want the following output: 378 # request server actual written supported response 379 # protocol protocol response protocol feature set 380 # a 1.0 1.0 1.0 1.0 381 # b 1.0 1.1 1.1 1.0 382 # c 1.1 1.0 1.0 1.0 383 # d 1.1 1.1 1.1 1.1 384 # Notice that, in (b), the response will be "HTTP/1.1" even though 385 # the client only understands 1.0. RFC 2616 10.5.6 says we should 386 # only return 505 if the _major_ version is different. 387 rp = int(req_protocol[5]), int(req_protocol[7]) 388 server_protocol = environ["ACTUAL_SERVER_PROTOCOL"] 389 sp = int(server_protocol[5]), int(server_protocol[7]) 390 if sp[0] != rp[0]: 391 self.simple_response("505 HTTP Version Not Supported") 392 return 393 # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol. 394 environ["SERVER_PROTOCOL"] = req_protocol 395 self.response_protocol = "HTTP/%s.%s" % min(rp, sp) 396 397 # If the Request-URI was an absoluteURI, use its location atom. 398 if location: 399 environ["SERVER_NAME"] = location 400 401 # then all the http headers 402 try: 403 self.read_headers() 404 except ValueError, ex: 405 self.simple_response("400 Bad Request", repr(ex.args)) 406 return 407 408 mrbs = self.max_request_body_size 409 if mrbs and int(environ.get("CONTENT_LENGTH", 0)) > mrbs: 410 self.simple_response("413 Request Entity Too Large") 411 return 412 413 # Persistent connection support 414 if self.response_protocol == "HTTP/1.1": 415 # Both server and client are HTTP/1.1 416 if environ.get("HTTP_CONNECTION", "") == "close": 417 self.close_connection = True 418 else: 419 # Either the server or client (or both) are HTTP/1.0 420 if environ.get("HTTP_CONNECTION", "") != "Keep-Alive": 421 self.close_connection = True 422 423 # Transfer-Encoding support 424 te = None 425 if self.response_protocol == "HTTP/1.1": 426 te = environ.get("HTTP_TRANSFER_ENCODING") 427 if te: 428 te = [x.strip().lower() for x in te.split(",") if x.strip()] 429 430 self.chunked_read = False 431 432 if te: 433 for enc in te: 434 if enc == "chunked": 435 self.chunked_read = True 436 else: 437 # Note that, even if we see "chunked", we must reject 438 # if there is an extension we don't recognize. 439 self.simple_response("501 Unimplemented") 440 self.close_connection = True 441 return 442 443 # From PEP 333: 444 # "Servers and gateways that implement HTTP 1.1 must provide 445 # transparent support for HTTP 1.1's "expect/continue" mechanism. 446 # This may be done in any of several ways: 447 # 1. Respond to requests containing an Expect: 100-continue request 448 # with an immediate "100 Continue" response, and proceed normally. 449 # 2. Proceed with the request normally, but provide the application 450 # with a wsgi.input stream that will send the "100 Continue" 451 # response if/when the application first attempts to read from 452 # the input stream. The read request must then remain blocked 453 # until the client responds. 454 # 3. Wait until the client decides that the server does not support 455 # expect/continue, and sends the request body on its own. 456 # (This is suboptimal, and is not recommended.) 457 # 458 # We used to do 3, but are now doing 1. Maybe we'll do 2 someday, 459 # but it seems like it would be a big slowdown for such a rare case. 460 if environ.get("HTTP_EXPECT", "") == "100-continue": 461 self.simple_response(100) 462 463 self.ready = True
464
465 - def read_headers(self):
466 """Read header lines from the incoming stream.""" 467 environ = self.environ 468 469 while True: 470 line = self.rfile.readline() 471 if not line: 472 # No more data--illegal end of headers 473 raise ValueError("Illegal end of headers.") 474 475 if line == '\r\n': 476 # Normal end of headers 477 break 478 479 if line[0] in ' \t': 480 # It's a continuation line. 481 v = line.strip() 482 else: 483 k, v = line.split(":", 1) 484 k, v = k.strip().upper(), v.strip() 485 envname = "HTTP_" + k.replace("-", "_") 486 487 if k in comma_separated_headers: 488 existing = environ.get(envname) 489 if existing: 490 v = ", ".join((existing, v)) 491 environ[envname] = v 492 493 ct = environ.pop("HTTP_CONTENT_TYPE", None) 494 if ct is not None: 495 environ["CONTENT_TYPE"] = ct 496 cl = environ.pop("HTTP_CONTENT_LENGTH", None) 497 if cl is not None: 498 environ["CONTENT_LENGTH"] = cl
499
500 - def decode_chunked(self):
501 """Decode the 'chunked' transfer coding.""" 502 cl = 0 503 data = StringIO.StringIO() 504 while True: 505 line = self.rfile.readline().strip().split(";", 1) 506 chunk_size = int(line.pop(0), 16) 507 if chunk_size <= 0: 508 break 509 ## if line: chunk_extension = line[0] 510 cl += chunk_size 511 data.write(self.rfile.read(chunk_size)) 512 crlf = self.rfile.read(2) 513 if crlf != "\r\n": 514 self.simple_response("400 Bad Request", 515 "Bad chunked transfer coding " 516 "(expected '\\r\\n', got %r)" % crlf) 517 return 518 519 # Grab any trailer headers 520 self.read_headers() 521 522 data.seek(0) 523 self.environ["wsgi.input"] = data 524 self.environ["CONTENT_LENGTH"] = str(cl) or "" 525 return True
526
527 - def respond(self):
528 """Call the appropriate WSGI app and write its iterable output.""" 529 # Set rfile.maxlen to ensure we don't read past Content-Length. 530 # This will also be used to read the entire request body if errors 531 # are raised before the app can read the body. 532 if self.chunked_read: 533 # If chunked, Content-Length will be 0. 534 self.rfile.maxlen = self.max_request_body_size 535 else: 536 cl = int(self.environ.get("CONTENT_LENGTH", 0)) 537 if self.max_request_body_size: 538 self.rfile.maxlen = min(cl, self.max_request_body_size) 539 else: 540 self.rfile.maxlen = cl 541 self.rfile.bytes_read = 0 542 543 try: 544 self._respond() 545 except MaxSizeExceeded: 546 if not self.sent_headers: 547 self.simple_response("413 Request Entity Too Large") 548 return
549
550 - def _respond(self):
551 if self.chunked_read: 552 if not self.decode_chunked(): 553 self.close_connection = True 554 return 555 556 response = self.wsgi_app(self.environ, self.start_response) 557 try: 558 for chunk in response: 559 # "The start_response callable must not actually transmit 560 # the response headers. Instead, it must store them for the 561 # server or gateway to transmit only after the first 562 # iteration of the application return value that yields 563 # a NON-EMPTY string, or upon the application's first 564 # invocation of the write() callable." (PEP 333) 565 if chunk: 566 self.write(chunk) 567 finally: 568 if hasattr(response, "close"): 569 response.close() 570 571 if (self.ready and not self.sent_headers): 572 self.sent_headers = True 573 self.send_headers() 574 if self.chunked_write: 575 self.wfile.sendall("0\r\n\r\n")
576
577 - def simple_response(self, status, msg=""):
578 """Write a simple response back to the client.""" 579 status = str(status) 580 buf = ["%s %s\r\n" % (self.environ['ACTUAL_SERVER_PROTOCOL'], status), 581 "Content-Length: %s\r\n" % len(msg), 582 "Content-Type: text/plain\r\n"] 583 584 if status[:3] == "413" and self.response_protocol == 'HTTP/1.1': 585 # Request Entity Too Large 586 self.close_connection = True 587 buf.append("Connection: close\r\n") 588 589 buf.append("\r\n") 590 if msg: 591 buf.append(msg) 592 593 try: 594 self.wfile.sendall("".join(buf)) 595 except socket.error, x: 596 if x.args[0] not in socket_errors_to_ignore: 597 raise
598
599 - def start_response(self, status, headers, exc_info = None):
600 """WSGI callable to begin the HTTP response.""" 601 # "The application may call start_response more than once, 602 # if and only if the exc_info argument is provided." 603 if self.started_response and not exc_info: 604 raise AssertionError("WSGI start_response called a second " 605 "time with no exc_info.") 606 607 # "if exc_info is provided, and the HTTP headers have already been 608 # sent, start_response must raise an error, and should raise the 609 # exc_info tuple." 610 if self.sent_headers: 611 try: 612 raise exc_info[0], exc_info[1], exc_info[2] 613 finally: 614 exc_info = None 615 616 self.started_response = True 617 self.status = status 618 self.outheaders.extend(headers) 619 return self.write
620
621 - def write(self, chunk):
622 """WSGI callable to write unbuffered data to the client. 623 624 This method is also used internally by start_response (to write 625 data from the iterable returned by the WSGI application). 626 """ 627 if not self.started_response: 628 raise AssertionError("WSGI write called before start_response.") 629 630 if not self.sent_headers: 631 self.sent_headers = True 632 self.send_headers() 633 634 if self.chunked_write and chunk: 635 buf = [hex(len(chunk))[2:], "\r\n", chunk, "\r\n"] 636 self.wfile.sendall("".join(buf)) 637 else: 638 self.wfile.sendall(chunk)
639
640 - def send_headers(self):
641 """Assert, process, and send the HTTP response message-headers.""" 642 hkeys = [key.lower() for key, value in self.outheaders] 643 status = int(self.status[:3]) 644 645 if status == 413: 646 # Request Entity Too Large. Close conn to avoid garbage. 647 self.close_connection = True 648 elif "content-length" not in hkeys: 649 # "All 1xx (informational), 204 (no content), 650 # and 304 (not modified) responses MUST NOT 651 # include a message-body." So no point chunking. 652 if status < 200 or status in (204, 205, 304): 653 pass 654 else: 655 if (self.response_protocol == 'HTTP/1.1' 656 and self.environ["REQUEST_METHOD"] != 'HEAD'): 657 # Use the chunked transfer-coding 658 self.chunked_write = True 659 self.outheaders.append(("Transfer-Encoding", "chunked")) 660 else: 661 # Closing the conn is the only way to determine len. 662 self.close_connection = True 663 664 if "connection" not in hkeys: 665 if self.response_protocol == 'HTTP/1.1': 666 # Both server and client are HTTP/1.1 or better 667 if self.close_connection: 668 self.outheaders.append(("Connection", "close")) 669 else: 670 # Server and/or client are HTTP/1.0 671 if not self.close_connection: 672 self.outheaders.append(("Connection", "Keep-Alive")) 673 674 if (not self.close_connection) and (not self.chunked_read): 675 # Read any remaining request body data on the socket. 676 # "If an origin server receives a request that does not include an 677 # Expect request-header field with the "100-continue" expectation, 678 # the request includes a request body, and the server responds 679 # with a final status code before reading the entire request body 680 # from the transport connection, then the server SHOULD NOT close 681 # the transport connection until it has read the entire request, 682 # or until the client closes the connection. Otherwise, the client 683 # might not reliably receive the response message. However, this 684 # requirement is not be construed as preventing a server from 685 # defending itself against denial-of-service attacks, or from 686 # badly broken client implementations." 687 size = self.rfile.maxlen - self.rfile.bytes_read 688 if size > 0: 689 self.rfile.read(size) 690 691 if "date" not in hkeys: 692 self.outheaders.append(("Date", rfc822.formatdate())) 693 694 if "server" not in hkeys: 695 self.outheaders.append(("Server", self.environ['SERVER_SOFTWARE'])) 696 697 buf = [self.environ['ACTUAL_SERVER_PROTOCOL'], " ", self.status, "\r\n"] 698 try: 699 buf += [k + ": " + v + "\r\n" for k, v in self.outheaders] 700 except TypeError: 701 if not isinstance(k, str): 702 raise TypeError("WSGI response header key %r is not a string.") 703 if not isinstance(v, str): 704 raise TypeError("WSGI response header value %r is not a string.") 705 else: 706 raise 707 buf.append("\r\n") 708 self.wfile.sendall("".join(buf))
709 710
711 -class NoSSLError(Exception):
712 """Exception raised when a client speaks HTTP to an HTTPS socket.""" 713 pass
714 715
716 -class FatalSSLAlert(Exception):
717 """Exception raised when the SSL implementation signals a fatal alert.""" 718 pass
719 720 721 if not _fileobject_uses_str_type:
722 - class CP_fileobject(socket._fileobject):
723 """Faux file object attached to a socket object.""" 724
725 - def sendall(self, data):
726 """Sendall for non-blocking sockets.""" 727 while data: 728 try: 729 bytes_sent = self.send(data) 730 data = data[bytes_sent:] 731 except socket.error, e: 732 if e.args[0] not in socket_errors_nonblocking: 733 raise
734
735 - def send(self, data):
736 return self._sock.send(data)
737
738 - def flush(self):
739 if self._wbuf: 740 buffer = "".join(self._wbuf) 741 self._wbuf = [] 742 self.sendall(buffer)
743
744 - def recv(self, size):
745 while True: 746 try: 747 return self._sock.recv(size) 748 except socket.error, e: 749 if (e.args[0] not in socket_errors_nonblocking 750 and e.args[0] not in socket_error_eintr): 751 raise
752
753 - def read(self, size=-1):
754 # Use max, disallow tiny reads in a loop as they are very inefficient. 755 # We never leave read() with any leftover data from a new recv() call 756 # in our internal buffer. 757 rbufsize = max(self._rbufsize, self.default_bufsize) 758 # Our use of StringIO rather than lists of string objects returned by 759 # recv() minimizes memory usage and fragmentation that occurs when 760 # rbufsize is large compared to the typical return value of recv(). 761 buf = self._rbuf 762 buf.seek(0, 2) # seek end 763 if size < 0: 764 # Read until EOF 765 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 766 while True: 767 data = self.recv(rbufsize) 768 if not data: 769 break 770 buf.write(data) 771 return buf.getvalue() 772 else: 773 # Read until size bytes or EOF seen, whichever comes first 774 buf_len = buf.tell() 775 if buf_len >= size: 776 # Already have size bytes in our buffer? Extract and return. 777 buf.seek(0) 778 rv = buf.read(size) 779 self._rbuf = StringIO.StringIO() 780 self._rbuf.write(buf.read()) 781 return rv 782 783 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 784 while True: 785 left = size - buf_len 786 # recv() will malloc the amount of memory given as its 787 # parameter even though it often returns much less data 788 # than that. The returned data string is short lived 789 # as we copy it into a StringIO and free it. This avoids 790 # fragmentation issues on many platforms. 791 data = self.recv(left) 792 if not data: 793 break 794 n = len(data) 795 if n == size and not buf_len: 796 # Shortcut. Avoid buffer data copies when: 797 # - We have no data in our buffer. 798 # AND 799 # - Our call to recv returned exactly the 800 # number of bytes we were asked to read. 801 return data 802 if n == left: 803 buf.write(data) 804 del data # explicit free 805 break 806 assert n <= left, "recv(%d) returned %d bytes" % (left, n) 807 buf.write(data) 808 buf_len += n 809 del data # explicit free 810 #assert buf_len == buf.tell() 811 return buf.getvalue()
812
813 - def readline(self, size=-1):
814 buf = self._rbuf 815 buf.seek(0, 2) # seek end 816 if buf.tell() > 0: 817 # check if we already have it in our buffer 818 buf.seek(0) 819 bline = buf.readline(size) 820 if bline.endswith('\n') or len(bline) == size: 821 self._rbuf = StringIO.StringIO() 822 self._rbuf.write(buf.read()) 823 return bline 824 del bline 825 if size < 0: 826 # Read until \n or EOF, whichever comes first 827 if self._rbufsize <= 1: 828 # Speed up unbuffered case 829 buf.seek(0) 830 buffers = [buf.read()] 831 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 832 data = None 833 recv = self.recv 834 while data != "\n": 835 data = recv(1) 836 if not data: 837 break 838 buffers.append(data) 839 return "".join(buffers) 840 841 buf.seek(0, 2) # seek end 842 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 843 while True: 844 data = self.recv(self._rbufsize) 845 if not data: 846 break 847 nl = data.find('\n') 848 if nl >= 0: 849 nl += 1 850 buf.write(data[:nl]) 851 self._rbuf.write(data[nl:]) 852 del data 853 break 854 buf.write(data) 855 return buf.getvalue() 856 else: 857 # Read until size bytes or \n or EOF seen, whichever comes first 858 buf.seek(0, 2) # seek end 859 buf_len = buf.tell() 860 if buf_len >= size: 861 buf.seek(0) 862 rv = buf.read(size) 863 self._rbuf = StringIO.StringIO() 864 self._rbuf.write(buf.read()) 865 return rv 866 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 867 while True: 868 data = self.recv(self._rbufsize) 869 if not data: 870 break 871 left = size - buf_len 872 # did we just receive a newline? 873 nl = data.find('\n', 0, left) 874 if nl >= 0: 875 nl += 1 876 # save the excess data to _rbuf 877 self._rbuf.write(data[nl:]) 878 if buf_len: 879 buf.write(data[:nl]) 880 break 881 else: 882 # Shortcut. Avoid data copy through buf when returning 883 # a substring of our first recv(). 884 return data[:nl] 885 n = len(data) 886 if n == size and not buf_len: 887 # Shortcut. Avoid data copy through buf when 888 # returning exactly all of our first recv(). 889 return data 890 if n >= left: 891 buf.write(data[:left]) 892 self._rbuf.write(data[left:]) 893 break 894 buf.write(data) 895 buf_len += n 896 #assert buf_len == buf.tell() 897 return buf.getvalue()
898 899 else:
900 - class CP_fileobject(socket._fileobject):
901 """Faux file object attached to a socket object.""" 902
903 - def sendall(self, data):
904 """Sendall for non-blocking sockets.""" 905 while data: 906 try: 907 bytes_sent = self.send(data) 908 data = data[bytes_sent:] 909 except socket.error, e: 910 if e.args[0] not in socket_errors_nonblocking: 911 raise
912
913 - def send(self, data):
914 return self._sock.send(data)
915
916 - def flush(self):
917 if self._wbuf: 918 buffer = "".join(self._wbuf) 919 self._wbuf = [] 920 self.sendall(buffer)
921
922 - def recv(self, size):
923 while True: 924 try: 925 return self._sock.recv(size) 926 except socket.error, e: 927 if (e.args[0] not in socket_errors_nonblocking 928 and e.args[0] not in socket_error_eintr): 929 raise
930
931 - def read(self, size=-1):
932 if size < 0: 933 # Read until EOF 934 buffers = [self._rbuf] 935 self._rbuf = "" 936 if self._rbufsize <= 1: 937 recv_size = self.default_bufsize 938 else: 939 recv_size = self._rbufsize 940 941 while True: 942 data = self.recv(recv_size) 943 if not data: 944 break 945 buffers.append(data) 946 return "".join(buffers) 947 else: 948 # Read until size bytes or EOF seen, whichever comes first 949 data = self._rbuf 950 buf_len = len(data) 951 if buf_len >= size: 952 self._rbuf = data[size:] 953 return data[:size] 954 buffers = [] 955 if data: 956 buffers.append(data) 957 self._rbuf = "" 958 while True: 959 left = size - buf_len 960 recv_size = max(self._rbufsize, left) 961 data = self.recv(recv_size) 962 if not data: 963 break 964 buffers.append(data) 965 n = len(data) 966 if n >= left: 967 self._rbuf = data[left:] 968 buffers[-1] = data[:left] 969 break 970 buf_len += n 971 return "".join(buffers)
972
973 - def readline(self, size=-1):
974 data = self._rbuf 975 if size < 0: 976 # Read until \n or EOF, whichever comes first 977 if self._rbufsize <= 1: 978 # Speed up unbuffered case 979 assert data == "" 980 buffers = [] 981 while data != "\n": 982 data = self.recv(1) 983 if not data: 984 break 985 buffers.append(data) 986 return "".join(buffers) 987 nl = data.find('\n') 988 if nl >= 0: 989 nl += 1 990 self._rbuf = data[nl:] 991 return data[:nl] 992 buffers = [] 993 if data: 994 buffers.append(data) 995 self._rbuf = "" 996 while True: 997 data = self.recv(self._rbufsize) 998 if not data: 999 break 1000 buffers.append(data) 1001 nl = data.find('\n') 1002 if nl >= 0: 1003 nl += 1 1004 self._rbuf = data[nl:] 1005 buffers[-1] = data[:nl] 1006 break 1007 return "".join(buffers) 1008 else: 1009 # Read until size bytes or \n or EOF seen, whichever comes first 1010 nl = data.find('\n', 0, size) 1011 if nl >= 0: 1012 nl += 1 1013 self._rbuf = data[nl:] 1014 return data[:nl] 1015 buf_len = len(data) 1016 if buf_len >= size: 1017 self._rbuf = data[size:] 1018 return data[:size] 1019 buffers = [] 1020 if data: 1021 buffers.append(data) 1022 self._rbuf = "" 1023 while True: 1024 data = self.recv(self._rbufsize) 1025 if not data: 1026 break 1027 buffers.append(data) 1028 left = size - buf_len 1029 nl = data.find('\n', 0, left) 1030 if nl >= 0: 1031 nl += 1 1032 self._rbuf = data[nl:] 1033 buffers[-1] = data[:nl] 1034 break 1035 n = len(data) 1036 if n >= left: 1037 self._rbuf = data[left:] 1038 buffers[-1] = data[:left] 1039 break 1040 buf_len += n 1041 return "".join(buffers)
1042 1043
1044 -class SSL_fileobject(CP_fileobject):
1045 """SSL file object attached to a socket object.""" 1046 1047 ssl_timeout = 3 1048 ssl_retry = .01 1049
1050 - def _safe_call(self, is_reader, call, *args, **kwargs):
1051 """Wrap the given call with SSL error-trapping. 1052 1053 is_reader: if False EOF errors will be raised. If True, EOF errors 1054 will return "" (to emulate normal sockets). 1055 """ 1056 start = time.time() 1057 while True: 1058 try: 1059 return call(*args, **kwargs) 1060 except SSL.WantReadError: 1061 # Sleep and try again. This is dangerous, because it means 1062 # the rest of the stack has no way of differentiating 1063 # between a "new handshake" error and "client dropped". 1064 # Note this isn't an endless loop: there's a timeout below. 1065 time.sleep(self.ssl_retry) 1066 except SSL.WantWriteError: 1067 time.sleep(self.ssl_retry) 1068 except SSL.SysCallError, e: 1069 if is_reader and e.args == (-1, 'Unexpected EOF'): 1070 return "" 1071 1072 errnum = e.args[0] 1073 if is_reader and errnum in socket_errors_to_ignore: 1074 return "" 1075 raise socket.error(errnum) 1076 except SSL.Error, e: 1077 if is_reader and e.args == (-1, 'Unexpected EOF'): 1078 return "" 1079 1080 thirdarg = None 1081 try: 1082 thirdarg = e.args[0][0][2] 1083 except IndexError: 1084 pass 1085 1086 if thirdarg == 'http request': 1087 # The client is talking HTTP to an HTTPS server. 1088 raise NoSSLError() 1089 raise FatalSSLAlert(*e.args) 1090 except: 1091 raise 1092 1093 if time.time() - start > self.ssl_timeout: 1094 raise socket.timeout("timed out")
1095
1096 - def recv(self, *args, **kwargs):
1097 buf = [] 1098 r = super(SSL_fileobject, self).recv 1099 while True: 1100 data = self._safe_call(True, r, *args, **kwargs) 1101 buf.append(data) 1102 p = self._sock.pending() 1103 if not p: 1104 return "".join(buf)
1105
1106 - def sendall(self, *args, **kwargs):
1107 return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs)
1108
1109 - def send(self, *args, **kwargs):
1110 return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs)
1111 1112
1113 -class HTTPConnection(object):
1114 """An HTTP connection (active socket). 1115 1116 socket: the raw socket object (usually TCP) for this connection. 1117 wsgi_app: the WSGI application for this server/connection. 1118 environ: a WSGI environ template. This will be copied for each request. 1119 1120 rfile: a fileobject for reading from the socket. 1121 send: a function for writing (+ flush) to the socket. 1122 """ 1123 1124 rbufsize = -1 1125 RequestHandlerClass = HTTPRequest 1126 environ = {"wsgi.version": (1, 0), 1127 "wsgi.url_scheme": "http", 1128 "wsgi.multithread": True, 1129 "wsgi.multiprocess": False, 1130 "wsgi.run_once": False, 1131 "wsgi.errors": sys.stderr, 1132 } 1133
1134 - def __init__(self, sock, wsgi_app, environ):
1135 self.socket = sock 1136 self.wsgi_app = wsgi_app 1137 1138 # Copy the class environ into self. 1139 self.environ = self.environ.copy() 1140 self.environ.update(environ) 1141 1142 if SSL and isinstance(sock, SSL.ConnectionType): 1143 timeout = sock.gettimeout() 1144 self.rfile = SSL_fileobject(sock, "rb", self.rbufsize) 1145 self.rfile.ssl_timeout = timeout 1146 self.wfile = SSL_fileobject(sock, "wb", -1) 1147 self.wfile.ssl_timeout = timeout 1148 else: 1149 self.rfile = CP_fileobject(sock, "rb", self.rbufsize) 1150 self.wfile = CP_fileobject(sock, "wb", -1) 1151 1152 # Wrap wsgi.input but not HTTPConnection.rfile itself. 1153 # We're also not setting maxlen yet; we'll do that separately 1154 # for headers and body for each iteration of self.communicate 1155 # (if maxlen is 0 the wrapper doesn't check length). 1156 self.environ["wsgi.input"] = SizeCheckWrapper(self.rfile, 0)
1157
1158 - def communicate(self):
1159 """Read each request and respond appropriately.""" 1160 try: 1161 while True: 1162 # (re)set req to None so that if something goes wrong in 1163 # the RequestHandlerClass constructor, the error doesn't 1164 # get written to the previous request. 1165 req = None 1166 req = self.RequestHandlerClass(self.wfile, self.environ, 1167 self.wsgi_app) 1168 1169 # This order of operations should guarantee correct pipelining. 1170 req.parse_request() 1171 if not req.ready: 1172 return 1173 1174 req.respond() 1175 if req.close_connection: 1176 return 1177 1178 except socket.error, e: 1179 errnum = e.args[0] 1180 if errnum == 'timed out': 1181 if req and not req.sent_headers: 1182 req.simple_response("408 Request Timeout") 1183 elif errnum not in socket_errors_to_ignore: 1184 if req and not req.sent_headers: 1185 req.simple_response("500 Internal Server Error", 1186 format_exc()) 1187 return 1188 except (KeyboardInterrupt, SystemExit): 1189 raise 1190 except FatalSSLAlert, e: 1191 # Close the connection. 1192 return 1193 except NoSSLError: 1194 if req and not req.sent_headers: 1195 # Unwrap our wfile 1196 req.wfile = CP_fileobject(self.socket._sock, "wb", -1) 1197 req.simple_response("400 Bad Request", 1198 "The client sent a plain HTTP request, but " 1199 "this server only speaks HTTPS on this port.") 1200 self.linger = True 1201 except Exception, e: 1202 if req and not req.sent_headers: 1203 req.simple_response("500 Internal Server Error", format_exc())
1204 1205 linger = False 1206
1207 - def close(self):
1208 """Close the socket underlying this connection.""" 1209 self.rfile.close() 1210 1211 if not self.linger: 1212 # Python's socket module does NOT call close on the kernel socket 1213 # when you call socket.close(). We do so manually here because we 1214 # want this server to send a FIN TCP segment immediately. Note this 1215 # must be called *before* calling socket.close(), because the latter 1216 # drops its reference to the kernel socket. 1217 self.socket._sock.close() 1218 self.socket.close() 1219 else: 1220 # On the other hand, sometimes we want to hang around for a bit 1221 # to make sure the client has a chance to read our entire 1222 # response. Skipping the close() calls here delays the FIN 1223 # packet until the socket object is garbage-collected later. 1224 # Someday, perhaps, we'll do the full lingering_close that 1225 # Apache does, but not today. 1226 pass
1227 1228
1229 -def format_exc(limit=None):
1230 """Like print_exc() but return a string. Backport for Python 2.3.""" 1231 try: 1232 etype, value, tb = sys.exc_info() 1233 return ''.join(traceback.format_exception(etype, value, tb, limit)) 1234 finally: 1235 etype = value = tb = None
1236 1237 1238 _SHUTDOWNREQUEST = None 1239
1240 -class WorkerThread(threading.Thread):
1241 """Thread which continuously polls a Queue for Connection objects. 1242 1243 server: the HTTP Server which spawned this thread, and which owns the 1244 Queue and is placing active connections into it. 1245 ready: a simple flag for the calling server to know when this thread 1246 has begun polling the Queue. 1247 1248 Due to the timing issues of polling a Queue, a WorkerThread does not 1249 check its own 'ready' flag after it has started. To stop the thread, 1250 it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue 1251 (one for each running WorkerThread). 1252 """ 1253 1254 conn = None 1255
1256 - def __init__(self, server):
1257 self.ready = False 1258 self.server = server 1259 threading.Thread.__init__(self)
1260
1261 - def run(self):
1262 try: 1263 self.ready = True 1264 while True: 1265 conn = self.server.requests.get() 1266 if conn is _SHUTDOWNREQUEST: 1267 return 1268 1269 self.conn = conn 1270 try: 1271 conn.communicate() 1272 finally: 1273 conn.close() 1274 self.conn = None 1275 except (KeyboardInterrupt, SystemExit), exc: 1276 self.server.interrupt = exc
1277 1278
1279 -class ThreadPool(object):
1280 """A Request Queue for the CherryPyWSGIServer which pools threads. 1281 1282 ThreadPool objects must provide min, get(), put(obj), start() 1283 and stop(timeout) attributes. 1284 """ 1285
1286 - def __init__(self, server, min=10, max=-1):
1287 self.server = server 1288 self.min = min 1289 self.max = max 1290 self._threads = [] 1291 self._queue = Queue.Queue() 1292 self.get = self._queue.get
1293
1294 - def start(self):
1295 """Start the pool of threads.""" 1296 for i in xrange(self.min): 1297 self._threads.append(WorkerThread(self.server)) 1298 for worker in self._threads: 1299 worker.setName("CP WSGIServer " + worker.getName()) 1300 worker.start() 1301 for worker in self._threads: 1302 while not worker.ready: 1303 time.sleep(.1)
1304
1305 - def _get_idle(self):
1306 """Number of worker threads which are idle. Read-only.""" 1307 return len([t for t in self._threads if t.conn is None])
1308 idle = property(_get_idle, doc=_get_idle.__doc__) 1309
1310 - def put(self, obj):
1311 self._queue.put(obj) 1312 if obj is _SHUTDOWNREQUEST: 1313 return
1314
1315 - def grow(self, amount):
1316 """Spawn new worker threads (not above self.max).""" 1317 for i in xrange(amount): 1318 if self.max > 0 and len(self._threads) >= self.max: 1319 break 1320 worker = WorkerThread(self.server) 1321 worker.setName("CP WSGIServer " + worker.getName()) 1322 self._threads.append(worker) 1323 worker.start()
1324
1325 - def shrink(self, amount):
1326 """Kill off worker threads (not below self.min).""" 1327 # Grow/shrink the pool if necessary. 1328 # Remove any dead threads from our list 1329 for t in self._threads: 1330 if not t.isAlive(): 1331 self._threads.remove(t) 1332 amount -= 1 1333 1334 if amount > 0: 1335 for i in xrange(min(amount, len(self._threads) - self.min)): 1336 # Put a number of shutdown requests on the queue equal 1337 # to 'amount'. Once each of those is processed by a worker, 1338 # that worker will terminate and be culled from our list 1339 # in self.put. 1340 self._queue.put(_SHUTDOWNREQUEST)
1341
1342 - def stop(self, timeout=5):
1343 # Must shut down threads here so the code that calls 1344 # this method can know when all threads are stopped. 1345 for worker in self._threads: 1346 self._queue.put(_SHUTDOWNREQUEST) 1347 1348 # Don't join currentThread (when stop is called inside a request). 1349 current = threading.currentThread() 1350 while self._threads: 1351 worker = self._threads.pop() 1352 if worker is not current and worker.isAlive(): 1353 try: 1354 if timeout is None or timeout < 0: 1355 worker.join() 1356 else: 1357 worker.join(timeout) 1358 if worker.isAlive(): 1359 # We exhausted the timeout. 1360 # Forcibly shut down the socket. 1361 c = worker.conn 1362 if c and not c.rfile.closed: 1363 if SSL and isinstance(c.socket, SSL.ConnectionType): 1364 # pyOpenSSL.socket.shutdown takes no args 1365 c.socket.shutdown() 1366 else: 1367 c.socket.shutdown(socket.SHUT_RD) 1368 worker.join() 1369 except (AssertionError, 1370 # Ignore repeated Ctrl-C. 1371 # See http://www.cherrypy.org/ticket/691. 1372 KeyboardInterrupt), exc1: 1373 pass
1374 1375 1376
1377 -class SSLConnection:
1378 """A thread-safe wrapper for an SSL.Connection. 1379 1380 *args: the arguments to create the wrapped SSL.Connection(*args). 1381 """ 1382
1383 - def __init__(self, *args):
1384 self._ssl_conn = SSL.Connection(*args) 1385 self._lock = threading.RLock()
1386 1387 for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read', 1388 'renegotiate', 'bind', 'listen', 'connect', 'accept', 1389 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list', 1390 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 1391 'makefile', 'get_app_data', 'set_app_data', 'state_string', 1392 'sock_shutdown', 'get_peer_certificate', 'want_read', 1393 'want_write', 'set_connect_state', 'set_accept_state', 1394 'connect_ex', 'sendall', 'settimeout'): 1395 exec """def %s(self, *args): 1396 self._lock.acquire() 1397 try: 1398 return self._ssl_conn.%s(*args) 1399 finally: 1400 self._lock.release() 1401 """ % (f, f)
1402 1403 1404 try: 1405 import fcntl 1406 except ImportError: 1407 try: 1408 from ctypes import windll, WinError 1409 except ImportError:
1410 - def prevent_socket_inheritance(sock):
1411 """Dummy function, since neither fcntl nor ctypes are available.""" 1412 pass
1413 else:
1414 - def prevent_socket_inheritance(sock):
1415 """Mark the given socket fd as non-inheritable (Windows).""" 1416 if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0): 1417 raise WinError()
1418 else:
1419 - def prevent_socket_inheritance(sock):
1420 """Mark the given socket fd as non-inheritable (POSIX).""" 1421 fd = sock.fileno() 1422 old_flags = fcntl.fcntl(fd, fcntl.F_GETFD) 1423 fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
1424 1425
1426 -class CherryPyWSGIServer(object):
1427 """An HTTP server for WSGI. 1428 1429 bind_addr: The interface on which to listen for connections. 1430 For TCP sockets, a (host, port) tuple. Host values may be any IPv4 1431 or IPv6 address, or any valid hostname. The string 'localhost' is a 1432 synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6). 1433 The string '0.0.0.0' is a special IPv4 entry meaning "any active 1434 interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for 1435 IPv6. The empty string or None are not allowed. 1436 1437 For UNIX sockets, supply the filename as a string. 1438 wsgi_app: the WSGI 'application callable'; multiple WSGI applications 1439 may be passed as (path_prefix, app) pairs. 1440 numthreads: the number of worker threads to create (default 10). 1441 server_name: the string to set for WSGI's SERVER_NAME environ entry. 1442 Defaults to socket.gethostname(). 1443 max: the maximum number of queued requests (defaults to -1 = no limit). 1444 request_queue_size: the 'backlog' argument to socket.listen(); 1445 specifies the maximum number of queued connections (default 5). 1446 timeout: the timeout in seconds for accepted connections (default 10). 1447 1448 nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket 1449 option. 1450 1451 protocol: the version string to write in the Status-Line of all 1452 HTTP responses. For example, "HTTP/1.1" (the default). This 1453 also limits the supported features used in the response. 1454 1455 1456 SSL/HTTPS 1457 --------- 1458 The OpenSSL module must be importable for SSL functionality. 1459 You can obtain it from http://pyopenssl.sourceforge.net/ 1460 1461 ssl_certificate: the filename of the server SSL certificate. 1462 ssl_privatekey: the filename of the server's private key file. 1463 1464 If either of these is None (both are None by default), this server 1465 will not use SSL. If both are given and are valid, they will be read 1466 on server start and used in the SSL context for the listening socket. 1467 """ 1468 1469 protocol = "HTTP/1.1" 1470 _bind_addr = "127.0.0.1" 1471 version = "CherryPy/3.1.2" 1472 ready = False 1473 _interrupt = None 1474 1475 nodelay = True 1476 1477 ConnectionClass = HTTPConnection 1478 environ = {} 1479 1480 # Paths to certificate and private key files 1481 ssl_certificate = None 1482 ssl_private_key = None 1483
1484 - def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, 1485 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
1486 self.requests = ThreadPool(self, min=numthreads or 1, max=max) 1487 1488 if callable(wsgi_app): 1489 # We've been handed a single wsgi_app, in CP-2.1 style. 1490 # Assume it's mounted at "". 1491 self.wsgi_app = wsgi_app 1492 else: 1493 # We've been handed a list of (path_prefix, wsgi_app) tuples, 1494 # so that the server can call different wsgi_apps, and also 1495 # correctly set SCRIPT_NAME. 1496 warnings.warn("The ability to pass multiple apps is deprecated " 1497 "and will be removed in 3.2. You should explicitly " 1498 "include a WSGIPathInfoDispatcher instead.", 1499 DeprecationWarning) 1500 self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app) 1501 1502 self.bind_addr = bind_addr 1503 if not server_name: 1504 server_name = socket.gethostname() 1505 self.server_name = server_name 1506 self.request_queue_size = request_queue_size 1507 1508 self.timeout = timeout 1509 self.shutdown_timeout = shutdown_timeout
1510
1511 - def _get_numthreads(self):
1512 return self.requests.min
1513 - def _set_numthreads(self, value):
1514 self.requests.min = value
1515 numthreads = property(_get_numthreads, _set_numthreads) 1516
1517 - def __str__(self):
1518 return "%s.%s(%r)" % (self.__module__, self.__class__.__name__, 1519 self.bind_addr)
1520
1521 - def _get_bind_addr(self):
1522 return self._bind_addr
1523 - def _set_bind_addr(self, value):
1524 if isinstance(value, tuple) and value[0] in ('', None): 1525 # Despite the socket module docs, using '' does not 1526 # allow AI_PASSIVE to work. Passing None instead 1527 # returns '0.0.0.0' like we want. In other words: 1528 # host AI_PASSIVE result 1529 # '' Y 192.168.x.y 1530 # '' N 192.168.x.y 1531 # None Y 0.0.0.0 1532 # None N 127.0.0.1 1533 # But since you can get the same effect with an explicit 1534 # '0.0.0.0', we deny both the empty string and None as values. 1535 raise ValueError("Host values of '' or None are not allowed. " 1536 "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead " 1537 "to listen on all active interfaces.") 1538 self._bind_addr = value
1539 bind_addr = property(_get_bind_addr, _set_bind_addr, 1540 doc="""The interface on which to listen for connections. 1541 1542 For TCP sockets, a (host, port) tuple. Host values may be any IPv4 1543 or IPv6 address, or any valid hostname. The string 'localhost' is a 1544 synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6). 1545 The string '0.0.0.0' is a special IPv4 entry meaning "any active 1546 interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for 1547 IPv6. The empty string or None are not allowed. 1548 1549 For UNIX sockets, supply the filename as a string.""") 1550
1551 - def start(self):
1552 """Run the server forever.""" 1553 # We don't have to trap KeyboardInterrupt or SystemExit here, 1554 # because cherrpy.server already does so, calling self.stop() for us. 1555 # If you're using this server with another framework, you should 1556 # trap those exceptions in whatever code block calls start(). 1557 self._interrupt = None 1558 1559 # Select the appropriate socket 1560 if isinstance(self.bind_addr, basestring): 1561 # AF_UNIX socket 1562 1563 # So we can reuse the socket... 1564 try: os.unlink(self.bind_addr) 1565 except: pass 1566 1567 # So everyone can access the socket... 1568 try: os.chmod(self.bind_addr, 0777) 1569 except: pass 1570 1571 info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)] 1572 else: 1573 # AF_INET or AF_INET6 socket 1574 # Get the correct address family for our host (allows IPv6 addresses) 1575 host, port = self.bind_addr 1576 try: 1577 info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, 1578 socket.SOCK_STREAM, 0, socket.AI_PASSIVE) 1579 except socket.gaierror: 1580 # Probably a DNS issue. Assume IPv4. 1581 info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", self.bind_addr)] 1582 1583 self.socket = None 1584 msg = "No socket could be created" 1585 for res in info: 1586 af, socktype, proto, canonname, sa = res 1587 try: 1588 self.bind(af, socktype, proto) 1589 except socket.error, msg: 1590 if self.socket: 1591 self.socket.close() 1592 self.socket = None 1593 continue 1594 break 1595 if not self.socket: 1596 raise socket.error, msg 1597 1598 # Timeout so KeyboardInterrupt can be caught on Win32 1599 self.socket.settimeout(1) 1600 self.socket.listen(self.request_queue_size) 1601 1602 # Create worker threads 1603 self.requests.start() 1604 1605 self.ready = True 1606 while self.ready: 1607 self.tick() 1608 if self.interrupt: 1609 while self.interrupt is True: 1610 # Wait for self.stop() to complete. See _set_interrupt. 1611 time.sleep(0.1) 1612 if self.interrupt: 1613 raise self.interrupt
1614
1615 - def bind(self, family, type, proto=0):
1616 """Create (or recreate) the actual socket object.""" 1617 self.socket = socket.socket(family, type, proto) 1618 prevent_socket_inheritance(self.socket) 1619 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 1620 if self.nodelay: 1621 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 1622 if self.ssl_certificate and self.ssl_private_key: 1623 if SSL is None: 1624 raise ImportError("You must install pyOpenSSL to use HTTPS.") 1625 1626 # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473 1627 ctx = SSL.Context(SSL.SSLv23_METHOD) 1628 ctx.use_privatekey_file(self.ssl_private_key) 1629 ctx.use_certificate_file(self.ssl_certificate) 1630 self.socket = SSLConnection(ctx, self.socket) 1631 self.populate_ssl_environ() 1632 1633 # If listening on the IPV6 any address ('::' = IN6ADDR_ANY), 1634 # activate dual-stack. See http://www.cherrypy.org/ticket/871. 1635 if (not isinstance(self.bind_addr, basestring) 1636 and self.bind_addr[0] == '::' and family == socket.AF_INET6): 1637 try: 1638 self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) 1639 except (AttributeError, socket.error): 1640 # Apparently, the socket option is not available in 1641 # this machine's TCP stack 1642 pass 1643 1644 self.socket.bind(self.bind_addr)
1645
1646 - def tick(self):
1647 """Accept a new connection and put it on the Queue.""" 1648 try: 1649 s, addr = self.socket.accept() 1650 prevent_socket_inheritance(s) 1651 if not self.ready: 1652 return 1653 if hasattr(s, 'settimeout'): 1654 s.settimeout(self.timeout) 1655 1656 environ = self.environ.copy() 1657 # SERVER_SOFTWARE is common for IIS. It's also helpful for 1658 # us to pass a default value for the "Server" response header. 1659 if environ.get("SERVER_SOFTWARE") is None: 1660 environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version 1661 # set a non-standard environ entry so the WSGI app can know what 1662 # the *real* server protocol is (and what features to support). 1663 # See http://www.faqs.org/rfcs/rfc2145.html. 1664 environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol 1665 environ["SERVER_NAME"] = self.server_name 1666 1667 if isinstance(self.bind_addr, basestring): 1668 # AF_UNIX. This isn't really allowed by WSGI, which doesn't 1669 # address unix domain sockets. But it's better than nothing. 1670 environ["SERVER_PORT"] = "" 1671 else: 1672 environ["SERVER_PORT"] = str(self.bind_addr[1]) 1673 # optional values 1674 # Until we do DNS lookups, omit REMOTE_HOST 1675 environ["REMOTE_ADDR"] = addr[0] 1676 environ["REMOTE_PORT"] = str(addr[1]) 1677 1678 conn = self.ConnectionClass(s, self.wsgi_app, environ) 1679 self.requests.put(conn) 1680 except socket.timeout: 1681 # The only reason for the timeout in start() is so we can 1682 # notice keyboard interrupts on Win32, which don't interrupt 1683 # accept() by default 1684 return 1685 except socket.error, x: 1686 if x.args[0] in socket_error_eintr: 1687 # I *think* this is right. EINTR should occur when a signal 1688 # is received during the accept() call; all docs say retry 1689 # the call, and I *think* I'm reading it right that Python 1690 # will then go ahead and poll for and handle the signal 1691 # elsewhere. See http://www.cherrypy.org/ticket/707. 1692 return 1693 if x.args[0] in socket_errors_nonblocking: 1694 # Just try again. See http://www.cherrypy.org/ticket/479. 1695 return 1696 if x.args[0] in socket_errors_to_ignore: 1697 # Our socket was closed. 1698 # See http://www.cherrypy.org/ticket/686. 1699 return 1700 raise
1701
1702 - def _get_interrupt(self):
1703 return self._interrupt
1704 - def _set_interrupt(self, interrupt):
1705 self._interrupt = True 1706 self.stop() 1707 self._interrupt = interrupt
1708 interrupt = property(_get_interrupt, _set_interrupt, 1709 doc="Set this to an Exception instance to " 1710 "interrupt the server.") 1711
1712 - def stop(self):
1713 """Gracefully shutdown a server that is serving forever.""" 1714 self.ready = False 1715 1716 sock = getattr(self, "socket", None) 1717 if sock: 1718 if not isinstance(self.bind_addr, basestring): 1719 # Touch our own socket to make accept() return immediately. 1720 try: 1721 host, port = sock.getsockname()[:2] 1722 except socket.error, x: 1723 if x.args[0] not in socket_errors_to_ignore: 1724 raise 1725 else: 1726 # Note that we're explicitly NOT using AI_PASSIVE, 1727 # here, because we want an actual IP to touch. 1728 # localhost won't work if we've bound to a public IP, 1729 # but it will if we bound to '0.0.0.0' (INADDR_ANY). 1730 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, 1731 socket.SOCK_STREAM): 1732 af, socktype, proto, canonname, sa = res 1733 s = None 1734 try: 1735 s = socket.socket(af, socktype, proto) 1736 # See http://groups.google.com/group/cherrypy-users/ 1737 # browse_frm/thread/bbfe5eb39c904fe0 1738 s.settimeout(1.0) 1739 s.connect((host, port)) 1740 s.close() 1741 except socket.error: 1742 if s: 1743 s.close() 1744 if hasattr(sock, "close"): 1745 sock.close() 1746 self.socket = None 1747 1748 self.requests.stop(self.shutdown_timeout)
1749
1750 - def populate_ssl_environ(self):
1751 """Create WSGI environ entries to be merged into each request.""" 1752 cert = open(self.ssl_certificate, 'rb').read() 1753 cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) 1754 ssl_environ = { 1755 "wsgi.url_scheme": "https", 1756 "HTTPS": "on", 1757 # pyOpenSSL doesn't provide access to any of these AFAICT 1758 ## 'SSL_PROTOCOL': 'SSLv2', 1759 ## SSL_CIPHER string The cipher specification name 1760 ## SSL_VERSION_INTERFACE string The mod_ssl program version 1761 ## SSL_VERSION_LIBRARY string The OpenSSL program version 1762 } 1763 1764 # Server certificate attributes 1765 ssl_environ.update({ 1766 'SSL_SERVER_M_VERSION': cert.get_version(), 1767 'SSL_SERVER_M_SERIAL': cert.get_serial_number(), 1768 ## 'SSL_SERVER_V_START': Validity of server's certificate (start time), 1769 ## 'SSL_SERVER_V_END': Validity of server's certificate (end time), 1770 }) 1771 1772 for prefix, dn in [("I", cert.get_issuer()), 1773 ("S", cert.get_subject())]: 1774 # X509Name objects don't seem to have a way to get the 1775 # complete DN string. Use str() and slice it instead, 1776 # because str(dn) == "<X509Name object '/C=US/ST=...'>" 1777 dnstr = str(dn)[18:-2] 1778 1779 wsgikey = 'SSL_SERVER_%s_DN' % prefix 1780 ssl_environ[wsgikey] = dnstr 1781 1782 # The DN should be of the form: /k1=v1/k2=v2, but we must allow 1783 # for any value to contain slashes itself (in a URL). 1784 while dnstr: 1785 pos = dnstr.rfind("=") 1786 dnstr, value = dnstr[:pos], dnstr[pos + 1:] 1787 pos = dnstr.rfind("/") 1788 dnstr, key = dnstr[:pos], dnstr[pos + 1:] 1789 if key and value: 1790 wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) 1791 ssl_environ[wsgikey] = value 1792 1793 self.environ.update(ssl_environ)
1794