rendered paste body=== modified file 'twisted/web2/channel/http.py'
--- twisted/web2/channel/http.py 2007-01-01 23:41:33 +0000
+++ twisted/web2/channel/http.py 2008-03-19 16:36:16 +0000
@@ -10,7 +10,7 @@
from twisted.web2 import http_headers
from twisted.web2 import http
-PERSIST_NO_PIPELINE, PERSIST_PIPELINE = (1,2)
+PERSIST_NO_PIPELINE, PERSIST_PIPELINE = (False, True)
_cachedHostNames = {}
def _cachedGetHostByAddr(hostaddr):
=== modified file 'twisted/web2/client/http.py'
--- twisted/web2/client/http.py 2007-10-19 08:16:52 +0000
+++ twisted/web2/client/http.py 2008-03-19 16:36:16 +0000
@@ -9,6 +9,7 @@
from zope.interface import implements
from twisted.internet.defer import Deferred
+from twisted.internet import error
from twisted.protocols.basic import LineReceiver
from twisted.protocols.policies import TimeoutMixin
@@ -152,8 +153,8 @@
the C{responseDefer} if no response has been sent yet, or close the
stream.
"""
- self.abortParse()
if hasattr(self, 'stream') and self.stream is not None:
+ self.abortParse()
self.stream.finish(err)
else:
self.responseDefer.errback(err)
@@ -314,7 +315,7 @@
self.manager.clientBusy(self)
if closeAfter:
- self.readPersistent = False
+ self.readPersistent = PERSIST_NO_PIPELINE
self.outRequest = chanRequest = HTTPClientChannelRequest(self,
request, closeAfter)
@@ -329,7 +330,7 @@
self.outRequest = None
# Tell the manager if more requests can be submitted.
self.setTimeout(self.inputTimeOut)
- if self.readPersistent is PERSIST_PIPELINE:
+ if self.readPersistent == PERSIST_PIPELINE:
self.manager.clientPipelining(self)
def requestReadFinished(self, request):
@@ -339,7 +340,7 @@
self.firstLine = True
if not self.inRequests:
- if self.readPersistent:
+ if self.readPersistent == PERSIST_PIPELINE:
self.setTimeout(None)
self.manager.clientIdle(self)
else:
@@ -347,14 +348,14 @@
def setReadPersistent(self, persist):
self.readPersistent = persist
- if not persist:
+ if persist == PERSIST_NO_PIPELINE:
# Tell all requests but first to abort.
for request in self.inRequests[1:]:
- request.connectionLost(None)
+ request.connectionLost(error.ConnectionDone())
del self.inRequests[1:]
def connectionLost(self, reason):
- self.readPersistent = False
+ self.readPersistent = PERSIST_NO_PIPELINE
self.setTimeout(None)
self.manager.clientGone(self)
# Tell all requests to abort.
=== modified file 'twisted/web2/test/test_client.py'
--- twisted/web2/test/test_client.py 2007-10-19 08:16:52 +0000
+++ twisted/web2/test/test_client.py 2008-03-19 16:29:47 +0000
@@ -5,7 +5,7 @@
Tests for HTTP client.
"""
-from twisted.internet import protocol, defer
+from twisted.internet import protocol, defer, error
from twisted.web2.client import http
from twisted.web2 import http_headers
@@ -13,7 +13,6 @@
from twisted.web2.test.test_http import LoopbackRelay, HTTPTests, TestConnection
-
class TestServer(protocol.Protocol):
data = ""
done = False
@@ -61,8 +60,10 @@
def assertReceived(self, cxn, expectedStatus, expectedHeaders,
expectedContent=None):
self.iterate(cxn)
-
- headers, content = cxn.server.data.split('\r\n\r\n', 1)
+
+ data = cxn.server.data
+ cxn.server.data = ''
+ headers, content = data.split('\r\n\r\n', 1)
status, headers = headers.split('\r\n', 1)
headers = headers.split('\r\n')
@@ -74,6 +75,7 @@
for x in headers:
self.assertIn(x, expectedHeaders)
+
if not expectedContent:
expectedContent = ''
@@ -152,7 +154,6 @@
d = cxn.client.submitRequest(req).addCallback(gotResp)
-
self.assertReceived(cxn, 'GET / HTTP/1.1',
['Connection: close'])
@@ -188,6 +189,60 @@
return d
+ def test_abortPipelining(self):
+ """
+ Ensure that pending requests are aborted if the server closes the
+ connection.
+ """
+ cxn = self.connect(inputTimeOut=None)
+ deferreds = []
+
+ req1 = http.ClientRequest('GET', '/', None, None)
+ d1 = cxn.client.submitRequest(req1, closeAfter=False).addCallback(
+ self.checkResponse, 200, [], 10, None)
+ deferreds.append(d1)
+
+ self.assertReceived(cxn, 'GET / HTTP/1.1',
+ ['Connection: Keep-Alive'])
+
+ self.writeLines(cxn, ('HTTP/1.1 200 OK',
+ 'Content-Length: 10',
+ 'Connection: Keep-Alive',
+ '',
+ '1234567890'))
+
+ # pipeline two requests, after the first one the server closes the
+ # connection
+ req2 = http.ClientRequest('GET', '/', None, None)
+ d2 = cxn.client.submitRequest(req2, closeAfter=False).addCallback(
+ self.checkResponse, 200, [], 0, None)
+ deferreds.append(d2)
+
+ self.assertReceived(cxn, 'GET / HTTP/1.1',
+ ['Connection: Keep-Alive'])
+
+ def requestEb(failure):
+ failure.trap(error.ConnectionDone)
+
+ def requestCb(result):
+ self.fail('this should not be reached')
+
+ req3 = http.ClientRequest('GET', '/', None, None)
+ d3 = cxn.client.submitRequest(req3, closeAfter=False)
+ d3.addCallbacks(requestCb, requestEb)
+ deferreds.append(d3)
+
+ self.assertReceived(cxn, 'GET / HTTP/1.1',
+ ['Connection: Keep-Alive'])
+
+ # close the connection, the client should cancel req3
+ self.writeLines(cxn, ('HTTP/1.1 200 OK',
+ 'Content-Length: 0',
+ 'Connection: close',
+ '\r\n'))
+
+ return defer.DeferredList(deferreds)
+
def test_userHeaders(self):
"""
Make sure that headers get through in both directions.