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