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