diff options
author | Helmut Grohne <helmut@subdivi.de> | 2015-04-18 20:38:13 +0200 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2015-04-18 20:38:13 +0200 |
commit | 828d8f22ee3e785692d019ed02c4f7874792a8e9 (patch) | |
tree | b14886c4cca174d993ee7e882ea4f4ba3eabb55e /wsgitools/scgi | |
parent | 42f5ccf09799d3e475eb996b023a0daabae49cdb (diff) | |
parent | c1ba0c783fc59dc8d00b9b8aed7250569bcc14d4 (diff) | |
download | wsgitools-828d8f22ee3e785692d019ed02c4f7874792a8e9.tar.gz |
Merge branch py3k
Diffstat (limited to 'wsgitools/scgi')
-rw-r--r-- | wsgitools/scgi/__init__.py | 4 | ||||
-rw-r--r-- | wsgitools/scgi/asynchronous.py | 50 | ||||
-rw-r--r-- | wsgitools/scgi/forkpool.py | 90 |
3 files changed, 74 insertions, 70 deletions
diff --git a/wsgitools/scgi/__init__.py b/wsgitools/scgi/__init__.py index 898fd61..f651264 100644 --- a/wsgitools/scgi/__init__.py +++ b/wsgitools/scgi/__init__.py @@ -45,13 +45,15 @@ class FileWrapper(object): def __iter__(self): return self - def next(self): + def __next__(self): assert self.offset <= 0 self.offset = -1 data = self.filelike.read(self.blksize) if data: return data raise StopIteration + def next(self): + return self.__next__() def _convert_environ(environ, multithread=False, multiprocess=False, run_once=False): diff --git a/wsgitools/scgi/asynchronous.py b/wsgitools/scgi/asynchronous.py index 386e1d0..51c1d55 100644 --- a/wsgitools/scgi/asynchronous.py +++ b/wsgitools/scgi/asynchronous.py @@ -1,16 +1,12 @@ __all__ = [] import asyncore +import io import socket import sys -# Cannot use io module as it is broken in 2.6. -# Writing a str to a io.StringIO results in an exception. -try: - import cStringIO as io -except ImportError: - import StringIO as io import errno +from wsgitools.internal import bytes2str, str2bytes from wsgitools.scgi import _convert_environ, FileWrapper if sys.version_info[0] >= 3: @@ -42,20 +38,21 @@ class SCGIConnection(asyncore.dispatcher): self.state = SCGIConnection.NEW # internal state self.environ = config.copy() # environment passed to wsgi app self.reqlen = -1 # request length used in two different meanings - self.inbuff = "" # input buffer - self.outbuff = "" # output buffer + self.inbuff = b"" # input buffer + self.outbuff = b"" # output buffer self.wsgihandler = None # wsgi application self.wsgiiterator = None # wsgi application iterator self.outheaders = () # headers to be sent # () -> unset, (..,..) -> set, True -> sent - self.body = io.StringIO() # request body + self.body = io.BytesIO() # request body def _try_send_headers(self): if self.outheaders != True: assert not self.outbuff status, headers = self.outheaders headdata = "".join(map("%s: %s\r\n".__mod__, headers)) - self.outbuff = "Status: %s\r\n%s\r\n" % (status, headdata) + headdata = "Status: %s\r\n%s\r\n" % (status, headdata) + self.outbuff = str2bytes(headdata) self.outheaders = True def _wsgi_write(self, data): @@ -79,12 +76,13 @@ class SCGIConnection(asyncore.dispatcher): data = self.recv(self.blocksize) self.inbuff += data if self.state == SCGIConnection.NEW: - if ':' in self.inbuff: - reqlen, self.inbuff = self.inbuff.split(':', 1) - if not reqlen.isdigit(): + if b':' in self.inbuff: + reqlen, self.inbuff = self.inbuff.split(b':', 1) + try: + reqlen = int(reqlen) + except ValueError: # invalid request format self.close() - return # invalid request format - reqlen = int(reqlen) + return if reqlen > self.maxrequestsize: self.close() return # request too long @@ -98,20 +96,21 @@ class SCGIConnection(asyncore.dispatcher): buff = self.inbuff[:self.reqlen] remainder = self.inbuff[self.reqlen:] - while buff.count('\0') >= 2: - key, value, buff = buff.split('\0', 2) - self.environ[key] = value + while buff.count(b'\0') >= 2: + key, value, buff = buff.split(b'\0', 2) + self.environ[bytes2str(key)] = bytes2str(value) self.reqlen -= len(key) + len(value) + 2 self.inbuff = buff + remainder if self.reqlen == 0: - if self.inbuff.startswith(','): + if self.inbuff.startswith(b','): self.inbuff = self.inbuff[1:] - if not self.environ.get("CONTENT_LENGTH", "bad").isdigit(): + try: + self.reqlen = int(self.environ["CONTENT_LENGTH"]) + except ValueError: self.close() return - self.reqlen = int(self.environ["CONTENT_LENGTH"]) if self.reqlen > self.maxpostsize: self.close() return @@ -124,7 +123,7 @@ class SCGIConnection(asyncore.dispatcher): if len(self.inbuff) >= self.reqlen: self.body.write(self.inbuff[:self.reqlen]) self.body.seek(0) - self.inbuff = "" + self.inbuff = b"" self.reqlen = 0 _convert_environ(self.environ) self.environ["wsgi.input"] = self.body @@ -141,7 +140,7 @@ class SCGIConnection(asyncore.dispatcher): else: self.body.write(self.inbuff) self.reqlen -= len(self.inbuff) - self.inbuff = "" + self.inbuff = b"" def start_response(self, status, headers, exc_info=None): assert isinstance(status, str) @@ -170,7 +169,7 @@ class SCGIConnection(asyncore.dispatcher): if len(self.outbuff) < self.blocksize: self._try_send_headers() for data in self.wsgiiterator: - assert isinstance(data, str) + assert isinstance(data, bytes) if data: self.outbuff += data break @@ -262,7 +261,7 @@ class SCGIServer(asyncore.dispatcher): """asyncore interface""" try: ret = self.accept() - except socket.error, err: + except socket.error as err: # See http://bugs.python.org/issue6706 if err.args[0] not in (errno.ECONNABORTED, errno.EAGAIN): raise @@ -275,4 +274,3 @@ class SCGIServer(asyncore.dispatcher): """Runs the server. It will not return and you can invoke C{asyncore.loop()} instead achieving the same effect.""" asyncore.loop() - diff --git a/wsgitools/scgi/forkpool.py b/wsgitools/scgi/forkpool.py index a49d1ec..7df1575 100644 --- a/wsgitools/scgi/forkpool.py +++ b/wsgitools/scgi/forkpool.py @@ -16,6 +16,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: @@ -32,22 +33,22 @@ class SocketFileWrapper(object): 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, why: + except socket.error as why: if why[0] in (errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN): - data = "" + data = b"" else: raise self.toread -= len(data) @@ -67,12 +68,12 @@ class SocketFileWrapper(object): 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: @@ -81,7 +82,7 @@ class SocketFileWrapper(object): break if not data: break - return "".join(retl) + return b"".join(retl) datalist = [self.buff] datalen = len(self.buff) while datalen < size: @@ -93,22 +94,22 @@ class SocketFileWrapper(object): 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:] @@ -123,14 +124,14 @@ class SocketFileWrapper(object): 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: @@ -139,21 +140,23 @@ class SocketFileWrapper(object): def __iter__(self): """see pep333""" return self - def next(self): + def __next__(self): """ see pep333 - @raise socket.error: + @raises socket.error: """ data = self.read(4096) if not data: raise StopIteration return data + def next(self): + return self.__next__() def flush(self): """see pep333""" pass def write(self, data): """see pep333""" - assert isinstance(data, str) + assert isinstance(data, bytes) try: self.sock.sendall(data) except socket.error: @@ -268,7 +271,7 @@ class SCGIServer(object): self.spawnworker() try: rs, _, _ = select.select(self.workers.keys(), [], []) - except select.error, e: + except select.error as e: if e[0] != errno.EINTR: raise rs = [] @@ -277,11 +280,11 @@ class SCGIServer(object): 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") @@ -368,18 +371,18 @@ class SCGIServer(object): 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 if self.timelimit: signal.alarm(self.timelimit) self.process(con) if self.timelimit: signal.alarm(0) - worksock.sendall('0') # tell server we've finished + worksock.sendall(b'0') # tell server we've finished if self.cpulimit: break @@ -398,14 +401,15 @@ class SCGIServer(object): except socket.error: con.close() return - if not ':' in data: + if not b':' in data: con.close() return - length, data = data.split(':', 1) - if not length.isdigit(): # clear protocol violation + length, data = data.split(b':', 1) + try: + length = int(length) + except ValueError: # clear protocol violation con.close() return - length = int(length) while len(data) != length + 1: # read one byte beyond try: @@ -419,35 +423,32 @@ class SCGIServer(object): 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 @@ -465,17 +466,20 @@ class SCGIServer(object): 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(): + try: + content_length = int(environ["CONTENT_LENGTH"]) + except ValueError: con.close() return _convert_environ(environ, multiprocess=True) - sfw = SocketFileWrapper(con, int(environ["CONTENT_LENGTH"])) + sfw = SocketFileWrapper(con, content_length) environ["wsgi.input"] = sfw result = self.wsgiapp(environ, start_response) @@ -490,7 +494,7 @@ class SCGIServer(object): result_iter = iter(result) for data in result_iter: assert response_head[0] is not None - assert isinstance(data, str) + assert isinstance(data, bytes) dumbsend(data) if response_head[0] != True: sendheaders() |