aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_newclient.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_newclient.py')
-rwxr-xr-xlib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_newclient.py2521
1 files changed, 0 insertions, 2521 deletions
diff --git a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_newclient.py b/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_newclient.py
deleted file mode 100755
index 516d0aa4..00000000
--- a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_newclient.py
+++ /dev/null
@@ -1,2521 +0,0 @@
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-Tests for L{twisted.web._newclient}.
-"""
-
-__metaclass__ = type
-
-from zope.interface import implements
-from zope.interface.verify import verifyObject
-
-from twisted.python import log
-from twisted.python.failure import Failure
-from twisted.internet.interfaces import IConsumer, IPushProducer
-from twisted.internet.error import ConnectionDone, ConnectionLost
-from twisted.internet.defer import Deferred, succeed, fail
-from twisted.internet.protocol import Protocol
-from twisted.trial.unittest import TestCase
-from twisted.test.proto_helpers import StringTransport, AccumulatingProtocol
-from twisted.web._newclient import UNKNOWN_LENGTH, STATUS, HEADER, BODY, DONE
-from twisted.web._newclient import Request, Response, HTTPParser, HTTPClientParser
-from twisted.web._newclient import BadResponseVersion, ParseError, HTTP11ClientProtocol
-from twisted.web._newclient import ChunkedEncoder, RequestGenerationFailed
-from twisted.web._newclient import RequestTransmissionFailed, ResponseFailed
-from twisted.web._newclient import WrongBodyLength, RequestNotSent
-from twisted.web._newclient import ConnectionAborted, ResponseNeverReceived
-from twisted.web._newclient import BadHeaders, ResponseDone, PotentialDataLoss, ExcessWrite
-from twisted.web._newclient import TransportProxyProducer, LengthEnforcingConsumer, makeStatefulDispatcher
-from twisted.web.http_headers import Headers
-from twisted.web.http import _DataLoss
-from twisted.web.iweb import IBodyProducer, IResponse
-
-
-
-class ArbitraryException(Exception):
- """
- A unique, arbitrary exception type which L{twisted.web._newclient} knows
- nothing about.
- """
-
-
-class AnotherArbitraryException(Exception):
- """
- Similar to L{ArbitraryException} but with a different identity.
- """
-
-
-# A re-usable Headers instance for tests which don't really care what headers
-# they're sending.
-_boringHeaders = Headers({'host': ['example.com']})
-
-
-def assertWrapperExceptionTypes(self, deferred, mainType, reasonTypes):
- """
- Assert that the given L{Deferred} fails with the exception given by
- C{mainType} and that the exceptions wrapped by the instance of C{mainType}
- it fails with match the list of exception types given by C{reasonTypes}.
-
- This is a helper for testing failures of exceptions which subclass
- L{_newclient._WrapperException}.
-
- @param self: A L{TestCase} instance which will be used to make the
- assertions.
-
- @param deferred: The L{Deferred} which is expected to fail with
- C{mainType}.
-
- @param mainType: A L{_newclient._WrapperException} subclass which will be
- trapped on C{deferred}.
-
- @param reasonTypes: A sequence of exception types which will be trapped on
- the resulting L{mainType} exception instance's C{reasons} sequence.
-
- @return: A L{Deferred} which fires with the C{mainType} instance
- C{deferred} fails with, or which fails somehow.
- """
- def cbFailed(err):
- for reason, type in zip(err.reasons, reasonTypes):
- reason.trap(type)
- self.assertEqual(len(err.reasons), len(reasonTypes),
- "len(%s) != len(%s)" % (err.reasons, reasonTypes))
- return err
- d = self.assertFailure(deferred, mainType)
- d.addCallback(cbFailed)
- return d
-
-
-
-def assertResponseFailed(self, deferred, reasonTypes):
- """
- A simple helper to invoke L{assertWrapperExceptionTypes} with a C{mainType}
- of L{ResponseFailed}.
- """
- return assertWrapperExceptionTypes(self, deferred, ResponseFailed, reasonTypes)
-
-
-
-def assertRequestGenerationFailed(self, deferred, reasonTypes):
- """
- A simple helper to invoke L{assertWrapperExceptionTypes} with a C{mainType}
- of L{RequestGenerationFailed}.
- """
- return assertWrapperExceptionTypes(self, deferred, RequestGenerationFailed, reasonTypes)
-
-
-
-def assertRequestTransmissionFailed(self, deferred, reasonTypes):
- """
- A simple helper to invoke L{assertWrapperExceptionTypes} with a C{mainType}
- of L{RequestTransmissionFailed}.
- """
- return assertWrapperExceptionTypes(self, deferred, RequestTransmissionFailed, reasonTypes)
-
-
-
-def justTransportResponse(transport):
- """
- Helper function for creating a Response which uses the given transport.
- All of the other parameters to L{Response.__init__} are filled with
- arbitrary values. Only use this method if you don't care about any of
- them.
- """
- return Response(('HTTP', 1, 1), 200, 'OK', _boringHeaders, transport)
-
-
-class MakeStatefulDispatcherTests(TestCase):
- """
- Tests for L{makeStatefulDispatcher}.
- """
- def test_functionCalledByState(self):
- """
- A method defined with L{makeStatefulDispatcher} invokes a second
- method based on the current state of the object.
- """
- class Foo:
- _state = 'A'
-
- def bar(self):
- pass
- bar = makeStatefulDispatcher('quux', bar)
-
- def _quux_A(self):
- return 'a'
-
- def _quux_B(self):
- return 'b'
-
- stateful = Foo()
- self.assertEqual(stateful.bar(), 'a')
- stateful._state = 'B'
- self.assertEqual(stateful.bar(), 'b')
- stateful._state = 'C'
- self.assertRaises(RuntimeError, stateful.bar)
-
-
-
-class _HTTPParserTests(object):
- """
- Base test class for L{HTTPParser} which is responsible for the bulk of
- the task of parsing HTTP bytes.
- """
- sep = None
-
- def test_statusCallback(self):
- """
- L{HTTPParser} calls its C{statusReceived} method when it receives a
- status line.
- """
- status = []
- protocol = HTTPParser()
- protocol.statusReceived = status.append
- protocol.makeConnection(StringTransport())
- self.assertEqual(protocol.state, STATUS)
- protocol.dataReceived('HTTP/1.1 200 OK' + self.sep)
- self.assertEqual(status, ['HTTP/1.1 200 OK'])
- self.assertEqual(protocol.state, HEADER)
-
-
- def _headerTestSetup(self):
- header = {}
- protocol = HTTPParser()
- protocol.headerReceived = header.__setitem__
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK' + self.sep)
- return header, protocol
-
-
- def test_headerCallback(self):
- """
- L{HTTPParser} calls its C{headerReceived} method when it receives a
- header.
- """
- header, protocol = self._headerTestSetup()
- protocol.dataReceived('X-Foo:bar' + self.sep)
- # Cannot tell it's not a continue header until the next line arrives
- # and is not a continuation
- protocol.dataReceived(self.sep)
- self.assertEqual(header, {'X-Foo': 'bar'})
- self.assertEqual(protocol.state, BODY)
-
-
- def test_continuedHeaderCallback(self):
- """
- If a header is split over multiple lines, L{HTTPParser} calls
- C{headerReceived} with the entire value once it is received.
- """
- header, protocol = self._headerTestSetup()
- protocol.dataReceived('X-Foo: bar' + self.sep)
- protocol.dataReceived(' baz' + self.sep)
- protocol.dataReceived('\tquux' + self.sep)
- protocol.dataReceived(self.sep)
- self.assertEqual(header, {'X-Foo': 'bar baz\tquux'})
- self.assertEqual(protocol.state, BODY)
-
-
- def test_fieldContentWhitespace(self):
- """
- Leading and trailing linear whitespace is stripped from the header
- value passed to the C{headerReceived} callback.
- """
- header, protocol = self._headerTestSetup()
- value = ' \t %(sep)s bar \t%(sep)s \t%(sep)s' % dict(sep=self.sep)
- protocol.dataReceived('X-Bar:' + value)
- protocol.dataReceived('X-Foo:' + value)
- protocol.dataReceived(self.sep)
- self.assertEqual(header, {'X-Foo': 'bar',
- 'X-Bar': 'bar'})
-
-
- def test_allHeadersCallback(self):
- """
- After the last header is received, L{HTTPParser} calls
- C{allHeadersReceived}.
- """
- called = []
- header, protocol = self._headerTestSetup()
- def allHeadersReceived():
- called.append(protocol.state)
- protocol.state = STATUS
- protocol.allHeadersReceived = allHeadersReceived
- protocol.dataReceived(self.sep)
- self.assertEqual(called, [HEADER])
- self.assertEqual(protocol.state, STATUS)
-
-
- def test_noHeaderCallback(self):
- """
- If there are no headers in the message, L{HTTPParser} does not call
- C{headerReceived}.
- """
- header, protocol = self._headerTestSetup()
- protocol.dataReceived(self.sep)
- self.assertEqual(header, {})
- self.assertEqual(protocol.state, BODY)
-
-
- def test_headersSavedOnResponse(self):
- """
- All headers received by L{HTTPParser} are added to
- L{HTTPParser.headers}.
- """
- protocol = HTTPParser()
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK' + self.sep)
- protocol.dataReceived('X-Foo: bar' + self.sep)
- protocol.dataReceived('X-Foo: baz' + self.sep)
- protocol.dataReceived(self.sep)
- expected = [('X-Foo', ['bar', 'baz'])]
- self.assertEqual(expected, list(protocol.headers.getAllRawHeaders()))
-
-
- def test_connectionControlHeaders(self):
- """
- L{HTTPParser.isConnectionControlHeader} returns C{True} for headers
- which are always connection control headers (similar to "hop-by-hop"
- headers from RFC 2616 section 13.5.1) and C{False} for other headers.
- """
- protocol = HTTPParser()
- connHeaderNames = [
- 'content-length', 'connection', 'keep-alive', 'te', 'trailers',
- 'transfer-encoding', 'upgrade', 'proxy-connection']
-
- for header in connHeaderNames:
- self.assertTrue(
- protocol.isConnectionControlHeader(header),
- "Expecting %r to be a connection control header, but "
- "wasn't" % (header,))
- self.assertFalse(
- protocol.isConnectionControlHeader("date"),
- "Expecting the arbitrarily selected 'date' header to not be "
- "a connection control header, but was.")
-
-
- def test_switchToBodyMode(self):
- """
- L{HTTPParser.switchToBodyMode} raises L{RuntimeError} if called more
- than once.
- """
- protocol = HTTPParser()
- protocol.makeConnection(StringTransport())
- protocol.switchToBodyMode(object())
- self.assertRaises(RuntimeError, protocol.switchToBodyMode, object())
-
-
-
-class HTTPParserTestsRFCComplaintDelimeter(_HTTPParserTests, TestCase):
- """
- L{_HTTPParserTests} using standard CR LF newlines.
- """
- sep = '\r\n'
-
-
-
-class HTTPParserTestsNonRFCComplaintDelimeter(_HTTPParserTests, TestCase):
- """
- L{_HTTPParserTests} using bare LF newlines.
- """
- sep = '\n'
-
-
-
-class HTTPClientParserTests(TestCase):
- """
- Tests for L{HTTPClientParser} which is responsible for parsing HTTP
- response messages.
- """
- def test_parseVersion(self):
- """
- L{HTTPClientParser.parseVersion} parses a status line into its three
- components.
- """
- protocol = HTTPClientParser(None, None)
- self.assertEqual(
- protocol.parseVersion('CANDY/7.2'),
- ('CANDY', 7, 2))
-
-
- def test_parseBadVersion(self):
- """
- L{HTTPClientParser.parseVersion} raises L{ValueError} when passed an
- unparsable version.
- """
- protocol = HTTPClientParser(None, None)
- e = BadResponseVersion
- f = protocol.parseVersion
-
- def checkParsing(s):
- exc = self.assertRaises(e, f, s)
- self.assertEqual(exc.data, s)
-
- checkParsing('foo')
- checkParsing('foo/bar/baz')
-
- checkParsing('foo/')
- checkParsing('foo/..')
-
- checkParsing('foo/a.b')
- checkParsing('foo/-1.-1')
-
-
- def test_responseStatusParsing(self):
- """
- L{HTTPClientParser.statusReceived} parses the version, code, and phrase
- from the status line and stores them on the response object.
- """
- request = Request('GET', '/', _boringHeaders, None)
- protocol = HTTPClientParser(request, None)
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- self.assertEqual(protocol.response.version, ('HTTP', 1, 1))
- self.assertEqual(protocol.response.code, 200)
- self.assertEqual(protocol.response.phrase, 'OK')
-
-
- def test_badResponseStatus(self):
- """
- L{HTTPClientParser.statusReceived} raises L{ParseError} if it is called
- with a status line which cannot be parsed.
- """
- protocol = HTTPClientParser(None, None)
-
- def checkParsing(s):
- exc = self.assertRaises(ParseError, protocol.statusReceived, s)
- self.assertEqual(exc.data, s)
-
- # If there are fewer than three whitespace-delimited parts to the
- # status line, it is not valid and cannot be parsed.
- checkParsing('foo')
- checkParsing('HTTP/1.1 200')
-
- # If the response code is not an integer, the status line is not valid
- # and cannot be parsed.
- checkParsing('HTTP/1.1 bar OK')
-
-
- def _noBodyTest(self, request, response):
- """
- Assert that L{HTTPClientParser} parses the given C{response} to
- C{request}, resulting in a response with no body and no extra bytes and
- leaving the transport in the producing state.
-
- @param request: A L{Request} instance which might have caused a server
- to return the given response.
- @param response: A string giving the response to be parsed.
-
- @return: A C{dict} of headers from the response.
- """
- header = {}
- finished = []
- protocol = HTTPClientParser(request, finished.append)
- protocol.headerReceived = header.__setitem__
- body = []
- protocol._bodyDataReceived = body.append
- transport = StringTransport()
- protocol.makeConnection(transport)
- protocol.dataReceived(response)
- self.assertEqual(transport.producerState, 'producing')
- self.assertEqual(protocol.state, DONE)
- self.assertEqual(body, [])
- self.assertEqual(finished, [''])
- self.assertEqual(protocol.response.length, 0)
- return header
-
-
- def test_headResponse(self):
- """
- If the response is to a HEAD request, no body is expected, the body
- callback is not invoked, and the I{Content-Length} header is passed to
- the header callback.
- """
- request = Request('HEAD', '/', _boringHeaders, None)
- status = (
- 'HTTP/1.1 200 OK\r\n'
- 'Content-Length: 10\r\n'
- '\r\n')
- header = self._noBodyTest(request, status)
- self.assertEqual(header, {'Content-Length': '10'})
-
-
- def test_noContentResponse(self):
- """
- If the response code is I{NO CONTENT} (204), no body is expected and
- the body callback is not invoked.
- """
- request = Request('GET', '/', _boringHeaders, None)
- status = (
- 'HTTP/1.1 204 NO CONTENT\r\n'
- '\r\n')
- self._noBodyTest(request, status)
-
-
- def test_notModifiedResponse(self):
- """
- If the response code is I{NOT MODIFIED} (304), no body is expected and
- the body callback is not invoked.
- """
- request = Request('GET', '/', _boringHeaders, None)
- status = (
- 'HTTP/1.1 304 NOT MODIFIED\r\n'
- '\r\n')
- self._noBodyTest(request, status)
-
-
- def test_responseHeaders(self):
- """
- The response headers are added to the response object's C{headers}
- L{Headers} instance.
- """
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- lambda rest: None)
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- protocol.dataReceived('X-Foo: bar\r\n')
- protocol.dataReceived('\r\n')
- self.assertEqual(
- protocol.connHeaders,
- Headers({}))
- self.assertEqual(
- protocol.response.headers,
- Headers({'x-foo': ['bar']}))
- self.assertIdentical(protocol.response.length, UNKNOWN_LENGTH)
-
-
- def test_connectionHeaders(self):
- """
- The connection control headers are added to the parser's C{connHeaders}
- L{Headers} instance.
- """
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- lambda rest: None)
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- protocol.dataReceived('Content-Length: 123\r\n')
- protocol.dataReceived('Connection: close\r\n')
- protocol.dataReceived('\r\n')
- self.assertEqual(
- protocol.response.headers,
- Headers({}))
- self.assertEqual(
- protocol.connHeaders,
- Headers({'content-length': ['123'],
- 'connection': ['close']}))
- self.assertEqual(protocol.response.length, 123)
-
-
- def test_headResponseContentLengthEntityHeader(self):
- """
- If a HEAD request is made, the I{Content-Length} header in the response
- is added to the response headers, not the connection control headers.
- """
- protocol = HTTPClientParser(
- Request('HEAD', '/', _boringHeaders, None),
- lambda rest: None)
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- protocol.dataReceived('Content-Length: 123\r\n')
- protocol.dataReceived('\r\n')
- self.assertEqual(
- protocol.response.headers,
- Headers({'content-length': ['123']}))
- self.assertEqual(
- protocol.connHeaders,
- Headers({}))
- self.assertEqual(protocol.response.length, 0)
-
-
- def test_contentLength(self):
- """
- If a response includes a body with a length given by the
- I{Content-Length} header, the bytes which make up the body are passed
- to the C{_bodyDataReceived} callback on the L{HTTPParser}.
- """
- finished = []
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- finished.append)
- transport = StringTransport()
- protocol.makeConnection(transport)
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- body = []
- protocol.response._bodyDataReceived = body.append
- protocol.dataReceived('Content-Length: 10\r\n')
- protocol.dataReceived('\r\n')
-
- # Incidentally, the transport should be paused now. It is the response
- # object's responsibility to resume this when it is ready for bytes.
- self.assertEqual(transport.producerState, 'paused')
-
- self.assertEqual(protocol.state, BODY)
- protocol.dataReceived('x' * 6)
- self.assertEqual(body, ['x' * 6])
- self.assertEqual(protocol.state, BODY)
- protocol.dataReceived('y' * 4)
- self.assertEqual(body, ['x' * 6, 'y' * 4])
- self.assertEqual(protocol.state, DONE)
- self.assertTrue(finished, [''])
-
-
- def test_zeroContentLength(self):
- """
- If a response includes a I{Content-Length} header indicating zero bytes
- in the response, L{Response.length} is set accordingly and no data is
- delivered to L{Response._bodyDataReceived}.
- """
- finished = []
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- finished.append)
-
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
-
- body = []
- protocol.response._bodyDataReceived = body.append
-
- protocol.dataReceived('Content-Length: 0\r\n')
- protocol.dataReceived('\r\n')
-
- self.assertEqual(protocol.state, DONE)
- self.assertEqual(body, [])
- self.assertTrue(finished, [''])
- self.assertEqual(protocol.response.length, 0)
-
-
-
- def test_multipleContentLengthHeaders(self):
- """
- If a response includes multiple I{Content-Length} headers,
- L{HTTPClientParser.dataReceived} raises L{ValueError} to indicate that
- the response is invalid and the transport is now unusable.
- """
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- None)
-
- protocol.makeConnection(StringTransport())
- self.assertRaises(
- ValueError,
- protocol.dataReceived,
- 'HTTP/1.1 200 OK\r\n'
- 'Content-Length: 1\r\n'
- 'Content-Length: 2\r\n'
- '\r\n')
-
-
- def test_extraBytesPassedBack(self):
- """
- If extra bytes are received past the end of a response, they are passed
- to the finish callback.
- """
- finished = []
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- finished.append)
-
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- protocol.dataReceived('Content-Length: 0\r\n')
- protocol.dataReceived('\r\nHere is another thing!')
- self.assertEqual(protocol.state, DONE)
- self.assertEqual(finished, ['Here is another thing!'])
-
-
- def test_extraBytesPassedBackHEAD(self):
- """
- If extra bytes are received past the end of the headers of a response
- to a HEAD request, they are passed to the finish callback.
- """
- finished = []
- protocol = HTTPClientParser(
- Request('HEAD', '/', _boringHeaders, None),
- finished.append)
-
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
- protocol.dataReceived('Content-Length: 12\r\n')
- protocol.dataReceived('\r\nHere is another thing!')
- self.assertEqual(protocol.state, DONE)
- self.assertEqual(finished, ['Here is another thing!'])
-
-
- def test_chunkedResponseBody(self):
- """
- If the response headers indicate the response body is encoded with the
- I{chunked} transfer encoding, the body is decoded according to that
- transfer encoding before being passed to L{Response._bodyDataReceived}.
- """
- finished = []
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None),
- finished.append)
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
-
- body = []
- protocol.response._bodyDataReceived = body.append
-
- protocol.dataReceived('Transfer-Encoding: chunked\r\n')
- protocol.dataReceived('\r\n')
-
- # No data delivered yet
- self.assertEqual(body, [])
-
- # Cannot predict the length of a chunked encoded response body.
- self.assertIdentical(protocol.response.length, UNKNOWN_LENGTH)
-
- # Deliver some chunks and make sure the data arrives
- protocol.dataReceived('3\r\na')
- self.assertEqual(body, ['a'])
- protocol.dataReceived('bc\r\n')
- self.assertEqual(body, ['a', 'bc'])
-
- # The response's _bodyDataFinished method should be called when the last
- # chunk is received. Extra data should be passed to the finished
- # callback.
- protocol.dataReceived('0\r\n\r\nextra')
- self.assertEqual(finished, ['extra'])
-
-
- def test_unknownContentLength(self):
- """
- If a response does not include a I{Transfer-Encoding} or a
- I{Content-Length}, the end of response body is indicated by the
- connection being closed.
- """
- finished = []
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None), finished.append)
- transport = StringTransport()
- protocol.makeConnection(transport)
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
-
- body = []
- protocol.response._bodyDataReceived = body.append
-
- protocol.dataReceived('\r\n')
- protocol.dataReceived('foo')
- protocol.dataReceived('bar')
- self.assertEqual(body, ['foo', 'bar'])
- protocol.connectionLost(ConnectionDone("simulated end of connection"))
- self.assertEqual(finished, [''])
-
-
- def test_contentLengthAndTransferEncoding(self):
- """
- According to RFC 2616, section 4.4, point 3, if I{Content-Length} and
- I{Transfer-Encoding: chunked} are present, I{Content-Length} MUST be
- ignored
- """
- finished = []
- protocol = HTTPClientParser(
- Request('GET', '/', _boringHeaders, None), finished.append)
- transport = StringTransport()
- protocol.makeConnection(transport)
- protocol.dataReceived('HTTP/1.1 200 OK\r\n')
-
- body = []
- protocol.response._bodyDataReceived = body.append
-
- protocol.dataReceived(
- 'Content-Length: 102\r\n'
- 'Transfer-Encoding: chunked\r\n'
- '\r\n'
- '3\r\n'
- 'abc\r\n'
- '0\r\n'
- '\r\n')
-
- self.assertEqual(body, ['abc'])
- self.assertEqual(finished, [''])
-
-
- def test_connectionLostBeforeBody(self):
- """
- If L{HTTPClientParser.connectionLost} is called before the headers are
- finished, the C{_responseDeferred} is fired with the L{Failure} passed
- to C{connectionLost}.
- """
- transport = StringTransport()
- protocol = HTTPClientParser(Request('GET', '/', _boringHeaders, None), None)
- protocol.makeConnection(transport)
- # Grab this here because connectionLost gets rid of the attribute
- responseDeferred = protocol._responseDeferred
- protocol.connectionLost(Failure(ArbitraryException()))
-
- return assertResponseFailed(
- self, responseDeferred, [ArbitraryException])
-
-
- def test_connectionLostWithError(self):
- """
- If one of the L{Response} methods called by
- L{HTTPClientParser.connectionLost} raises an exception, the exception
- is logged and not re-raised.
- """
- transport = StringTransport()
- protocol = HTTPClientParser(Request('GET', '/', _boringHeaders, None),
- None)
- protocol.makeConnection(transport)
-
- response = []
- protocol._responseDeferred.addCallback(response.append)
- protocol.dataReceived(
- 'HTTP/1.1 200 OK\r\n'
- 'Content-Length: 1\r\n'
- '\r\n')
- response = response[0]
-
- # Arrange for an exception
- def fakeBodyDataFinished(err=None):
- raise ArbitraryException()
- response._bodyDataFinished = fakeBodyDataFinished
-
- protocol.connectionLost(None)
-
- self.assertEqual(len(self.flushLoggedErrors(ArbitraryException)), 1)
-
-
- def test_noResponseAtAll(self):
- """
- If no response at all was received and the connection is lost, the
- resulting error is L{ResponseNeverReceived}.
- """
- protocol = HTTPClientParser(
- Request('HEAD', '/', _boringHeaders, None),
- lambda ign: None)
- d = protocol._responseDeferred
-
- protocol.makeConnection(StringTransport())
- protocol.connectionLost(ConnectionLost())
- return self.assertFailure(d, ResponseNeverReceived)
-
-
- def test_someResponseButNotAll(self):
- """
- If a partial response was received and the connection is lost, the
- resulting error is L{ResponseFailed}, but not
- L{ResponseNeverReceived}.
- """
- protocol = HTTPClientParser(
- Request('HEAD', '/', _boringHeaders, None),
- lambda ign: None)
- d = protocol._responseDeferred
-
- protocol.makeConnection(StringTransport())
- protocol.dataReceived('2')
- protocol.connectionLost(ConnectionLost())
- return self.assertFailure(d, ResponseFailed).addCallback(
- self.assertIsInstance, ResponseFailed)
-
-
-
-class SlowRequest:
- """
- L{SlowRequest} is a fake implementation of L{Request} which is easily
- controlled externally (for example, by code in a test method).
-
- @ivar stopped: A flag indicating whether C{stopWriting} has been called.
-
- @ivar finished: After C{writeTo} is called, a L{Deferred} which was
- returned by that method. L{SlowRequest} will never fire this
- L{Deferred}.
- """
- method = 'GET'
- stopped = False
- persistent = False
-
- def writeTo(self, transport):
- self.finished = Deferred()
- return self.finished
-
-
- def stopWriting(self):
- self.stopped = True
-
-
-
-class SimpleRequest:
- """
- L{SimpleRequest} is a fake implementation of L{Request} which writes a
- short, fixed string to the transport passed to its C{writeTo} method and
- returns a succeeded L{Deferred}. This vaguely emulates the behavior of a
- L{Request} with no body producer.
- """
- persistent = False
-
- def writeTo(self, transport):
- transport.write('SOME BYTES')
- return succeed(None)
-
-
-
-class HTTP11ClientProtocolTests(TestCase):
- """
- Tests for the HTTP 1.1 client protocol implementation,
- L{HTTP11ClientProtocol}.
- """
- def setUp(self):
- """
- Create an L{HTTP11ClientProtocol} connected to a fake transport.
- """
- self.transport = StringTransport()
- self.protocol = HTTP11ClientProtocol()
- self.protocol.makeConnection(self.transport)
-
-
- def test_request(self):
- """
- L{HTTP11ClientProtocol.request} accepts a L{Request} and calls its
- C{writeTo} method with its own transport.
- """
- self.protocol.request(SimpleRequest())
- self.assertEqual(self.transport.value(), 'SOME BYTES')
-
-
- def test_secondRequest(self):
- """
- The second time L{HTTP11ClientProtocol.request} is called, it returns a
- L{Deferred} which immediately fires with a L{Failure} wrapping a
- L{RequestNotSent} exception.
- """
- self.protocol.request(SlowRequest())
- def cbNotSent(ignored):
- self.assertEqual(self.transport.value(), '')
- d = self.assertFailure(
- self.protocol.request(SimpleRequest()), RequestNotSent)
- d.addCallback(cbNotSent)
- return d
-
-
- def test_requestAfterConnectionLost(self):
- """
- L{HTTP11ClientProtocol.request} returns a L{Deferred} which immediately
- fires with a L{Failure} wrapping a L{RequestNotSent} if called after
- the protocol has been disconnected.
- """
- self.protocol.connectionLost(
- Failure(ConnectionDone("sad transport")))
- def cbNotSent(ignored):
- self.assertEqual(self.transport.value(), '')
- d = self.assertFailure(
- self.protocol.request(SimpleRequest()), RequestNotSent)
- d.addCallback(cbNotSent)
- return d
-
-
- def test_failedWriteTo(self):
- """
- If the L{Deferred} returned by L{Request.writeTo} fires with a
- L{Failure}, L{HTTP11ClientProtocol.request} disconnects its transport
- and returns a L{Deferred} which fires with a L{Failure} of
- L{RequestGenerationFailed} wrapping the underlying failure.
- """
- class BrokenRequest:
- persistent = False
- def writeTo(self, transport):
- return fail(ArbitraryException())
-
- d = self.protocol.request(BrokenRequest())
- def cbFailed(ignored):
- self.assertTrue(self.transport.disconnecting)
- # Simulate what would happen if the protocol had a real transport
- # and make sure no exception is raised.
- self.protocol.connectionLost(
- Failure(ConnectionDone("you asked for it")))
- d = assertRequestGenerationFailed(self, d, [ArbitraryException])
- d.addCallback(cbFailed)
- return d
-
-
- def test_synchronousWriteToError(self):
- """
- If L{Request.writeTo} raises an exception,
- L{HTTP11ClientProtocol.request} returns a L{Deferred} which fires with
- a L{Failure} of L{RequestGenerationFailed} wrapping that exception.
- """
- class BrokenRequest:
- persistent = False
- def writeTo(self, transport):
- raise ArbitraryException()
-
- d = self.protocol.request(BrokenRequest())
- return assertRequestGenerationFailed(self, d, [ArbitraryException])
-
-
- def test_connectionLostDuringRequestGeneration(self, mode=None):
- """
- If L{HTTP11ClientProtocol}'s transport is disconnected before the
- L{Deferred} returned by L{Request.writeTo} fires, the L{Deferred}
- returned by L{HTTP11ClientProtocol.request} fires with a L{Failure} of
- L{RequestTransmissionFailed} wrapping the underlying failure.
- """
- request = SlowRequest()
- d = self.protocol.request(request)
- d = assertRequestTransmissionFailed(self, d, [ArbitraryException])
-
- # The connection hasn't been lost yet. The request should still be
- # allowed to do its thing.
- self.assertFalse(request.stopped)
-
- self.protocol.connectionLost(Failure(ArbitraryException()))
-
- # Now the connection has been lost. The request should have been told
- # to stop writing itself.
- self.assertTrue(request.stopped)
-
- if mode == 'callback':
- request.finished.callback(None)
- elif mode == 'errback':
- request.finished.errback(Failure(AnotherArbitraryException()))
- errors = self.flushLoggedErrors(AnotherArbitraryException)
- self.assertEqual(len(errors), 1)
- else:
- # Don't fire the writeTo Deferred at all.
- pass
- return d
-
-
- def test_connectionLostBeforeGenerationFinished(self):
- """
- If the request passed to L{HTTP11ClientProtocol} finishes generation
- successfully after the L{HTTP11ClientProtocol}'s connection has been
- lost, nothing happens.
- """
- return self.test_connectionLostDuringRequestGeneration('callback')
-
-
- def test_connectionLostBeforeGenerationFailed(self):
- """
- If the request passed to L{HTTP11ClientProtocol} finished generation
- with an error after the L{HTTP11ClientProtocol}'s connection has been
- lost, nothing happens.
- """
- return self.test_connectionLostDuringRequestGeneration('errback')
-
-
- def test_errorMessageOnConnectionLostBeforeGenerationFailedDoesNotConfuse(self):
- """
- If the request passed to L{HTTP11ClientProtocol} finished generation
- with an error after the L{HTTP11ClientProtocol}'s connection has been
- lost, an error is logged that gives a non-confusing hint to user on what
- went wrong.
- """
- errors = []
- log.addObserver(errors.append)
- self.addCleanup(log.removeObserver, errors.append)
-
- def check(ignore):
- error = errors[0]
- self.assertEqual(error['why'],
- 'Error writing request, but not in valid state '
- 'to finalize request: CONNECTION_LOST')
-
- return self.test_connectionLostDuringRequestGeneration(
- 'errback').addCallback(check)
-
-
- def test_receiveSimplestResponse(self):
- """
- When a response is delivered to L{HTTP11ClientProtocol}, the
- L{Deferred} previously returned by the C{request} method is called back
- with a L{Response} instance and the connection is closed.
- """
- d = self.protocol.request(Request('GET', '/', _boringHeaders, None))
- def cbRequest(response):
- self.assertEqual(response.code, 200)
- self.assertEqual(response.headers, Headers())
- self.assertTrue(self.transport.disconnecting)
- self.assertEqual(self.protocol.state, 'QUIESCENT')
- d.addCallback(cbRequest)
- self.protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 0\r\n"
- "Connection: close\r\n"
- "\r\n")
- return d
-
-
- def test_receiveResponseHeaders(self):
- """
- The headers included in a response delivered to L{HTTP11ClientProtocol}
- are included on the L{Response} instance passed to the callback
- returned by the C{request} method.
- """
- d = self.protocol.request(Request('GET', '/', _boringHeaders, None))
- def cbRequest(response):
- expected = Headers({'x-foo': ['bar', 'baz']})
- self.assertEqual(response.headers, expected)
- d.addCallback(cbRequest)
- self.protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "X-Foo: bar\r\n"
- "X-Foo: baz\r\n"
- "\r\n")
- return d
-
-
- def test_receiveResponseBeforeRequestGenerationDone(self):
- """
- If response bytes are delivered to L{HTTP11ClientProtocol} before the
- L{Deferred} returned by L{Request.writeTo} fires, those response bytes
- are parsed as part of the response.
-
- The connection is also closed, because we're in a confusing state, and
- therefore the C{quiescentCallback} isn't called.
- """
- quiescentResult = []
- transport = StringTransport()
- protocol = HTTP11ClientProtocol(quiescentResult.append)
- protocol.makeConnection(transport)
-
- request = SlowRequest()
- d = protocol.request(request)
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "X-Foo: bar\r\n"
- "Content-Length: 6\r\n"
- "\r\n"
- "foobar")
- def cbResponse(response):
- p = AccumulatingProtocol()
- whenFinished = p.closedDeferred = Deferred()
- response.deliverBody(p)
- self.assertEqual(
- protocol.state, 'TRANSMITTING_AFTER_RECEIVING_RESPONSE')
- self.assertTrue(transport.disconnecting)
- self.assertEqual(quiescentResult, [])
- return whenFinished.addCallback(
- lambda ign: (response, p.data))
- d.addCallback(cbResponse)
- def cbAllResponse((response, body)):
- self.assertEqual(response.version, ('HTTP', 1, 1))
- self.assertEqual(response.code, 200)
- self.assertEqual(response.phrase, 'OK')
- self.assertEqual(response.headers, Headers({'x-foo': ['bar']}))
- self.assertEqual(body, "foobar")
-
- # Also nothing bad should happen if the request does finally
- # finish, even though it is completely irrelevant.
- request.finished.callback(None)
-
- d.addCallback(cbAllResponse)
- return d
-
-
- def test_connectionLostAfterReceivingResponseBeforeRequestGenerationDone(self):
- """
- If response bytes are delivered to L{HTTP11ClientProtocol} before the
- request completes, calling L{connectionLost} on the protocol will
- result in protocol being moved to C{'CONNECTION_LOST'} state.
- """
- request = SlowRequest()
- d = self.protocol.request(request)
- self.protocol.dataReceived(
- "HTTP/1.1 400 BAD REQUEST\r\n"
- "Content-Length: 9\r\n"
- "\r\n"
- "tisk tisk")
- def cbResponse(response):
- p = AccumulatingProtocol()
- whenFinished = p.closedDeferred = Deferred()
- response.deliverBody(p)
- return whenFinished.addCallback(
- lambda ign: (response, p.data))
- d.addCallback(cbResponse)
- def cbAllResponse(ignore):
- request.finished.callback(None)
- # Nothing dire will happen when the connection is lost
- self.protocol.connectionLost(Failure(ArbitraryException()))
- self.assertEqual(self.protocol._state, 'CONNECTION_LOST')
- d.addCallback(cbAllResponse)
- return d
-
-
- def test_receiveResponseBody(self):
- """
- The C{deliverBody} method of the response object with which the
- L{Deferred} returned by L{HTTP11ClientProtocol.request} fires can be
- used to get the body of the response.
- """
- protocol = AccumulatingProtocol()
- whenFinished = protocol.closedDeferred = Deferred()
- requestDeferred = self.protocol.request(Request('GET', '/', _boringHeaders, None))
-
- self.protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 6\r\n"
- "\r")
-
- # Here's what's going on: all the response headers have been delivered
- # by this point, so the request Deferred can fire with a Response
- # object. The body is yet to come, but that's okay, because the
- # Response object is how you *get* the body.
- result = []
- requestDeferred.addCallback(result.append)
-
- self.assertEqual(result, [])
- # Deliver the very last byte of the response. It is exactly at this
- # point which the Deferred returned by request should fire.
- self.protocol.dataReceived("\n")
- response = result[0]
-
- response.deliverBody(protocol)
-
- self.protocol.dataReceived("foo")
- self.protocol.dataReceived("bar")
-
- def cbAllResponse(ignored):
- self.assertEqual(protocol.data, "foobar")
- protocol.closedReason.trap(ResponseDone)
- whenFinished.addCallback(cbAllResponse)
- return whenFinished
-
-
- def test_responseBodyFinishedWhenConnectionLostWhenContentLengthIsUnknown(
- self):
- """
- If the length of the response body is unknown, the protocol passed to
- the response's C{deliverBody} method has its C{connectionLost}
- method called with a L{Failure} wrapping a L{PotentialDataLoss}
- exception.
- """
- requestDeferred = self.protocol.request(Request('GET', '/', _boringHeaders, None))
- self.protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "\r\n")
-
- result = []
- requestDeferred.addCallback(result.append)
- response = result[0]
-
- protocol = AccumulatingProtocol()
- response.deliverBody(protocol)
-
- self.protocol.dataReceived("foo")
- self.protocol.dataReceived("bar")
-
- self.assertEqual(protocol.data, "foobar")
- self.protocol.connectionLost(
- Failure(ConnectionDone("low-level transport disconnected")))
-
- protocol.closedReason.trap(PotentialDataLoss)
-
-
- def test_chunkedResponseBodyUnfinishedWhenConnectionLost(self):
- """
- If the final chunk has not been received when the connection is lost
- (for any reason), the protocol passed to C{deliverBody} has its
- C{connectionLost} method called with a L{Failure} wrapping the
- exception for that reason.
- """
- requestDeferred = self.protocol.request(Request('GET', '/', _boringHeaders, None))
- self.protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Transfer-Encoding: chunked\r\n"
- "\r\n")
-
- result = []
- requestDeferred.addCallback(result.append)
- response = result[0]
-
- protocol = AccumulatingProtocol()
- response.deliverBody(protocol)
-
- self.protocol.dataReceived("3\r\nfoo\r\n")
- self.protocol.dataReceived("3\r\nbar\r\n")
-
- self.assertEqual(protocol.data, "foobar")
-
- self.protocol.connectionLost(Failure(ArbitraryException()))
-
- return assertResponseFailed(
- self, fail(protocol.closedReason), [ArbitraryException, _DataLoss])
-
-
- def test_parserDataReceivedException(self):
- """
- If the parser L{HTTP11ClientProtocol} delivers bytes to in
- C{dataReceived} raises an exception, the exception is wrapped in a
- L{Failure} and passed to the parser's C{connectionLost} and then the
- L{HTTP11ClientProtocol}'s transport is disconnected.
- """
- requestDeferred = self.protocol.request(Request('GET', '/', _boringHeaders, None))
- self.protocol.dataReceived('unparseable garbage goes here\r\n')
- d = assertResponseFailed(self, requestDeferred, [ParseError])
- def cbFailed(exc):
- self.assertTrue(self.transport.disconnecting)
- self.assertEqual(
- exc.reasons[0].value.data, 'unparseable garbage goes here')
-
- # Now do what StringTransport doesn't do but a real transport would
- # have, call connectionLost on the HTTP11ClientProtocol. Nothing
- # is asserted about this, but it's important for it to not raise an
- # exception.
- self.protocol.connectionLost(Failure(ConnectionDone("it is done")))
-
- d.addCallback(cbFailed)
- return d
-
-
- def test_proxyStopped(self):
- """
- When the HTTP response parser is disconnected, the
- L{TransportProxyProducer} which was connected to it as a transport is
- stopped.
- """
- requestDeferred = self.protocol.request(Request('GET', '/', _boringHeaders, None))
- transport = self.protocol._parser.transport
- self.assertIdentical(transport._producer, self.transport)
- self.protocol._disconnectParser(Failure(ConnectionDone("connection done")))
- self.assertIdentical(transport._producer, None)
- return assertResponseFailed(self, requestDeferred, [ConnectionDone])
-
-
- def test_abortClosesConnection(self):
- """
- L{HTTP11ClientProtocol.abort} will tell the transport to close its
- connection when it is invoked, and returns a C{Deferred} that fires
- when the connection is lost.
- """
- transport = StringTransport()
- protocol = HTTP11ClientProtocol()
- protocol.makeConnection(transport)
- r1 = []
- r2 = []
- protocol.abort().addCallback(r1.append)
- protocol.abort().addCallback(r2.append)
- self.assertEqual((r1, r2), ([], []))
- self.assertTrue(transport.disconnecting)
-
- # Disconnect protocol, the Deferreds will fire:
- protocol.connectionLost(Failure(ConnectionDone()))
- self.assertEqual(r1, [None])
- self.assertEqual(r2, [None])
-
-
- def test_abortAfterConnectionLost(self):
- """
- L{HTTP11ClientProtocol.abort} called after the connection is lost
- returns a C{Deferred} that fires immediately.
- """
- transport = StringTransport()
- protocol = HTTP11ClientProtocol()
- protocol.makeConnection(transport)
- protocol.connectionLost(Failure(ConnectionDone()))
-
- result = []
- protocol.abort().addCallback(result.append)
- self.assertEqual(result, [None])
- self.assertEqual(protocol._state, "CONNECTION_LOST")
-
-
- def test_abortBeforeResponseBody(self):
- """
- The Deferred returned by L{HTTP11ClientProtocol.request} will fire
- with a L{ResponseFailed} failure containing a L{ConnectionAborted}
- exception, if the connection was aborted before all response headers
- have been received.
- """
- transport = StringTransport()
- protocol = HTTP11ClientProtocol()
- protocol.makeConnection(transport)
- result = protocol.request(Request('GET', '/', _boringHeaders, None))
- protocol.abort()
- self.assertTrue(transport.disconnecting)
- protocol.connectionLost(Failure(ConnectionDone()))
- return assertResponseFailed(self, result, [ConnectionAborted])
-
-
- def test_abortAfterResponseHeaders(self):
- """
- When the connection is aborted after the response headers have
- been received and the L{Response} has been made available to
- application code, the response body protocol's C{connectionLost}
- method will be invoked with a L{ResponseFailed} failure containing a
- L{ConnectionAborted} exception.
- """
- transport = StringTransport()
- protocol = HTTP11ClientProtocol()
- protocol.makeConnection(transport)
- result = protocol.request(Request('GET', '/', _boringHeaders, None))
-
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 1\r\n"
- "\r\n"
- )
-
- testResult = Deferred()
-
- class BodyDestination(Protocol):
- """
- A body response protocol which immediately aborts the HTTP
- connection.
- """
- def connectionMade(self):
- """
- Abort the HTTP connection.
- """
- protocol.abort()
-
- def connectionLost(self, reason):
- """
- Make the reason for the losing of the connection available to
- the unit test via C{testResult}.
- """
- testResult.errback(reason)
-
-
- def deliverBody(response):
- """
- Connect the L{BodyDestination} response body protocol to the
- response, and then simulate connection loss after ensuring that
- the HTTP connection has been aborted.
- """
- response.deliverBody(BodyDestination())
- self.assertTrue(transport.disconnecting)
- protocol.connectionLost(Failure(ConnectionDone()))
-
-
- def checkError(error):
- self.assertIsInstance(error.response, Response)
-
-
- result.addCallback(deliverBody)
- deferred = assertResponseFailed(self, testResult,
- [ConnectionAborted, _DataLoss])
- return deferred.addCallback(checkError)
-
-
- def test_quiescentCallbackCalled(self):
- """
- If after a response is done the {HTTP11ClientProtocol} stays open and
- returns to QUIESCENT state, all per-request state is reset and the
- C{quiescentCallback} is called with the protocol instance.
-
- This is useful for implementing a persistent connection pool.
-
- The C{quiescentCallback} is called *before* the response-receiving
- protocol's C{connectionLost}, so that new requests triggered by end of
- first request can re-use a persistent connection.
- """
- quiescentResult = []
- def callback(p):
- self.assertEqual(p, protocol)
- self.assertEqual(p.state, "QUIESCENT")
- quiescentResult.append(p)
-
- transport = StringTransport()
- protocol = HTTP11ClientProtocol(callback)
- protocol.makeConnection(transport)
-
- requestDeferred = protocol.request(
- Request('GET', '/', _boringHeaders, None, persistent=True))
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-length: 3\r\n"
- "\r\n")
-
- # Headers done, but still no quiescent callback:
- self.assertEqual(quiescentResult, [])
-
- result = []
- requestDeferred.addCallback(result.append)
- response = result[0]
-
- # When response body is done (i.e. connectionLost is called), note the
- # fact in quiescentResult:
- bodyProtocol = AccumulatingProtocol()
- bodyProtocol.closedDeferred = Deferred()
- bodyProtocol.closedDeferred.addCallback(
- lambda ign: quiescentResult.append("response done"))
-
- response.deliverBody(bodyProtocol)
- protocol.dataReceived("abc")
- bodyProtocol.closedReason.trap(ResponseDone)
- # Quiescent callback called *before* protocol handling the response
- # body gets its connectionLost called:
- self.assertEqual(quiescentResult, [protocol, "response done"])
-
- # Make sure everything was cleaned up:
- self.assertEqual(protocol._parser, None)
- self.assertEqual(protocol._finishedRequest, None)
- self.assertEqual(protocol._currentRequest, None)
- self.assertEqual(protocol._transportProxy, None)
- self.assertEqual(protocol._responseDeferred, None)
-
-
- def test_quiescentCallbackCalledEmptyResponse(self):
- """
- The quiescentCallback is called before the request C{Deferred} fires,
- in cases where the response has no body.
- """
- quiescentResult = []
- def callback(p):
- self.assertEqual(p, protocol)
- self.assertEqual(p.state, "QUIESCENT")
- quiescentResult.append(p)
-
- transport = StringTransport()
- protocol = HTTP11ClientProtocol(callback)
- protocol.makeConnection(transport)
-
- requestDeferred = protocol.request(
- Request('GET', '/', _boringHeaders, None, persistent=True))
- requestDeferred.addCallback(quiescentResult.append)
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-length: 0\r\n"
- "\r\n")
-
- self.assertEqual(len(quiescentResult), 2)
- self.assertIdentical(quiescentResult[0], protocol)
- self.assertIsInstance(quiescentResult[1], Response)
-
-
- def test_quiescentCallbackNotCalled(self):
- """
- If after a response is done the {HTTP11ClientProtocol} returns a
- C{Connection: close} header in the response, the C{quiescentCallback}
- is not called and the connection is lost.
- """
- quiescentResult = []
- transport = StringTransport()
- protocol = HTTP11ClientProtocol(quiescentResult.append)
- protocol.makeConnection(transport)
-
- requestDeferred = protocol.request(
- Request('GET', '/', _boringHeaders, None, persistent=True))
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-length: 0\r\n"
- "Connection: close\r\n"
- "\r\n")
-
- result = []
- requestDeferred.addCallback(result.append)
- response = result[0]
-
- bodyProtocol = AccumulatingProtocol()
- response.deliverBody(bodyProtocol)
- bodyProtocol.closedReason.trap(ResponseDone)
- self.assertEqual(quiescentResult, [])
- self.assertTrue(transport.disconnecting)
-
-
- def test_quiescentCallbackNotCalledNonPersistentQuery(self):
- """
- If the request was non-persistent (i.e. sent C{Connection: close}),
- the C{quiescentCallback} is not called and the connection is lost.
- """
- quiescentResult = []
- transport = StringTransport()
- protocol = HTTP11ClientProtocol(quiescentResult.append)
- protocol.makeConnection(transport)
-
- requestDeferred = protocol.request(
- Request('GET', '/', _boringHeaders, None, persistent=False))
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-length: 0\r\n"
- "\r\n")
-
- result = []
- requestDeferred.addCallback(result.append)
- response = result[0]
-
- bodyProtocol = AccumulatingProtocol()
- response.deliverBody(bodyProtocol)
- bodyProtocol.closedReason.trap(ResponseDone)
- self.assertEqual(quiescentResult, [])
- self.assertTrue(transport.disconnecting)
-
-
- def test_quiescentCallbackThrows(self):
- """
- If C{quiescentCallback} throws an exception, the error is logged and
- protocol is disconnected.
- """
- def callback(p):
- raise ZeroDivisionError()
-
- transport = StringTransport()
- protocol = HTTP11ClientProtocol(callback)
- protocol.makeConnection(transport)
-
- requestDeferred = protocol.request(
- Request('GET', '/', _boringHeaders, None, persistent=True))
- protocol.dataReceived(
- "HTTP/1.1 200 OK\r\n"
- "Content-length: 0\r\n"
- "\r\n")
-
- result = []
- requestDeferred.addCallback(result.append)
- response = result[0]
- bodyProtocol = AccumulatingProtocol()
- response.deliverBody(bodyProtocol)
- bodyProtocol.closedReason.trap(ResponseDone)
-
- errors = self.flushLoggedErrors(ZeroDivisionError)
- self.assertEqual(len(errors), 1)
- self.assertTrue(transport.disconnecting)
-
-
-
-class StringProducer:
- """
- L{StringProducer} is a dummy body producer.
-
- @ivar stopped: A flag which indicates whether or not C{stopProducing} has
- been called.
- @ivar consumer: After C{startProducing} is called, the value of the
- C{consumer} argument to that method.
- @ivar finished: After C{startProducing} is called, a L{Deferred} which was
- returned by that method. L{StringProducer} will never fire this
- L{Deferred}.
- """
- implements(IBodyProducer)
-
- stopped = False
-
- def __init__(self, length):
- self.length = length
-
-
- def startProducing(self, consumer):
- self.consumer = consumer
- self.finished = Deferred()
- return self.finished
-
-
- def stopProducing(self):
- self.stopped = True
-
-
-
-class RequestTests(TestCase):
- """
- Tests for L{Request}.
- """
- def setUp(self):
- self.transport = StringTransport()
-
-
- def test_sendSimplestRequest(self):
- """
- L{Request.writeTo} formats the request data and writes it to the given
- transport.
- """
- Request('GET', '/', _boringHeaders, None).writeTo(self.transport)
- self.assertEqual(
- self.transport.value(),
- "GET / HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host: example.com\r\n"
- "\r\n")
-
-
- def test_sendSimplestPersistentRequest(self):
- """
- A pesistent request does not send 'Connection: close' header.
- """
- req = Request('GET', '/', _boringHeaders, None, persistent=True)
- req.writeTo(self.transport)
- self.assertEqual(
- self.transport.value(),
- "GET / HTTP/1.1\r\n"
- "Host: example.com\r\n"
- "\r\n")
-
-
- def test_sendRequestHeaders(self):
- """
- L{Request.writeTo} formats header data and writes it to the given
- transport.
- """
- headers = Headers({'x-foo': ['bar', 'baz'], 'host': ['example.com']})
- Request('GET', '/foo', headers, None).writeTo(self.transport)
- lines = self.transport.value().split('\r\n')
- self.assertEqual(lines[0], "GET /foo HTTP/1.1")
- self.assertEqual(lines[-2:], ["", ""])
- del lines[0], lines[-2:]
- lines.sort()
- self.assertEqual(
- lines,
- ["Connection: close",
- "Host: example.com",
- "X-Foo: bar",
- "X-Foo: baz"])
-
-
- def test_sendChunkedRequestBody(self):
- """
- L{Request.writeTo} uses chunked encoding to write data from the request
- body producer to the given transport. It registers the request body
- producer with the transport.
- """
- producer = StringProducer(UNKNOWN_LENGTH)
- request = Request('POST', '/bar', _boringHeaders, producer)
- request.writeTo(self.transport)
-
- self.assertNotIdentical(producer.consumer, None)
- self.assertIdentical(self.transport.producer, producer)
- self.assertTrue(self.transport.streaming)
-
- self.assertEqual(
- self.transport.value(),
- "POST /bar HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Transfer-Encoding: chunked\r\n"
- "Host: example.com\r\n"
- "\r\n")
- self.transport.clear()
-
- producer.consumer.write('x' * 3)
- producer.consumer.write('y' * 15)
- producer.finished.callback(None)
- self.assertIdentical(self.transport.producer, None)
- self.assertEqual(
- self.transport.value(),
- "3\r\n"
- "xxx\r\n"
- "f\r\n"
- "yyyyyyyyyyyyyyy\r\n"
- "0\r\n"
- "\r\n")
-
-
- def test_sendChunkedRequestBodyWithError(self):
- """
- If L{Request} is created with a C{bodyProducer} without a known length
- and the L{Deferred} returned from its C{startProducing} method fires
- with a L{Failure}, the L{Deferred} returned by L{Request.writeTo} fires
- with that L{Failure} and the body producer is unregistered from the
- transport. The final zero-length chunk is not written to the
- transport.
- """
- producer = StringProducer(UNKNOWN_LENGTH)
- request = Request('POST', '/bar', _boringHeaders, producer)
- writeDeferred = request.writeTo(self.transport)
- self.transport.clear()
- producer.finished.errback(ArbitraryException())
- def cbFailed(ignored):
- self.assertEqual(self.transport.value(), "")
- self.assertIdentical(self.transport.producer, None)
- d = self.assertFailure(writeDeferred, ArbitraryException)
- d.addCallback(cbFailed)
- return d
-
-
- def test_sendRequestBodyWithLength(self):
- """
- If L{Request} is created with a C{bodyProducer} with a known length,
- that length is sent as the value for the I{Content-Length} header and
- chunked encoding is not used.
- """
- producer = StringProducer(3)
- request = Request('POST', '/bar', _boringHeaders, producer)
- request.writeTo(self.transport)
-
- self.assertNotIdentical(producer.consumer, None)
- self.assertIdentical(self.transport.producer, producer)
- self.assertTrue(self.transport.streaming)
-
- self.assertEqual(
- self.transport.value(),
- "POST /bar HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Content-Length: 3\r\n"
- "Host: example.com\r\n"
- "\r\n")
- self.transport.clear()
-
- producer.consumer.write('abc')
- producer.finished.callback(None)
- self.assertIdentical(self.transport.producer, None)
- self.assertEqual(self.transport.value(), "abc")
-
-
- def test_sendRequestBodyWithTooFewBytes(self):
- """
- If L{Request} is created with a C{bodyProducer} with a known length and
- the producer does not produce that many bytes, the L{Deferred} returned
- by L{Request.writeTo} fires with a L{Failure} wrapping a
- L{WrongBodyLength} exception.
- """
- producer = StringProducer(3)
- request = Request('POST', '/bar', _boringHeaders, producer)
- writeDeferred = request.writeTo(self.transport)
- producer.consumer.write('ab')
- producer.finished.callback(None)
- self.assertIdentical(self.transport.producer, None)
- return self.assertFailure(writeDeferred, WrongBodyLength)
-
-
- def _sendRequestBodyWithTooManyBytesTest(self, finisher):
- """
- Verify that when too many bytes have been written by a body producer
- and then the body producer's C{startProducing} L{Deferred} fires that
- the producer is unregistered from the transport and that the
- L{Deferred} returned from L{Request.writeTo} is fired with a L{Failure}
- wrapping a L{WrongBodyLength}.
-
- @param finisher: A callable which will be invoked with the body
- producer after too many bytes have been written to the transport.
- It should fire the startProducing Deferred somehow.
- """
- producer = StringProducer(3)
- request = Request('POST', '/bar', _boringHeaders, producer)
- writeDeferred = request.writeTo(self.transport)
-
- producer.consumer.write('ab')
-
- # The producer hasn't misbehaved yet, so it shouldn't have been
- # stopped.
- self.assertFalse(producer.stopped)
-
- producer.consumer.write('cd')
-
- # Now the producer *has* misbehaved, so we should have tried to
- # make it stop.
- self.assertTrue(producer.stopped)
-
- # The transport should have had the producer unregistered from it as
- # well.
- self.assertIdentical(self.transport.producer, None)
-
- def cbFailed(exc):
- # The "cd" should not have been written to the transport because
- # the request can now locally be recognized to be invalid. If we
- # had written the extra bytes, the server could have decided to
- # start processing the request, which would be bad since we're
- # going to indicate failure locally.
- self.assertEqual(
- self.transport.value(),
- "POST /bar HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Content-Length: 3\r\n"
- "Host: example.com\r\n"
- "\r\n"
- "ab")
- self.transport.clear()
-
- # Subsequent writes should be ignored, as should firing the
- # Deferred returned from startProducing.
- self.assertRaises(ExcessWrite, producer.consumer.write, 'ef')
-
- # Likewise, if the Deferred returned from startProducing fires,
- # this should more or less be ignored (aside from possibly logging
- # an error).
- finisher(producer)
-
- # There should have been nothing further written to the transport.
- self.assertEqual(self.transport.value(), "")
-
- d = self.assertFailure(writeDeferred, WrongBodyLength)
- d.addCallback(cbFailed)
- return d
-
-
- def test_sendRequestBodyWithTooManyBytes(self):
- """
- If L{Request} is created with a C{bodyProducer} with a known length and
- the producer tries to produce more than than many bytes, the
- L{Deferred} returned by L{Request.writeTo} fires with a L{Failure}
- wrapping a L{WrongBodyLength} exception.
- """
- def finisher(producer):
- producer.finished.callback(None)
- return self._sendRequestBodyWithTooManyBytesTest(finisher)
-
-
- def test_sendRequestBodyErrorWithTooManyBytes(self):
- """
- If L{Request} is created with a C{bodyProducer} with a known length and
- the producer tries to produce more than than many bytes, the
- L{Deferred} returned by L{Request.writeTo} fires with a L{Failure}
- wrapping a L{WrongBodyLength} exception.
- """
- def finisher(producer):
- producer.finished.errback(ArbitraryException())
- errors = self.flushLoggedErrors(ArbitraryException)
- self.assertEqual(len(errors), 1)
- return self._sendRequestBodyWithTooManyBytesTest(finisher)
-
-
- def test_sendRequestBodyErrorWithConsumerError(self):
- """
- Though there should be no way for the internal C{finishedConsuming}
- L{Deferred} in L{Request._writeToContentLength} to fire a L{Failure}
- after the C{finishedProducing} L{Deferred} has fired, in case this does
- happen, the error should be logged with a message about how there's
- probably a bug in L{Request}.
-
- This is a whitebox test.
- """
- producer = StringProducer(3)
- request = Request('POST', '/bar', _boringHeaders, producer)
- request.writeTo(self.transport)
-
- finishedConsuming = producer.consumer._finished
-
- producer.consumer.write('abc')
- producer.finished.callback(None)
-
- finishedConsuming.errback(ArbitraryException())
- self.assertEqual(len(self.flushLoggedErrors(ArbitraryException)), 1)
-
-
- def _sendRequestBodyFinishedEarlyThenTooManyBytes(self, finisher):
- """
- Verify that if the body producer fires its Deferred and then keeps
- writing to the consumer that the extra writes are ignored and the
- L{Deferred} returned by L{Request.writeTo} fires with a L{Failure}
- wrapping the most appropriate exception type.
- """
- producer = StringProducer(3)
- request = Request('POST', '/bar', _boringHeaders, producer)
- writeDeferred = request.writeTo(self.transport)
-
- producer.consumer.write('ab')
- finisher(producer)
- self.assertIdentical(self.transport.producer, None)
- self.transport.clear()
- self.assertRaises(ExcessWrite, producer.consumer.write, 'cd')
- self.assertEqual(self.transport.value(), "")
- return writeDeferred
-
-
- def test_sendRequestBodyFinishedEarlyThenTooManyBytes(self):
- """
- If the request body producer indicates it is done by firing the
- L{Deferred} returned from its C{startProducing} method but then goes on
- to write too many bytes, the L{Deferred} returned by {Request.writeTo}
- fires with a L{Failure} wrapping L{WrongBodyLength}.
- """
- def finisher(producer):
- producer.finished.callback(None)
- return self.assertFailure(
- self._sendRequestBodyFinishedEarlyThenTooManyBytes(finisher),
- WrongBodyLength)
-
-
- def test_sendRequestBodyErroredEarlyThenTooManyBytes(self):
- """
- If the request body producer indicates an error by firing the
- L{Deferred} returned from its C{startProducing} method but then goes on
- to write too many bytes, the L{Deferred} returned by {Request.writeTo}
- fires with that L{Failure} and L{WrongBodyLength} is logged.
- """
- def finisher(producer):
- producer.finished.errback(ArbitraryException())
- return self.assertFailure(
- self._sendRequestBodyFinishedEarlyThenTooManyBytes(finisher),
- ArbitraryException)
-
-
- def test_sendChunkedRequestBodyFinishedThenWriteMore(self, _with=None):
- """
- If the request body producer with an unknown length tries to write
- after firing the L{Deferred} returned by its C{startProducing} method,
- the C{write} call raises an exception and does not write anything to
- the underlying transport.
- """
- producer = StringProducer(UNKNOWN_LENGTH)
- request = Request('POST', '/bar', _boringHeaders, producer)
- writeDeferred = request.writeTo(self.transport)
- producer.finished.callback(_with)
- self.transport.clear()
-
- self.assertRaises(ExcessWrite, producer.consumer.write, 'foo')
- self.assertEqual(self.transport.value(), "")
- return writeDeferred
-
-
- def test_sendChunkedRequestBodyFinishedWithErrorThenWriteMore(self):
- """
- If the request body producer with an unknown length tries to write
- after firing the L{Deferred} returned by its C{startProducing} method
- with a L{Failure}, the C{write} call raises an exception and does not
- write anything to the underlying transport.
- """
- d = self.test_sendChunkedRequestBodyFinishedThenWriteMore(
- Failure(ArbitraryException()))
- return self.assertFailure(d, ArbitraryException)
-
-
- def test_sendRequestBodyWithError(self):
- """
- If the L{Deferred} returned from the C{startProducing} method of the
- L{IBodyProducer} passed to L{Request} fires with a L{Failure}, the
- L{Deferred} returned from L{Request.writeTo} fails with that
- L{Failure}.
- """
- producer = StringProducer(5)
- request = Request('POST', '/bar', _boringHeaders, producer)
- writeDeferred = request.writeTo(self.transport)
-
- # Sanity check - the producer should be registered with the underlying
- # transport.
- self.assertIdentical(self.transport.producer, producer)
- self.assertTrue(self.transport.streaming)
-
- producer.consumer.write('ab')
- self.assertEqual(
- self.transport.value(),
- "POST /bar HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Content-Length: 5\r\n"
- "Host: example.com\r\n"
- "\r\n"
- "ab")
-
- self.assertFalse(self.transport.disconnecting)
- producer.finished.errback(Failure(ArbitraryException()))
-
- # Disconnection is handled by a higher level. Request should leave the
- # transport alone in this case.
- self.assertFalse(self.transport.disconnecting)
-
- # Oh. Except it should unregister the producer that it registered.
- self.assertIdentical(self.transport.producer, None)
-
- return self.assertFailure(writeDeferred, ArbitraryException)
-
-
- def test_hostHeaderRequired(self):
- """
- L{Request.writeTo} raises L{BadHeaders} if there is not exactly one
- I{Host} header and writes nothing to the given transport.
- """
- request = Request('GET', '/', Headers({}), None)
- self.assertRaises(BadHeaders, request.writeTo, self.transport)
- self.assertEqual(self.transport.value(), '')
-
- request = Request('GET', '/', Headers({'Host': ['example.com', 'example.org']}), None)
- self.assertRaises(BadHeaders, request.writeTo, self.transport)
- self.assertEqual(self.transport.value(), '')
-
-
- def test_stopWriting(self):
- """
- L{Request.stopWriting} calls its body producer's C{stopProducing}
- method.
- """
- producer = StringProducer(3)
- request = Request('GET', '/', _boringHeaders, producer)
- request.writeTo(self.transport)
- self.assertFalse(producer.stopped)
- request.stopWriting()
- self.assertTrue(producer.stopped)
-
-
- def test_brokenStopProducing(self):
- """
- If the body producer's C{stopProducing} method raises an exception,
- L{Request.stopWriting} logs it and does not re-raise it.
- """
- producer = StringProducer(3)
- def brokenStopProducing():
- raise ArbitraryException("stopProducing is busted")
- producer.stopProducing = brokenStopProducing
-
- request = Request('GET', '/', _boringHeaders, producer)
- request.writeTo(self.transport)
- request.stopWriting()
- self.assertEqual(
- len(self.flushLoggedErrors(ArbitraryException)), 1)
-
-
-
-class LengthEnforcingConsumerTests(TestCase):
- """
- Tests for L{LengthEnforcingConsumer}.
- """
- def setUp(self):
- self.result = Deferred()
- self.producer = StringProducer(10)
- self.transport = StringTransport()
- self.enforcer = LengthEnforcingConsumer(
- self.producer, self.transport, self.result)
-
-
- def test_write(self):
- """
- L{LengthEnforcingConsumer.write} calls the wrapped consumer's C{write}
- method with the bytes it is passed as long as there are fewer of them
- than the C{length} attribute indicates remain to be received.
- """
- self.enforcer.write('abc')
- self.assertEqual(self.transport.value(), 'abc')
- self.transport.clear()
- self.enforcer.write('def')
- self.assertEqual(self.transport.value(), 'def')
-
-
- def test_finishedEarly(self):
- """
- L{LengthEnforcingConsumer._noMoreWritesExpected} raises
- L{WrongBodyLength} if it is called before the indicated number of bytes
- have been written.
- """
- self.enforcer.write('x' * 9)
- self.assertRaises(WrongBodyLength, self.enforcer._noMoreWritesExpected)
-
-
- def test_writeTooMany(self, _unregisterAfter=False):
- """
- If it is called with a total number of bytes exceeding the indicated
- limit passed to L{LengthEnforcingConsumer.__init__},
- L{LengthEnforcingConsumer.write} fires the L{Deferred} with a
- L{Failure} wrapping a L{WrongBodyLength} and also calls the
- C{stopProducing} method of the producer.
- """
- self.enforcer.write('x' * 10)
- self.assertFalse(self.producer.stopped)
- self.enforcer.write('x')
- self.assertTrue(self.producer.stopped)
- if _unregisterAfter:
- self.enforcer._noMoreWritesExpected()
- return self.assertFailure(self.result, WrongBodyLength)
-
-
- def test_writeAfterNoMoreExpected(self):
- """
- If L{LengthEnforcingConsumer.write} is called after
- L{LengthEnforcingConsumer._noMoreWritesExpected}, it calls the
- producer's C{stopProducing} method and raises L{ExcessWrite}.
- """
- self.enforcer.write('x' * 10)
- self.enforcer._noMoreWritesExpected()
- self.assertFalse(self.producer.stopped)
- self.assertRaises(ExcessWrite, self.enforcer.write, 'x')
- self.assertTrue(self.producer.stopped)
-
-
- def test_finishedLate(self):
- """
- L{LengthEnforcingConsumer._noMoreWritesExpected} does nothing (in
- particular, it does not raise any exception) if called after too many
- bytes have been passed to C{write}.
- """
- return self.test_writeTooMany(True)
-
-
- def test_finished(self):
- """
- If L{LengthEnforcingConsumer._noMoreWritesExpected} is called after
- the correct number of bytes have been written it returns C{None}.
- """
- self.enforcer.write('x' * 10)
- self.assertIdentical(self.enforcer._noMoreWritesExpected(), None)
-
-
- def test_stopProducingRaises(self):
- """
- If L{LengthEnforcingConsumer.write} calls the producer's
- C{stopProducing} because too many bytes were written and the
- C{stopProducing} method raises an exception, the exception is logged
- and the L{LengthEnforcingConsumer} still errbacks the finished
- L{Deferred}.
- """
- def brokenStopProducing():
- StringProducer.stopProducing(self.producer)
- raise ArbitraryException("stopProducing is busted")
- self.producer.stopProducing = brokenStopProducing
-
- def cbFinished(ignored):
- self.assertEqual(
- len(self.flushLoggedErrors(ArbitraryException)), 1)
- d = self.test_writeTooMany()
- d.addCallback(cbFinished)
- return d
-
-
-
-class RequestBodyConsumerTests(TestCase):
- """
- Tests for L{ChunkedEncoder} which sits between an L{ITransport} and a
- request/response body producer and chunked encodes everything written to
- it.
- """
- def test_interface(self):
- """
- L{ChunkedEncoder} instances provide L{IConsumer}.
- """
- self.assertTrue(
- verifyObject(IConsumer, ChunkedEncoder(StringTransport())))
-
-
- def test_write(self):
- """
- L{ChunkedEncoder.write} writes to the transport the chunked encoded
- form of the bytes passed to it.
- """
- transport = StringTransport()
- encoder = ChunkedEncoder(transport)
- encoder.write('foo')
- self.assertEqual(transport.value(), '3\r\nfoo\r\n')
- transport.clear()
- encoder.write('x' * 16)
- self.assertEqual(transport.value(), '10\r\n' + 'x' * 16 + '\r\n')
-
-
- def test_producerRegistration(self):
- """
- L{ChunkedEncoder.registerProducer} registers the given streaming
- producer with its transport and L{ChunkedEncoder.unregisterProducer}
- writes a zero-length chunk to its transport and unregisters the
- transport's producer.
- """
- transport = StringTransport()
- producer = object()
- encoder = ChunkedEncoder(transport)
- encoder.registerProducer(producer, True)
- self.assertIdentical(transport.producer, producer)
- self.assertTrue(transport.streaming)
- encoder.unregisterProducer()
- self.assertIdentical(transport.producer, None)
- self.assertEqual(transport.value(), '0\r\n\r\n')
-
-
-
-class TransportProxyProducerTests(TestCase):
- """
- Tests for L{TransportProxyProducer} which proxies the L{IPushProducer}
- interface of a transport.
- """
- def test_interface(self):
- """
- L{TransportProxyProducer} instances provide L{IPushProducer}.
- """
- self.assertTrue(
- verifyObject(IPushProducer, TransportProxyProducer(None)))
-
-
- def test_stopProxyingUnreferencesProducer(self):
- """
- L{TransportProxyProducer._stopProxying} drops the reference to the
- wrapped L{IPushProducer} provider.
- """
- transport = StringTransport()
- proxy = TransportProxyProducer(transport)
- self.assertIdentical(proxy._producer, transport)
- proxy._stopProxying()
- self.assertIdentical(proxy._producer, None)
-
-
- def test_resumeProducing(self):
- """
- L{TransportProxyProducer.resumeProducing} calls the wrapped
- transport's C{resumeProducing} method unless told to stop proxying.
- """
- transport = StringTransport()
- transport.pauseProducing()
-
- proxy = TransportProxyProducer(transport)
- # The transport should still be paused.
- self.assertEqual(transport.producerState, 'paused')
- proxy.resumeProducing()
- # The transport should now be resumed.
- self.assertEqual(transport.producerState, 'producing')
-
- transport.pauseProducing()
- proxy._stopProxying()
-
- # The proxy should no longer do anything to the transport.
- proxy.resumeProducing()
- self.assertEqual(transport.producerState, 'paused')
-
-
- def test_pauseProducing(self):
- """
- L{TransportProxyProducer.pauseProducing} calls the wrapped transport's
- C{pauseProducing} method unless told to stop proxying.
- """
- transport = StringTransport()
-
- proxy = TransportProxyProducer(transport)
- # The transport should still be producing.
- self.assertEqual(transport.producerState, 'producing')
- proxy.pauseProducing()
- # The transport should now be paused.
- self.assertEqual(transport.producerState, 'paused')
-
- transport.resumeProducing()
- proxy._stopProxying()
-
- # The proxy should no longer do anything to the transport.
- proxy.pauseProducing()
- self.assertEqual(transport.producerState, 'producing')
-
-
- def test_stopProducing(self):
- """
- L{TransportProxyProducer.stopProducing} calls the wrapped transport's
- C{stopProducing} method unless told to stop proxying.
- """
- transport = StringTransport()
- proxy = TransportProxyProducer(transport)
- # The transport should still be producing.
- self.assertEqual(transport.producerState, 'producing')
- proxy.stopProducing()
- # The transport should now be stopped.
- self.assertEqual(transport.producerState, 'stopped')
-
- transport = StringTransport()
- proxy = TransportProxyProducer(transport)
- proxy._stopProxying()
- proxy.stopProducing()
- # The transport should not have been stopped.
- self.assertEqual(transport.producerState, 'producing')
-
-
-
-class ResponseTests(TestCase):
- """
- Tests for L{Response}.
- """
-
- def test_verifyInterface(self):
- """
- L{Response} instances provide L{IResponse}.
- """
- response = justTransportResponse(StringTransport())
- self.assertTrue(verifyObject(IResponse, response))
-
-
- def test_makeConnection(self):
- """
- The L{IProtocol} provider passed to L{Response.deliverBody} has its
- C{makeConnection} method called with an L{IPushProducer} provider
- hooked up to the response as an argument.
- """
- producers = []
- transport = StringTransport()
- class SomeProtocol(Protocol):
- def makeConnection(self, producer):
- producers.append(producer)
-
- consumer = SomeProtocol()
- response = justTransportResponse(transport)
- response.deliverBody(consumer)
- [theProducer] = producers
- theProducer.pauseProducing()
- self.assertEqual(transport.producerState, 'paused')
- theProducer.resumeProducing()
- self.assertEqual(transport.producerState, 'producing')
-
-
- def test_dataReceived(self):
- """
- The L{IProtocol} provider passed to L{Response.deliverBody} has its
- C{dataReceived} method called with bytes received as part of the
- response body.
- """
- bytes = []
- class ListConsumer(Protocol):
- def dataReceived(self, data):
- bytes.append(data)
-
-
- consumer = ListConsumer()
- response = justTransportResponse(StringTransport())
- response.deliverBody(consumer)
-
- response._bodyDataReceived('foo')
- self.assertEqual(bytes, ['foo'])
-
-
- def test_connectionLost(self):
- """
- The L{IProtocol} provider passed to L{Response.deliverBody} has its
- C{connectionLost} method called with a L{Failure} wrapping
- L{ResponseDone} when the response's C{_bodyDataFinished} method is
- called.
- """
- lost = []
- class ListConsumer(Protocol):
- def connectionLost(self, reason):
- lost.append(reason)
-
- consumer = ListConsumer()
- response = justTransportResponse(StringTransport())
- response.deliverBody(consumer)
-
- response._bodyDataFinished()
- lost[0].trap(ResponseDone)
- self.assertEqual(len(lost), 1)
-
- # The protocol reference should be dropped, too, to facilitate GC or
- # whatever.
- self.assertIdentical(response._bodyProtocol, None)
-
-
- def test_bufferEarlyData(self):
- """
- If data is delivered to the L{Response} before a protocol is registered
- with C{deliverBody}, that data is buffered until the protocol is
- registered and then is delivered.
- """
- bytes = []
- class ListConsumer(Protocol):
- def dataReceived(self, data):
- bytes.append(data)
-
- protocol = ListConsumer()
- response = justTransportResponse(StringTransport())
- response._bodyDataReceived('foo')
- response._bodyDataReceived('bar')
- response.deliverBody(protocol)
- response._bodyDataReceived('baz')
- self.assertEqual(bytes, ['foo', 'bar', 'baz'])
- # Make sure the implementation-detail-byte-buffer is cleared because
- # not clearing it wastes memory.
- self.assertIdentical(response._bodyBuffer, None)
-
-
- def test_multipleStartProducingFails(self):
- """
- L{Response.deliverBody} raises L{RuntimeError} if called more than
- once.
- """
- response = justTransportResponse(StringTransport())
- response.deliverBody(Protocol())
- self.assertRaises(RuntimeError, response.deliverBody, Protocol())
-
-
- def test_startProducingAfterFinishedFails(self):
- """
- L{Response.deliverBody} raises L{RuntimeError} if called after
- L{Response._bodyDataFinished}.
- """
- response = justTransportResponse(StringTransport())
- response.deliverBody(Protocol())
- response._bodyDataFinished()
- self.assertRaises(RuntimeError, response.deliverBody, Protocol())
-
-
- def test_bodyDataReceivedAfterFinishedFails(self):
- """
- L{Response._bodyDataReceived} raises L{RuntimeError} if called after
- L{Response._bodyDataFinished} but before L{Response.deliverBody}.
- """
- response = justTransportResponse(StringTransport())
- response._bodyDataFinished()
- self.assertRaises(RuntimeError, response._bodyDataReceived, 'foo')
-
-
- def test_bodyDataReceivedAfterDeliveryFails(self):
- """
- L{Response._bodyDataReceived} raises L{RuntimeError} if called after
- L{Response._bodyDataFinished} and after L{Response.deliverBody}.
- """
- response = justTransportResponse(StringTransport())
- response._bodyDataFinished()
- response.deliverBody(Protocol())
- self.assertRaises(RuntimeError, response._bodyDataReceived, 'foo')
-
-
- def test_bodyDataFinishedAfterFinishedFails(self):
- """
- L{Response._bodyDataFinished} raises L{RuntimeError} if called more
- than once.
- """
- response = justTransportResponse(StringTransport())
- response._bodyDataFinished()
- self.assertRaises(RuntimeError, response._bodyDataFinished)
-
-
- def test_bodyDataFinishedAfterDeliveryFails(self):
- """
- L{Response._bodyDataFinished} raises L{RuntimeError} if called after
- the body has been delivered.
- """
- response = justTransportResponse(StringTransport())
- response._bodyDataFinished()
- response.deliverBody(Protocol())
- self.assertRaises(RuntimeError, response._bodyDataFinished)
-
-
- def test_transportResumed(self):
- """
- L{Response.deliverBody} resumes the HTTP connection's transport
- before passing it to the transport's C{makeConnection} method.
- """
- transportState = []
- class ListConsumer(Protocol):
- def makeConnection(self, transport):
- transportState.append(transport.producerState)
-
- transport = StringTransport()
- transport.pauseProducing()
- protocol = ListConsumer()
- response = justTransportResponse(transport)
- self.assertEqual(transport.producerState, 'paused')
- response.deliverBody(protocol)
- self.assertEqual(transportState, ['producing'])
-
-
- def test_bodyDataFinishedBeforeStartProducing(self):
- """
- If the entire body is delivered to the L{Response} before the
- response's C{deliverBody} method is called, the protocol passed to
- C{deliverBody} is immediately given the body data and then
- disconnected.
- """
- transport = StringTransport()
- response = justTransportResponse(transport)
- response._bodyDataReceived('foo')
- response._bodyDataReceived('bar')
- response._bodyDataFinished()
-
- protocol = AccumulatingProtocol()
- response.deliverBody(protocol)
- self.assertEqual(protocol.data, 'foobar')
- protocol.closedReason.trap(ResponseDone)
-
-
- def test_finishedWithErrorWhenConnected(self):
- """
- The L{Failure} passed to L{Response._bodyDataFinished} when the response
- is in the I{connected} state is passed to the C{connectionLost} method
- of the L{IProtocol} provider passed to the L{Response}'s
- C{deliverBody} method.
- """
- transport = StringTransport()
- response = justTransportResponse(transport)
-
- protocol = AccumulatingProtocol()
- response.deliverBody(protocol)
-
- # Sanity check - this test is for the connected state
- self.assertEqual(response._state, 'CONNECTED')
- response._bodyDataFinished(Failure(ArbitraryException()))
-
- protocol.closedReason.trap(ArbitraryException)
-
-
- def test_finishedWithErrorWhenInitial(self):
- """
- The L{Failure} passed to L{Response._bodyDataFinished} when the response
- is in the I{initial} state is passed to the C{connectionLost} method of
- the L{IProtocol} provider passed to the L{Response}'s C{deliverBody}
- method.
- """
- transport = StringTransport()
- response = justTransportResponse(transport)
-
- # Sanity check - this test is for the initial state
- self.assertEqual(response._state, 'INITIAL')
- response._bodyDataFinished(Failure(ArbitraryException()))
-
- protocol = AccumulatingProtocol()
- response.deliverBody(protocol)
-
- protocol.closedReason.trap(ArbitraryException)