| Home | Trees | Indices | Help |
|
|---|
|
|
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
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
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
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
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
185
187 """Wraps a file-like object, raising MaxSizeExceeded if too large."""
188
193
197
199 data = self.rfile.read(size)
200 self.bytes_read += len(data)
201 self._check_length()
202 return data
203
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
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
238
241
247
248
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
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
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
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
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
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
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
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
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
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
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
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
714
715
719
720
721 if not _fileobject_uses_str_type:
723 """Faux file object attached to a socket object."""
724
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
737
743
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
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
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:
901 """Faux file object attached to a socket object."""
902
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
915
921
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
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
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
1045 """SSL file object attached to a socket object."""
1046
1047 ssl_timeout = 3
1048 ssl_retry = .01
1049
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
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
1108
1111
1112
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
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
1159 """Read each request and respond appropriately."""
1160 try:
1161 while True:
1162 # (re)set req to None so that if something goes wrong in
1163 # the RequestHandlerClass constructor, the error doesn't
1164 # get written to the previous request.
1165 req = None
1166 req = self.RequestHandlerClass(self.wfile, self.environ,
1167 self.wsgi_app)
1168
1169 # This order of operations should guarantee correct pipelining.
1170 req.parse_request()
1171 if not req.ready:
1172 return
1173
1174 req.respond()
1175 if req.close_connection:
1176 return
1177
1178 except socket.error, e:
1179 errnum = e.args[0]
1180 if errnum == 'timed out':
1181 if req and not req.sent_headers:
1182 req.simple_response("408 Request Timeout")
1183 elif errnum not in socket_errors_to_ignore:
1184 if req and not req.sent_headers:
1185 req.simple_response("500 Internal Server Error",
1186 format_exc())
1187 return
1188 except (KeyboardInterrupt, SystemExit):
1189 raise
1190 except FatalSSLAlert, e:
1191 # Close the connection.
1192 return
1193 except NoSSLError:
1194 if req and not req.sent_headers:
1195 # Unwrap our wfile
1196 req.wfile = CP_fileobject(self.socket._sock, "wb", -1)
1197 req.simple_response("400 Bad Request",
1198 "The client sent a plain HTTP request, but "
1199 "this server only speaks HTTPS on this port.")
1200 self.linger = True
1201 except Exception, e:
1202 if req and not req.sent_headers:
1203 req.simple_response("500 Internal Server Error", format_exc())
1204
1205 linger = False
1206
1208 """Close the socket underlying this connection."""
1209 self.rfile.close()
1210
1211 if not self.linger:
1212 # Python's socket module does NOT call close on the kernel socket
1213 # when you call socket.close(). We do so manually here because we
1214 # want this server to send a FIN TCP segment immediately. Note this
1215 # must be called *before* calling socket.close(), because the latter
1216 # drops its reference to the kernel socket.
1217 self.socket._sock.close()
1218 self.socket.close()
1219 else:
1220 # On the other hand, sometimes we want to hang around for a bit
1221 # to make sure the client has a chance to read our entire
1222 # response. Skipping the close() calls here delays the FIN
1223 # packet until the socket object is garbage-collected later.
1224 # Someday, perhaps, we'll do the full lingering_close that
1225 # Apache does, but not today.
1226 pass
1227
1228
1230 """Like print_exc() but return a string. Backport for Python 2.3."""
1231 try:
1232 etype, value, tb = sys.exc_info()
1233 return ''.join(traceback.format_exception(etype, value, tb, limit))
1234 finally:
1235 etype = value = tb = None
1236
1237
1238 _SHUTDOWNREQUEST = None
1239
1241 """Thread which continuously polls a Queue for Connection objects.
1242
1243 server: the HTTP Server which spawned this thread, and which owns the
1244 Queue and is placing active connections into it.
1245 ready: a simple flag for the calling server to know when this thread
1246 has begun polling the Queue.
1247
1248 Due to the timing issues of polling a Queue, a WorkerThread does not
1249 check its own 'ready' flag after it has started. To stop the thread,
1250 it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
1251 (one for each running WorkerThread).
1252 """
1253
1254 conn = None
1255
1260
1262 try:
1263 self.ready = True
1264 while True:
1265 conn = self.server.requests.get()
1266 if conn is _SHUTDOWNREQUEST:
1267 return
1268
1269 self.conn = conn
1270 try:
1271 conn.communicate()
1272 finally:
1273 conn.close()
1274 self.conn = None
1275 except (KeyboardInterrupt, SystemExit), exc:
1276 self.server.interrupt = exc
1277
1278
1280 """A Request Queue for the CherryPyWSGIServer which pools threads.
1281
1282 ThreadPool objects must provide min, get(), put(obj), start()
1283 and stop(timeout) attributes.
1284 """
1285
1287 self.server = server
1288 self.min = min
1289 self.max = max
1290 self._threads = []
1291 self._queue = Queue.Queue()
1292 self.get = self._queue.get
1293
1295 """Start the pool of threads."""
1296 for i in xrange(self.min):
1297 self._threads.append(WorkerThread(self.server))
1298 for worker in self._threads:
1299 worker.setName("CP WSGIServer " + worker.getName())
1300 worker.start()
1301 for worker in self._threads:
1302 while not worker.ready:
1303 time.sleep(.1)
1304
1306 """Number of worker threads which are idle. Read-only."""
1307 return len([t for t in self._threads if t.conn is None])
1308 idle = property(_get_idle, doc=_get_idle.__doc__)
1309
1314
1316 """Spawn new worker threads (not above self.max)."""
1317 for i in xrange(amount):
1318 if self.max > 0 and len(self._threads) >= self.max:
1319 break
1320 worker = WorkerThread(self.server)
1321 worker.setName("CP WSGIServer " + worker.getName())
1322 self._threads.append(worker)
1323 worker.start()
1324
1326 """Kill off worker threads (not below self.min)."""
1327 # Grow/shrink the pool if necessary.
1328 # Remove any dead threads from our list
1329 for t in self._threads:
1330 if not t.isAlive():
1331 self._threads.remove(t)
1332 amount -= 1
1333
1334 if amount > 0:
1335 for i in xrange(min(amount, len(self._threads) - self.min)):
1336 # Put a number of shutdown requests on the queue equal
1337 # to 'amount'. Once each of those is processed by a worker,
1338 # that worker will terminate and be culled from our list
1339 # in self.put.
1340 self._queue.put(_SHUTDOWNREQUEST)
1341
1343 # Must shut down threads here so the code that calls
1344 # this method can know when all threads are stopped.
1345 for worker in self._threads:
1346 self._queue.put(_SHUTDOWNREQUEST)
1347
1348 # Don't join currentThread (when stop is called inside a request).
1349 current = threading.currentThread()
1350 while self._threads:
1351 worker = self._threads.pop()
1352 if worker is not current and worker.isAlive():
1353 try:
1354 if timeout is None or timeout < 0:
1355 worker.join()
1356 else:
1357 worker.join(timeout)
1358 if worker.isAlive():
1359 # We exhausted the timeout.
1360 # Forcibly shut down the socket.
1361 c = worker.conn
1362 if c and not c.rfile.closed:
1363 if SSL and isinstance(c.socket, SSL.ConnectionType):
1364 # pyOpenSSL.socket.shutdown takes no args
1365 c.socket.shutdown()
1366 else:
1367 c.socket.shutdown(socket.SHUT_RD)
1368 worker.join()
1369 except (AssertionError,
1370 # Ignore repeated Ctrl-C.
1371 # See http://www.cherrypy.org/ticket/691.
1372 KeyboardInterrupt), exc1:
1373 pass
1374
1375
1376
1378 """A thread-safe wrapper for an SSL.Connection.
1379
1380 *args: the arguments to create the wrapped SSL.Connection(*args).
1381 """
1382
1386
1387 for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
1388 'renegotiate', 'bind', 'listen', 'connect', 'accept',
1389 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
1390 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
1391 'makefile', 'get_app_data', 'set_app_data', 'state_string',
1392 'sock_shutdown', 'get_peer_certificate', 'want_read',
1393 'want_write', 'set_connect_state', 'set_accept_state',
1394 'connect_ex', 'sendall', 'settimeout'):
1395 exec """def %s(self, *args):
1396 self._lock.acquire()
1397 try:
1398 return self._ssl_conn.%s(*args)
1399 finally:
1400 self._lock.release()
1401 """ % (f, f)
1402
1403
1404 try:
1405 import fcntl
1406 except ImportError:
1407 try:
1408 from ctypes import windll, WinError
1409 except ImportError:
1413 else:
1415 """Mark the given socket fd as non-inheritable (Windows)."""
1416 if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
1417 raise WinError()
1418 else:
1420 """Mark the given socket fd as non-inheritable (POSIX)."""
1421 fd = sock.fileno()
1422 old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
1423 fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
1424
1425
1427 """An HTTP server for WSGI.
1428
1429 bind_addr: The interface on which to listen for connections.
1430 For TCP sockets, a (host, port) tuple. Host values may be any IPv4
1431 or IPv6 address, or any valid hostname. The string 'localhost' is a
1432 synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
1433 The string '0.0.0.0' is a special IPv4 entry meaning "any active
1434 interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
1435 IPv6. The empty string or None are not allowed.
1436
1437 For UNIX sockets, supply the filename as a string.
1438 wsgi_app: the WSGI 'application callable'; multiple WSGI applications
1439 may be passed as (path_prefix, app) pairs.
1440 numthreads: the number of worker threads to create (default 10).
1441 server_name: the string to set for WSGI's SERVER_NAME environ entry.
1442 Defaults to socket.gethostname().
1443 max: the maximum number of queued requests (defaults to -1 = no limit).
1444 request_queue_size: the 'backlog' argument to socket.listen();
1445 specifies the maximum number of queued connections (default 5).
1446 timeout: the timeout in seconds for accepted connections (default 10).
1447
1448 nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket
1449 option.
1450
1451 protocol: the version string to write in the Status-Line of all
1452 HTTP responses. For example, "HTTP/1.1" (the default). This
1453 also limits the supported features used in the response.
1454
1455
1456 SSL/HTTPS
1457 ---------
1458 The OpenSSL module must be importable for SSL functionality.
1459 You can obtain it from http://pyopenssl.sourceforge.net/
1460
1461 ssl_certificate: the filename of the server SSL certificate.
1462 ssl_privatekey: the filename of the server's private key file.
1463
1464 If either of these is None (both are None by default), this server
1465 will not use SSL. If both are given and are valid, they will be read
1466 on server start and used in the SSL context for the listening socket.
1467 """
1468
1469 protocol = "HTTP/1.1"
1470 _bind_addr = "127.0.0.1"
1471 version = "CherryPy/3.1.2"
1472 ready = False
1473 _interrupt = None
1474
1475 nodelay = True
1476
1477 ConnectionClass = HTTPConnection
1478 environ = {}
1479
1480 # Paths to certificate and private key files
1481 ssl_certificate = None
1482 ssl_private_key = None
1483
1484 - def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
1485 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
1486 self.requests = ThreadPool(self, min=numthreads or 1, max=max)
1487
1488 if callable(wsgi_app):
1489 # We've been handed a single wsgi_app, in CP-2.1 style.
1490 # Assume it's mounted at "".
1491 self.wsgi_app = wsgi_app
1492 else:
1493 # We've been handed a list of (path_prefix, wsgi_app) tuples,
1494 # so that the server can call different wsgi_apps, and also
1495 # correctly set SCRIPT_NAME.
1496 warnings.warn("The ability to pass multiple apps is deprecated "
1497 "and will be removed in 3.2. You should explicitly "
1498 "include a WSGIPathInfoDispatcher instead.",
1499 DeprecationWarning)
1500 self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app)
1501
1502 self.bind_addr = bind_addr
1503 if not server_name:
1504 server_name = socket.gethostname()
1505 self.server_name = server_name
1506 self.request_queue_size = request_queue_size
1507
1508 self.timeout = timeout
1509 self.shutdown_timeout = shutdown_timeout
1510
1515 numthreads = property(_get_numthreads, _set_numthreads)
1516
1520
1522 return self._bind_addr
1524 if isinstance(value, tuple) and value[0] in ('', None):
1525 # Despite the socket module docs, using '' does not
1526 # allow AI_PASSIVE to work. Passing None instead
1527 # returns '0.0.0.0' like we want. In other words:
1528 # host AI_PASSIVE result
1529 # '' Y 192.168.x.y
1530 # '' N 192.168.x.y
1531 # None Y 0.0.0.0
1532 # None N 127.0.0.1
1533 # But since you can get the same effect with an explicit
1534 # '0.0.0.0', we deny both the empty string and None as values.
1535 raise ValueError("Host values of '' or None are not allowed. "
1536 "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
1537 "to listen on all active interfaces.")
1538 self._bind_addr = value
1539 bind_addr = property(_get_bind_addr, _set_bind_addr,
1540 doc="""The interface on which to listen for connections.
1541
1542 For TCP sockets, a (host, port) tuple. Host values may be any IPv4
1543 or IPv6 address, or any valid hostname. The string 'localhost' is a
1544 synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
1545 The string '0.0.0.0' is a special IPv4 entry meaning "any active
1546 interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
1547 IPv6. The empty string or None are not allowed.
1548
1549 For UNIX sockets, supply the filename as a string.""")
1550
1552 """Run the server forever."""
1553 # We don't have to trap KeyboardInterrupt or SystemExit here,
1554 # because cherrpy.server already does so, calling self.stop() for us.
1555 # If you're using this server with another framework, you should
1556 # trap those exceptions in whatever code block calls start().
1557 self._interrupt = None
1558
1559 # Select the appropriate socket
1560 if isinstance(self.bind_addr, basestring):
1561 # AF_UNIX socket
1562
1563 # So we can reuse the socket...
1564 try: os.unlink(self.bind_addr)
1565 except: pass
1566
1567 # So everyone can access the socket...
1568 try: os.chmod(self.bind_addr, 0777)
1569 except: pass
1570
1571 info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
1572 else:
1573 # AF_INET or AF_INET6 socket
1574 # Get the correct address family for our host (allows IPv6 addresses)
1575 host, port = self.bind_addr
1576 try:
1577 info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
1578 socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
1579 except socket.gaierror:
1580 # Probably a DNS issue. Assume IPv4.
1581 info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", self.bind_addr)]
1582
1583 self.socket = None
1584 msg = "No socket could be created"
1585 for res in info:
1586 af, socktype, proto, canonname, sa = res
1587 try:
1588 self.bind(af, socktype, proto)
1589 except socket.error, msg:
1590 if self.socket:
1591 self.socket.close()
1592 self.socket = None
1593 continue
1594 break
1595 if not self.socket:
1596 raise socket.error, msg
1597
1598 # Timeout so KeyboardInterrupt can be caught on Win32
1599 self.socket.settimeout(1)
1600 self.socket.listen(self.request_queue_size)
1601
1602 # Create worker threads
1603 self.requests.start()
1604
1605 self.ready = True
1606 while self.ready:
1607 self.tick()
1608 if self.interrupt:
1609 while self.interrupt is True:
1610 # Wait for self.stop() to complete. See _set_interrupt.
1611 time.sleep(0.1)
1612 if self.interrupt:
1613 raise self.interrupt
1614
1616 """Create (or recreate) the actual socket object."""
1617 self.socket = socket.socket(family, type, proto)
1618 prevent_socket_inheritance(self.socket)
1619 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1620 if self.nodelay:
1621 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
1622 if self.ssl_certificate and self.ssl_private_key:
1623 if SSL is None:
1624 raise ImportError("You must install pyOpenSSL to use HTTPS.")
1625
1626 # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
1627 ctx = SSL.Context(SSL.SSLv23_METHOD)
1628 ctx.use_privatekey_file(self.ssl_private_key)
1629 ctx.use_certificate_file(self.ssl_certificate)
1630 self.socket = SSLConnection(ctx, self.socket)
1631 self.populate_ssl_environ()
1632
1633 # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
1634 # activate dual-stack. See http://www.cherrypy.org/ticket/871.
1635 if (not isinstance(self.bind_addr, basestring)
1636 and self.bind_addr[0] == '::' and family == socket.AF_INET6):
1637 try:
1638 self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
1639 except (AttributeError, socket.error):
1640 # Apparently, the socket option is not available in
1641 # this machine's TCP stack
1642 pass
1643
1644 self.socket.bind(self.bind_addr)
1645
1647 """Accept a new connection and put it on the Queue."""
1648 try:
1649 s, addr = self.socket.accept()
1650 prevent_socket_inheritance(s)
1651 if not self.ready:
1652 return
1653 if hasattr(s, 'settimeout'):
1654 s.settimeout(self.timeout)
1655
1656 environ = self.environ.copy()
1657 # SERVER_SOFTWARE is common for IIS. It's also helpful for
1658 # us to pass a default value for the "Server" response header.
1659 if environ.get("SERVER_SOFTWARE") is None:
1660 environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version
1661 # set a non-standard environ entry so the WSGI app can know what
1662 # the *real* server protocol is (and what features to support).
1663 # See http://www.faqs.org/rfcs/rfc2145.html.
1664 environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol
1665 environ["SERVER_NAME"] = self.server_name
1666
1667 if isinstance(self.bind_addr, basestring):
1668 # AF_UNIX. This isn't really allowed by WSGI, which doesn't
1669 # address unix domain sockets. But it's better than nothing.
1670 environ["SERVER_PORT"] = ""
1671 else:
1672 environ["SERVER_PORT"] = str(self.bind_addr[1])
1673 # optional values
1674 # Until we do DNS lookups, omit REMOTE_HOST
1675 environ["REMOTE_ADDR"] = addr[0]
1676 environ["REMOTE_PORT"] = str(addr[1])
1677
1678 conn = self.ConnectionClass(s, self.wsgi_app, environ)
1679 self.requests.put(conn)
1680 except socket.timeout:
1681 # The only reason for the timeout in start() is so we can
1682 # notice keyboard interrupts on Win32, which don't interrupt
1683 # accept() by default
1684 return
1685 except socket.error, x:
1686 if x.args[0] in socket_error_eintr:
1687 # I *think* this is right. EINTR should occur when a signal
1688 # is received during the accept() call; all docs say retry
1689 # the call, and I *think* I'm reading it right that Python
1690 # will then go ahead and poll for and handle the signal
1691 # elsewhere. See http://www.cherrypy.org/ticket/707.
1692 return
1693 if x.args[0] in socket_errors_nonblocking:
1694 # Just try again. See http://www.cherrypy.org/ticket/479.
1695 return
1696 if x.args[0] in socket_errors_to_ignore:
1697 # Our socket was closed.
1698 # See http://www.cherrypy.org/ticket/686.
1699 return
1700 raise
1701
1703 return self._interrupt
1708 interrupt = property(_get_interrupt, _set_interrupt,
1709 doc="Set this to an Exception instance to "
1710 "interrupt the server.")
1711
1713 """Gracefully shutdown a server that is serving forever."""
1714 self.ready = False
1715
1716 sock = getattr(self, "socket", None)
1717 if sock:
1718 if not isinstance(self.bind_addr, basestring):
1719 # Touch our own socket to make accept() return immediately.
1720 try:
1721 host, port = sock.getsockname()[:2]
1722 except socket.error, x:
1723 if x.args[0] not in socket_errors_to_ignore:
1724 raise
1725 else:
1726 # Note that we're explicitly NOT using AI_PASSIVE,
1727 # here, because we want an actual IP to touch.
1728 # localhost won't work if we've bound to a public IP,
1729 # but it will if we bound to '0.0.0.0' (INADDR_ANY).
1730 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
1731 socket.SOCK_STREAM):
1732 af, socktype, proto, canonname, sa = res
1733 s = None
1734 try:
1735 s = socket.socket(af, socktype, proto)
1736 # See http://groups.google.com/group/cherrypy-users/
1737 # browse_frm/thread/bbfe5eb39c904fe0
1738 s.settimeout(1.0)
1739 s.connect((host, port))
1740 s.close()
1741 except socket.error:
1742 if s:
1743 s.close()
1744 if hasattr(sock, "close"):
1745 sock.close()
1746 self.socket = None
1747
1748 self.requests.stop(self.shutdown_timeout)
1749
1751 """Create WSGI environ entries to be merged into each request."""
1752 cert = open(self.ssl_certificate, 'rb').read()
1753 cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
1754 ssl_environ = {
1755 "wsgi.url_scheme": "https",
1756 "HTTPS": "on",
1757 # pyOpenSSL doesn't provide access to any of these AFAICT
1758 ## 'SSL_PROTOCOL': 'SSLv2',
1759 ## SSL_CIPHER string The cipher specification name
1760 ## SSL_VERSION_INTERFACE string The mod_ssl program version
1761 ## SSL_VERSION_LIBRARY string The OpenSSL program version
1762 }
1763
1764 # Server certificate attributes
1765 ssl_environ.update({
1766 'SSL_SERVER_M_VERSION': cert.get_version(),
1767 'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
1768 ## 'SSL_SERVER_V_START': Validity of server's certificate (start time),
1769 ## 'SSL_SERVER_V_END': Validity of server's certificate (end time),
1770 })
1771
1772 for prefix, dn in [("I", cert.get_issuer()),
1773 ("S", cert.get_subject())]:
1774 # X509Name objects don't seem to have a way to get the
1775 # complete DN string. Use str() and slice it instead,
1776 # because str(dn) == "<X509Name object '/C=US/ST=...'>"
1777 dnstr = str(dn)[18:-2]
1778
1779 wsgikey = 'SSL_SERVER_%s_DN' % prefix
1780 ssl_environ[wsgikey] = dnstr
1781
1782 # The DN should be of the form: /k1=v1/k2=v2, but we must allow
1783 # for any value to contain slashes itself (in a URL).
1784 while dnstr:
1785 pos = dnstr.rfind("=")
1786 dnstr, value = dnstr[:pos], dnstr[pos + 1:]
1787 pos = dnstr.rfind("/")
1788 dnstr, key = dnstr[:pos], dnstr[pos + 1:]
1789 if key and value:
1790 wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
1791 ssl_environ[wsgikey] = value
1792
1793 self.environ.update(ssl_environ)
1794
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Aug 15 15:33:03 2009 | http://epydoc.sourceforge.net |