From eba0855c881bea9f533a8d4b359f8711125e5037 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Fri, 29 Jun 2012 07:41:56 +0200 Subject: make scgi.forkpool work with py3k Note that the construction of the header moved from our internal sendheaders function to the start_response function. This way users supplying unicode characters no representable in iso-8859-1 will get a UnicodeEncodeError back from start_response, which is more useful than failing later while yielding bytes. --- wsgitools/scgi/forkpool.py | 71 +++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/wsgitools/scgi/forkpool.py b/wsgitools/scgi/forkpool.py index 7cc6d18..88f64be 100644 --- a/wsgitools/scgi/forkpool.py +++ b/wsgitools/scgi/forkpool.py @@ -12,6 +12,7 @@ import sys import errno import signal +from wsgitools.internal import bytes2str, str2bytes from wsgitools.scgi import _convert_environ, FileWrapper if sys.version_info[0] >= 3: @@ -28,22 +29,22 @@ class SocketFileWrapper: def __init__(self, sock, toread): """@param sock: is a C{socket.socket()}""" self.sock = sock - self.buff = "" + self.buff = b"" self.toread = toread def _recv(self, size=4096): """ internal method for receiving and counting incoming data - @raise socket.error: + @raises socket.error: """ toread = min(size, self.toread) if not toread: - return "" + return b"" try: data = self.sock.recv(toread) except socket.error as why: if why[0] in (errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN): - data = "" + data = b"" else: raise self.toread -= len(data) @@ -63,12 +64,12 @@ class SocketFileWrapper: def read(self, size=None): """ see pep333 - @raise socket.error: + @raises socket.error: """ if size is None: retl = [] data = self.buff - self.buff = "" + self.buff = b"" while True: retl.append(data) try: @@ -77,7 +78,7 @@ class SocketFileWrapper: break if not data: break - return "".join(retl) + return b"".join(retl) datalist = [self.buff] datalen = len(self.buff) while datalen < size: @@ -89,22 +90,22 @@ class SocketFileWrapper: break datalist.append(data) datalen += len(data) - self.buff = "".join(datalist) + self.buff = b"".join(datalist) if size <= len(self.buff): ret, self.buff = self.buff[:size], self.buff[size:] return ret - ret, self.buff = self.buff, "" + ret, self.buff = self.buff, b"" return ret def readline(self, size=None): """ see pep333 - @raise socket.error: + @raises socket.error: """ while True: try: - split = self.buff.index('\n') + 1 + split = self.buff.index(b'\n') + 1 if size is not None and split > size: split = size ret, self.buff = self.buff[:split], self.buff[split:] @@ -119,14 +120,14 @@ class SocketFileWrapper: else: data = self._recv(4096) if not data: - ret, self.buff = self.buff, "" + ret, self.buff = self.buff, b"" return ret self.buff += data def readlines(self): """ see pep333 - @raise socket.error: + @raises socket.error: """ data = self.readline() while data: @@ -138,7 +139,7 @@ class SocketFileWrapper: def __next__(self): """ see pep333 - @raise socket.error: + @raises socket.error: """ data = self.read(4096) if not data: @@ -151,7 +152,7 @@ class SocketFileWrapper: pass def write(self, data): """see pep333""" - assert isinstance(data, str) + assert isinstance(data, bytes) try: self.sock.sendall(data) except socket.error: @@ -261,11 +262,11 @@ class SCGIServer: data = self.workers[s].sock.recv(1) except socket.error: # we cannot handle errors here, so drop the connection. - data = '' - if data == '': + data = b'' + if data == b'': self.workers[s].sock.close() del self.workers[s] - elif data in ('0', '1'): + elif data in (b'0', b'1'): self.workers[s].state = int(data) else: raise RuntimeError("unexpected data from worker") @@ -338,14 +339,14 @@ class SCGIServer: def work(self, worksock): """ internal! serves maxrequests times - @raise socket.error: + @raises socket.error: """ for _ in range(self.maxrequests): (con, addr) = self.server.accept() # we cannot handle socket.errors here. - worksock.sendall('1') # tell server we're working + worksock.sendall(b'1') # tell server we're working self.process(con) - worksock.sendall('0') # tell server we've finished + worksock.sendall(b'0') # tell server we've finished def process(self, con): """ @@ -362,10 +363,10 @@ class SCGIServer: except socket.error: con.close() return - if not ':' in data: + if not b':' in data: con.close() return - length, data = data.split(':', 1) + length, data = data.split(b':', 1) if not length.isdigit(): # clear protocol violation con.close() return @@ -383,35 +384,32 @@ class SCGIServer: data += t # netstrings! - data = data.split('\0') + data = data.split(b'\0') # the byte beyond has to be a ','. # and the number of netstrings excluding the final ',' has to be even - if data.pop() != ',' or len(data) % 2 != 0: + if data.pop() != b',' or len(data) % 2 != 0: con.close() return environ = self.config.copy() while data: - key = data.pop(0) - value = data.pop(0) + key = bytes2str(data.pop(0)) + value = bytes2str(data.pop(0)) environ[key] = value # elements: # 0 -> None: no headers set # 0 -> False: set but unsent # 0 -> True: sent - # 1 -> status string - # 2 -> header list - response_head = [None, None, None] + # 1 -> bytes of the complete header + response_head = [None, None] def sendheaders(): assert response_head[0] is not None # headers set if response_head[0] != True: response_head[0] = True try: - con.sendall('Status: %s\r\n%s\r\n\r\n' % (response_head[1], - '\r\n'.join(map("%s: %s".__mod__, - response_head[2])))) + con.sendall(response_head[1]) except socket.error: pass @@ -429,9 +427,10 @@ class SCGIServer: finally: exc_info = None assert not response_head[0] # unset or not sent + headers = "".join(map("%s: %s\r\n".__mod__, headers)) + full_header = "Status: %s\r\n%s\r\n" % (status, headers) + response_head[1] = str2bytes(full_header) response_head[0] = False # set but nothing sent - response_head[1] = status - response_head[2] = headers return dumbsend if not environ.get("CONTENT_LENGTH", "bad").isdigit(): @@ -455,7 +454,7 @@ class SCGIServer: assert response_head[0] is not None result_iter = iter(result) for data in result_iter: - assert isinstance(data, str) + assert isinstance(data, bytes) dumbsend(data) if response_head[0] != True: sendheaders() -- cgit v1.2.3