Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/wsgiserver/__init__.py

Revision 2469 (checked in by visteya, 3 days ago)

string formatting missing required value

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

Hosted by WebFaction

Log in as guest/cpguest to create tickets