diff options
Diffstat (limited to 'wsgitools/scgi/asynchronous.py')
-rw-r--r-- | wsgitools/scgi/asynchronous.py | 81 |
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""" |