summaryrefslogtreecommitdiff
path: root/wsgitools/scgi
diff options
context:
space:
mode:
Diffstat (limited to 'wsgitools/scgi')
-rw-r--r--wsgitools/scgi/__init__.py4
-rw-r--r--wsgitools/scgi/asynchronous.py50
-rw-r--r--wsgitools/scgi/forkpool.py90
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()