Package wsgiwapi :: Package cpwsgiserver
[frames] | no frames]

Source Code for Package wsgiwapi.cpwsgiserver

   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 368 # Lemur: experimental hack: leave path in raw form to be processed 369 # by WSGIWAPI 370 # atoms = [unquote(x) for x in quoted_slash.split(path)] 371 # path = "%2F".join(atoms) 372 environ["PATH_INFO"] = path 373 374 # Note that, like wsgiref and most other WSGI servers, 375 # we unquote the path but not the query string. 376 environ["QUERY_STRING"] = qs 377 378 # Compare request and server HTTP protocol versions, in case our 379 # server does not support the requested protocol. Limit our output 380 # to min(req, server). We want the following output: 381 # request server actual written supported response 382 # protocol protocol response protocol feature set 383 # a 1.0 1.0 1.0 1.0 384 # b 1.0 1.1 1.1 1.0 385 # c 1.1 1.0 1.0 1.0 386 # d 1.1 1.1 1.1 1.1 387 # Notice that, in (b), the response will be "HTTP/1.1" even though 388 # the client only understands 1.0. RFC 2616 10.5.6 says we should 389 # only return 505 if the _major_ version is different. 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 # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol. 397 environ["SERVER_PROTOCOL"] = req_protocol 398 self.response_protocol = "HTTP/%s.%s" % min(rp, sp) 399 400 # If the Request-URI was an absoluteURI, use its location atom. 401 if location: 402 environ["SERVER_NAME"] = location 403 404 # then all the http headers 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 # Persistent connection support 417 if self.response_protocol == "HTTP/1.1": 418 # Both server and client are HTTP/1.1 419 if environ.get("HTTP_CONNECTION", "") == "close": 420 self.close_connection = True 421 else: 422 # Either the server or client (or both) are HTTP/1.0 423 if environ.get("HTTP_CONNECTION", "") != "Keep-Alive": 424 self.close_connection = True 425 426 # Transfer-Encoding support 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 # Note that, even if we see "chunked", we must reject 441 # if there is an extension we don't recognize. 442 self.simple_response("501 Unimplemented") 443 self.close_connection = True 444 return 445 446 # From PEP 333: 447 # "Servers and gateways that implement HTTP 1.1 must provide 448 # transparent support for HTTP 1.1's "expect/continue" mechanism. 449 # This may be done in any of several ways: 450 # 1. Respond to requests containing an Expect: 100-continue request 451 # with an immediate "100 Continue" response, and proceed normally. 452 # 2. Proceed with the request normally, but provide the application 453 # with a wsgi.input stream that will send the "100 Continue" 454 # response if/when the application first attempts to read from 455 # the input stream. The read request must then remain blocked 456 # until the client responds. 457 # 3. Wait until the client decides that the server does not support 458 # expect/continue, and sends the request body on its own. 459 # (This is suboptimal, and is not recommended.) 460 # 461 # We used to do 3, but are now doing 1. Maybe we'll do 2 someday, 462 # but it seems like it would be a big slowdown for such a rare case. 463 if environ.get("HTTP_EXPECT", "") == "100-continue": 464 self.simple_response(100) 465 466 self.ready = True
467
468 - def read_headers(self):
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 # No more data--illegal end of headers 476 raise ValueError("Illegal end of headers.") 477 478 if line == '\r\n': 479 # Normal end of headers 480 break 481 482 if line[0] in ' \t': 483 # It's a continuation line. 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
503 - def decode_chunked(self):
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 ## if line: chunk_extension = line[0] 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 # Grab any trailer headers 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
530 - def respond(self):
531 """Call the appropriate WSGI app and write its iterable output.""" 532 # Set rfile.maxlen to ensure we don't read past Content-Length. 533 # This will also be used to read the entire request body if errors 534 # are raised before the app can read the body. 535 if self.chunked_read: 536 # If chunked, Content-Length will be 0. 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
553 - def _respond(self):
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 # "The start_response callable must not actually transmit 563 # the response headers. Instead, it must store them for the 564 # server or gateway to transmit only after the first 565 # iteration of the application return value that yields 566 # a NON-EMPTY string, or upon the application's first 567 # invocation of the write() callable." (PEP 333) 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
580 - def simple_response(self, status, msg=""):
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 # Request Entity Too Large 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
602 - def start_response(self, status, headers, exc_info = None):
603 """WSGI callable to begin the HTTP response.""" 604 # "The application may call start_response more than once, 605 # if and only if the exc_info argument is provided." 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 # "if exc_info is provided, and the HTTP headers have already been 611 # sent, start_response must raise an error, and should raise the 612 # exc_info tuple." 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
624 - def write(self, chunk):
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
643 - def send_headers(self):
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 # Request Entity Too Large. Close conn to avoid garbage. 650 self.close_connection = True 651 elif "content-length" not in hkeys: 652 # "All 1xx (informational), 204 (no content), 653 # and 304 (not modified) responses MUST NOT 654 # include a message-body." So no point chunking. 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 # Use the chunked transfer-coding 661 self.chunked_write = True 662 self.outheaders.append(("Transfer-Encoding", "chunked")) 663 else: 664 # Closing the conn is the only way to determine len. 665 self.close_connection = True 666 667 if "connection" not in hkeys: 668 if self.response_protocol == 'HTTP/1.1': 669 # Both server and client are HTTP/1.1 or better 670 if self.close_connection: 671 self.outheaders.append(("Connection", "close")) 672 else: 673 # Server and/or client are HTTP/1.0 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 # Read any remaining request body data on the socket. 679 # "If an origin server receives a request that does not include an 680 # Expect request-header field with the "100-continue" expectation, 681 # the request includes a request body, and the server responds 682 # with a final status code before reading the entire request body 683 # from the transport connection, then the server SHOULD NOT close 684 # the transport connection until it has read the entire request, 685 # or until the client closes the connection. Otherwise, the client 686 # might not reliably receive the response message. However, this 687 # requirement is not be construed as preventing a server from 688 # defending itself against denial-of-service attacks, or from 689 # badly broken client implementations." 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
714 -class NoSSLError(Exception):
715 """Exception raised when a client speaks HTTP to an HTTPS socket.""" 716 pass
717 718
719 -class FatalSSLAlert(Exception):
720 """Exception raised when the SSL implementation signals a fatal alert.""" 721 pass
722 723 724 if not _fileobject_uses_str_type:
725 - class CP_fileobject(socket._fileobject):
726 """Faux file object attached to a socket object.""" 727
728 - def sendall(self, data):
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
741 - def flush(self):
742 if self._wbuf: 743 buffer = "".join(self._wbuf) 744 self._wbuf = [] 745 self.sendall(buffer)
746
747 - def recv(self, size):
748 while True: 749 try: 750 return self._sock.recv(size) 751 except socket.error, e: 752 if (e.args[0] not in socket_errors_nonblocking 753 and e.args[0] not in socket_error_eintr): 754 raise
755
756 - def read(self, size=-1):
757 # Use max, disallow tiny reads in a loop as they are very inefficient. 758 # We never leave read() with any leftover data from a new recv() call 759 # in our internal buffer. 760 rbufsize = max(self._rbufsize, self.default_bufsize) 761 # Our use of StringIO rather than lists of string objects returned by 762 # recv() minimizes memory usage and fragmentation that occurs when 763 # rbufsize is large compared to the typical return value of recv(). 764 buf = self._rbuf 765 buf.seek(0, 2) # seek end 766 if size < 0: 767 # Read until EOF 768 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 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 # Read until size bytes or EOF seen, whichever comes first 777 buf_len = buf.tell() 778 if buf_len >= size: 779 # Already have size bytes in our buffer? Extract and return. 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() # reset _rbuf. we consume it via buf. 787 while True: 788 left = size - buf_len 789 # recv() will malloc the amount of memory given as its 790 # parameter even though it often returns much less data 791 # than that. The returned data string is short lived 792 # as we copy it into a StringIO and free it. This avoids 793 # fragmentation issues on many platforms. 794 data = self.recv(left) 795 if not data: 796 break 797 n = len(data) 798 if n == size and not buf_len: 799 # Shortcut. Avoid buffer data copies when: 800 # - We have no data in our buffer. 801 # AND 802 # - Our call to recv returned exactly the 803 # number of bytes we were asked to read. 804 return data 805 if n == left: 806 buf.write(data) 807 del data # explicit free 808 break 809 assert n <= left, "recv(%d) returned %d bytes" % (left, n) 810 buf.write(data) 811 buf_len += n 812 del data # explicit free 813 #assert buf_len == buf.tell() 814 return buf.getvalue()
815
816 - def readline(self, size=-1):
817 buf = self._rbuf 818 buf.seek(0, 2) # seek end 819 if buf.tell() > 0: 820 # check if we already have it in our buffer 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 # Read until \n or EOF, whichever comes first 830 if self._rbufsize <= 1: 831 # Speed up unbuffered case 832 buf.seek(0) 833 buffers = [buf.read()] 834 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 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) # seek end 845 self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf. 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 # Read until size bytes or \n or EOF seen, whichever comes first 861 buf.seek(0, 2) # seek end 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() # reset _rbuf. we consume it via buf. 870 while True: 871 data = self.recv(self._rbufsize) 872 if not data: 873 break 874 left = size - buf_len 875 # did we just receive a newline? 876 nl = data.find('\n', 0, left) 877 if nl >= 0: 878 nl += 1 879 # save the excess data to _rbuf 880 self._rbuf.write(data[nl:]) 881 if buf_len: 882 buf.write(data[:nl]) 883 break 884 else: 885 # Shortcut. Avoid data copy through buf when returning 886 # a substring of our first recv(). 887 return data[:nl] 888 n = len(data) 889 if n == size and not buf_len: 890 # Shortcut. Avoid data copy through buf when 891 # returning exactly all of our first recv(). 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 #assert buf_len == buf.tell() 900 return buf.getvalue()
901 902 else:
903 - class CP_fileobject(socket._fileobject):
904 """Faux file object attached to a socket object.""" 905
906 - def sendall(self, data):
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
919 - def flush(self):
920 if self._wbuf: 921 buffer = "".join(self._wbuf) 922 self._wbuf = [] 923 self.sendall(buffer)
924
925 - def recv(self, size):
926 while True: 927 try: 928 return self._sock.recv(size) 929 except socket.error, e: 930 if (e.args[0] not in socket_errors_nonblocking 931 and e.args[0] not in socket_error_eintr): 932 raise
933
934 - def read(self, size=-1):
935 if size < 0: 936 # Read until EOF 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 # Read until size bytes or EOF seen, whichever comes first 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
976 - def readline(self, size=-1):
977 data = self._rbuf 978 if size < 0: 979 # Read until \n or EOF, whichever comes first 980 if self._rbufsize <= 1: 981 # Speed up unbuffered case 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 # Read until size bytes or \n or EOF seen, whichever comes first 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
1047 -class SSL_fileobject(CP_fileobject):
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 # Sleep and try again. This is dangerous, because it means 1065 # the rest of the stack has no way of differentiating 1066 # between a "new handshake" error and "client dropped". 1067 # Note this isn't an endless loop: there's a timeout below. 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 # The client is talking HTTP to an HTTPS server. 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):
1110 return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs)
1111
1112 - def send(self, *args, **kwargs):
1113 return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs)
1114 1115
1116 -class HTTPConnection(object):
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 # Copy the class environ into self. 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 # Wrap wsgi.input but not HTTPConnection.rfile itself. 1156 # We're also not setting maxlen yet; we'll do that separately 1157 # for headers and body for each iteration of self.communicate 1158 # (if maxlen is 0 the wrapper doesn't check length). 1159 self.environ["wsgi.input"] = SizeCheckWrapper(self.rfile, 0)
1160
1161 - def communicate(self):
1162 """Read each request and respond appropriately.""" 1163 try: 1164 while True: 1165 # (re)set req to None so that if something goes wrong in 1166 # the RequestHandlerClass constructor, the error doesn't 1167 # get written to the previous request. 1168 req = None 1169 req = self.RequestHandlerClass(self.wfile, self.environ, 1170 self.wsgi_app) 1171 1172 # This order of operations should guarantee correct pipelining. 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 # Close the connection. 1195 return 1196 except NoSSLError: 1197 # Unwrap our wfile 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
1207 - def close(self):
1208 """Close the socket underlying this connection.""" 1209 self.rfile.close() 1210 1211 # Python's socket module does NOT call close on the kernel socket 1212 # when you call socket.close(). We do so manually here because we 1213 # want this server to send a FIN TCP segment immediately. Note this 1214 # must be called *before* calling socket.close(), because the latter 1215 # drops its reference to the kernel socket. 1216 self.socket._sock.close() 1217 1218 self.socket.close()
1219 1220
1221 -def format_exc(limit=None):
1222 """Like print_exc() but return a string. Backport for Python 2.3.""" 1223 try: 1224 etype, value, tb = sys.exc_info() 1225 return ''.join(traceback.format_exception(etype, value, tb, limit)) 1226 finally: 1227 etype = value = tb = None
1228 1229 1230 _SHUTDOWNREQUEST = None 1231
1232 -class WorkerThread(threading.Thread):
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
1248 - def __init__(self, server):
1249 self.ready = False 1250 self.server = server 1251 threading.Thread.__init__(self)
1252
1253 - def run(self):
1254 try: 1255 self.ready = True 1256 while True: 1257 conn = self.server.requests.get() 1258 if conn is _SHUTDOWNREQUEST: 1259 return 1260 1261 self.conn = conn 1262 try: 1263 conn.communicate() 1264 finally: 1265 conn.close() 1266 self.conn = None 1267 except (KeyboardInterrupt, SystemExit), exc: 1268 self.server.interrupt = exc
1269 1270
1271 -class ThreadPool(object):
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
1286 - def start(self):
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
1297 - def _get_idle(self):
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):
1303 self._queue.put(obj) 1304 if obj is _SHUTDOWNREQUEST: 1305 return
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
1317 - def shrink(self, amount):
1318 """Kill off worker threads (not below self.min).""" 1319 # Grow/shrink the pool if necessary. 1320 # Remove any dead threads from our list 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 # Put a number of shutdown requests on the queue equal 1329 # to 'amount'. Once each of those is processed by a worker, 1330 # that worker will terminate and be culled from our list 1331 # in self.put. 1332 self._queue.put(_SHUTDOWNREQUEST)
1333
1334 - def stop(self, timeout=5):
1335 # Must shut down threads here so the code that calls 1336 # this method can know when all threads are stopped. 1337 for worker in self._threads: 1338 self._queue.put(_SHUTDOWNREQUEST) 1339 1340 # Don't join currentThread (when stop is called inside a request). 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 # We exhausted the timeout. 1352 # Forcibly shut down the socket. 1353 c = worker.conn 1354 if c and not c.rfile.closed: 1355 if SSL and isinstance(c.socket, SSL.ConnectionType): 1356 # pyOpenSSL.socket.shutdown takes no args 1357 c.socket.shutdown() 1358 else: 1359 c.socket.shutdown(socket.SHUT_RD) 1360 worker.join() 1361 except (AssertionError, 1362 # Ignore repeated Ctrl-C. 1363 # See http://www.cherrypy.org/ticket/691. 1364 KeyboardInterrupt), exc1: 1365 pass
1366 1367 1368
1369 -class SSLConnection:
1370 """A thread-safe wrapper for an SSL.Connection. 1371 1372 *args: the arguments to create the wrapped SSL.Connection(*args). 1373 """ 1374
1375 - def __init__(self, *args):
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:
1402 - def prevent_socket_inheritance(sock):
1403 """Dummy function, since neither fcntl nor ctypes are available.""" 1404 pass
1405 else:
1406 - def prevent_socket_inheritance(sock):
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:
1411 - def prevent_socket_inheritance(sock):
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
1418 -class CherryPyWSGIServer(object):
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 # Paths to certificate and private key files 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 # We've been handed a single wsgi_app, in CP-2.1 style. 1482 # Assume it's mounted at "". 1483 self.wsgi_app = wsgi_app 1484 else: 1485 # We've been handed a list of (path_prefix, wsgi_app) tuples, 1486 # so that the server can call different wsgi_apps, and also 1487 # correctly set SCRIPT_NAME. 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
1503 - def _get_numthreads(self):
1504 return self.requests.min
1505 - def _set_numthreads(self, value):
1506 self.requests.min = value
1507 numthreads = property(_get_numthreads, _set_numthreads) 1508
1509 - def __str__(self):
1510 return "%s.%s(%r)" % (self.__module__, self.__class__.__name__, 1511 self.bind_addr)
1512
1513 - def _get_bind_addr(self):
1514 return self._bind_addr
1515 - def _set_bind_addr(self, value):
1516 if isinstance(value, tuple) and value[0] in ('', None): 1517 # Despite the socket module docs, using '' does not 1518 # allow AI_PASSIVE to work. Passing None instead 1519 # returns '0.0.0.0' like we want. In other words: 1520 # host AI_PASSIVE result 1521 # '' Y 192.168.x.y 1522 # '' N 192.168.x.y 1523 # None Y 0.0.0.0 1524 # None N 127.0.0.1 1525 # But since you can get the same effect with an explicit 1526 # '0.0.0.0', we deny both the empty string and None as values. 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
1543 - def start(self):
1544 """Run the server forever.""" 1545 # We don't have to trap KeyboardInterrupt or SystemExit here, 1546 # because cherrpy.server already does so, calling self.stop() for us. 1547 # If you're using this server with another framework, you should 1548 # trap those exceptions in whatever code block calls start(). 1549 self._interrupt = None 1550 1551 # Select the appropriate socket 1552 if isinstance(self.bind_addr, basestring): 1553 # AF_UNIX socket 1554 1555 # So we can reuse the socket... 1556 try: os.unlink(self.bind_addr) 1557 except: pass 1558 1559 # So everyone can access the socket... 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 # AF_INET or AF_INET6 socket 1566 # Get the correct address family for our host (allows IPv6 addresses) 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 # Probably a DNS issue. Assume IPv4. 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 # Timeout so KeyboardInterrupt can be caught on Win32 1591 self.socket.settimeout(1) 1592 self.socket.listen(self.request_queue_size) 1593 1594 # Create worker threads 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 # Wait for self.stop() to complete. See _set_interrupt. 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 # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473 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 # If listening on the IPV6 any address ('::' = IN6ADDR_ANY), 1626 # activate dual-stack. See http://www.cherrypy.org/ticket/871. 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 # Apparently, the socket option is not available in 1633 # this machine's TCP stack 1634 pass 1635 1636 self.socket.bind(self.bind_addr)
1637
1638 - def tick(self):
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 # SERVER_SOFTWARE is common for IIS. It's also helpful for 1650 # us to pass a default value for the "Server" response header. 1651 if environ.get("SERVER_SOFTWARE") is None: 1652 environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version 1653 # set a non-standard environ entry so the WSGI app can know what 1654 # the *real* server protocol is (and what features to support). 1655 # See http://www.faqs.org/rfcs/rfc2145.html. 1656 environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol 1657 environ["SERVER_NAME"] = self.server_name 1658 1659 if isinstance(self.bind_addr, basestring): 1660 # AF_UNIX. This isn't really allowed by WSGI, which doesn't 1661 # address unix domain sockets. But it's better than nothing. 1662 environ["SERVER_PORT"] = "" 1663 else: 1664 environ["SERVER_PORT"] = str(self.bind_addr[1]) 1665 # optional values 1666 # Until we do DNS lookups, omit REMOTE_HOST 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 # The only reason for the timeout in start() is so we can 1674 # notice keyboard interrupts on Win32, which don't interrupt 1675 # accept() by default 1676 return 1677 except socket.error, x: 1678 if x.args[0] in socket_error_eintr: 1679 # I *think* this is right. EINTR should occur when a signal 1680 # is received during the accept() call; all docs say retry 1681 # the call, and I *think* I'm reading it right that Python 1682 # will then go ahead and poll for and handle the signal 1683 # elsewhere. See http://www.cherrypy.org/ticket/707. 1684 return 1685 if x.args[0] in socket_errors_nonblocking: 1686 # Just try again. See http://www.cherrypy.org/ticket/479. 1687 return 1688 if x.args[0] in socket_errors_to_ignore: 1689 # Our socket was closed. 1690 # See http://www.cherrypy.org/ticket/686. 1691 return 1692 raise
1693
1694 - def _get_interrupt(self):
1695 return self._interrupt
1696 - def _set_interrupt(self, interrupt):
1697 self._interrupt = True 1698 self.stop() 1699 self._interrupt = interrupt
1700 interrupt = property(_get_interrupt, _set_interrupt, 1701 doc="Set this to an Exception instance to " 1702 "interrupt the server.") 1703
1704 - def stop(self):
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 # Touch our own socket to make accept() return immediately. 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 # Note that we're explicitly NOT using AI_PASSIVE, 1719 # here, because we want an actual IP to touch. 1720 # localhost won't work if we've bound to a public IP, 1721 # but it will if we bound to '0.0.0.0' (INADDR_ANY). 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 # See http://groups.google.com/group/cherrypy-users/ 1729 # browse_frm/thread/bbfe5eb39c904fe0 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
1742 - def populate_ssl_environ(self):
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 # pyOpenSSL doesn't provide access to any of these AFAICT 1750 ## 'SSL_PROTOCOL': 'SSLv2', 1751 ## SSL_CIPHER string The cipher specification name 1752 ## SSL_VERSION_INTERFACE string The mod_ssl program version 1753 ## SSL_VERSION_LIBRARY string The OpenSSL program version 1754 } 1755 1756 # Server certificate attributes 1757 ssl_environ.update({ 1758 'SSL_SERVER_M_VERSION': cert.get_version(), 1759 'SSL_SERVER_M_SERIAL': cert.get_serial_number(), 1760 ## 'SSL_SERVER_V_START': Validity of server's certificate (start time), 1761 ## 'SSL_SERVER_V_END': Validity of server's certificate (end time), 1762 }) 1763 1764 for prefix, dn in [("I", cert.get_issuer()), 1765 ("S", cert.get_subject())]: 1766 # X509Name objects don't seem to have a way to get the 1767 # complete DN string. Use str() and slice it instead, 1768 # because str(dn) == "<X509Name object '/C=US/ST=...'>" 1769 dnstr = str(dn)[18:-2] 1770 1771 wsgikey = 'SSL_SERVER_%s_DN' % prefix 1772 ssl_environ[wsgikey] = dnstr 1773 1774 # The DN should be of the form: /k1=v1/k2=v2, but we must allow 1775 # for any value to contain slashes itself (in a URL). 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