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

Source Code for Package web.web.wsgiserver

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