__init__.py (55203B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\wsgiserver\__init__.py 2 CRLF = '\r\n' 3 import os 4 import Queue 5 import re 6 quoted_slash = re.compile('(?i)%2F') 7 import rfc822 8 import socket 9 import sys 10 if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'): 11 socket.IPPROTO_IPV6 = 41 12 try: 13 import cStringIO as StringIO 14 except ImportError: 15 import StringIO 16 17 DEFAULT_BUFFER_SIZE = -1 18 _fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring) 19 import threading 20 import time 21 import traceback 22 23 def format_exc(limit = None): 24 try: 25 etype, value, tb = sys.exc_info() 26 return ''.join(traceback.format_exception(etype, value, tb, limit)) 27 finally: 28 etype = value = tb = None 29 30 31 from urllib import unquote 32 from urlparse import urlparse 33 import warnings 34 import errno 35 36 def plat_specific_errors(*errnames): 37 errno_names = dir(errno) 38 nums = [ getattr(errno, k) for k in errnames if k in errno_names ] 39 return dict.fromkeys(nums).keys() 40 41 42 socket_error_eintr = plat_specific_errors('EINTR', 'WSAEINTR') 43 socket_errors_to_ignore = plat_specific_errors('EPIPE', 'EBADF', 'WSAEBADF', 'ENOTSOCK', 'WSAENOTSOCK', 'ETIMEDOUT', 'WSAETIMEDOUT', 'ECONNREFUSED', 'WSAECONNREFUSED', 'ECONNRESET', 'WSAECONNRESET', 'ECONNABORTED', 'WSAECONNABORTED', 'ENETRESET', 'WSAENETRESET', 'EHOSTDOWN', 'EHOSTUNREACH') 44 socket_errors_to_ignore.append('timed out') 45 socket_errors_to_ignore.append('The read operation timed out') 46 socket_errors_nonblocking = plat_specific_errors('EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK') 47 comma_separated_headers = ['Accept', 48 'Accept-Charset', 49 'Accept-Encoding', 50 'Accept-Language', 51 'Accept-Ranges', 52 'Allow', 53 'Cache-Control', 54 'Connection', 55 'Content-Encoding', 56 'Content-Language', 57 'Expect', 58 'If-Match', 59 'If-None-Match', 60 'Pragma', 61 'Proxy-Authenticate', 62 'TE', 63 'Trailer', 64 'Transfer-Encoding', 65 'Upgrade', 66 'Vary', 67 'Via', 68 'Warning', 69 'WWW-Authenticate'] 70 import logging 71 if not hasattr(logging, 'statistics'): 72 logging.statistics = {} 73 74 def read_headers(rfile, hdict = None): 75 if hdict is None: 76 hdict = {} 77 while True: 78 line = rfile.readline() 79 if not line: 80 raise ValueError('Illegal end of headers.') 81 if line == CRLF: 82 break 83 if not line.endswith(CRLF): 84 raise ValueError('HTTP requires CRLF terminators') 85 if line[0] in ' \t': 86 v = line.strip() 87 else: 88 try: 89 k, v = line.split(':', 1) 90 except ValueError: 91 raise ValueError('Illegal header line.') 92 93 k = k.strip().title() 94 v = v.strip() 95 hname = k 96 if k in comma_separated_headers: 97 existing = hdict.get(hname) 98 if existing: 99 v = ', '.join((existing, v)) 100 hdict[hname] = v 101 102 return hdict 103 104 105 class MaxSizeExceeded(Exception): 106 pass 107 108 109 class SizeCheckWrapper(object): 110 111 def __init__(self, rfile, maxlen): 112 self.rfile = rfile 113 self.maxlen = maxlen 114 self.bytes_read = 0 115 116 def _check_length(self): 117 if self.maxlen and self.bytes_read > self.maxlen: 118 raise MaxSizeExceeded() 119 120 def read(self, size = None): 121 data = self.rfile.read(size) 122 self.bytes_read += len(data) 123 self._check_length() 124 return data 125 126 def readline(self, size = None): 127 if size is not None: 128 data = self.rfile.readline(size) 129 self.bytes_read += len(data) 130 self._check_length() 131 return data 132 res = [] 133 while True: 134 data = self.rfile.readline(256) 135 self.bytes_read += len(data) 136 self._check_length() 137 res.append(data) 138 if len(data) < 256 or data[-1:] == '\n': 139 return ''.join(res) 140 141 def readlines(self, sizehint = 0): 142 total = 0 143 lines = [] 144 line = self.readline() 145 while line: 146 lines.append(line) 147 total += len(line) 148 if 0 < sizehint <= total: 149 break 150 line = self.readline() 151 152 return lines 153 154 def close(self): 155 self.rfile.close() 156 157 def __iter__(self): 158 return self 159 160 def next(self): 161 data = self.rfile.next() 162 self.bytes_read += len(data) 163 self._check_length() 164 return data 165 166 167 class KnownLengthRFile(object): 168 169 def __init__(self, rfile, content_length): 170 self.rfile = rfile 171 self.remaining = content_length 172 173 def read(self, size = None): 174 if self.remaining == 0: 175 return '' 176 if size is None: 177 size = self.remaining 178 else: 179 size = min(size, self.remaining) 180 data = self.rfile.read(size) 181 self.remaining -= len(data) 182 return data 183 184 def readline(self, size = None): 185 if self.remaining == 0: 186 return '' 187 if size is None: 188 size = self.remaining 189 else: 190 size = min(size, self.remaining) 191 data = self.rfile.readline(size) 192 self.remaining -= len(data) 193 return data 194 195 def readlines(self, sizehint = 0): 196 total = 0 197 lines = [] 198 line = self.readline(sizehint) 199 while line: 200 lines.append(line) 201 total += len(line) 202 if 0 < sizehint <= total: 203 break 204 line = self.readline(sizehint) 205 206 return lines 207 208 def close(self): 209 self.rfile.close() 210 211 def __iter__(self): 212 return self 213 214 def __next__(self): 215 data = next(self.rfile) 216 self.remaining -= len(data) 217 return data 218 219 220 class ChunkedRFile(object): 221 222 def __init__(self, rfile, maxlen, bufsize = 8192): 223 self.rfile = rfile 224 self.maxlen = maxlen 225 self.bytes_read = 0 226 self.buffer = '' 227 self.bufsize = bufsize 228 self.closed = False 229 230 def _fetch(self): 231 if self.closed: 232 return 233 line = self.rfile.readline() 234 self.bytes_read += len(line) 235 if self.maxlen and self.bytes_read > self.maxlen: 236 raise MaxSizeExceeded('Request Entity Too Large', self.maxlen) 237 line = line.strip().split(';', 1) 238 try: 239 chunk_size = line.pop(0) 240 chunk_size = int(chunk_size, 16) 241 except ValueError: 242 raise ValueError('Bad chunked transfer size: ' + repr(chunk_size)) 243 244 if chunk_size <= 0: 245 self.closed = True 246 return 247 if self.maxlen and self.bytes_read + chunk_size > self.maxlen: 248 raise IOError('Request Entity Too Large') 249 chunk = self.rfile.read(chunk_size) 250 self.bytes_read += len(chunk) 251 self.buffer += chunk 252 crlf = self.rfile.read(2) 253 if crlf != CRLF: 254 raise ValueError("Bad chunked transfer coding (expected '\\r\\n', got " + repr(crlf) + ')') 255 256 def read(self, size = None): 257 data = '' 258 while True: 259 if size and len(data) >= size: 260 return data 261 if not self.buffer: 262 self._fetch() 263 if not self.buffer: 264 return data 265 if size: 266 remaining = size - len(data) 267 data += self.buffer[:remaining] 268 self.buffer = self.buffer[remaining:] 269 else: 270 data += self.buffer 271 272 def readline(self, size = None): 273 data = '' 274 while True: 275 if size and len(data) >= size: 276 return data 277 if not self.buffer: 278 self._fetch() 279 if not self.buffer: 280 return data 281 newline_pos = self.buffer.find('\n') 282 if size: 283 if newline_pos == -1: 284 remaining = size - len(data) 285 data += self.buffer[:remaining] 286 self.buffer = self.buffer[remaining:] 287 else: 288 remaining = min(size - len(data), newline_pos) 289 data += self.buffer[:remaining] 290 self.buffer = self.buffer[remaining:] 291 elif newline_pos == -1: 292 data += self.buffer 293 else: 294 data += self.buffer[:newline_pos] 295 self.buffer = self.buffer[newline_pos:] 296 297 def readlines(self, sizehint = 0): 298 total = 0 299 lines = [] 300 line = self.readline(sizehint) 301 while line: 302 lines.append(line) 303 total += len(line) 304 if 0 < sizehint <= total: 305 break 306 line = self.readline(sizehint) 307 308 return lines 309 310 def read_trailer_lines(self): 311 if not self.closed: 312 raise ValueError('Cannot read trailers until the request body has been read.') 313 while True: 314 line = self.rfile.readline() 315 if not line: 316 raise ValueError('Illegal end of headers.') 317 self.bytes_read += len(line) 318 if self.maxlen and self.bytes_read > self.maxlen: 319 raise IOError('Request Entity Too Large') 320 if line == CRLF: 321 break 322 if not line.endswith(CRLF): 323 raise ValueError('HTTP requires CRLF terminators') 324 yield line 325 326 def close(self): 327 self.rfile.close() 328 329 def __iter__(self): 330 total = 0 331 line = self.readline(sizehint) 332 while line: 333 yield line 334 total += len(line) 335 if 0 < sizehint <= total: 336 break 337 line = self.readline(sizehint) 338 339 340 class HTTPRequest(object): 341 server = None 342 conn = None 343 inheaders = {} 344 outheaders = [] 345 ready = False 346 close_connection = False 347 chunked_write = False 348 349 def __init__(self, server, conn): 350 self.server = server 351 self.conn = conn 352 self.ready = False 353 self.started_request = False 354 self.scheme = 'http' 355 if self.server.ssl_adapter is not None: 356 self.scheme = 'https' 357 self.response_protocol = 'HTTP/1.0' 358 self.inheaders = {} 359 self.status = '' 360 self.outheaders = [] 361 self.sent_headers = False 362 self.close_connection = self.__class__.close_connection 363 self.chunked_read = False 364 self.chunked_write = self.__class__.chunked_write 365 366 def parse_request(self): 367 self.rfile = SizeCheckWrapper(self.conn.rfile, self.server.max_request_header_size) 368 try: 369 self.read_request_line() 370 except MaxSizeExceeded: 371 self.simple_response('414 Request-URI Too Long', 'The Request-URI sent with the request exceeds the maximum allowed bytes.') 372 return 373 374 try: 375 success = self.read_request_headers() 376 except MaxSizeExceeded: 377 self.simple_response('413 Request Entity Too Large', 'The headers sent with the request exceed the maximum allowed bytes.') 378 return 379 380 if not success: 381 return 382 self.ready = True 383 384 def read_request_line(self): 385 request_line = self.rfile.readline() 386 self.started_request = True 387 if not request_line: 388 self.ready = False 389 return 390 if request_line == CRLF: 391 request_line = self.rfile.readline() 392 if not request_line: 393 self.ready = False 394 return 395 if not request_line.endswith(CRLF): 396 self.simple_response('400 Bad Request', 'HTTP requires CRLF terminators') 397 return 398 try: 399 method, uri, req_protocol = request_line.strip().split(' ', 2) 400 rp = (int(req_protocol[5]), int(req_protocol[7])) 401 except (ValueError, IndexError): 402 self.simple_response('400 Bad Request', 'Malformed Request-Line') 403 return 404 405 self.uri = uri 406 self.method = method 407 scheme, authority, path = self.parse_request_uri(uri) 408 if '#' in path: 409 self.simple_response('400 Bad Request', 'Illegal #fragment in Request-URI.') 410 return 411 if scheme: 412 self.scheme = scheme 413 qs = '' 414 if '?' in path: 415 path, qs = path.split('?', 1) 416 try: 417 atoms = [ unquote(x) for x in quoted_slash.split(path) ] 418 except ValueError as ex: 419 self.simple_response('400 Bad Request', ex.args[0]) 420 return 421 422 path = '%2F'.join(atoms) 423 self.path = path 424 self.qs = qs 425 sp = (int(self.server.protocol[5]), int(self.server.protocol[7])) 426 if sp[0] != rp[0]: 427 self.simple_response('505 HTTP Version Not Supported') 428 return 429 self.request_protocol = req_protocol 430 self.response_protocol = 'HTTP/%s.%s' % min(rp, sp) 431 432 def read_request_headers(self): 433 try: 434 read_headers(self.rfile, self.inheaders) 435 except ValueError as ex: 436 self.simple_response('400 Bad Request', ex.args[0]) 437 return False 438 439 mrbs = self.server.max_request_body_size 440 if mrbs and int(self.inheaders.get('Content-Length', 0)) > mrbs: 441 self.simple_response('413 Request Entity Too Large', 'The entity sent with the request exceeds the maximum allowed bytes.') 442 return False 443 if self.response_protocol == 'HTTP/1.1': 444 if self.inheaders.get('Connection', '') == 'close': 445 self.close_connection = True 446 elif self.inheaders.get('Connection', '') != 'Keep-Alive': 447 self.close_connection = True 448 te = None 449 if self.response_protocol == 'HTTP/1.1': 450 te = self.inheaders.get('Transfer-Encoding') 451 if te: 452 te = [ x.strip().lower() for x in te.split(',') if x.strip() ] 453 self.chunked_read = False 454 if te: 455 for enc in te: 456 if enc == 'chunked': 457 self.chunked_read = True 458 else: 459 self.simple_response('501 Unimplemented') 460 self.close_connection = True 461 return False 462 463 if self.inheaders.get('Expect', '') == '100-continue': 464 msg = self.server.protocol + ' 100 Continue\r\n\r\n' 465 try: 466 self.conn.wfile.sendall(msg) 467 except socket.error as x: 468 if x.args[0] not in socket_errors_to_ignore: 469 raise 470 471 return True 472 473 def parse_request_uri(self, uri): 474 if uri == '*': 475 return (None, None, uri) 476 i = uri.find('://') 477 if i > 0 and '?' not in uri[:i]: 478 scheme, remainder = uri[:i].lower(), uri[i + 3:] 479 authority, path = remainder.split('/', 1) 480 return (scheme, authority, path) 481 elif uri.startswith('/'): 482 return (None, None, uri) 483 else: 484 return (None, uri, None) 485 486 def respond(self): 487 mrbs = self.server.max_request_body_size 488 if self.chunked_read: 489 self.rfile = ChunkedRFile(self.conn.rfile, mrbs) 490 else: 491 cl = int(self.inheaders.get('Content-Length', 0)) 492 if mrbs and mrbs < cl: 493 if not self.sent_headers: 494 self.simple_response('413 Request Entity Too Large', 'The entity sent with the request exceeds the maximum allowed bytes.') 495 return 496 self.rfile = KnownLengthRFile(self.conn.rfile, cl) 497 self.server.gateway(self).respond() 498 if self.ready and not self.sent_headers: 499 self.sent_headers = True 500 self.send_headers() 501 if self.chunked_write: 502 self.conn.wfile.sendall('0\r\n\r\n') 503 504 def simple_response(self, status, msg = ''): 505 status = str(status) 506 buf = [self.server.protocol + ' ' + status + CRLF, 'Content-Length: %s\r\n' % len(msg), 'Content-Type: text/plain\r\n'] 507 if status[:3] in ('413', '414'): 508 self.close_connection = True 509 if self.response_protocol == 'HTTP/1.1': 510 buf.append('Connection: close\r\n') 511 else: 512 status = '400 Bad Request' 513 buf.append(CRLF) 514 if msg: 515 if isinstance(msg, unicode): 516 msg = msg.encode('ISO-8859-1') 517 buf.append(msg) 518 try: 519 self.conn.wfile.sendall(''.join(buf)) 520 except socket.error as x: 521 if x.args[0] not in socket_errors_to_ignore: 522 raise 523 524 def write(self, chunk): 525 if self.chunked_write and chunk: 526 buf = [hex(len(chunk))[2:], 527 CRLF, 528 chunk, 529 CRLF] 530 self.conn.wfile.sendall(''.join(buf)) 531 else: 532 self.conn.wfile.sendall(chunk) 533 534 def send_headers(self): 535 hkeys = [ key.lower() for key, value in self.outheaders ] 536 status = int(self.status[:3]) 537 if status == 413: 538 self.close_connection = True 539 elif 'content-length' not in hkeys: 540 if status < 200 or status in (204, 205, 304): 541 pass 542 elif self.response_protocol == 'HTTP/1.1' and self.method != 'HEAD': 543 self.chunked_write = True 544 self.outheaders.append(('Transfer-Encoding', 'chunked')) 545 else: 546 self.close_connection = True 547 if 'connection' not in hkeys: 548 if self.response_protocol == 'HTTP/1.1': 549 if self.close_connection: 550 self.outheaders.append(('Connection', 'close')) 551 elif not self.close_connection: 552 self.outheaders.append(('Connection', 'Keep-Alive')) 553 if not self.close_connection and not self.chunked_read: 554 remaining = getattr(self.rfile, 'remaining', 0) 555 if remaining > 0: 556 self.rfile.read(remaining) 557 if 'date' not in hkeys: 558 self.outheaders.append(('Date', rfc822.formatdate())) 559 if 'server' not in hkeys: 560 self.outheaders.append(('Server', self.server.server_name)) 561 buf = [self.server.protocol + ' ' + self.status + CRLF] 562 for k, v in self.outheaders: 563 buf.append(k + ': ' + v + CRLF) 564 565 buf.append(CRLF) 566 self.conn.wfile.sendall(''.join(buf)) 567 568 569 class NoSSLError(Exception): 570 pass 571 572 573 class FatalSSLAlert(Exception): 574 pass 575 576 577 class CP_fileobject(socket._fileobject): 578 579 def __init__(self, *args, **kwargs): 580 self.bytes_read = 0 581 self.bytes_written = 0 582 socket._fileobject.__init__(self, *args, **kwargs) 583 584 def sendall(self, data): 585 while data: 586 try: 587 bytes_sent = self.send(data) 588 data = data[bytes_sent:] 589 except socket.error as e: 590 if e.args[0] not in socket_errors_nonblocking: 591 raise 592 593 def send(self, data): 594 bytes_sent = self._sock.send(data) 595 self.bytes_written += bytes_sent 596 return bytes_sent 597 598 def flush(self): 599 if self._wbuf: 600 buffer = ''.join(self._wbuf) 601 self._wbuf = [] 602 self.sendall(buffer) 603 604 def recv(self, size): 605 while True: 606 try: 607 data = self._sock.recv(size) 608 self.bytes_read += len(data) 609 return data 610 except socket.error as e: 611 if e.args[0] not in socket_errors_nonblocking and e.args[0] not in socket_error_eintr: 612 raise 613 614 if not _fileobject_uses_str_type: 615 616 def read(self, size = -1): 617 rbufsize = max(self._rbufsize, self.default_bufsize) 618 buf = self._rbuf 619 buf.seek(0, 2) 620 if size < 0: 621 self._rbuf = StringIO.StringIO() 622 while True: 623 data = self.recv(rbufsize) 624 if not data: 625 break 626 buf.write(data) 627 628 return buf.getvalue() 629 else: 630 buf_len = buf.tell() 631 if buf_len >= size: 632 buf.seek(0) 633 rv = buf.read(size) 634 self._rbuf = StringIO.StringIO() 635 self._rbuf.write(buf.read()) 636 return rv 637 self._rbuf = StringIO.StringIO() 638 while True: 639 left = size - buf_len 640 data = self.recv(left) 641 if not data: 642 break 643 n = len(data) 644 if n == size and not buf_len: 645 return data 646 if n == left: 647 buf.write(data) 648 del data 649 break 650 buf.write(data) 651 buf_len += n 652 del data 653 654 return buf.getvalue() 655 656 def readline(self, size = -1): 657 buf = self._rbuf 658 buf.seek(0, 2) 659 if buf.tell() > 0: 660 buf.seek(0) 661 bline = buf.readline(size) 662 if bline.endswith('\n') or len(bline) == size: 663 self._rbuf = StringIO.StringIO() 664 self._rbuf.write(buf.read()) 665 return bline 666 del bline 667 if size < 0: 668 if self._rbufsize <= 1: 669 buf.seek(0) 670 buffers = [buf.read()] 671 self._rbuf = StringIO.StringIO() 672 data = None 673 recv = self.recv 674 while data != '\n': 675 data = recv(1) 676 if not data: 677 break 678 buffers.append(data) 679 680 return ''.join(buffers) 681 buf.seek(0, 2) 682 self._rbuf = StringIO.StringIO() 683 while True: 684 data = self.recv(self._rbufsize) 685 if not data: 686 break 687 nl = data.find('\n') 688 if nl >= 0: 689 nl += 1 690 buf.write(data[:nl]) 691 self._rbuf.write(data[nl:]) 692 del data 693 break 694 buf.write(data) 695 696 return buf.getvalue() 697 else: 698 buf.seek(0, 2) 699 buf_len = buf.tell() 700 if buf_len >= size: 701 buf.seek(0) 702 rv = buf.read(size) 703 self._rbuf = StringIO.StringIO() 704 self._rbuf.write(buf.read()) 705 return rv 706 self._rbuf = StringIO.StringIO() 707 while True: 708 data = self.recv(self._rbufsize) 709 if not data: 710 break 711 left = size - buf_len 712 nl = data.find('\n', 0, left) 713 if nl >= 0: 714 nl += 1 715 self._rbuf.write(data[nl:]) 716 if buf_len: 717 buf.write(data[:nl]) 718 break 719 else: 720 return data[:nl] 721 n = len(data) 722 if n == size and not buf_len: 723 return data 724 if n >= left: 725 buf.write(data[:left]) 726 self._rbuf.write(data[left:]) 727 break 728 buf.write(data) 729 buf_len += n 730 731 return buf.getvalue() 732 733 else: 734 735 def read(self, size = -1): 736 if size < 0: 737 buffers = [self._rbuf] 738 self._rbuf = '' 739 if self._rbufsize <= 1: 740 recv_size = self.default_bufsize 741 else: 742 recv_size = self._rbufsize 743 while True: 744 data = self.recv(recv_size) 745 if not data: 746 break 747 buffers.append(data) 748 749 return ''.join(buffers) 750 else: 751 data = self._rbuf 752 buf_len = len(data) 753 if buf_len >= size: 754 self._rbuf = data[size:] 755 return data[:size] 756 buffers = [] 757 if data: 758 buffers.append(data) 759 self._rbuf = '' 760 while True: 761 left = size - buf_len 762 recv_size = max(self._rbufsize, left) 763 data = self.recv(recv_size) 764 if not data: 765 break 766 buffers.append(data) 767 n = len(data) 768 if n >= left: 769 self._rbuf = data[left:] 770 buffers[-1] = data[:left] 771 break 772 buf_len += n 773 774 return ''.join(buffers) 775 776 def readline(self, size = -1): 777 data = self._rbuf 778 if size < 0: 779 if self._rbufsize <= 1: 780 buffers = [] 781 while data != '\n': 782 data = self.recv(1) 783 if not data: 784 break 785 buffers.append(data) 786 787 return ''.join(buffers) 788 nl = data.find('\n') 789 if nl >= 0: 790 nl += 1 791 self._rbuf = data[nl:] 792 return data[:nl] 793 buffers = [] 794 if data: 795 buffers.append(data) 796 self._rbuf = '' 797 while True: 798 data = self.recv(self._rbufsize) 799 if not data: 800 break 801 buffers.append(data) 802 nl = data.find('\n') 803 if nl >= 0: 804 nl += 1 805 self._rbuf = data[nl:] 806 buffers[-1] = data[:nl] 807 break 808 809 return ''.join(buffers) 810 else: 811 nl = data.find('\n', 0, size) 812 if nl >= 0: 813 nl += 1 814 self._rbuf = data[nl:] 815 return data[:nl] 816 buf_len = len(data) 817 if buf_len >= size: 818 self._rbuf = data[size:] 819 return data[:size] 820 buffers = [] 821 if data: 822 buffers.append(data) 823 self._rbuf = '' 824 while True: 825 data = self.recv(self._rbufsize) 826 if not data: 827 break 828 buffers.append(data) 829 left = size - buf_len 830 nl = data.find('\n', 0, left) 831 if nl >= 0: 832 nl += 1 833 self._rbuf = data[nl:] 834 buffers[-1] = data[:nl] 835 break 836 n = len(data) 837 if n >= left: 838 self._rbuf = data[left:] 839 buffers[-1] = data[:left] 840 break 841 buf_len += n 842 843 return ''.join(buffers) 844 845 846 class HTTPConnection(object): 847 remote_addr = None 848 remote_port = None 849 ssl_env = None 850 rbufsize = DEFAULT_BUFFER_SIZE 851 wbufsize = DEFAULT_BUFFER_SIZE 852 RequestHandlerClass = HTTPRequest 853 854 def __init__(self, server, sock, makefile = CP_fileobject): 855 self.server = server 856 self.socket = sock 857 self.rfile = makefile(sock, 'rb', self.rbufsize) 858 self.wfile = makefile(sock, 'wb', self.wbufsize) 859 self.requests_seen = 0 860 861 def communicate(self): 862 request_seen = False 863 try: 864 while True: 865 req = None 866 req = self.RequestHandlerClass(self.server, self) 867 req.parse_request() 868 if self.server.stats['Enabled']: 869 self.requests_seen += 1 870 if not req.ready: 871 return 872 request_seen = True 873 req.respond() 874 if req.close_connection: 875 return 876 877 except socket.error as e: 878 errnum = e.args[0] 879 if errnum == 'timed out' or errnum == 'The read operation timed out': 880 if not request_seen or req and req.started_request: 881 if req and not req.sent_headers: 882 try: 883 req.simple_response('408 Request Timeout') 884 except FatalSSLAlert: 885 return 886 887 elif errnum not in socket_errors_to_ignore: 888 LogError() 889 if req and not req.sent_headers: 890 try: 891 req.simple_response('500 Internal Server Error', format_exc()) 892 except FatalSSLAlert: 893 return 894 895 return 896 except (KeyboardInterrupt, SystemExit): 897 raise 898 except FatalSSLAlert: 899 return 900 except NoSSLError: 901 if req and not req.sent_headers: 902 self.wfile = CP_fileobject(self.socket._sock, 'wb', self.wbufsize) 903 req.simple_response('400 Bad Request', 'The client sent a plain HTTP request, but this server only speaks HTTPS on this port.') 904 self.linger = True 905 except Exception: 906 LogError() 907 if req and not req.sent_headers: 908 try: 909 req.simple_response('500 Internal Server Error', format_exc()) 910 except FatalSSLAlert: 911 return 912 913 linger = False 914 915 def close(self): 916 self.rfile.close() 917 if not self.linger: 918 if hasattr(self.socket, '_sock'): 919 self.socket._sock.close() 920 self.socket.close() 921 922 923 def LogError(): 924 import traceback 925 traceback.print_exc() 926 927 928 _SHUTDOWNREQUEST = None 929 930 class WorkerThread(threading.Thread): 931 conn = None 932 server = None 933 ready = False 934 935 def __init__(self, server): 936 self.ready = False 937 self.server = server 938 self.requests_seen = 0 939 self.bytes_read = 0 940 self.bytes_written = 0 941 self.start_time = None 942 self.work_time = 0 943 self.stats = {'Requests': lambda s: self.requests_seen + (self.start_time is None and 0 or self.conn.requests_seen), 944 'Bytes Read': lambda s: self.bytes_read + (self.start_time is None and 0 or self.conn.rfile.bytes_read), 945 'Bytes Written': lambda s: self.bytes_written + (self.start_time is None and 0 or self.conn.wfile.bytes_written), 946 'Work Time': lambda s: self.work_time + (self.start_time is None and 0 or time.time() - self.start_time), 947 'Read Throughput': lambda s: s['Bytes Read'](s) / (s['Work Time'](s) or 1e-06), 948 'Write Throughput': lambda s: s['Bytes Written'](s) / (s['Work Time'](s) or 1e-06)} 949 threading.Thread.__init__(self) 950 951 def run(self): 952 self.server.stats['Worker Threads'][self.getName()] = self.stats 953 try: 954 self.ready = True 955 while True: 956 conn = self.server.requests.get() 957 if conn is _SHUTDOWNREQUEST: 958 return 959 self.conn = conn 960 if self.server.stats['Enabled']: 961 self.start_time = time.time() 962 try: 963 conn.communicate() 964 finally: 965 conn.close() 966 if self.server.stats['Enabled']: 967 self.requests_seen += self.conn.requests_seen 968 self.bytes_read += self.conn.rfile.bytes_read 969 self.bytes_written += self.conn.wfile.bytes_written 970 self.work_time += time.time() - self.start_time 971 self.start_time = None 972 self.conn = None 973 974 except (KeyboardInterrupt, SystemExit) as exc: 975 self.server.interrupt = exc 976 977 978 class ThreadPool(object): 979 980 def __init__(self, server, min = 10, max = -1): 981 self.server = server 982 self.min = min 983 self.max = max 984 self._threads = [] 985 self._queue = Queue.Queue() 986 self.get = self._queue.get 987 988 def start(self): 989 for i in range(self.min): 990 self._threads.append(WorkerThread(self.server)) 991 992 for worker in self._threads: 993 worker.setName('CP Server ' + worker.getName()) 994 worker.start() 995 996 for worker in self._threads: 997 while not worker.ready: 998 time.sleep(0.1) 999 1000 def _get_idle(self): 1001 return len([ t for t in self._threads if t.conn is None ]) 1002 1003 idle = property(_get_idle, doc=_get_idle.__doc__) 1004 1005 def put(self, obj): 1006 self._queue.put(obj) 1007 if obj is _SHUTDOWNREQUEST: 1008 return 1009 1010 def grow(self, amount): 1011 for i in range(amount): 1012 if self.max > 0 and len(self._threads) >= self.max: 1013 break 1014 worker = WorkerThread(self.server) 1015 worker.setName('CP Server ' + worker.getName()) 1016 self._threads.append(worker) 1017 worker.start() 1018 1019 def shrink(self, amount): 1020 for t in self._threads: 1021 if not t.isAlive(): 1022 self._threads.remove(t) 1023 amount -= 1 1024 1025 if amount > 0: 1026 for i in range(min(amount, len(self._threads) - self.min)): 1027 self._queue.put(_SHUTDOWNREQUEST) 1028 1029 def stop(self, timeout = 5): 1030 for worker in self._threads: 1031 self._queue.put(_SHUTDOWNREQUEST) 1032 1033 current = threading.currentThread() 1034 if timeout and timeout >= 0: 1035 endtime = time.time() + timeout 1036 while self._threads: 1037 worker = self._threads.pop() 1038 if worker is not current and worker.isAlive(): 1039 try: 1040 if timeout is None or timeout < 0: 1041 worker.join() 1042 else: 1043 remaining_time = endtime - time.time() 1044 if remaining_time > 0: 1045 worker.join(remaining_time) 1046 if worker.isAlive(): 1047 c = worker.conn 1048 if c and not c.rfile.closed: 1049 try: 1050 c.socket.shutdown(socket.SHUT_RD) 1051 except TypeError: 1052 c.socket.shutdown() 1053 1054 worker.join() 1055 except (AssertionError, KeyboardInterrupt) as exc1: 1056 pass 1057 1058 def _get_qsize(self): 1059 return self._queue.qsize() 1060 1061 qsize = property(_get_qsize) 1062 1063 1064 try: 1065 import fcntl 1066 except ImportError: 1067 try: 1068 from ctypes import windll, WinError 1069 import ctypes 1070 import ctypes.wintypes 1071 except ImportError: 1072 1073 def prevent_socket_inheritance(sock): 1074 pass 1075 1076 1077 else: 1078 kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) 1079 GetLastError = ctypes.get_last_error 1080 _SetHandleInformation = kernel32.SetHandleInformation 1081 _SetHandleInformation.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD] 1082 _SetHandleInformation.restype = ctypes.wintypes.BOOL 1083 1084 def WinError(code = None, descr = None): 1085 if code is None: 1086 code = GetLastError() 1087 return ctypes.WinError(code, descr) 1088 1089 1090 def prevent_socket_inheritance(sock): 1091 try: 1092 if not kernel32.SetHandleInformation(sock.fileno(), 1, 0): 1093 raise WinError() 1094 except Exception: 1095 logging.exception('WSGI main ticker got an error') 1096 1097 1098 else: 1099 1100 def prevent_socket_inheritance(sock): 1101 fd = sock.fileno() 1102 old_flags = fcntl.fcntl(fd, fcntl.F_GETFD) 1103 fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) 1104 1105 1106 class SSLAdapter(object): 1107 1108 def __init__(self, certificate, private_key, certificate_chain = None): 1109 self.certificate = certificate 1110 self.private_key = private_key 1111 self.certificate_chain = certificate_chain 1112 1113 def wrap(self, sock): 1114 raise NotImplemented 1115 1116 def makefile(self, sock, mode = 'r', bufsize = DEFAULT_BUFFER_SIZE): 1117 raise NotImplemented 1118 1119 1120 class HTTPServer(object): 1121 _bind_addr = '127.0.0.1' 1122 _interrupt = None 1123 gateway = None 1124 minthreads = None 1125 maxthreads = None 1126 server_name = None 1127 protocol = 'HTTP/1.1' 1128 request_queue_size = 5 1129 shutdown_timeout = 5 1130 timeout = 10 1131 version = 'CherryPy/3.2.0' 1132 software = None 1133 ready = False 1134 max_request_header_size = 0 1135 max_request_body_size = 0 1136 nodelay = True 1137 ConnectionClass = HTTPConnection 1138 ssl_adapter = None 1139 1140 def __init__(self, bind_addr, gateway, minthreads = 10, maxthreads = -1, server_name = None): 1141 self.bind_addr = bind_addr 1142 self.gateway = gateway 1143 self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads) 1144 if not server_name: 1145 server_name = socket.gethostname() 1146 self.server_name = server_name 1147 self.clear_stats() 1148 1149 def clear_stats(self): 1150 self._start_time = None 1151 self._run_time = 0 1152 self.stats = {'Enabled': False, 1153 'Bind Address': lambda s: repr(self.bind_addr), 1154 'Run time': lambda s: not s['Enabled'] and 0 or self.runtime(), 1155 'Accepts': 0, 1156 'Accepts/sec': lambda s: s['Accepts'] / self.runtime(), 1157 'Queue': lambda s: getattr(self.requests, 'qsize', None), 1158 'Threads': lambda s: len(getattr(self.requests, '_threads', [])), 1159 'Threads Idle': lambda s: getattr(self.requests, 'idle', None), 1160 'Socket Errors': 0, 1161 'Requests': lambda s: not s['Enabled'] and 0 or sum([ w['Requests'](w) for w in s['Worker Threads'].values() ], 0), 1162 'Bytes Read': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Read'](w) for w in s['Worker Threads'].values() ], 0), 1163 'Bytes Written': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Written'](w) for w in s['Worker Threads'].values() ], 0), 1164 'Work Time': lambda s: not s['Enabled'] and 0 or sum([ w['Work Time'](w) for w in s['Worker Threads'].values() ], 0), 1165 'Read Throughput': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Read'](w) / (w['Work Time'](w) or 1e-06) for w in s['Worker Threads'].values() ], 0), 1166 'Write Throughput': lambda s: not s['Enabled'] and 0 or sum([ w['Bytes Written'](w) / (w['Work Time'](w) or 1e-06) for w in s['Worker Threads'].values() ], 0), 1167 'Worker Threads': {}} 1168 logging.statistics['CherryPy HTTPServer %d' % id(self)] = self.stats 1169 1170 def runtime(self): 1171 if self._start_time is None: 1172 return self._run_time 1173 else: 1174 return self._run_time + (time.time() - self._start_time) 1175 1176 def __str__(self): 1177 return '%s.%s(%r)' % (self.__module__, self.__class__.__name__, self.bind_addr) 1178 1179 def _get_bind_addr(self): 1180 return self._bind_addr 1181 1182 def _set_bind_addr(self, value): 1183 if isinstance(value, tuple) and value[0] in ('', None): 1184 raise ValueError("Host values of '' or None are not allowed. Use '0.0.0.0' (IPv4) or '::' (IPv6) instead to listen on all active interfaces.") 1185 self._bind_addr = value 1186 1187 bind_addr = property(_get_bind_addr, _set_bind_addr, doc='The interface on which to listen for connections.\n\n For TCP sockets, a (host, port) tuple. Host values may be any IPv4\n or IPv6 address, or any valid hostname. The string \'localhost\' is a\n synonym for \'127.0.0.1\' (or \'::1\', if your hosts file prefers IPv6).\n The string \'0.0.0.0\' is a special IPv4 entry meaning "any active\n interface" (INADDR_ANY), and \'::\' is the similar IN6ADDR_ANY for\n IPv6. The empty string or None are not allowed.\n\n For UNIX sockets, supply the filename as a string.') 1188 1189 def start(self): 1190 self._interrupt = None 1191 if self.software is None: 1192 self.software = '%s Server' % self.version 1193 if self.ssl_adapter is None and getattr(self, 'ssl_certificate', None) and getattr(self, 'ssl_private_key', None): 1194 warnings.warn('SSL attributes are deprecated in CherryPy 3.2, and will be removed in CherryPy 3.3. Use an ssl_adapter attribute instead.', DeprecationWarning) 1195 try: 1196 from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter 1197 except ImportError: 1198 pass 1199 else: 1200 self.ssl_adapter = pyOpenSSLAdapter(self.ssl_certificate, self.ssl_private_key, getattr(self, 'ssl_certificate_chain', None)) 1201 1202 if isinstance(self.bind_addr, basestring): 1203 try: 1204 os.unlink(self.bind_addr) 1205 except: 1206 pass 1207 1208 try: 1209 os.chmod(self.bind_addr, 511) 1210 except: 1211 pass 1212 1213 info = [(socket.AF_UNIX, 1214 socket.SOCK_STREAM, 1215 0, 1216 '', 1217 self.bind_addr)] 1218 else: 1219 host, port = self.bind_addr 1220 try: 1221 info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) 1222 except socket.gaierror: 1223 if ':' in self.bind_addr[0]: 1224 info = [(socket.AF_INET6, 1225 socket.SOCK_STREAM, 1226 0, 1227 '', 1228 self.bind_addr + (0, 0))] 1229 else: 1230 info = [(socket.AF_INET, 1231 socket.SOCK_STREAM, 1232 0, 1233 '', 1234 self.bind_addr)] 1235 1236 self.socket = None 1237 msg = 'No socket could be created' 1238 for res in info: 1239 af, socktype, proto, canonname, sa = res 1240 try: 1241 self.bind(af, socktype, proto) 1242 except socket.error: 1243 if self.socket: 1244 self.socket.close() 1245 self.socket = None 1246 continue 1247 1248 break 1249 1250 if not self.socket: 1251 raise socket.error(msg) 1252 self.socket.settimeout(1) 1253 self.socket.listen(self.request_queue_size) 1254 self.requests.start() 1255 self.ready = True 1256 self._start_time = time.time() 1257 while self.ready: 1258 self.tick() 1259 if self.interrupt: 1260 while self.interrupt is True: 1261 time.sleep(0.1) 1262 1263 if self.interrupt: 1264 raise self.interrupt 1265 1266 def bind(self, family, type, proto = 0): 1267 self.socket = socket.socket(family, type, proto) 1268 prevent_socket_inheritance(self.socket) 1269 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 1270 if self.nodelay and not isinstance(self.bind_addr, str): 1271 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 1272 if self.ssl_adapter is not None: 1273 self.socket = self.ssl_adapter.bind(self.socket) 1274 if hasattr(socket, 'AF_INET6') and family == socket.AF_INET6 and self.bind_addr[0] in ('::', '::0', '::0.0.0.0'): 1275 try: 1276 self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) 1277 except (AttributeError, socket.error): 1278 pass 1279 1280 self.socket.bind(self.bind_addr) 1281 1282 def tick(self): 1283 try: 1284 s, addr = self.socket.accept() 1285 if self.stats['Enabled']: 1286 self.stats['Accepts'] += 1 1287 if not self.ready: 1288 return 1289 prevent_socket_inheritance(s) 1290 if hasattr(s, 'settimeout'): 1291 s.settimeout(self.timeout) 1292 makefile = CP_fileobject 1293 ssl_env = {} 1294 if self.ssl_adapter is not None: 1295 try: 1296 s, ssl_env = self.ssl_adapter.wrap(s) 1297 except NoSSLError: 1298 msg = 'The client sent a plain HTTP request, but this server only speaks HTTPS on this port.' 1299 buf = ['%s 400 Bad Request\r\n' % self.protocol, 1300 'Content-Length: %s\r\n' % len(msg), 1301 'Content-Type: text/plain\r\n\r\n', 1302 msg] 1303 wfile = CP_fileobject(s, 'wb', DEFAULT_BUFFER_SIZE) 1304 try: 1305 wfile.sendall(''.join(buf)) 1306 except socket.error as x: 1307 if x.args[0] not in socket_errors_to_ignore: 1308 raise 1309 1310 return 1311 1312 if not s: 1313 return 1314 makefile = self.ssl_adapter.makefile 1315 if hasattr(s, 'settimeout'): 1316 s.settimeout(self.timeout) 1317 conn = self.ConnectionClass(self, s, makefile) 1318 if not isinstance(self.bind_addr, basestring): 1319 if addr is None: 1320 if len(s.getsockname()) == 2: 1321 addr = ('0.0.0.0', 0) 1322 else: 1323 addr = ('::', 0) 1324 conn.remote_addr = addr[0] 1325 conn.remote_port = addr[1] 1326 conn.ssl_env = ssl_env 1327 self.requests.put(conn) 1328 except socket.timeout: 1329 return 1330 except socket.error as x: 1331 if self.stats['Enabled']: 1332 self.stats['Socket Errors'] += 1 1333 if x.args[0] in socket_error_eintr: 1334 return 1335 if x.args[0] in socket_errors_nonblocking: 1336 return 1337 if x.args[0] in socket_errors_to_ignore: 1338 return 1339 raise 1340 1341 def _get_interrupt(self): 1342 return self._interrupt 1343 1344 def _set_interrupt(self, interrupt): 1345 self._interrupt = True 1346 self.stop() 1347 self._interrupt = interrupt 1348 1349 interrupt = property(_get_interrupt, _set_interrupt, doc='Set this to an Exception instance to interrupt the server.') 1350 1351 def stop(self): 1352 self.ready = False 1353 if self._start_time is not None: 1354 self._run_time += time.time() - self._start_time 1355 self._start_time = None 1356 sock = getattr(self, 'socket', None) 1357 if sock: 1358 if not isinstance(self.bind_addr, basestring): 1359 try: 1360 host, port = sock.getsockname()[:2] 1361 except socket.error as x: 1362 if x.args[0] not in socket_errors_to_ignore: 1363 raise 1364 else: 1365 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): 1366 af, socktype, proto, canonname, sa = res 1367 s = None 1368 try: 1369 s = socket.socket(af, socktype, proto) 1370 s.settimeout(1.0) 1371 s.connect((host, port)) 1372 s.close() 1373 except socket.error: 1374 if s: 1375 s.close() 1376 1377 if hasattr(sock, 'close'): 1378 sock.close() 1379 self.socket = None 1380 self.requests.stop(self.shutdown_timeout) 1381 1382 1383 class Gateway(object): 1384 1385 def __init__(self, req): 1386 self.req = req 1387 1388 def respond(self): 1389 raise NotImplemented 1390 1391 1392 ssl_adapters = {'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter', 1393 'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter'} 1394 1395 def get_ssl_adapter_class(name = 'pyopenssl'): 1396 adapter = ssl_adapters[name.lower()] 1397 if isinstance(adapter, basestring): 1398 last_dot = adapter.rfind('.') 1399 attr_name = adapter[last_dot + 1:] 1400 mod_path = adapter[:last_dot] 1401 try: 1402 mod = sys.modules[mod_path] 1403 if mod is None: 1404 raise KeyError() 1405 except KeyError: 1406 mod = __import__(mod_path, globals(), locals(), ['']) 1407 1408 try: 1409 adapter = getattr(mod, attr_name) 1410 except AttributeError: 1411 raise AttributeError("'%s' object has no attribute '%s'" % (mod_path, attr_name)) 1412 1413 return adapter 1414 1415 1416 class CherryPyWSGIServer(HTTPServer): 1417 wsgi_version = (1, 0) 1418 1419 def __init__(self, bind_addr, wsgi_app, numthreads = 10, server_name = None, max = -1, request_queue_size = 5, timeout = 10, shutdown_timeout = 5): 1420 self.requests = ThreadPool(self, min=numthreads or 1, max=max) 1421 self.wsgi_app = wsgi_app 1422 self.gateway = wsgi_gateways[self.wsgi_version] 1423 self.bind_addr = bind_addr 1424 if not server_name: 1425 server_name = socket.gethostname() 1426 self.server_name = server_name 1427 self.request_queue_size = request_queue_size 1428 self.timeout = timeout 1429 self.shutdown_timeout = shutdown_timeout 1430 self.clear_stats() 1431 1432 def _get_numthreads(self): 1433 return self.requests.min 1434 1435 def _set_numthreads(self, value): 1436 self.requests.min = value 1437 1438 numthreads = property(_get_numthreads, _set_numthreads) 1439 1440 1441 class WSGIGateway(Gateway): 1442 1443 def __init__(self, req): 1444 self.req = req 1445 self.started_response = False 1446 self.env = self.get_environ() 1447 self.remaining_bytes_out = None 1448 1449 def get_environ(self): 1450 raise NotImplemented 1451 1452 def respond(self): 1453 response = self.req.server.wsgi_app(self.env, self.start_response) 1454 try: 1455 for chunk in response: 1456 if chunk: 1457 if isinstance(chunk, unicode): 1458 chunk = chunk.encode('ISO-8859-1') 1459 self.write(chunk) 1460 1461 finally: 1462 if hasattr(response, 'close'): 1463 response.close() 1464 1465 def start_response(self, status, headers, exc_info = None): 1466 if self.started_response and not exc_info: 1467 raise AssertionError('WSGI start_response called a second time with no exc_info.') 1468 self.started_response = True 1469 if self.req.sent_headers: 1470 try: 1471 raise exc_info[0], exc_info[1], exc_info[2] 1472 finally: 1473 exc_info = None 1474 1475 self.req.status = status 1476 for k, v in headers: 1477 if not isinstance(k, str): 1478 raise TypeError('WSGI response header key %r is not a byte string.' % k) 1479 if not isinstance(v, str): 1480 raise TypeError('WSGI response header value %r is not a byte string.' % v) 1481 if k.lower() == 'content-length': 1482 self.remaining_bytes_out = int(v) 1483 1484 self.req.outheaders.extend(headers) 1485 return self.write 1486 1487 def write(self, chunk): 1488 if not self.started_response: 1489 raise AssertionError('WSGI write called before start_response.') 1490 chunklen = len(chunk) 1491 rbo = self.remaining_bytes_out 1492 if rbo is not None and chunklen > rbo: 1493 if not self.req.sent_headers: 1494 self.req.simple_response('500 Internal Server Error', 'The requested resource returned more bytes than the declared Content-Length.') 1495 else: 1496 chunk = chunk[:rbo] 1497 if not self.req.sent_headers: 1498 self.req.sent_headers = True 1499 self.req.send_headers() 1500 self.req.write(chunk) 1501 if rbo is not None: 1502 rbo -= chunklen 1503 if rbo < 0: 1504 raise ValueError('Response body exceeds the declared Content-Length.') 1505 1506 1507 class WSGIGateway_10(WSGIGateway): 1508 1509 def get_environ(self): 1510 req = self.req 1511 env = {'ACTUAL_SERVER_PROTOCOL': req.server.protocol, 1512 'PATH_INFO': req.path, 1513 'QUERY_STRING': req.qs, 1514 'REMOTE_ADDR': req.conn.remote_addr or '', 1515 'REMOTE_PORT': str(req.conn.remote_port or ''), 1516 'REQUEST_METHOD': req.method, 1517 'REQUEST_URI': req.uri, 1518 'SCRIPT_NAME': '', 1519 'SERVER_NAME': req.server.server_name, 1520 'SERVER_PROTOCOL': req.request_protocol, 1521 'SERVER_SOFTWARE': req.server.software, 1522 'wsgi.errors': sys.stderr, 1523 'wsgi.input': req.rfile, 1524 'wsgi.multiprocess': False, 1525 'wsgi.multithread': True, 1526 'wsgi.run_once': False, 1527 'wsgi.url_scheme': req.scheme, 1528 'wsgi.version': (1, 0)} 1529 if isinstance(req.server.bind_addr, basestring): 1530 env['SERVER_PORT'] = '' 1531 else: 1532 env['SERVER_PORT'] = str(req.server.bind_addr[1]) 1533 for k, v in req.inheaders.iteritems(): 1534 env['HTTP_' + k.upper().replace('-', '_')] = v 1535 1536 ct = env.pop('HTTP_CONTENT_TYPE', None) 1537 if ct is not None: 1538 env['CONTENT_TYPE'] = ct 1539 cl = env.pop('HTTP_CONTENT_LENGTH', None) 1540 if cl is not None: 1541 env['CONTENT_LENGTH'] = cl 1542 if req.conn.ssl_env: 1543 env.update(req.conn.ssl_env) 1544 return env 1545 1546 1547 class WSGIGateway_u0(WSGIGateway_10): 1548 1549 def get_environ(self): 1550 req = self.req 1551 env_10 = WSGIGateway_10.get_environ(self) 1552 env = dict([ (k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems() ]) 1553 env[u'wsgi.version'] = ('u', 0) 1554 env.setdefault(u'wsgi.url_encoding', u'utf-8') 1555 try: 1556 for key in [u'PATH_INFO', u'SCRIPT_NAME', u'QUERY_STRING']: 1557 env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding']) 1558 1559 except UnicodeDecodeError: 1560 env[u'wsgi.url_encoding'] = u'ISO-8859-1' 1561 for key in [u'PATH_INFO', u'SCRIPT_NAME', u'QUERY_STRING']: 1562 env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding']) 1563 1564 for k, v in sorted(env.items()): 1565 if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'): 1566 env[k] = v.decode('ISO-8859-1') 1567 1568 return env 1569 1570 1571 wsgi_gateways = {(1, 0): WSGIGateway_10, 1572 ('u', 0): WSGIGateway_u0} 1573 1574 class WSGIPathInfoDispatcher(object): 1575 1576 def __init__(self, apps): 1577 try: 1578 apps = apps.items() 1579 except AttributeError: 1580 pass 1581 1582 apps.sort(cmp=lambda x, y: cmp(len(x[0]), len(y[0]))) 1583 apps.reverse() 1584 self.apps = [ (p.rstrip('/'), a) for p, a in apps ] 1585 1586 def __call__(self, environ, start_response): 1587 path = environ['PATH_INFO'] or '/' 1588 for p, app in self.apps: 1589 if path.startswith(p + '/') or path == p: 1590 environ = environ.copy() 1591 environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'] + p 1592 environ['PATH_INFO'] = path[len(p):] 1593 return app(environ, start_response) 1594 1595 start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', '0')]) 1596 return ['']