summaryrefslogtreecommitdiff
path: root/wsgitools/scgi/asynchronous.py
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2012-03-17 22:14:47 +0100
committerHelmut Grohne <helmut@subdivi.de>2012-03-17 22:14:47 +0100
commit863e29c4d2ed76ddf35a0bc3b66abe7423125362 (patch)
treee3c5008f31574a5e84b159b660f38e17d53dfa1e /wsgitools/scgi/asynchronous.py
parent31aef51e3badfd2fdafdc6c15b3a58fcb04607fd (diff)
downloadwsgitools-863e29c4d2ed76ddf35a0bc3b66abe7423125362.tar.gz
sendfile support
When a sendfile library is available, expose it via wsgi.file_wrapper. This support spans both asynchronous and forkpool.
Diffstat (limited to 'wsgitools/scgi/asynchronous.py')
-rw-r--r--wsgitools/scgi/asynchronous.py81
1 files changed, 54 insertions, 27 deletions
diff --git a/wsgitools/scgi/asynchronous.py b/wsgitools/scgi/asynchronous.py
index 4ecb00a..386e1d0 100644
--- a/wsgitools/scgi/asynchronous.py
+++ b/wsgitools/scgi/asynchronous.py
@@ -11,7 +11,7 @@ except ImportError:
import StringIO as io
import errno
-from wsgitools.scgi import _convert_environ
+from wsgitools.scgi import _convert_environ, FileWrapper
if sys.version_info[0] >= 3:
def exc_info_for_raise(exc_info):
@@ -25,8 +25,11 @@ class SCGIConnection(asyncore.dispatcher):
# connection states
NEW = 0*4 | 1 # connection established, waiting for request
HEADER = 1*4 | 1 # the request length was received, waiting for the rest
- BODY = 2*4 | 1 # the request header was received, waiting for the body
- REQ = 3*4 | 2 # request received, sending response
+ BODY = 2*4 | 1 # the request header was received, waiting for the body,
+ # to RESP or RESPH
+ RESP = 3*4 | 2 # sending response, end state
+ RESPH = 4*4 | 2 # buffered response headers, sending headers only, to TRANS
+ TRANS = 5*4 | 2 # transferring using FileWrapper, end state
def __init__(self, server, connection, addr, maxrequestsize=65536,
maxpostsize=8<<20, blocksize=4096, config={}):
asyncore.dispatcher.__init__(self, connection)
@@ -41,7 +44,8 @@ class SCGIConnection(asyncore.dispatcher):
self.reqlen = -1 # request length used in two different meanings
self.inbuff = "" # input buffer
self.outbuff = "" # output buffer
- self.wsgihandler = None # wsgi application iterator
+ 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
@@ -55,7 +59,8 @@ class SCGIConnection(asyncore.dispatcher):
self.outheaders = True
def _wsgi_write(self, data):
- assert self.state >= SCGIConnection.REQ
+ assert self.state >= SCGIConnection.RESP
+ assert self.state < SCGIConnection.TRANS
assert isinstance(data, str)
if data:
self._try_send_headers()
@@ -124,9 +129,15 @@ class SCGIConnection(asyncore.dispatcher):
_convert_environ(self.environ)
self.environ["wsgi.input"] = self.body
self.environ["wsgi.errors"] = self.server.error
- self.wsgihandler = iter(self.server.wsgiapp(
- self.environ, self.start_response))
- self.state = SCGIConnection.REQ
+ self.wsgihandler = self.server.wsgiapp(self.environ,
+ self.start_response)
+ if isinstance(self.wsgihandler, FileWrapper) and \
+ self.wsgihandler.can_transfer():
+ self._try_send_headers()
+ self.state = SCGIConnection.RESPH
+ else:
+ self.wsgiiterator = iter(self.wsgihandler)
+ self.state = SCGIConnection.RESP
else:
self.body.write(self.inbuff)
self.reqlen -= len(self.inbuff)
@@ -145,29 +156,45 @@ class SCGIConnection(asyncore.dispatcher):
self.outheaders = (status, headers)
return self._wsgi_write
- def handle_write(self):
- """C{asyncore} interface"""
- assert self.state >= SCGIConnection.REQ
- if len(self.outbuff) < self.blocksize:
- self._try_send_headers()
- for data in self.wsgihandler:
- assert isinstance(data, str)
- if data:
- self.outbuff += data
- break
- if len(self.outbuff) == 0:
- if hasattr(self.wsgihandler, "close"):
- self.wsgihandler.close()
- self.close()
- return
+ def send_buff(self):
try:
sentbytes = self.send(self.outbuff[:self.blocksize])
except socket.error:
- if hasattr(self.wsgihandler, "close"):
- self.wsgihandler.close()
self.close()
- return
- self.outbuff = self.outbuff[sentbytes:]
+ else:
+ self.outbuff = self.outbuff[sentbytes:]
+
+ def handle_write(self):
+ """C{asyncore} interface"""
+ if self.state == SCGIConnection.RESP:
+ if len(self.outbuff) < self.blocksize:
+ self._try_send_headers()
+ for data in self.wsgiiterator:
+ assert isinstance(data, str)
+ if data:
+ self.outbuff += data
+ break
+ if len(self.outbuff) == 0:
+ self.close()
+ return
+ self.send_buff()
+ elif self.state == SCGIConnection.RESPH:
+ assert len(self.outbuff) > 0
+ self.send_buff()
+ if not self.outbuff:
+ self.state = SCGIConnection.TRANS
+ else:
+ assert self.state == SCGIConnection.TRANS
+ assert self.wsgihandler.can_transfer()
+ sent = self.wsgihandler.transfer(self.socket, self.blocksize)
+ if sent <= 0:
+ self.close()
+
+ def close(self):
+ # None doesn't have a close attribute
+ if hasattr(self.wsgihandler, "close"):
+ self.wsgihandler.close()
+ asyncore.dispatcher.close(self)
def handle_close(self):
"""C{asyncore} interface"""