aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_http.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_http.py')
-rwxr-xr-xlib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_http.py1663
1 files changed, 0 insertions, 1663 deletions
diff --git a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_http.py b/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_http.py
deleted file mode 100755
index 959acfa5..00000000
--- a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/web/test/test_http.py
+++ /dev/null
@@ -1,1663 +0,0 @@
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-Test HTTP support.
-"""
-
-from urlparse import urlparse, urlunsplit, clear_cache
-import random, urllib, cgi
-
-from twisted.python.compat import set
-from twisted.python.failure import Failure
-from twisted.trial import unittest
-from twisted.trial.unittest import TestCase
-from twisted.web import http, http_headers
-from twisted.web.http import PotentialDataLoss, _DataLoss
-from twisted.web.http import _IdentityTransferDecoder
-from twisted.protocols import loopback
-from twisted.internet.task import Clock
-from twisted.internet.error import ConnectionLost
-from twisted.test.proto_helpers import StringTransport
-from twisted.test.test_internet import DummyProducer
-from twisted.web.test.test_web import DummyChannel
-
-
-class DateTimeTest(unittest.TestCase):
- """Test date parsing functions."""
-
- def testRoundtrip(self):
- for i in range(10000):
- time = random.randint(0, 2000000000)
- timestr = http.datetimeToString(time)
- time2 = http.stringToDatetime(timestr)
- self.assertEqual(time, time2)
-
-
-class DummyHTTPHandler(http.Request):
-
- def process(self):
- self.content.seek(0, 0)
- data = self.content.read()
- length = self.getHeader('content-length')
- request = "'''\n"+str(length)+"\n"+data+"'''\n"
- self.setResponseCode(200)
- self.setHeader("Request", self.uri)
- self.setHeader("Command", self.method)
- self.setHeader("Version", self.clientproto)
- self.setHeader("Content-Length", len(request))
- self.write(request)
- self.finish()
-
-
-class LoopbackHTTPClient(http.HTTPClient):
-
- def connectionMade(self):
- self.sendCommand("GET", "/foo/bar")
- self.sendHeader("Content-Length", 10)
- self.endHeaders()
- self.transport.write("0123456789")
-
-
-class ResponseTestMixin(object):
- """
- A mixin that provides a simple means of comparing an actual response string
- to an expected response string by performing the minimal parsing.
- """
-
- def assertResponseEquals(self, responses, expected):
- """
- Assert that the C{responses} matches the C{expected} responses.
-
- @type responses: C{str}
- @param responses: The bytes sent in response to one or more requests.
-
- @type expected: C{list} of C{tuple} of C{str}
- @param expected: The expected values for the responses. Each tuple
- element of the list represents one response. Each string element
- of the tuple is a full header line without delimiter, except for
- the last element which gives the full response body.
- """
- for response in expected:
- expectedHeaders, expectedContent = response[:-1], response[-1]
- headers, rest = responses.split('\r\n\r\n', 1)
- headers = headers.splitlines()
- self.assertEqual(set(headers), set(expectedHeaders))
- content = rest[:len(expectedContent)]
- responses = rest[len(expectedContent):]
- self.assertEqual(content, expectedContent)
-
-
-
-class HTTP1_0TestCase(unittest.TestCase, ResponseTestMixin):
- requests = (
- "GET / HTTP/1.0\r\n"
- "\r\n"
- "GET / HTTP/1.1\r\n"
- "Accept: text/html\r\n"
- "\r\n")
-
- expected_response = [
- ("HTTP/1.0 200 OK",
- "Request: /",
- "Command: GET",
- "Version: HTTP/1.0",
- "Content-Length: 13",
- "'''\nNone\n'''\n")]
-
- def test_buffer(self):
- """
- Send requests over a channel and check responses match what is expected.
- """
- b = StringTransport()
- a = http.HTTPChannel()
- a.requestFactory = DummyHTTPHandler
- a.makeConnection(b)
- # one byte at a time, to stress it.
- for byte in self.requests:
- a.dataReceived(byte)
- a.connectionLost(IOError("all one"))
- value = b.value()
- self.assertResponseEquals(value, self.expected_response)
-
-
- def test_requestBodyTimeout(self):
- """
- L{HTTPChannel} resets its timeout whenever data from a request body is
- delivered to it.
- """
- clock = Clock()
- transport = StringTransport()
- protocol = http.HTTPChannel()
- protocol.timeOut = 100
- protocol.callLater = clock.callLater
- protocol.makeConnection(transport)
- protocol.dataReceived('POST / HTTP/1.0\r\nContent-Length: 2\r\n\r\n')
- clock.advance(99)
- self.assertFalse(transport.disconnecting)
- protocol.dataReceived('x')
- clock.advance(99)
- self.assertFalse(transport.disconnecting)
- protocol.dataReceived('x')
- self.assertEqual(len(protocol.requests), 1)
-
-
-
-class HTTP1_1TestCase(HTTP1_0TestCase):
-
- requests = (
- "GET / HTTP/1.1\r\n"
- "Accept: text/html\r\n"
- "\r\n"
- "POST / HTTP/1.1\r\n"
- "Content-Length: 10\r\n"
- "\r\n"
- "0123456789POST / HTTP/1.1\r\n"
- "Content-Length: 10\r\n"
- "\r\n"
- "0123456789HEAD / HTTP/1.1\r\n"
- "\r\n")
-
- expected_response = [
- ("HTTP/1.1 200 OK",
- "Request: /",
- "Command: GET",
- "Version: HTTP/1.1",
- "Content-Length: 13",
- "'''\nNone\n'''\n"),
- ("HTTP/1.1 200 OK",
- "Request: /",
- "Command: POST",
- "Version: HTTP/1.1",
- "Content-Length: 21",
- "'''\n10\n0123456789'''\n"),
- ("HTTP/1.1 200 OK",
- "Request: /",
- "Command: POST",
- "Version: HTTP/1.1",
- "Content-Length: 21",
- "'''\n10\n0123456789'''\n"),
- ("HTTP/1.1 200 OK",
- "Request: /",
- "Command: HEAD",
- "Version: HTTP/1.1",
- "Content-Length: 13",
- "")]
-
-
-
-class HTTP1_1_close_TestCase(HTTP1_0TestCase):
-
- requests = (
- "GET / HTTP/1.1\r\n"
- "Accept: text/html\r\n"
- "Connection: close\r\n"
- "\r\n"
- "GET / HTTP/1.0\r\n"
- "\r\n")
-
- expected_response = [
- ("HTTP/1.1 200 OK",
- "Connection: close",
- "Request: /",
- "Command: GET",
- "Version: HTTP/1.1",
- "Content-Length: 13",
- "'''\nNone\n'''\n")]
-
-
-
-class HTTP0_9TestCase(HTTP1_0TestCase):
-
- requests = (
- "GET /\r\n")
-
- expected_response = "HTTP/1.1 400 Bad Request\r\n\r\n"
-
-
- def assertResponseEquals(self, response, expectedResponse):
- self.assertEqual(response, expectedResponse)
-
-
-class HTTPLoopbackTestCase(unittest.TestCase):
-
- expectedHeaders = {'request' : '/foo/bar',
- 'command' : 'GET',
- 'version' : 'HTTP/1.0',
- 'content-length' : '21'}
- numHeaders = 0
- gotStatus = 0
- gotResponse = 0
- gotEndHeaders = 0
-
- def _handleStatus(self, version, status, message):
- self.gotStatus = 1
- self.assertEqual(version, "HTTP/1.0")
- self.assertEqual(status, "200")
-
- def _handleResponse(self, data):
- self.gotResponse = 1
- self.assertEqual(data, "'''\n10\n0123456789'''\n")
-
- def _handleHeader(self, key, value):
- self.numHeaders = self.numHeaders + 1
- self.assertEqual(self.expectedHeaders[key.lower()], value)
-
- def _handleEndHeaders(self):
- self.gotEndHeaders = 1
- self.assertEqual(self.numHeaders, 4)
-
- def testLoopback(self):
- server = http.HTTPChannel()
- server.requestFactory = DummyHTTPHandler
- client = LoopbackHTTPClient()
- client.handleResponse = self._handleResponse
- client.handleHeader = self._handleHeader
- client.handleEndHeaders = self._handleEndHeaders
- client.handleStatus = self._handleStatus
- d = loopback.loopbackAsync(server, client)
- d.addCallback(self._cbTestLoopback)
- return d
-
- def _cbTestLoopback(self, ignored):
- if not (self.gotStatus and self.gotResponse and self.gotEndHeaders):
- raise RuntimeError(
- "didn't got all callbacks %s"
- % [self.gotStatus, self.gotResponse, self.gotEndHeaders])
- del self.gotEndHeaders
- del self.gotResponse
- del self.gotStatus
- del self.numHeaders
-
-
-
-def _prequest(**headers):
- """
- Make a request with the given request headers for the persistence tests.
- """
- request = http.Request(DummyChannel(), None)
- for k, v in headers.iteritems():
- request.requestHeaders.setRawHeaders(k, v)
- return request
-
-
-class PersistenceTestCase(unittest.TestCase):
- """
- Tests for persistent HTTP connections.
- """
-
- ptests = [#(PRequest(connection="Keep-Alive"), "HTTP/1.0", 1, {'connection' : 'Keep-Alive'}),
- (_prequest(), "HTTP/1.0", 0, {'connection': None}),
- (_prequest(connection=["close"]), "HTTP/1.1", 0, {'connection' : ['close']}),
- (_prequest(), "HTTP/1.1", 1, {'connection': None}),
- (_prequest(), "HTTP/0.9", 0, {'connection': None}),
- ]
-
-
- def testAlgorithm(self):
- c = http.HTTPChannel()
- for req, version, correctResult, resultHeaders in self.ptests:
- result = c.checkPersistence(req, version)
- self.assertEqual(result, correctResult)
- for header in resultHeaders.keys():
- self.assertEqual(req.responseHeaders.getRawHeaders(header, None), resultHeaders[header])
-
-
-
-class IdentityTransferEncodingTests(TestCase):
- """
- Tests for L{_IdentityTransferDecoder}.
- """
- def setUp(self):
- """
- Create an L{_IdentityTransferDecoder} with callbacks hooked up so that
- calls to them can be inspected.
- """
- self.data = []
- self.finish = []
- self.contentLength = 10
- self.decoder = _IdentityTransferDecoder(
- self.contentLength, self.data.append, self.finish.append)
-
-
- def test_exactAmountReceived(self):
- """
- If L{_IdentityTransferDecoder.dataReceived} is called with a string
- with length equal to the content length passed to
- L{_IdentityTransferDecoder}'s initializer, the data callback is invoked
- with that string and the finish callback is invoked with a zero-length
- string.
- """
- self.decoder.dataReceived('x' * self.contentLength)
- self.assertEqual(self.data, ['x' * self.contentLength])
- self.assertEqual(self.finish, [''])
-
-
- def test_shortStrings(self):
- """
- If L{_IdentityTransferDecoder.dataReceived} is called multiple times
- with strings which, when concatenated, are as long as the content
- length provided, the data callback is invoked with each string and the
- finish callback is invoked only after the second call.
- """
- self.decoder.dataReceived('x')
- self.assertEqual(self.data, ['x'])
- self.assertEqual(self.finish, [])
- self.decoder.dataReceived('y' * (self.contentLength - 1))
- self.assertEqual(self.data, ['x', 'y' * (self.contentLength - 1)])
- self.assertEqual(self.finish, [''])
-
-
- def test_longString(self):
- """
- If L{_IdentityTransferDecoder.dataReceived} is called with a string
- with length greater than the provided content length, only the prefix
- of that string up to the content length is passed to the data callback
- and the remainder is passed to the finish callback.
- """
- self.decoder.dataReceived('x' * self.contentLength + 'y')
- self.assertEqual(self.data, ['x' * self.contentLength])
- self.assertEqual(self.finish, ['y'])
-
-
- def test_rejectDataAfterFinished(self):
- """
- If data is passed to L{_IdentityTransferDecoder.dataReceived} after the
- finish callback has been invoked, L{RuntimeError} is raised.
- """
- failures = []
- def finish(bytes):
- try:
- decoder.dataReceived('foo')
- except:
- failures.append(Failure())
- decoder = _IdentityTransferDecoder(5, self.data.append, finish)
- decoder.dataReceived('x' * 4)
- self.assertEqual(failures, [])
- decoder.dataReceived('y')
- failures[0].trap(RuntimeError)
- self.assertEqual(
- str(failures[0].value),
- "_IdentityTransferDecoder cannot decode data after finishing")
-
-
- def test_unknownContentLength(self):
- """
- If L{_IdentityTransferDecoder} is constructed with C{None} for the
- content length, it passes all data delivered to it through to the data
- callback.
- """
- data = []
- finish = []
- decoder = _IdentityTransferDecoder(None, data.append, finish.append)
- decoder.dataReceived('x')
- self.assertEqual(data, ['x'])
- decoder.dataReceived('y')
- self.assertEqual(data, ['x', 'y'])
- self.assertEqual(finish, [])
-
-
- def _verifyCallbacksUnreferenced(self, decoder):
- """
- Check the decoder's data and finish callbacks and make sure they are
- None in order to help avoid references cycles.
- """
- self.assertIdentical(decoder.dataCallback, None)
- self.assertIdentical(decoder.finishCallback, None)
-
-
- def test_earlyConnectionLose(self):
- """
- L{_IdentityTransferDecoder.noMoreData} raises L{_DataLoss} if it is
- called and the content length is known but not enough bytes have been
- delivered.
- """
- self.decoder.dataReceived('x' * (self.contentLength - 1))
- self.assertRaises(_DataLoss, self.decoder.noMoreData)
- self._verifyCallbacksUnreferenced(self.decoder)
-
-
- def test_unknownContentLengthConnectionLose(self):
- """
- L{_IdentityTransferDecoder.noMoreData} calls the finish callback and
- raises L{PotentialDataLoss} if it is called and the content length is
- unknown.
- """
- body = []
- finished = []
- decoder = _IdentityTransferDecoder(None, body.append, finished.append)
- self.assertRaises(PotentialDataLoss, decoder.noMoreData)
- self.assertEqual(body, [])
- self.assertEqual(finished, [''])
- self._verifyCallbacksUnreferenced(decoder)
-
-
- def test_finishedConnectionLose(self):
- """
- L{_IdentityTransferDecoder.noMoreData} does not raise any exception if
- it is called when the content length is known and that many bytes have
- been delivered.
- """
- self.decoder.dataReceived('x' * self.contentLength)
- self.decoder.noMoreData()
- self._verifyCallbacksUnreferenced(self.decoder)
-
-
-
-class ChunkedTransferEncodingTests(unittest.TestCase):
- """
- Tests for L{_ChunkedTransferDecoder}, which turns a byte stream encoded
- using HTTP I{chunked} C{Transfer-Encoding} back into the original byte
- stream.
- """
- def test_decoding(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} decodes chunked-encoded data
- and passes the result to the specified callback.
- """
- L = []
- p = http._ChunkedTransferDecoder(L.append, None)
- p.dataReceived('3\r\nabc\r\n5\r\n12345\r\n')
- p.dataReceived('a\r\n0123456789\r\n')
- self.assertEqual(L, ['abc', '12345', '0123456789'])
-
-
- def test_short(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} decodes chunks broken up and
- delivered in multiple calls.
- """
- L = []
- finished = []
- p = http._ChunkedTransferDecoder(L.append, finished.append)
- for s in '3\r\nabc\r\n5\r\n12345\r\n0\r\n\r\n':
- p.dataReceived(s)
- self.assertEqual(L, ['a', 'b', 'c', '1', '2', '3', '4', '5'])
- self.assertEqual(finished, [''])
-
-
- def test_newlines(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} doesn't treat CR LF pairs
- embedded in chunk bodies specially.
- """
- L = []
- p = http._ChunkedTransferDecoder(L.append, None)
- p.dataReceived('2\r\n\r\n\r\n')
- self.assertEqual(L, ['\r\n'])
-
-
- def test_extensions(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} disregards chunk-extension
- fields.
- """
- L = []
- p = http._ChunkedTransferDecoder(L.append, None)
- p.dataReceived('3; x-foo=bar\r\nabc\r\n')
- self.assertEqual(L, ['abc'])
-
-
- def test_finish(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} interprets a zero-length
- chunk as the end of the chunked data stream and calls the completion
- callback.
- """
- finished = []
- p = http._ChunkedTransferDecoder(None, finished.append)
- p.dataReceived('0\r\n\r\n')
- self.assertEqual(finished, [''])
-
-
- def test_extra(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} passes any bytes which come
- after the terminating zero-length chunk to the completion callback.
- """
- finished = []
- p = http._ChunkedTransferDecoder(None, finished.append)
- p.dataReceived('0\r\n\r\nhello')
- self.assertEqual(finished, ['hello'])
-
-
- def test_afterFinished(self):
- """
- L{_ChunkedTransferDecoder.dataReceived} raises L{RuntimeError} if it
- is called after it has seen the last chunk.
- """
- p = http._ChunkedTransferDecoder(None, lambda bytes: None)
- p.dataReceived('0\r\n\r\n')
- self.assertRaises(RuntimeError, p.dataReceived, 'hello')
-
-
- def test_earlyConnectionLose(self):
- """
- L{_ChunkedTransferDecoder.noMoreData} raises L{_DataLoss} if it is
- called and the end of the last trailer has not yet been received.
- """
- parser = http._ChunkedTransferDecoder(None, lambda bytes: None)
- parser.dataReceived('0\r\n\r')
- exc = self.assertRaises(_DataLoss, parser.noMoreData)
- self.assertEqual(
- str(exc),
- "Chunked decoder in 'TRAILER' state, still expecting more data "
- "to get to 'FINISHED' state.")
-
-
- def test_finishedConnectionLose(self):
- """
- L{_ChunkedTransferDecoder.noMoreData} does not raise any exception if
- it is called after the terminal zero length chunk is received.
- """
- parser = http._ChunkedTransferDecoder(None, lambda bytes: None)
- parser.dataReceived('0\r\n\r\n')
- parser.noMoreData()
-
-
- def test_reentrantFinishedNoMoreData(self):
- """
- L{_ChunkedTransferDecoder.noMoreData} can be called from the finished
- callback without raising an exception.
- """
- errors = []
- successes = []
- def finished(extra):
- try:
- parser.noMoreData()
- except:
- errors.append(Failure())
- else:
- successes.append(True)
- parser = http._ChunkedTransferDecoder(None, finished)
- parser.dataReceived('0\r\n\r\n')
- self.assertEqual(errors, [])
- self.assertEqual(successes, [True])
-
-
-
-class ChunkingTestCase(unittest.TestCase):
-
- strings = ["abcv", "", "fdfsd423", "Ffasfas\r\n",
- "523523\n\rfsdf", "4234"]
-
- def testChunks(self):
- for s in self.strings:
- self.assertEqual((s, ''), http.fromChunk(''.join(http.toChunk(s))))
- self.assertRaises(ValueError, http.fromChunk, '-5\r\nmalformed!\r\n')
-
- def testConcatenatedChunks(self):
- chunked = ''.join([''.join(http.toChunk(t)) for t in self.strings])
- result = []
- buffer = ""
- for c in chunked:
- buffer = buffer + c
- try:
- data, buffer = http.fromChunk(buffer)
- result.append(data)
- except ValueError:
- pass
- self.assertEqual(result, self.strings)
-
-
-
-class ParsingTestCase(unittest.TestCase):
- """
- Tests for protocol parsing in L{HTTPChannel}.
- """
- def runRequest(self, httpRequest, requestClass, success=1):
- httpRequest = httpRequest.replace("\n", "\r\n")
- b = StringTransport()
- a = http.HTTPChannel()
- a.requestFactory = requestClass
- a.makeConnection(b)
- # one byte at a time, to stress it.
- for byte in httpRequest:
- if a.transport.disconnecting:
- break
- a.dataReceived(byte)
- a.connectionLost(IOError("all done"))
- if success:
- self.assertEqual(self.didRequest, 1)
- del self.didRequest
- else:
- self.assert_(not hasattr(self, "didRequest"))
- return a
-
-
- def test_basicAuth(self):
- """
- L{HTTPChannel} provides username and password information supplied in
- an I{Authorization} header to the L{Request} which makes it available
- via its C{getUser} and C{getPassword} methods.
- """
- testcase = self
- class Request(http.Request):
- l = []
- def process(self):
- testcase.assertEqual(self.getUser(), self.l[0])
- testcase.assertEqual(self.getPassword(), self.l[1])
- for u, p in [("foo", "bar"), ("hello", "there:z")]:
- Request.l[:] = [u, p]
- s = "%s:%s" % (u, p)
- f = "GET / HTTP/1.0\nAuthorization: Basic %s\n\n" % (s.encode("base64").strip(), )
- self.runRequest(f, Request, 0)
-
-
- def test_headers(self):
- """
- Headers received by L{HTTPChannel} in a request are made available to
- the L{Request}.
- """
- processed = []
- class MyRequest(http.Request):
- def process(self):
- processed.append(self)
- self.finish()
-
- requestLines = [
- "GET / HTTP/1.0",
- "Foo: bar",
- "baz: Quux",
- "baz: quux",
- "",
- ""]
-
- self.runRequest('\n'.join(requestLines), MyRequest, 0)
- [request] = processed
- self.assertEqual(
- request.requestHeaders.getRawHeaders('foo'), ['bar'])
- self.assertEqual(
- request.requestHeaders.getRawHeaders('bAz'), ['Quux', 'quux'])
-
-
- def test_tooManyHeaders(self):
- """
- L{HTTPChannel} enforces a limit of C{HTTPChannel.maxHeaders} on the
- number of headers received per request.
- """
- processed = []
- class MyRequest(http.Request):
- def process(self):
- processed.append(self)
-
- requestLines = ["GET / HTTP/1.0"]
- for i in range(http.HTTPChannel.maxHeaders + 2):
- requestLines.append("%s: foo" % (i,))
- requestLines.extend(["", ""])
-
- channel = self.runRequest("\n".join(requestLines), MyRequest, 0)
- self.assertEqual(processed, [])
- self.assertEqual(
- channel.transport.value(),
- "HTTP/1.1 400 Bad Request\r\n\r\n")
-
-
- def test_headerLimitPerRequest(self):
- """
- L{HTTPChannel} enforces the limit of C{HTTPChannel.maxHeaders} per
- request so that headers received in an earlier request do not count
- towards the limit when processing a later request.
- """
- processed = []
- class MyRequest(http.Request):
- def process(self):
- processed.append(self)
- self.finish()
-
- self.patch(http.HTTPChannel, 'maxHeaders', 1)
- requestLines = [
- "GET / HTTP/1.1",
- "Foo: bar",
- "",
- "",
- "GET / HTTP/1.1",
- "Bar: baz",
- "",
- ""]
-
- channel = self.runRequest("\n".join(requestLines), MyRequest, 0)
- [first, second] = processed
- self.assertEqual(first.getHeader('foo'), 'bar')
- self.assertEqual(second.getHeader('bar'), 'baz')
- self.assertEqual(
- channel.transport.value(),
- 'HTTP/1.1 200 OK\r\n'
- 'Transfer-Encoding: chunked\r\n'
- '\r\n'
- '0\r\n'
- '\r\n'
- 'HTTP/1.1 200 OK\r\n'
- 'Transfer-Encoding: chunked\r\n'
- '\r\n'
- '0\r\n'
- '\r\n')
-
-
- def testCookies(self):
- """
- Test cookies parsing and reading.
- """
- httpRequest = '''\
-GET / HTTP/1.0
-Cookie: rabbit="eat carrot"; ninja=secret; spam="hey 1=1!"
-
-'''
- testcase = self
-
- class MyRequest(http.Request):
- def process(self):
- testcase.assertEqual(self.getCookie('rabbit'), '"eat carrot"')
- testcase.assertEqual(self.getCookie('ninja'), 'secret')
- testcase.assertEqual(self.getCookie('spam'), '"hey 1=1!"')
- testcase.didRequest = 1
- self.finish()
-
- self.runRequest(httpRequest, MyRequest)
-
- def testGET(self):
- httpRequest = '''\
-GET /?key=value&multiple=two+words&multiple=more%20words&empty= HTTP/1.0
-
-'''
- testcase = self
- class MyRequest(http.Request):
- def process(self):
- testcase.assertEqual(self.method, "GET")
- testcase.assertEqual(self.args["key"], ["value"])
- testcase.assertEqual(self.args["empty"], [""])
- testcase.assertEqual(self.args["multiple"], ["two words", "more words"])
- testcase.didRequest = 1
- self.finish()
-
- self.runRequest(httpRequest, MyRequest)
-
-
- def test_extraQuestionMark(self):
- """
- While only a single '?' is allowed in an URL, several other servers
- allow several and pass all after the first through as part of the
- query arguments. Test that we emulate this behavior.
- """
- httpRequest = 'GET /foo?bar=?&baz=quux HTTP/1.0\n\n'
-
- testcase = self
- class MyRequest(http.Request):
- def process(self):
- testcase.assertEqual(self.method, 'GET')
- testcase.assertEqual(self.path, '/foo')
- testcase.assertEqual(self.args['bar'], ['?'])
- testcase.assertEqual(self.args['baz'], ['quux'])
- testcase.didRequest = 1
- self.finish()
-
- self.runRequest(httpRequest, MyRequest)
-
-
- def test_formPOSTRequest(self):
- """
- The request body of a I{POST} request with a I{Content-Type} header
- of I{application/x-www-form-urlencoded} is parsed according to that
- content type and made available in the C{args} attribute of the
- request object. The original bytes of the request may still be read
- from the C{content} attribute.
- """
- query = 'key=value&multiple=two+words&multiple=more%20words&empty='
- httpRequest = '''\
-POST / HTTP/1.0
-Content-Length: %d
-Content-Type: application/x-www-form-urlencoded
-
-%s''' % (len(query), query)
-
- testcase = self
- class MyRequest(http.Request):
- def process(self):
- testcase.assertEqual(self.method, "POST")
- testcase.assertEqual(self.args["key"], ["value"])
- testcase.assertEqual(self.args["empty"], [""])
- testcase.assertEqual(self.args["multiple"], ["two words", "more words"])
-
- # Reading from the content file-like must produce the entire
- # request body.
- testcase.assertEqual(self.content.read(), query)
- testcase.didRequest = 1
- self.finish()
-
- self.runRequest(httpRequest, MyRequest)
-
- def testMissingContentDisposition(self):
- req = '''\
-POST / HTTP/1.0
-Content-Type: multipart/form-data; boundary=AaB03x
-Content-Length: 103
-
---AaB03x
-Content-Type: text/plain
-Content-Transfer-Encoding: quoted-printable
-
-abasdfg
---AaB03x--
-'''
- self.runRequest(req, http.Request, success=False)
-
- def test_chunkedEncoding(self):
- """
- If a request uses the I{chunked} transfer encoding, the request body is
- decoded accordingly before it is made available on the request.
- """
- httpRequest = '''\
-GET / HTTP/1.0
-Content-Type: text/plain
-Transfer-Encoding: chunked
-
-6
-Hello,
-14
- spam,eggs spam spam
-0
-
-'''
- testcase = self
- class MyRequest(http.Request):
- def process(self):
- # The tempfile API used to create content returns an
- # instance of a different type depending on what platform
- # we're running on. The point here is to verify that the
- # request body is in a file that's on the filesystem.
- # Having a fileno method that returns an int is a somewhat
- # close approximation of this. -exarkun
- testcase.assertIsInstance(self.content.fileno(), int)
- testcase.assertEqual(self.method, 'GET')
- testcase.assertEqual(self.path, '/')
- content = self.content.read()
- testcase.assertEqual(content, 'Hello, spam,eggs spam spam')
- testcase.assertIdentical(self.channel._transferDecoder, None)
- testcase.didRequest = 1
- self.finish()
-
- self.runRequest(httpRequest, MyRequest)
-
-
-
-class QueryArgumentsTestCase(unittest.TestCase):
- def testParseqs(self):
- self.assertEqual(cgi.parse_qs("a=b&d=c;+=f"),
- http.parse_qs("a=b&d=c;+=f"))
- self.failUnlessRaises(ValueError, http.parse_qs, "blah",
- strict_parsing = 1)
- self.assertEqual(cgi.parse_qs("a=&b=c", keep_blank_values = 1),
- http.parse_qs("a=&b=c", keep_blank_values = 1))
- self.assertEqual(cgi.parse_qs("a=&b=c"),
- http.parse_qs("a=&b=c"))
-
-
- def test_urlparse(self):
- """
- For a given URL, L{http.urlparse} should behave the same as
- L{urlparse}, except it should always return C{str}, never C{unicode}.
- """
- def urls():
- for scheme in ('http', 'https'):
- for host in ('example.com',):
- for port in (None, 100):
- for path in ('', 'path'):
- if port is not None:
- host = host + ':' + str(port)
- yield urlunsplit((scheme, host, path, '', ''))
-
-
- def assertSameParsing(url, decode):
- """
- Verify that C{url} is parsed into the same objects by both
- L{http.urlparse} and L{urlparse}.
- """
- urlToStandardImplementation = url
- if decode:
- urlToStandardImplementation = url.decode('ascii')
- standardResult = urlparse(urlToStandardImplementation)
- scheme, netloc, path, params, query, fragment = http.urlparse(url)
- self.assertEqual(
- (scheme, netloc, path, params, query, fragment),
- standardResult)
- self.assertTrue(isinstance(scheme, str))
- self.assertTrue(isinstance(netloc, str))
- self.assertTrue(isinstance(path, str))
- self.assertTrue(isinstance(params, str))
- self.assertTrue(isinstance(query, str))
- self.assertTrue(isinstance(fragment, str))
-
- # With caching, unicode then str
- clear_cache()
- for url in urls():
- assertSameParsing(url, True)
- assertSameParsing(url, False)
-
- # With caching, str then unicode
- clear_cache()
- for url in urls():
- assertSameParsing(url, False)
- assertSameParsing(url, True)
-
- # Without caching
- for url in urls():
- clear_cache()
- assertSameParsing(url, True)
- clear_cache()
- assertSameParsing(url, False)
-
-
- def test_urlparseRejectsUnicode(self):
- """
- L{http.urlparse} should reject unicode input early.
- """
- self.assertRaises(TypeError, http.urlparse, u'http://example.org/path')
-
-
-
-class ClientDriver(http.HTTPClient):
- def handleStatus(self, version, status, message):
- self.version = version
- self.status = status
- self.message = message
-
-class ClientStatusParsing(unittest.TestCase):
- def testBaseline(self):
- c = ClientDriver()
- c.lineReceived('HTTP/1.0 201 foo')
- self.assertEqual(c.version, 'HTTP/1.0')
- self.assertEqual(c.status, '201')
- self.assertEqual(c.message, 'foo')
-
- def testNoMessage(self):
- c = ClientDriver()
- c.lineReceived('HTTP/1.0 201')
- self.assertEqual(c.version, 'HTTP/1.0')
- self.assertEqual(c.status, '201')
- self.assertEqual(c.message, '')
-
- def testNoMessage_trailingSpace(self):
- c = ClientDriver()
- c.lineReceived('HTTP/1.0 201 ')
- self.assertEqual(c.version, 'HTTP/1.0')
- self.assertEqual(c.status, '201')
- self.assertEqual(c.message, '')
-
-
-
-class RequestTests(unittest.TestCase, ResponseTestMixin):
- """
- Tests for L{http.Request}
- """
- def _compatHeadersTest(self, oldName, newName):
- """
- Verify that each of two different attributes which are associated with
- the same state properly reflect changes made through the other.
-
- This is used to test that the C{headers}/C{responseHeaders} and
- C{received_headers}/C{requestHeaders} pairs interact properly.
- """
- req = http.Request(DummyChannel(), None)
- getattr(req, newName).setRawHeaders("test", ["lemur"])
- self.assertEqual(getattr(req, oldName)["test"], "lemur")
- setattr(req, oldName, {"foo": "bar"})
- self.assertEqual(
- list(getattr(req, newName).getAllRawHeaders()),
- [("Foo", ["bar"])])
- setattr(req, newName, http_headers.Headers())
- self.assertEqual(getattr(req, oldName), {})
-
-
- def test_received_headers(self):
- """
- L{Request.received_headers} is a backwards compatible API which
- accesses and allows mutation of the state at L{Request.requestHeaders}.
- """
- self._compatHeadersTest('received_headers', 'requestHeaders')
-
-
- def test_headers(self):
- """
- L{Request.headers} is a backwards compatible API which accesses and
- allows mutation of the state at L{Request.responseHeaders}.
- """
- self._compatHeadersTest('headers', 'responseHeaders')
-
-
- def test_getHeader(self):
- """
- L{http.Request.getHeader} returns the value of the named request
- header.
- """
- req = http.Request(DummyChannel(), None)
- req.requestHeaders.setRawHeaders("test", ["lemur"])
- self.assertEqual(req.getHeader("test"), "lemur")
-
-
- def test_getHeaderReceivedMultiples(self):
- """
- When there are multiple values for a single request header,
- L{http.Request.getHeader} returns the last value.
- """
- req = http.Request(DummyChannel(), None)
- req.requestHeaders.setRawHeaders("test", ["lemur", "panda"])
- self.assertEqual(req.getHeader("test"), "panda")
-
-
- def test_getHeaderNotFound(self):
- """
- L{http.Request.getHeader} returns C{None} when asked for the value of a
- request header which is not present.
- """
- req = http.Request(DummyChannel(), None)
- self.assertEqual(req.getHeader("test"), None)
-
-
- def test_getAllHeaders(self):
- """
- L{http.Request.getAllheaders} returns a C{dict} mapping all request
- header names to their corresponding values.
- """
- req = http.Request(DummyChannel(), None)
- req.requestHeaders.setRawHeaders("test", ["lemur"])
- self.assertEqual(req.getAllHeaders(), {"test": "lemur"})
-
-
- def test_getAllHeadersNoHeaders(self):
- """
- L{http.Request.getAllHeaders} returns an empty C{dict} if there are no
- request headers.
- """
- req = http.Request(DummyChannel(), None)
- self.assertEqual(req.getAllHeaders(), {})
-
-
- def test_getAllHeadersMultipleHeaders(self):
- """
- When there are multiple values for a single request header,
- L{http.Request.getAllHeaders} returns only the last value.
- """
- req = http.Request(DummyChannel(), None)
- req.requestHeaders.setRawHeaders("test", ["lemur", "panda"])
- self.assertEqual(req.getAllHeaders(), {"test": "panda"})
-
-
- def test_setResponseCode(self):
- """
- L{http.Request.setResponseCode} takes a status code and causes it to be
- used as the response status.
- """
- channel = DummyChannel()
- req = http.Request(channel, None)
- req.setResponseCode(201)
- req.write('')
- self.assertEqual(
- channel.transport.written.getvalue().splitlines()[0],
- '%s 201 Created' % (req.clientproto,))
-
-
- def test_setResponseCodeAndMessage(self):
- """
- L{http.Request.setResponseCode} takes a status code and a message and
- causes them to be used as the response status.
- """
- channel = DummyChannel()
- req = http.Request(channel, None)
- req.setResponseCode(202, "happily accepted")
- req.write('')
- self.assertEqual(
- channel.transport.written.getvalue().splitlines()[0],
- '%s 202 happily accepted' % (req.clientproto,))
-
-
- def test_setResponseCodeAcceptsIntegers(self):
- """
- L{http.Request.setResponseCode} accepts C{int} or C{long} for the code
- parameter and raises L{TypeError} if passed anything else.
- """
- req = http.Request(DummyChannel(), None)
- req.setResponseCode(1)
- req.setResponseCode(1L)
- self.assertRaises(TypeError, req.setResponseCode, "1")
-
-
- def test_setHost(self):
- """
- L{http.Request.setHost} sets the value of the host request header.
- The port should not be added because it is the default.
- """
- req = http.Request(DummyChannel(), None)
- req.setHost("example.com", 80)
- self.assertEqual(
- req.requestHeaders.getRawHeaders("host"), ["example.com"])
-
-
- def test_setHostSSL(self):
- """
- L{http.Request.setHost} sets the value of the host request header.
- The port should not be added because it is the default.
- """
- d = DummyChannel()
- d.transport = DummyChannel.SSL()
- req = http.Request(d, None)
- req.setHost("example.com", 443)
- self.assertEqual(
- req.requestHeaders.getRawHeaders("host"), ["example.com"])
-
-
- def test_setHostNonDefaultPort(self):
- """
- L{http.Request.setHost} sets the value of the host request header.
- The port should be added because it is not the default.
- """
- req = http.Request(DummyChannel(), None)
- req.setHost("example.com", 81)
- self.assertEqual(
- req.requestHeaders.getRawHeaders("host"), ["example.com:81"])
-
-
- def test_setHostSSLNonDefaultPort(self):
- """
- L{http.Request.setHost} sets the value of the host request header.
- The port should be added because it is not the default.
- """
- d = DummyChannel()
- d.transport = DummyChannel.SSL()
- req = http.Request(d, None)
- req.setHost("example.com", 81)
- self.assertEqual(
- req.requestHeaders.getRawHeaders("host"), ["example.com:81"])
-
-
- def test_setHeader(self):
- """
- L{http.Request.setHeader} sets the value of the given response header.
- """
- req = http.Request(DummyChannel(), None)
- req.setHeader("test", "lemur")
- self.assertEqual(req.responseHeaders.getRawHeaders("test"), ["lemur"])
-
-
- def test_firstWrite(self):
- """
- For an HTTP 1.0 request, L{http.Request.write} sends an HTTP 1.0
- Response-Line and whatever response headers are set.
- """
- req = http.Request(DummyChannel(), None)
- trans = StringTransport()
-
- req.transport = trans
-
- req.setResponseCode(200)
- req.clientproto = "HTTP/1.0"
- req.responseHeaders.setRawHeaders("test", ["lemur"])
- req.write('Hello')
-
- self.assertResponseEquals(
- trans.value(),
- [("HTTP/1.0 200 OK",
- "Test: lemur",
- "Hello")])
-
-
- def test_firstWriteHTTP11Chunked(self):
- """
- For an HTTP 1.1 request, L{http.Request.write} sends an HTTP 1.1
- Response-Line, whatever response headers are set, and uses chunked
- encoding for the response body.
- """
- req = http.Request(DummyChannel(), None)
- trans = StringTransport()
-
- req.transport = trans
-
- req.setResponseCode(200)
- req.clientproto = "HTTP/1.1"
- req.responseHeaders.setRawHeaders("test", ["lemur"])
- req.write('Hello')
- req.write('World!')
-
- self.assertResponseEquals(
- trans.value(),
- [("HTTP/1.1 200 OK",
- "Test: lemur",
- "Transfer-Encoding: chunked",
- "5\r\nHello\r\n6\r\nWorld!\r\n")])
-
-
- def test_firstWriteLastModified(self):
- """
- For an HTTP 1.0 request for a resource with a known last modified time,
- L{http.Request.write} sends an HTTP Response-Line, whatever response
- headers are set, and a last-modified header with that time.
- """
- req = http.Request(DummyChannel(), None)
- trans = StringTransport()
-
- req.transport = trans
-
- req.setResponseCode(200)
- req.clientproto = "HTTP/1.0"
- req.lastModified = 0
- req.responseHeaders.setRawHeaders("test", ["lemur"])
- req.write('Hello')
-
- self.assertResponseEquals(
- trans.value(),
- [("HTTP/1.0 200 OK",
- "Test: lemur",
- "Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT",
- "Hello")])
-
-
- def test_parseCookies(self):
- """
- L{http.Request.parseCookies} extracts cookies from C{requestHeaders}
- and adds them to C{received_cookies}.
- """
- req = http.Request(DummyChannel(), None)
- req.requestHeaders.setRawHeaders(
- "cookie", ['test="lemur"; test2="panda"'])
- req.parseCookies()
- self.assertEqual(req.received_cookies, {"test": '"lemur"',
- "test2": '"panda"'})
-
-
- def test_parseCookiesMultipleHeaders(self):
- """
- L{http.Request.parseCookies} can extract cookies from multiple Cookie
- headers.
- """
- req = http.Request(DummyChannel(), None)
- req.requestHeaders.setRawHeaders(
- "cookie", ['test="lemur"', 'test2="panda"'])
- req.parseCookies()
- self.assertEqual(req.received_cookies, {"test": '"lemur"',
- "test2": '"panda"'})
-
-
- def test_connectionLost(self):
- """
- L{http.Request.connectionLost} closes L{Request.content} and drops the
- reference to the L{HTTPChannel} to assist with garbage collection.
- """
- req = http.Request(DummyChannel(), None)
-
- # Cause Request.content to be created at all.
- req.gotLength(10)
-
- # Grab a reference to content in case the Request drops it later on.
- content = req.content
-
- # Put some bytes into it
- req.handleContentChunk("hello")
-
- # Then something goes wrong and content should get closed.
- req.connectionLost(Failure(ConnectionLost("Finished")))
- self.assertTrue(content.closed)
- self.assertIdentical(req.channel, None)
-
-
- def test_registerProducerTwiceFails(self):
- """
- Calling L{Request.registerProducer} when a producer is already
- registered raises ValueError.
- """
- req = http.Request(DummyChannel(), None)
- req.registerProducer(DummyProducer(), True)
- self.assertRaises(
- ValueError, req.registerProducer, DummyProducer(), True)
-
-
- def test_registerProducerWhenQueuedPausesPushProducer(self):
- """
- Calling L{Request.registerProducer} with an IPushProducer when the
- request is queued pauses the producer.
- """
- req = http.Request(DummyChannel(), True)
- producer = DummyProducer()
- req.registerProducer(producer, True)
- self.assertEqual(['pause'], producer.events)
-
-
- def test_registerProducerWhenQueuedDoesntPausePullProducer(self):
- """
- Calling L{Request.registerProducer} with an IPullProducer when the
- request is queued does not pause the producer, because it doesn't make
- sense to pause a pull producer.
- """
- req = http.Request(DummyChannel(), True)
- producer = DummyProducer()
- req.registerProducer(producer, False)
- self.assertEqual([], producer.events)
-
-
- def test_registerProducerWhenQueuedDoesntRegisterPushProducer(self):
- """
- Calling L{Request.registerProducer} with an IPushProducer when the
- request is queued does not register the producer on the request's
- transport.
- """
- self.assertIdentical(
- None, getattr(http.StringTransport, 'registerProducer', None),
- "StringTransport cannot implement registerProducer for this test "
- "to be valid.")
- req = http.Request(DummyChannel(), True)
- producer = DummyProducer()
- req.registerProducer(producer, True)
- # This is a roundabout assertion: http.StringTransport doesn't
- # implement registerProducer, so Request.registerProducer can't have
- # tried to call registerProducer on the transport.
- self.assertIsInstance(req.transport, http.StringTransport)
-
-
- def test_registerProducerWhenQueuedDoesntRegisterPullProducer(self):
- """
- Calling L{Request.registerProducer} with an IPullProducer when the
- request is queued does not register the producer on the request's
- transport.
- """
- self.assertIdentical(
- None, getattr(http.StringTransport, 'registerProducer', None),
- "StringTransport cannot implement registerProducer for this test "
- "to be valid.")
- req = http.Request(DummyChannel(), True)
- producer = DummyProducer()
- req.registerProducer(producer, False)
- # This is a roundabout assertion: http.StringTransport doesn't
- # implement registerProducer, so Request.registerProducer can't have
- # tried to call registerProducer on the transport.
- self.assertIsInstance(req.transport, http.StringTransport)
-
-
- def test_registerProducerWhenNotQueuedRegistersPushProducer(self):
- """
- Calling L{Request.registerProducer} with an IPushProducer when the
- request is not queued registers the producer as a push producer on the
- request's transport.
- """
- req = http.Request(DummyChannel(), False)
- producer = DummyProducer()
- req.registerProducer(producer, True)
- self.assertEqual([(producer, True)], req.transport.producers)
-
-
- def test_registerProducerWhenNotQueuedRegistersPullProducer(self):
- """
- Calling L{Request.registerProducer} with an IPullProducer when the
- request is not queued registers the producer as a pull producer on the
- request's transport.
- """
- req = http.Request(DummyChannel(), False)
- producer = DummyProducer()
- req.registerProducer(producer, False)
- self.assertEqual([(producer, False)], req.transport.producers)
-
-
- def test_connectionLostNotification(self):
- """
- L{Request.connectionLost} triggers all finish notification Deferreds
- and cleans up per-request state.
- """
- d = DummyChannel()
- request = http.Request(d, True)
- finished = request.notifyFinish()
- request.connectionLost(Failure(ConnectionLost("Connection done")))
- self.assertIdentical(request.channel, None)
- return self.assertFailure(finished, ConnectionLost)
-
-
- def test_finishNotification(self):
- """
- L{Request.finish} triggers all finish notification Deferreds.
- """
- request = http.Request(DummyChannel(), False)
- finished = request.notifyFinish()
- # Force the request to have a non-None content attribute. This is
- # probably a bug in Request.
- request.gotLength(1)
- request.finish()
- return finished
-
-
- def test_writeAfterFinish(self):
- """
- Calling L{Request.write} after L{Request.finish} has been called results
- in a L{RuntimeError} being raised.
- """
- request = http.Request(DummyChannel(), False)
- finished = request.notifyFinish()
- # Force the request to have a non-None content attribute. This is
- # probably a bug in Request.
- request.gotLength(1)
- request.write('foobar')
- request.finish()
- self.assertRaises(RuntimeError, request.write, 'foobar')
- return finished
-
-
- def test_finishAfterConnectionLost(self):
- """
- Calling L{Request.finish} after L{Request.connectionLost} has been
- called results in a L{RuntimeError} being raised.
- """
- channel = DummyChannel()
- transport = channel.transport
- req = http.Request(channel, False)
- req.connectionLost(Failure(ConnectionLost("The end.")))
- self.assertRaises(RuntimeError, req.finish)
-
-
-
-class MultilineHeadersTestCase(unittest.TestCase):
- """
- Tests to exercise handling of multiline headers by L{HTTPClient}. RFCs 1945
- (HTTP 1.0) and 2616 (HTTP 1.1) state that HTTP message header fields can
- span multiple lines if each extra line is preceded by at least one space or
- horizontal tab.
- """
- def setUp(self):
- """
- Initialize variables used to verify that the header-processing functions
- are getting called.
- """
- self.handleHeaderCalled = False
- self.handleEndHeadersCalled = False
-
- # Dictionary of sample complete HTTP header key/value pairs, including
- # multiline headers.
- expectedHeaders = {'Content-Length': '10',
- 'X-Multiline' : 'line-0\tline-1',
- 'X-Multiline2' : 'line-2 line-3'}
-
- def ourHandleHeader(self, key, val):
- """
- Dummy implementation of L{HTTPClient.handleHeader}.
- """
- self.handleHeaderCalled = True
- self.assertEqual(val, self.expectedHeaders[key])
-
-
- def ourHandleEndHeaders(self):
- """
- Dummy implementation of L{HTTPClient.handleEndHeaders}.
- """
- self.handleEndHeadersCalled = True
-
-
- def test_extractHeader(self):
- """
- A header isn't processed by L{HTTPClient.extractHeader} until it is
- confirmed in L{HTTPClient.lineReceived} that the header has been
- received completely.
- """
- c = ClientDriver()
- c.handleHeader = self.ourHandleHeader
- c.handleEndHeaders = self.ourHandleEndHeaders
-
- c.lineReceived('HTTP/1.0 201')
- c.lineReceived('Content-Length: 10')
- self.assertIdentical(c.length, None)
- self.assertFalse(self.handleHeaderCalled)
- self.assertFalse(self.handleEndHeadersCalled)
-
- # Signal end of headers.
- c.lineReceived('')
- self.assertTrue(self.handleHeaderCalled)
- self.assertTrue(self.handleEndHeadersCalled)
-
- self.assertEqual(c.length, 10)
-
-
- def test_noHeaders(self):
- """
- An HTTP request with no headers will not cause any calls to
- L{handleHeader} but will cause L{handleEndHeaders} to be called on
- L{HTTPClient} subclasses.
- """
- c = ClientDriver()
- c.handleHeader = self.ourHandleHeader
- c.handleEndHeaders = self.ourHandleEndHeaders
- c.lineReceived('HTTP/1.0 201')
-
- # Signal end of headers.
- c.lineReceived('')
- self.assertFalse(self.handleHeaderCalled)
- self.assertTrue(self.handleEndHeadersCalled)
-
- self.assertEqual(c.version, 'HTTP/1.0')
- self.assertEqual(c.status, '201')
-
-
- def test_multilineHeaders(self):
- """
- L{HTTPClient} parses multiline headers by buffering header lines until
- an empty line or a line that does not start with whitespace hits
- lineReceived, confirming that the header has been received completely.
- """
- c = ClientDriver()
- c.handleHeader = self.ourHandleHeader
- c.handleEndHeaders = self.ourHandleEndHeaders
-
- c.lineReceived('HTTP/1.0 201')
- c.lineReceived('X-Multiline: line-0')
- self.assertFalse(self.handleHeaderCalled)
- # Start continuing line with a tab.
- c.lineReceived('\tline-1')
- c.lineReceived('X-Multiline2: line-2')
- # The previous header must be complete, so now it can be processed.
- self.assertTrue(self.handleHeaderCalled)
- # Start continuing line with a space.
- c.lineReceived(' line-3')
- c.lineReceived('Content-Length: 10')
-
- # Signal end of headers.
- c.lineReceived('')
- self.assertTrue(self.handleEndHeadersCalled)
-
- self.assertEqual(c.version, 'HTTP/1.0')
- self.assertEqual(c.status, '201')
- self.assertEqual(c.length, 10)
-
-
-
-class Expect100ContinueServerTests(unittest.TestCase):
- """
- Test that the HTTP server handles 'Expect: 100-continue' header correctly.
-
- The tests in this class all assume a simplistic behavior where user code
- cannot choose to deny a request. Once ticket #288 is implemented and user
- code can run before the body of a POST is processed this should be
- extended to support overriding this behavior.
- """
-
- def test_HTTP10(self):
- """
- HTTP/1.0 requests do not get 100-continue returned, even if 'Expect:
- 100-continue' is included (RFC 2616 10.1.1).
- """
- transport = StringTransport()
- channel = http.HTTPChannel()
- channel.requestFactory = DummyHTTPHandler
- channel.makeConnection(transport)
- channel.dataReceived("GET / HTTP/1.0\r\n")
- channel.dataReceived("Host: www.example.com\r\n")
- channel.dataReceived("Content-Length: 3\r\n")
- channel.dataReceived("Expect: 100-continue\r\n")
- channel.dataReceived("\r\n")
- self.assertEqual(transport.value(), "")
- channel.dataReceived("abc")
- self.assertEqual(transport.value(),
- "HTTP/1.0 200 OK\r\n"
- "Command: GET\r\n"
- "Content-Length: 13\r\n"
- "Version: HTTP/1.0\r\n"
- "Request: /\r\n\r\n'''\n3\nabc'''\n")
-
-
- def test_expect100ContinueHeader(self):
- """
- If a HTTP/1.1 client sends a 'Expect: 100-continue' header, the server
- responds with a 100 response code before handling the request body, if
- any. The normal resource rendering code will then be called, which
- will send an additional response code.
- """
- transport = StringTransport()
- channel = http.HTTPChannel()
- channel.requestFactory = DummyHTTPHandler
- channel.makeConnection(transport)
- channel.dataReceived("GET / HTTP/1.1\r\n")
- channel.dataReceived("Host: www.example.com\r\n")
- channel.dataReceived("Expect: 100-continue\r\n")
- channel.dataReceived("Content-Length: 3\r\n")
- # The 100 continue response is not sent until all headers are
- # received:
- self.assertEqual(transport.value(), "")
- channel.dataReceived("\r\n")
- # The 100 continue response is sent *before* the body is even
- # received:
- self.assertEqual(transport.value(), "HTTP/1.1 100 Continue\r\n\r\n")
- channel.dataReceived("abc")
- self.assertEqual(transport.value(),
- "HTTP/1.1 100 Continue\r\n\r\n"
- "HTTP/1.1 200 OK\r\n"
- "Command: GET\r\n"
- "Content-Length: 13\r\n"
- "Version: HTTP/1.1\r\n"
- "Request: /\r\n\r\n'''\n3\nabc'''\n")
-
-
- def test_expect100ContinueWithPipelining(self):
- """
- If a HTTP/1.1 client sends a 'Expect: 100-continue' header, followed
- by another pipelined request, the 100 response does not interfere with
- the response to the second request.
- """
- transport = StringTransport()
- channel = http.HTTPChannel()
- channel.requestFactory = DummyHTTPHandler
- channel.makeConnection(transport)
- channel.dataReceived(
- "GET / HTTP/1.1\r\n"
- "Host: www.example.com\r\n"
- "Expect: 100-continue\r\n"
- "Content-Length: 3\r\n"
- "\r\nabc"
- "POST /foo HTTP/1.1\r\n"
- "Host: www.example.com\r\n"
- "Content-Length: 4\r\n"
- "\r\ndefg")
- self.assertEqual(transport.value(),
- "HTTP/1.1 100 Continue\r\n\r\n"
- "HTTP/1.1 200 OK\r\n"
- "Command: GET\r\n"
- "Content-Length: 13\r\n"
- "Version: HTTP/1.1\r\n"
- "Request: /\r\n\r\n"
- "'''\n3\nabc'''\n"
- "HTTP/1.1 200 OK\r\n"
- "Command: POST\r\n"
- "Content-Length: 14\r\n"
- "Version: HTTP/1.1\r\n"
- "Request: /foo\r\n\r\n"
- "'''\n4\ndefg'''\n")