Author: chrisz
Date: Fri Mar 16 17:08:06 2007
New Revision: 6322
Modified:
Webware/trunk/WebKit/ASStreamOut.py
Webware/trunk/WebKit/Application.py
Webware/trunk/WebKit/ThreadedAppServer.py
Log:
Fixed bug regarding aborted connections (as reported by Stephan Diehl on Webware-discuss).
Modified: Webware/trunk/WebKit/ASStreamOut.py
==============================================================================
--- Webware/trunk/WebKit/ASStreamOut.py (original)
+++ Webware/trunk/WebKit/ASStreamOut.py Fri Mar 16 17:08:06 2007
@@ -8,11 +8,15 @@
True, False = 1, 0
bool = lambda x: x and True or False
-debug = 0
+debug = False
class InvalidCommandSequence(exceptions.Exception):
pass
+class ConnectionAbortedError(Exception):
+ pass
+
+
class ASStreamOut:
"""This is a response stream to the client.
@@ -39,8 +43,8 @@
self._committed = False
self._needCommit = False
self._chunks = []
- self._buffer=''
- self._chunkLen= 0
+ self._buffer = ''
+ self._chunkLen = 0
self._closed = False
def autoCommit(self):
@@ -68,7 +72,8 @@
if the buffer is full enough).
"""
- assert not self._closed, "Trying to flush when already closed"
+ if self._closed:
+ raise ConnectionAbortedError
if debug:
print ">>> Flushing ASStreamOut"
if not self._committed:
@@ -180,7 +185,8 @@
"""Write a string to the buffer."""
if debug:
print ">>> ASStreamOut writing %s characters" % len(charstr)
- assert not self._closed, "Stream Already Closed"
+ if self._closed:
+ raise ConnectionAbortedError
self._chunks.append(charstr)
self._chunkLen += len(charstr)
if self._autoCommit and self._chunkLen > self._bufferSize:
Modified: Webware/trunk/WebKit/Application.py
==============================================================================
--- Webware/trunk/WebKit/Application.py (original)
+++ Webware/trunk/WebKit/Application.py Fri Mar 16 17:08:06 2007
@@ -10,10 +10,11 @@
from HTTPRequest import HTTPRequest
from Transaction import Transaction
from Session import Session
+from ASStreamOut import ConnectionAbortedError
import URLParser
import HTTPExceptions
-debug = 0
+debug = False
class EndResponse(Exception):
@@ -478,13 +479,18 @@
trans.setResponse(response)
try:
self.runTransaction(trans)
+ except ConnectionAbortedError:
+ trans.setErrorOccurred(True)
except:
- trans.setErrorOccurred(1)
+ trans.setErrorOccurred(True)
if self.setting('EnterDebuggerOnException') and sys.stdin.isatty():
import pdb
pdb.post_mortem(sys.exc_info()[2])
self.handleExceptionInTransaction(sys.exc_info(), trans)
- trans.response().deliver()
+ try:
+ trans.response().deliver()
+ except ConnectionAbortedError:
+ trans.setErrorOccurred(True)
response.clearTransaction()
if self.setting('LogActivity'):
self.writeActivityLog(trans)
Modified: Webware/trunk/WebKit/ThreadedAppServer.py
==============================================================================
--- Webware/trunk/WebKit/ThreadedAppServer.py (original)
+++ Webware/trunk/WebKit/ThreadedAppServer.py Fri Mar 16 17:08:06 2007
@@ -21,17 +21,18 @@
"""
+from marshal import dumps, loads
+import os, sys, threading, Queue, select, socket, time, errno, traceback
+
from Common import *
import AppServer as AppServerModule
from AutoReloadingAppServer import AutoReloadingAppServer as AppServer
+from ASStreamOut import ASStreamOut, ConnectionAbortedError
+
from MiscUtils.Funcs import timestamp
from WebUtils import Funcs
-from marshal import dumps, loads
-import os, sys, threading, Queue, select, socket, time, errno, traceback
-
-
-debug = 0
+debug = False
DefaultConfig = {
'Host': '127.0.0.1',
@@ -441,7 +442,7 @@
finally:
self.delThread()
if debug:
- print threading.currentThread(), "Quitting."
+ print threading.currentThread(), "Quitting."
def initThread(self):
"""Initialize thread.
@@ -715,7 +716,13 @@
self._server.shutDown()
-from WebKit.ASStreamOut import ASStreamOut
+silent_errnos = [] # silently ignore these errors:
+for e in 'EPIPE', 'ECONNABORTED', 'ECONNRESET':
+ try:
+ silent_errnos.append(getattr(errno, e))
+ except AttributeError:
+ pass
+
class TASASStreamOut(ASStreamOut):
"""Response stream for ThreadedAppServer.
@@ -747,22 +754,19 @@
"""
result = ASStreamOut.flush(self)
- if result: # a True return value means we can send
+ if result: # a true return value means we can send
reslen = len(self._buffer)
sent = 0
+ bufferSize = self._bufferSize
while sent < reslen:
try:
sent += self._socket.send(
- self._buffer[sent:sent+8192])
+ self._buffer[sent:sent+bufferSize])
except socket.error, e:
- if e[0] == errno.EPIPE: # broken pipe
- pass
- elif hasattr(errno, 'ECONNRESET') \
- and e[0] == errno.ECONNRESET:
- pass
- else:
- print "StreamOut Error: ", e
- break
+ if debug or e[0] not in silent_errnos:
+ print "StreamOut Error:", e
+ self._closed = True
+ raise ConnectionAbortedError
self.pop(sent)
@@ -808,9 +812,14 @@
requestDict['input'] = self.makeInput()
requestDict['requestID'] = self._requestID
+
streamOut = TASASStreamOut(self._sock)
transaction = self._server._app.dispatchRawRequest(requestDict, streamOut)
- streamOut.close()
+ try:
+ streamOut.close()
+ aborted = False
+ except ConnectionAbortedError:
+ aborted = True
try:
self._sock.shutdown(1)
@@ -821,12 +830,13 @@
if verbose:
duration = ('%0.2f secs' % (time.time() - self._startTime)).ljust(19)
sys.stdout.write('%5i %s %s\n\n' % (self._requestID,
- duration, requestURI))
+ duration, aborted and '*connection aborted*' or requestURI))
transaction._application = None
transaction.die()
del transaction
+
def makeInput(self):
"""Create a file-like object from the socket."""
return self._sock.makefile("rb", 8012)
|