home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.6) import base64 import os import Queue import re quoted_slash = re.compile('(?i)%2F') import rfc822 import socket try: import cStringIO as StringIO except ImportError: import StringIO _fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring) import sys import threading import time import traceback from urllib import unquote from urlparse import urlparse import warnings try: from OpenSSL import SSL from OpenSSL import crypto except ImportError: SSL = None import errno def plat_specific_errors(*errnames): errno_names = dir(errno) nums = _[1] return dict.fromkeys(nums).keys() socket_error_eintr = plat_specific_errors('EINTR', 'WSAEINTR') socket_errors_to_ignore = plat_specific_errors('EPIPE', 'EBADF', 'WSAEBADF', 'ENOTSOCK', 'WSAENOTSOCK', 'ETIMEDOUT', 'WSAETIMEDOUT', 'ECONNREFUSED', 'WSAECONNREFUSED', 'ECONNRESET', 'WSAECONNRESET', 'ECONNABORTED', 'WSAECONNABORTED', 'ENETRESET', 'WSAENETRESET', 'EHOSTDOWN', 'EHOSTUNREACH') socket_errors_to_ignore.append('timed out') socket_errors_nonblocking = plat_specific_errors('EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK') comma_separated_headers = [ 'ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING', 'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL', 'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT', 'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE', 'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING', 'WWW-AUTHENTICATE'] class WSGIPathInfoDispatcher(object): def __init__(self, apps): try: apps = apps.items() except AttributeError: pass apps.sort() apps.reverse() self.apps = [ (p.rstrip('/'), a) for p, a in apps ] def __call__(self, environ, start_response): if not environ['PATH_INFO']: pass path = '/' for p, app in self.apps: if path.startswith(p + '/') or path == p: environ = environ.copy() environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'] + p environ['PATH_INFO'] = path[len(p):] return app(environ, start_response) start_response('404 Not Found', [ ('Content-Type', 'text/plain'), ('Content-Length', '0')]) return [ ''] class MaxSizeExceeded(Exception): pass class SizeCheckWrapper(object): def __init__(self, rfile, maxlen): self.rfile = rfile self.maxlen = maxlen self.bytes_read = 0 def _check_length(self): if self.maxlen and self.bytes_read > self.maxlen: raise MaxSizeExceeded() self.bytes_read > self.maxlen def read(self, size = None): data = self.rfile.read(size) self.bytes_read += len(data) self._check_length() return data def readline(self, size = None): if size is not None: data = self.rfile.readline(size) self.bytes_read += len(data) self._check_length() return data res = [] while True: data = self.rfile.readline(256) self.bytes_read += len(data) self._check_length() res.append(data) if len(data) < 256 or data[-1:] == '\n': return ''.join(res) continue data[-1:] == '\n' def readlines(self, sizehint = 0): total = 0 lines = [] line = self.readline() while line: lines.append(line) total += len(line) if sizehint < sizehint: pass elif sizehint <= total: break line = self.readline() continue 0 return lines def close(self): self.rfile.close() def __iter__(self): return self def next(self): data = self.rfile.next() self.bytes_read += len(data) self._check_length() return data class HTTPRequest(object): max_request_header_size = 0 max_request_body_size = 0 def __init__(self, wfile, environ, wsgi_app): self.rfile = environ['wsgi.input'] self.wfile = wfile self.environ = environ.copy() self.wsgi_app = wsgi_app self.ready = False self.started_response = False self.status = '' self.outheaders = [] self.sent_headers = False self.close_connection = False self.chunked_write = False def parse_request(self): self.rfile.maxlen = self.max_request_header_size self.rfile.bytes_read = 0 try: self._parse_request() except MaxSizeExceeded: self.simple_response('413 Request Entity Too Large') return None def _parse_request(self): request_line = self.rfile.readline() if not request_line: self.ready = False return None environ = self.environ try: (method, path, req_protocol) = request_line.strip().split(' ', 2) except ValueError: None if request_line == '\r\n' else request_line None if request_line == '\r\n' else request_line self.simple_response(400, 'Malformed Request-Line') return None environ['REQUEST_METHOD'] = method (scheme, location, path, params, qs, frag) = urlparse(path) if frag: self.simple_response('400 Bad Request', 'Illegal #fragment in Request-URI.') return None if params: path = path + ';' + params environ['SCRIPT_NAME'] = '' atoms = [ unquote(x) for x in quoted_slash.split(path) ] path = '%2F'.join(atoms) environ['PATH_INFO'] = path environ['QUERY_STRING'] = qs rp = (int(req_protocol[5]), int(req_protocol[7])) server_protocol = environ['ACTUAL_SERVER_PROTOCOL'] sp = (int(server_protocol[5]), int(server_protocol[7])) if sp[0] != rp[0]: self.simple_response('505 HTTP Version Not Supported') return None environ['SERVER_PROTOCOL'] = req_protocol self.response_protocol = 'HTTP/%s.%s' % min(rp, sp) try: self.read_headers() except ValueError: [] if location else [] ex = [] if location else [] self.simple_response('400 Bad Request', repr(ex.args)) return None mrbs = self.max_request_body_size if mrbs and int(environ.get('CONTENT_LENGTH', 0)) > mrbs: self.simple_response('413 Request Entity Too Large') return None if self.response_protocol == 'HTTP/1.1': if environ.get('HTTP_CONNECTION', '') == 'close': self.close_connection = True elif environ.get('HTTP_CONNECTION', '') != 'Keep-Alive': self.close_connection = True te = None if self.response_protocol == 'HTTP/1.1': te = environ.get('HTTP_TRANSFER_ENCODING') if te: te = _[2] self.chunked_read = False if te: for enc in te: if enc == 'chunked': self.chunked_read = True continue self.simple_response('501 Unimplemented') self.close_connection = True return None if environ.get('HTTP_EXPECT', '') == '100-continue': self.simple_response(100) self.ready = True def read_headers(self): environ = self.environ while True: line = self.rfile.readline() if not line: raise ValueError('Illegal end of headers.') line if line == '\r\n': break if line[0] in ' \t': v = line.strip() else: (k, v) = line.split(':', 1) k = k.strip().upper() v = v.strip() envname = 'HTTP_' + k.replace('-', '_') if k in comma_separated_headers: existing = environ.get(envname) if existing: v = ', '.join((existing, v)) environ[envname] = v ct = environ.pop('HTTP_CONTENT_TYPE', None) if ct is not None: environ['CONTENT_TYPE'] = ct cl = environ.pop('HTTP_CONTENT_LENGTH', None) if cl is not None: environ['CONTENT_LENGTH'] = cl def decode_chunked(self): cl = 0 data = StringIO.StringIO() while True: line = self.rfile.readline().strip().split(';', 1) chunk_size = int(line.pop(0), 16) if chunk_size <= 0: break cl += chunk_size data.write(self.rfile.read(chunk_size)) crlf = self.rfile.read(2) if crlf != '\r\n': self.simple_response('400 Bad Request', "Bad chunked transfer coding (expected '\\r\\n', got %r)" % crlf) return None continue crlf != '\r\n' self.read_headers() data.seek(0) self.environ['wsgi.input'] = data if not str(cl): pass self.environ['CONTENT_LENGTH'] = '' return True def respond(self): if self.chunked_read: self.rfile.maxlen = self.max_request_body_size else: cl = int(self.environ.get('CONTENT_LENGTH', 0)) if self.max_request_body_size: self.rfile.maxlen = min(cl, self.max_request_body_size) else: self.rfile.maxlen = cl self.rfile.bytes_read = 0 try: self._respond() except MaxSizeExceeded: if not self.sent_headers: self.simple_response('413 Request Entity Too Large') return None def _respond(self): if self.chunked_read: if not self.decode_chunked(): self.close_connection = True return None response = self.wsgi_app(self.environ, self.start_response) try: for chunk in response: if chunk: self.write(chunk) continue finally: if hasattr(response, 'close'): response.close() if self.ready and not (self.sent_headers): self.sent_headers = True self.send_headers() if self.chunked_write: self.wfile.sendall('0\r\n\r\n') def simple_response(self, status, msg = ''): status = str(status) buf = [ '%s %s\r\n' % (self.environ['ACTUAL_SERVER_PROTOCOL'], status), 'Content-Length: %s\r\n' % len(msg), 'Content-Type: text/plain\r\n'] if status[:3] == '413' and self.response_protocol == 'HTTP/1.1': self.close_connection = True buf.append('Connection: close\r\n') buf.append('\r\n') if msg: buf.append(msg) try: self.wfile.sendall(''.join(buf)) except socket.error: x = None if x.args[0] not in socket_errors_to_ignore: raise x.args[0] not in socket_errors_to_ignore def start_response(self, status, headers, exc_info = None): if self.started_response and not exc_info: raise AssertionError('WSGI start_response called a second time with no exc_info.') not exc_info if self.sent_headers: try: raise exc_info[0], exc_info[1], exc_info[2] finally: exc_info = None self.started_response = True self.status = status self.outheaders.extend(headers) return self.write def write(self, chunk): if not self.started_response: raise AssertionError('WSGI write called before start_response.') self.started_response if not self.sent_headers: self.sent_headers = True self.send_headers() if self.chunked_write and chunk: buf = [ hex(len(chunk))[2:], '\r\n', chunk, '\r\n'] self.wfile.sendall(''.join(buf)) else: self.wfile.sendall(chunk) def send_headers(self): hkeys = [ key.lower() for key, value in self.outheaders ] status = int(self.status[:3]) if status == 413: self.close_connection = True elif 'content-length' not in hkeys: if status < 200 or status in (204, 205, 304): pass elif self.response_protocol == 'HTTP/1.1' and self.environ['REQUEST_METHOD'] != 'HEAD': self.chunked_write = True self.outheaders.append(('Transfer-Encoding', 'chunked')) else: self.close_connection = True if 'connection' not in hkeys: if self.response_protocol == 'HTTP/1.1': if self.close_connection: self.outheaders.append(('Connection', 'close')) elif not self.close_connection: self.outheaders.append(('Connection', 'Keep-Alive')) if not (self.close_connection) and not (self.chunked_read): size = self.rfile.maxlen - self.rfile.bytes_read if size > 0: self.rfile.read(size) if 'date' not in hkeys: self.outheaders.append(('Date', rfc822.formatdate())) if 'server' not in hkeys: self.outheaders.append(('Server', self.environ['SERVER_SOFTWARE'])) buf = [ self.environ['ACTUAL_SERVER_PROTOCOL'], ' ', self.status, '\r\n'] try: [] += [ k + ': ' + v + '\r\n' for k, v in self.outheaders ] except TypeError: if not isinstance(k, str): raise TypeError('WSGI response header key %r is not a string.') isinstance(k, str) if not isinstance(v, str): raise TypeError('WSGI response header value %r is not a string.') isinstance(v, str) raise buf.append('\r\n') self.wfile.sendall(''.join(buf)) class NoSSLError(Exception): pass class FatalSSLAlert(Exception): pass if not _fileobject_uses_str_type: class CP_fileobject(socket._fileobject): def sendall(self, data): while data: try: bytes_sent = self.send(data) data = data[bytes_sent:] continue except socket.error: e = None if e.args[0] not in socket_errors_nonblocking: raise e.args[0] not in socket_errors_nonblocking continue None<EXCEPTION MATCH>socket.error def send(self, data): return self._sock.send(data) def flush(self): if self._wbuf: buffer = ''.join(self._wbuf) self._wbuf = [] self.sendall(buffer) def recv(self, size): while True: try: return self._sock.recv(size) continue except socket.error: e = None if e.args[0] not in socket_errors_nonblocking and e.args[0] not in socket_error_eintr: raise e.args[0] not in socket_error_eintr continue None<EXCEPTION MATCH>socket.error def read(self, size = -1): rbufsize = max(self._rbufsize, self.default_bufsize) buf = self._rbuf buf.seek(0, 2) if size < 0: self._rbuf = StringIO.StringIO() while True: data = self.recv(rbufsize) if not data: break buf.write(data) return buf.getvalue() buf_len = buf.tell() if buf_len >= size: buf.seek(0) rv = buf.read(size) self._rbuf = StringIO.StringIO() self._rbuf.write(buf.read()) return rv self._rbuf = StringIO.StringIO() while True: left = size - buf_len data = self.recv(left) n = len(data) if n == size and not buf_len: return data buf.write(data) buf_len += n del data continue None if n == left else None if not data else size < 0 return buf.getvalue() def readline(self, size = -1): buf = self._rbuf buf.seek(0, 2) if buf.tell() > 0: buf.seek(0) bline = buf.readline(size) if bline.endswith('\n') or len(bline) == size: self._rbuf = StringIO.StringIO() self._rbuf.write(buf.read()) return bline del bline if size < 0: if self._rbufsize <= 1: buf.seek(0) buffers = [ buf.read()] self._rbuf = StringIO.StringIO() data = None recv = self.recv while data != '\n': data = recv(1) if not data: break buffers.append(data) return ''.join(buffers) buf.seek(0, 2) self._rbuf = StringIO.StringIO() while True: data = self.recv(self._rbufsize) if not data: break nl = data.find('\n') if nl >= 0: nl += 1 buf.write(data[:nl]) self._rbuf.write(data[nl:]) del data break buf.write(data) return buf.getvalue() buf.seek(0, 2) buf_len = buf.tell() if buf_len >= size: buf.seek(0) rv = buf.read(size) self._rbuf = StringIO.StringIO() self._rbuf.write(buf.read()) return rv self._rbuf = StringIO.StringIO() while True: data = self.recv(self._rbufsize) left = size - buf_len nl = data.find('\n', 0, left) if nl >= 0: nl += 1 self._rbuf.write(data[nl:]) if buf_len: buf.write(data[:nl]) break else: return data[:nl] buf_len n = len(data) if n == size and not buf_len: return data buf.write(data) buf_len += n continue None if n >= left else None if not data else size < 0 return buf.getvalue() else: class CP_fileobject(socket._fileobject): def sendall(self, data): while data: try: bytes_sent = self.send(data) data = data[bytes_sent:] continue except socket.error: e = None if e.args[0] not in socket_errors_nonblocking: raise e.args[0] not in socket_errors_nonblocking continue None<EXCEPTION MATCH>socket.error def send(self, data): return self._sock.send(data) def flush(self): if self._wbuf: buffer = ''.join(self._wbuf) self._wbuf = [] self.sendall(buffer) def recv(self, size): while True: try: return self._sock.recv(size) continue except socket.error: e = None if e.args[0] not in socket_errors_nonblocking and e.args[0] not in socket_error_eintr: raise e.args[0] not in socket_error_eintr continue None<EXCEPTION MATCH>socket.error def read(self, size = -1): if size < 0: buffers = [ self._rbuf] self._rbuf = '' if self._rbufsize <= 1: recv_size = self.default_bufsize else: recv_size = self._rbufsize while True: data = self.recv(recv_size) if not data: break buffers.append(data) return ''.join(buffers) data = self._rbuf buf_len = len(data) if buf_len >= size: self._rbuf = data[size:] return data[:size] buffers = [] self._rbuf = '' while True: left = size - buf_len recv_size = max(self._rbufsize, left) data = self.recv(recv_size) if not data: break buffers.append(data) n = len(data) if n >= left: self._rbuf = data[left:] buffers[-1] = data[:left] break buf_len += n return ''.join(buffers) def readline(self, size = -1): data = self._rbuf if size < 0: if self._rbufsize <= 1: buffers = [] while data != '\n': data = self.recv(1) if not data: break buffers.append(data) return ''.join(buffers) nl = data.find('\n') if nl >= 0: nl += 1 self._rbuf = data[nl:] return data[:nl] buffers = [] self._rbuf = '' while True: data = self.recv(self._rbufsize) if not data: break buffers.append(data) nl = data.find('\n') if nl >= 0: nl += 1 self._rbuf = data[nl:] buffers[-1] = data[:nl] break continue return ''.join(buffers) nl = data.find('\n', 0, size) if nl >= 0: nl += 1 self._rbuf = data[nl:] return data[:nl] buf_len = len(data) if buf_len >= size: self._rbuf = data[size:] return data[:size] buffers = [] self._rbuf = '' while True: data = self.recv(self._rbufsize) if not data: break buffers.append(data) left = size - buf_len nl = data.find('\n', 0, left) if nl >= 0: nl += 1 self._rbuf = data[nl:] buffers[-1] = data[:nl] break n = len(data) if n >= left: self._rbuf = data[left:] buffers[-1] = data[:left] break buf_len += n return ''.join(buffers) class SSL_fileobject(CP_fileobject): ssl_timeout = 3 ssl_retry = 0.01 def _safe_call(self, is_reader, call, *args, **kwargs): start = time.time() while True: try: return call(*args, **kwargs) except SSL.WantReadError: time.sleep(self.ssl_retry) except SSL.WantWriteError: time.sleep(self.ssl_retry) except SSL.SysCallError: e = None if is_reader and e.args == (-1, 'Unexpected EOF'): return '' errnum = e.args[0] if is_reader and errnum in socket_errors_to_ignore: return '' raise socket.error(errnum) except SSL.Error: e.args == (-1, 'Unexpected EOF') e = e.args == (-1, 'Unexpected EOF') if is_reader and e.args == (-1, 'Unexpected EOF'): return '' thirdarg = None try: thirdarg = e.args[0][0][2] except IndexError: e.args == (-1, 'Unexpected EOF') e.args == (-1, 'Unexpected EOF') except: e.args == (-1, 'Unexpected EOF') if thirdarg == 'http request': raise NoSSLError() thirdarg == 'http request' raise FatalSSLAlert(*e.args) except: e.args == (-1, 'Unexpected EOF') raise if time.time() - start > self.ssl_timeout: raise socket.timeout('timed out') time.time() - start > self.ssl_timeout continue e.args == (-1, 'Unexpected EOF') def recv(self, *args, **kwargs): buf = [] r = super(SSL_fileobject, self).recv while True: data = self._safe_call(True, r, *args, **kwargs) buf.append(data) p = self._sock.pending() if not p: return ''.join(buf) continue p def sendall(self, *args, **kwargs): return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs) def send(self, *args, **kwargs): return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs) class HTTPConnection(object): rbufsize = -1 RequestHandlerClass = HTTPRequest environ = { 'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.errors': sys.stderr } def __init__(self, sock, wsgi_app, environ): self.socket = sock self.wsgi_app = wsgi_app self.environ = self.environ.copy() self.environ.update(environ) if SSL and isinstance(sock, SSL.ConnectionType): timeout = sock.gettimeout() self.rfile = SSL_fileobject(sock, 'rb', self.rbufsize) self.rfile.ssl_timeout = timeout self.wfile = SSL_fileobject(sock, 'wb', -1) self.wfile.ssl_timeout = timeout else: self.rfile = CP_fileobject(sock, 'rb', self.rbufsize) self.wfile = CP_fileobject(sock, 'wb', -1) self.environ['wsgi.input'] = SizeCheckWrapper(self.rfile, 0) def communicate(self): try: while True: req = None req = self.RequestHandlerClass(self.wfile, self.environ, self.wsgi_app) req.parse_request() if not req.ready: return None req.respond() if req.close_connection: return None continue req.close_connection except socket.error: e = None errnum = e.args[0] if errnum == 'timed out': if req and not (req.sent_headers): req.simple_response('408 Request Timeout') elif errnum not in socket_errors_to_ignore: if req and not (req.sent_headers): req.simple_response('500 Internal Server Error', format_exc()) return None except (KeyboardInterrupt, SystemExit): raise except FatalSSLAlert: e = None return None except NoSSLError: if req and not (req.sent_headers): req.wfile = CP_fileobject(self.socket._sock, 'wb', -1) req.simple_response('400 Bad Request', 'The client sent a plain HTTP request, but this server only speaks HTTPS on this port.') self.linger = True except Exception: e = None if req and not (req.sent_headers): req.simple_response('500 Internal Server Error', format_exc()) except: not (req.sent_headers) linger = False def close(self): self.rfile.close() if not self.linger: self.socket._sock.close() self.socket.close() def format_exc(limit = None): try: (etype, value, tb) = sys.exc_info() return ''.join(traceback.format_exception(etype, value, tb, limit)) finally: etype = None value = None tb = None _SHUTDOWNREQUEST = None class WorkerThread(threading.Thread): conn = None def __init__(self, server): self.ready = False self.server = server threading.Thread.__init__(self) def run(self): try: self.ready = True while True: conn = self.server.requests.get() if conn is _SHUTDOWNREQUEST: return None self.conn = conn try: conn.communicate() finally: conn.close() self.conn = None continue conn is _SHUTDOWNREQUEST except (KeyboardInterrupt, SystemExit): exc = None self.server.interrupt = exc class ThreadPool(object): def __init__(self, server, min = 10, max = -1): self.server = server self.min = min self.max = max self._threads = [] self._queue = Queue.Queue() self.get = self._queue.get def start(self): for i in xrange(self.min): self._threads.append(WorkerThread(self.server)) for worker in self._threads: worker.setName('CP WSGIServer ' + worker.getName()) worker.start() for worker in self._threads: while not worker.ready: time.sleep(0.1) def _get_idle(self): return [](_[1]) idle = property(_get_idle, doc = _get_idle.__doc__) def put(self, obj): self._queue.put(obj) if obj is _SHUTDOWNREQUEST: return None def grow(self, amount): for i in xrange(amount): if self.max > 0 and len(self._threads) >= self.max: break worker = WorkerThread(self.server) worker.setName('CP WSGIServer ' + worker.getName()) self._threads.append(worker) worker.start() def shrink(self, amount): for t in self._threads: if not t.isAlive(): self._threads.remove(t) amount -= 1 continue if amount > 0: for i in xrange(min(amount, len(self._threads) - self.min)): self._queue.put(_SHUTDOWNREQUEST) def stop(self, timeout = 5): for worker in self._threads: self._queue.put(_SHUTDOWNREQUEST) current = threading.currentThread() while self._threads: worker = self._threads.pop() if worker is not current and worker.isAlive(): try: if timeout is None or timeout < 0: worker.join() else: worker.join(timeout) if worker.isAlive(): c = worker.conn if c and not (c.rfile.closed): if SSL and isinstance(c.socket, SSL.ConnectionType): c.socket.shutdown() else: c.socket.shutdown(socket.SHUT_RD) worker.join() except (AssertionError, KeyboardInterrupt): timeout < 0 exc1 = timeout < 0 except: timeout < 0<EXCEPTION MATCH>(AssertionError, KeyboardInterrupt) timeout < 0<EXCEPTION MATCH>(AssertionError, KeyboardInterrupt) continue timeout < 0 class SSLConnection: def __init__(self, *args): self._ssl_conn = SSL.Connection(*args) self._lock = threading.RLock() for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read', 'renegotiate', 'bind', 'listen', 'connect', 'accept', 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list', 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 'makefile', 'get_app_data', 'set_app_data', 'state_string', 'sock_shutdown', 'get_peer_certificate', 'want_read', 'want_write', 'set_connect_state', 'set_accept_state', 'connect_ex', 'sendall', 'settimeout'): exec 'def %s(self, *args):\n self._lock.acquire()\n try:\n return self._ssl_conn.%s(*args)\n finally:\n self._lock.release()\n' % (f, f) try: import fcntl except ImportError: try: from ctypes import windll, WinError except ImportError: def prevent_socket_inheritance(sock): pass def prevent_socket_inheritance(sock): if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0): raise WinError() windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0) def prevent_socket_inheritance(sock): fd = sock.fileno() old_flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) class CherryPyWSGIServer(object): protocol = 'HTTP/1.1' _bind_addr = '127.0.0.1' version = 'CherryPy/3.1.2' ready = False _interrupt = None nodelay = True ConnectionClass = HTTPConnection environ = { } ssl_certificate = None ssl_private_key = None def __init__(self, bind_addr, wsgi_app, numthreads = 10, server_name = None, max = -1, request_queue_size = 5, timeout = 10, shutdown_timeout = 5): if not numthreads: pass self.requests = ThreadPool(self, min = 1, max = max) if callable(wsgi_app): self.wsgi_app = wsgi_app else: warnings.warn('The ability to pass multiple apps is deprecated and will be removed in 3.2. You should explicitly include a WSGIPathInfoDispatcher instead.', DeprecationWarning) self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app) self.bind_addr = bind_addr if not server_name: server_name = socket.gethostname() self.server_name = server_name self.request_queue_size = request_queue_size self.timeout = timeout self.shutdown_timeout = shutdown_timeout def _get_numthreads(self): return self.requests.min def _set_numthreads(self, value): self.requests.min = value numthreads = property(_get_numthreads, _set_numthreads) def __str__(self): return '%s.%s(%r)' % (self.__module__, self.__class__.__name__, self.bind_addr) def _get_bind_addr(self): return self._bind_addr def _set_bind_addr(self, value): if isinstance(value, tuple) and value[0] in ('', None): 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.") value[0] in ('', None) self._bind_addr = value 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.') def start(self): self._interrupt = None if isinstance(self.bind_addr, basestring): try: os.unlink(self.bind_addr) except: pass try: os.chmod(self.bind_addr, 511) except: pass info = [ (socket.AF_UNIX, socket.SOCK_STREAM, 0, '', self.bind_addr)] else: (host, port) = self.bind_addr try: info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) except socket.gaierror: info = [ (socket.AF_INET, socket.SOCK_STREAM, 0, '', self.bind_addr)] self.socket = None msg = 'No socket could be created' for res in info: (af, socktype, proto, canonname, sa) = res try: self.bind(af, socktype, proto) except socket.error: msg = None if self.socket: self.socket.close() self.socket = None continue if not self.socket: raise socket.error, msg self.socket self.socket.settimeout(1) self.socket.listen(self.request_queue_size) self.requests.start() self.ready = True while self.ready: self.tick() if self.interrupt: while self.interrupt is True: time.sleep(0.1) if self.interrupt: raise self.interrupt self.interrupt continue def bind(self, family, type, proto = 0): self.socket = socket.socket(family, type, proto) prevent_socket_inheritance(self.socket) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.nodelay: self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if self.ssl_certificate and self.ssl_private_key: if SSL is None: raise ImportError('You must install pyOpenSSL to use HTTPS.') SSL is None ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.use_privatekey_file(self.ssl_private_key) ctx.use_certificate_file(self.ssl_certificate) self.socket = SSLConnection(ctx, self.socket) self.populate_ssl_environ() if not isinstance(self.bind_addr, basestring) and self.bind_addr[0] == '::' and family == socket.AF_INET6: try: self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) except (AttributeError, socket.error): pass except: None<EXCEPTION MATCH>(AttributeError, socket.error) None<EXCEPTION MATCH>(AttributeError, socket.error) self.socket.bind(self.bind_addr) def tick(self): try: (s, addr) = self.socket.accept() prevent_socket_inheritance(s) if not self.ready: return None if hasattr(s, 'settimeout'): s.settimeout(self.timeout) environ = self.environ.copy() if environ.get('SERVER_SOFTWARE') is None: environ['SERVER_SOFTWARE'] = '%s WSGI Server' % self.version environ['ACTUAL_SERVER_PROTOCOL'] = self.protocol environ['SERVER_NAME'] = self.server_name if isinstance(self.bind_addr, basestring): environ['SERVER_PORT'] = '' else: environ['SERVER_PORT'] = str(self.bind_addr[1]) environ['REMOTE_ADDR'] = addr[0] environ['REMOTE_PORT'] = str(addr[1]) conn = self.ConnectionClass(s, self.wsgi_app, environ) self.requests.put(conn) except socket.timeout: return None except socket.error: x = None if x.args[0] in socket_error_eintr: return None if x.args[0] in socket_errors_nonblocking: return None if x.args[0] in socket_errors_to_ignore: return None raise except: x.args[0] in socket_errors_to_ignore def _get_interrupt(self): return self._interrupt def _set_interrupt(self, interrupt): self._interrupt = True self.stop() self._interrupt = interrupt interrupt = property(_get_interrupt, _set_interrupt, doc = 'Set this to an Exception instance to interrupt the server.') def stop(self): self.ready = False sock = getattr(self, 'socket', None) if sock: if not isinstance(self.bind_addr, basestring): try: (host, port) = sock.getsockname()[:2] except socket.error: x = None if x.args[0] not in socket_errors_to_ignore: raise x.args[0] not in socket_errors_to_ignore for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): (af, socktype, proto, canonname, sa) = res s = None try: s = socket.socket(af, socktype, proto) s.settimeout(1) s.connect((host, port)) s.close() continue except socket.error: if s: s.close() s if hasattr(sock, 'close'): sock.close() self.socket = None self.requests.stop(self.shutdown_timeout) def populate_ssl_environ(self): cert = open(self.ssl_certificate, 'rb').read() cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) ssl_environ = { 'wsgi.url_scheme': 'https', 'HTTPS': 'on' } ssl_environ.update({ 'SSL_SERVER_M_VERSION': cert.get_version(), 'SSL_SERVER_M_SERIAL': cert.get_serial_number() }) for prefix, dn in [ ('I', cert.get_issuer()), ('S', cert.get_subject())]: dnstr = str(dn)[18:-2] wsgikey = 'SSL_SERVER_%s_DN' % prefix ssl_environ[wsgikey] = dnstr while dnstr: pos = dnstr.rfind('=') dnstr = dnstr[:pos] value = dnstr[pos + 1:] pos = dnstr.rfind('/') dnstr = dnstr[:pos] key = dnstr[pos + 1:] if key and value: wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) ssl_environ[wsgikey] = value continue self.environ.update(ssl_environ)