📄 gtk_cherrypy_wsgiserver.py
字号:
environ["CONTENT_LENGTH"] = cl def decode_chunked(self): """Decode the 'chunked' transfer coding.""" 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## if line: chunk_extension = line[0] 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 # Grab any trailer headers self.read_headers() data.seek(0) self.environ["wsgi.input"] = data self.environ["CONTENT_LENGTH"] = str(cl) or "" return True def respond(self): """Call the appropriate WSGI app and write its iterable output.""" response = self.wsgi_app(self.environ, self.start_response) try: for chunk in response: # "The start_response callable must not actually transmit # the response headers. Instead, it must store them for the # server or gateway to transmit only after the first # iteration of the application return value that yields # a NON-EMPTY string, or upon the application's first # invocation of the write() callable." (PEP 333) if chunk: self.write(chunk) finally: if hasattr(response, "close"): response.close() if (self.ready and not self.sent_headers and not self.connection.server.interrupt): self.sent_headers = True self.send_headers() if self.chunked_write: self.sendall("0\r\n\r\n") def simple_response(self, status, msg=""): """Write a simple response back to the client.""" status = str(status) buf = ["%s %s\r\n" % (self.connection.server.protocol, status), "Content-Length: %s\r\n" % len(msg)] if status[:3] == "413" and self.response_protocol == 'HTTP/1.1': # Request Entity Too Large self.close_connection = True buf.append("Connection: close\r\n") buf.append("\r\n") if msg: buf.append(msg) self.sendall("".join(buf)) def start_response(self, status, headers, exc_info = None): """WSGI callable to begin the HTTP response.""" if self.started_response: if not exc_info: raise AssertionError("WSGI start_response called a second " "time with no exc_info.") else: 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): """WSGI callable to write unbuffered data to the client. This method is also used internally by start_response (to write data from the iterable returned by the WSGI application). """ if not self.started_response: raise AssertionError("WSGI write called before start_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.sendall("".join(buf)) else: self.sendall(chunk) def send_headers(self): """Assert, process, and send the HTTP response message-headers.""" hkeys = [key.lower() for key, value in self.outheaders] status = int(self.status[:3]) if status == 413: # Request Entity Too Large. Close conn to avoid garbage. self.close_connection = True elif "content-length" not in hkeys: # "All 1xx (informational), 204 (no content), # and 304 (not modified) responses MUST NOT # include a message-body." So no point chunking. if status < 200 or status in (204, 205, 304): pass else: if self.response_protocol == 'HTTP/1.1': # Use the chunked transfer-coding self.chunked_write = True self.outheaders.append(("Transfer-Encoding", "chunked")) else: # Closing the conn is the only way to determine len. 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")) else: if not self.close_connection: self.outheaders.append(("Connection", "Keep-Alive")) if "date" not in hkeys: self.outheaders.append(("Date", rfc822.formatdate())) server = self.connection.server if "server" not in hkeys: self.outheaders.append(("Server", server.version)) buf = [server.protocol, " ", self.status, "\r\n"] try: buf += [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.") if not isinstance(v, str): raise TypeError("WSGI response header value %r is not a string.") else: raise buf.append("\r\n") self.sendall("".join(buf))class NoSSLError(Exception): """Exception raised when a client speaks HTTP to an HTTPS socket.""" passdef _ssl_wrap_method(method, is_reader=False): """Wrap the given method with SSL error-trapping. is_reader: if False (the default), EOF errors will be raised. If True, EOF errors will return "" (to emulate normal sockets). """ def ssl_method_wrapper(self, *args, **kwargs):## print (id(self), method, args, kwargs) start = time.time() while True: try: return method(self, *args, **kwargs) except (SSL.WantReadError, SSL.WantWriteError): # Sleep and try again. This is dangerous, because it means # the rest of the stack has no way of differentiating # between a "new handshake" error and "client dropped". # Note this isn't an endless loop: there's a timeout below. time.sleep(self.ssl_retry) except SSL.SysCallError, e: if is_reader and e.args == (-1, 'Unexpected EOF'): return "" errno = e.args[0] if is_reader and errno in socket_errors_to_ignore: return "" raise socket.error(errno) except SSL.Error, e: if is_reader and e.args == (-1, 'Unexpected EOF'): return "" thirdarg = None try: thirdarg = e.args[0][0][2] except IndexError: pass if is_reader and thirdarg == 'ssl handshake failure': return "" if thirdarg == 'http request': # The client is talking HTTP to an HTTPS server. raise NoSSLError() raise if time.time() - start > self.ssl_timeout: raise socket.timeout("timed out") return ssl_method_wrapperclass SSL_fileobject(socket._fileobject): """Faux file object attached to a socket object.""" ssl_timeout = 3 ssl_retry = .01 close = _ssl_wrap_method(socket._fileobject.close) flush = _ssl_wrap_method(socket._fileobject.flush) write = _ssl_wrap_method(socket._fileobject.write) writelines = _ssl_wrap_method(socket._fileobject.writelines) read = _ssl_wrap_method(socket._fileobject.read, is_reader=True) readline = _ssl_wrap_method(socket._fileobject.readline, is_reader=True) readlines = _ssl_wrap_method(socket._fileobject.readlines, is_reader=True)class HTTPConnection(object): """An HTTP connection (active socket). socket: the raw socket object (usually TCP) for this connection. addr: the "bind address" for the remote end of the socket. For IP sockets, this is a tuple of (REMOTE_ADDR, REMOTE_PORT). For UNIX domain sockets, this will be a string. server: the HTTP Server for this Connection. Usually, the server object possesses a passive (server) socket which spawns multiple, active (client) sockets, one for each connection. environ: a WSGI environ template. This will be copied for each request. rfile: a fileobject for reading from the socket. sendall: a function for writing (+ flush) to the socket. """ 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, addr, server): self.socket = sock self.addr = addr self.server = server # Copy the class environ into self. self.environ = self.environ.copy() if SSL and isinstance(sock, SSL.ConnectionType): timeout = sock.gettimeout() self.rfile = SSL_fileobject(sock, "r", self.rbufsize) self.rfile.ssl_timeout = timeout self.sendall = _ssl_wrap_method(sock.sendall) self.environ["wsgi.url_scheme"] = "https" self.environ["HTTPS"] = "on" sslenv = getattr(server, "ssl_environ", None) if sslenv: self.environ.update(sslenv) else: self.rfile = sock.makefile("rb", self.rbufsize) self.sendall = sock.sendall self.environ.update({"wsgi.input": self.rfile, "SERVER_NAME": self.server.server_name, }) if isinstance(self.server.bind_addr, basestring): # AF_UNIX. This isn't really allowed by WSGI, which doesn't # address unix domain sockets. But it's better than nothing. self.environ["SERVER_PORT"] = "" else: self.environ["SERVER_PORT"] = str(self.server.bind_addr[1]) # optional values # Until we do DNS lookups, omit REMOTE_HOST self.environ["REMOTE_ADDR"] = self.addr[0] self.environ["REMOTE_PORT"] = str(self.addr[1]) def communicate(self): """Read each request and respond appropriately.""" try: while True: # (re)set req to None so that if something goes wrong in # the RequestHandlerClass constructor, the error doesn't # get written to the previous request. req = None req = self.RequestHandlerClass(self) # This order of operations should guarantee correct pipelining. req.parse_request() if not req.ready: return req.respond() if req.close_connection: return except socket.error, e: errno = e.args[0] if errno not in socket_errors_to_ignore: if req: req.simple_response("500 Internal Server Error", format_exc()) return except (KeyboardInterrupt, SystemExit): raise except NoSSLError: # Unwrap our sendall req.sendall = self.socket._sock.sendall req.simple_response("400 Bad Request", "The client sent a plain HTTP request, but " "this server only speaks HTTPS on this port.") except: if req: req.simple_response("500 Internal Server Error", format_exc()) def close(self): """Close the socket underlying this connection.""" self.rfile.close() self.socket.close()def format_exc(limit=None): """Like print_exc() but return a string. Backport for Python 2.3.""" try: etype, value, tb = sys.exc_info() return ''.join(traceback.format_exception(etype, value, tb, limit)) finally: etype = value = tb = None_SHUTDOWNREQUEST = Noneclass WorkerThread(threading.Thread): """Thread which continuously polls a Queue for Connection objects. server: the HTTP Server which spawned this thread, and which owns the Queue and is placing active connections into it. ready: a simple flag for the calling server to know when this thread has begun polling the Queue. Due to the timing issues of polling a Queue, a WorkerThread does not check its own 'ready' flag after it has started. To stop the thread, it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue (one for each running WorkerThread). """ def __init__(self, server): self.ready = False self.server = server threading.Thread.__init__(self) def run(self): try: self.ready = True while True:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -