1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
8 from urlparse import urlparse, urlunsplit, clear_cache
9 import random, urllib, cgi
11 from twisted.python.compat import set
12 from twisted.python.failure import Failure
13 from twisted.trial import unittest
14 from twisted.trial.unittest import TestCase
15 from twisted.web import http, http_headers
16 from twisted.web.http import PotentialDataLoss, _DataLoss
17 from twisted.web.http import _IdentityTransferDecoder
18 from twisted.protocols import loopback
19 from twisted.internet.task import Clock
20 from twisted.internet.error import ConnectionLost
21 from twisted.test.proto_helpers import StringTransport
22 from twisted.test.test_internet import DummyProducer
23 from twisted.web.test.test_web import DummyChannel
26 class DateTimeTest(unittest.TestCase):
27 """Test date parsing functions."""
29 def testRoundtrip(self):
30 for i in range(10000):
31 time = random.randint(0, 2000000000)
32 timestr = http.datetimeToString(time)
33 time2 = http.stringToDatetime(timestr)
34 self.assertEqual(time, time2)
37 class DummyHTTPHandler(http.Request):
40 self.content.seek(0, 0)
41 data = self.content.read()
42 length = self.getHeader('content-length')
43 request = "'''\n"+str(length)+"\n"+data+"'''\n"
44 self.setResponseCode(200)
45 self.setHeader("Request", self.uri)
46 self.setHeader("Command", self.method)
47 self.setHeader("Version", self.clientproto)
48 self.setHeader("Content-Length", len(request))
53 class LoopbackHTTPClient(http.HTTPClient):
55 def connectionMade(self):
56 self.sendCommand("GET", "/foo/bar")
57 self.sendHeader("Content-Length", 10)
59 self.transport.write("0123456789")
62 class ResponseTestMixin(object):
64 A mixin that provides a simple means of comparing an actual response string
65 to an expected response string by performing the minimal parsing.
68 def assertResponseEquals(self, responses, expected):
70 Assert that the C{responses} matches the C{expected} responses.
72 @type responses: C{str}
73 @param responses: The bytes sent in response to one or more requests.
75 @type expected: C{list} of C{tuple} of C{str}
76 @param expected: The expected values for the responses. Each tuple
77 element of the list represents one response. Each string element
78 of the tuple is a full header line without delimiter, except for
79 the last element which gives the full response body.
81 for response in expected:
82 expectedHeaders, expectedContent = response[:-1], response[-1]
83 headers, rest = responses.split('\r\n\r\n', 1)
84 headers = headers.splitlines()
85 self.assertEqual(set(headers), set(expectedHeaders))
86 content = rest[:len(expectedContent)]
87 responses = rest[len(expectedContent):]
88 self.assertEqual(content, expectedContent)
92 class HTTP1_0TestCase(unittest.TestCase, ResponseTestMixin):
97 "Accept: text/html\r\n"
100 expected_response = [
105 "Content-Length: 13",
108 def test_buffer(self):
110 Send requests over a channel and check responses match what is expected.
112 b = StringTransport()
113 a = http.HTTPChannel()
114 a.requestFactory = DummyHTTPHandler
116 # one byte at a time, to stress it.
117 for byte in self.requests:
119 a.connectionLost(IOError("all one"))
121 self.assertResponseEquals(value, self.expected_response)
124 def test_requestBodyTimeout(self):
126 L{HTTPChannel} resets its timeout whenever data from a request body is
130 transport = StringTransport()
131 protocol = http.HTTPChannel()
132 protocol.timeOut = 100
133 protocol.callLater = clock.callLater
134 protocol.makeConnection(transport)
135 protocol.dataReceived('POST / HTTP/1.0\r\nContent-Length: 2\r\n\r\n')
137 self.assertFalse(transport.disconnecting)
138 protocol.dataReceived('x')
140 self.assertFalse(transport.disconnecting)
141 protocol.dataReceived('x')
142 self.assertEqual(len(protocol.requests), 1)
146 class HTTP1_1TestCase(HTTP1_0TestCase):
150 "Accept: text/html\r\n"
152 "POST / HTTP/1.1\r\n"
153 "Content-Length: 10\r\n"
155 "0123456789POST / HTTP/1.1\r\n"
156 "Content-Length: 10\r\n"
158 "0123456789HEAD / HTTP/1.1\r\n"
161 expected_response = [
166 "Content-Length: 13",
172 "Content-Length: 21",
173 "'''\n10\n0123456789'''\n"),
178 "Content-Length: 21",
179 "'''\n10\n0123456789'''\n"),
184 "Content-Length: 13",
189 class HTTP1_1_close_TestCase(HTTP1_0TestCase):
193 "Accept: text/html\r\n"
194 "Connection: close\r\n"
199 expected_response = [
205 "Content-Length: 13",
210 class HTTP0_9TestCase(HTTP1_0TestCase):
215 expected_response = "HTTP/1.1 400 Bad Request\r\n\r\n"
218 def assertResponseEquals(self, response, expectedResponse):
219 self.assertEqual(response, expectedResponse)
222 class HTTPLoopbackTestCase(unittest.TestCase):
224 expectedHeaders = {'request' : '/foo/bar',
226 'version' : 'HTTP/1.0',
227 'content-length' : '21'}
233 def _handleStatus(self, version, status, message):
235 self.assertEqual(version, "HTTP/1.0")
236 self.assertEqual(status, "200")
238 def _handleResponse(self, data):
240 self.assertEqual(data, "'''\n10\n0123456789'''\n")
242 def _handleHeader(self, key, value):
243 self.numHeaders = self.numHeaders + 1
244 self.assertEqual(self.expectedHeaders[key.lower()], value)
246 def _handleEndHeaders(self):
247 self.gotEndHeaders = 1
248 self.assertEqual(self.numHeaders, 4)
250 def testLoopback(self):
251 server = http.HTTPChannel()
252 server.requestFactory = DummyHTTPHandler
253 client = LoopbackHTTPClient()
254 client.handleResponse = self._handleResponse
255 client.handleHeader = self._handleHeader
256 client.handleEndHeaders = self._handleEndHeaders
257 client.handleStatus = self._handleStatus
258 d = loopback.loopbackAsync(server, client)
259 d.addCallback(self._cbTestLoopback)
262 def _cbTestLoopback(self, ignored):
263 if not (self.gotStatus and self.gotResponse and self.gotEndHeaders):
265 "didn't got all callbacks %s"
266 % [self.gotStatus, self.gotResponse, self.gotEndHeaders])
267 del self.gotEndHeaders
274 def _prequest(**headers):
276 Make a request with the given request headers for the persistence tests.
278 request = http.Request(DummyChannel(), None)
279 for k, v in headers.iteritems():
280 request.requestHeaders.setRawHeaders(k, v)
284 class PersistenceTestCase(unittest.TestCase):
286 Tests for persistent HTTP connections.
289 ptests = [#(PRequest(connection="Keep-Alive"), "HTTP/1.0", 1, {'connection' : 'Keep-Alive'}),
290 (_prequest(), "HTTP/1.0", 0, {'connection': None}),
291 (_prequest(connection=["close"]), "HTTP/1.1", 0, {'connection' : ['close']}),
292 (_prequest(), "HTTP/1.1", 1, {'connection': None}),
293 (_prequest(), "HTTP/0.9", 0, {'connection': None}),
297 def testAlgorithm(self):
298 c = http.HTTPChannel()
299 for req, version, correctResult, resultHeaders in self.ptests:
300 result = c.checkPersistence(req, version)
301 self.assertEqual(result, correctResult)
302 for header in resultHeaders.keys():
303 self.assertEqual(req.responseHeaders.getRawHeaders(header, None), resultHeaders[header])
307 class IdentityTransferEncodingTests(TestCase):
309 Tests for L{_IdentityTransferDecoder}.
313 Create an L{_IdentityTransferDecoder} with callbacks hooked up so that
314 calls to them can be inspected.
318 self.contentLength = 10
319 self.decoder = _IdentityTransferDecoder(
320 self.contentLength, self.data.append, self.finish.append)
323 def test_exactAmountReceived(self):
325 If L{_IdentityTransferDecoder.dataReceived} is called with a string
326 with length equal to the content length passed to
327 L{_IdentityTransferDecoder}'s initializer, the data callback is invoked
328 with that string and the finish callback is invoked with a zero-length
331 self.decoder.dataReceived('x' * self.contentLength)
332 self.assertEqual(self.data, ['x' * self.contentLength])
333 self.assertEqual(self.finish, [''])
336 def test_shortStrings(self):
338 If L{_IdentityTransferDecoder.dataReceived} is called multiple times
339 with strings which, when concatenated, are as long as the content
340 length provided, the data callback is invoked with each string and the
341 finish callback is invoked only after the second call.
343 self.decoder.dataReceived('x')
344 self.assertEqual(self.data, ['x'])
345 self.assertEqual(self.finish, [])
346 self.decoder.dataReceived('y' * (self.contentLength - 1))
347 self.assertEqual(self.data, ['x', 'y' * (self.contentLength - 1)])
348 self.assertEqual(self.finish, [''])
351 def test_longString(self):
353 If L{_IdentityTransferDecoder.dataReceived} is called with a string
354 with length greater than the provided content length, only the prefix
355 of that string up to the content length is passed to the data callback
356 and the remainder is passed to the finish callback.
358 self.decoder.dataReceived('x' * self.contentLength + 'y')
359 self.assertEqual(self.data, ['x' * self.contentLength])
360 self.assertEqual(self.finish, ['y'])
363 def test_rejectDataAfterFinished(self):
365 If data is passed to L{_IdentityTransferDecoder.dataReceived} after the
366 finish callback has been invoked, L{RuntimeError} is raised.
371 decoder.dataReceived('foo')
373 failures.append(Failure())
374 decoder = _IdentityTransferDecoder(5, self.data.append, finish)
375 decoder.dataReceived('x' * 4)
376 self.assertEqual(failures, [])
377 decoder.dataReceived('y')
378 failures[0].trap(RuntimeError)
380 str(failures[0].value),
381 "_IdentityTransferDecoder cannot decode data after finishing")
384 def test_unknownContentLength(self):
386 If L{_IdentityTransferDecoder} is constructed with C{None} for the
387 content length, it passes all data delivered to it through to the data
392 decoder = _IdentityTransferDecoder(None, data.append, finish.append)
393 decoder.dataReceived('x')
394 self.assertEqual(data, ['x'])
395 decoder.dataReceived('y')
396 self.assertEqual(data, ['x', 'y'])
397 self.assertEqual(finish, [])
400 def _verifyCallbacksUnreferenced(self, decoder):
402 Check the decoder's data and finish callbacks and make sure they are
403 None in order to help avoid references cycles.
405 self.assertIdentical(decoder.dataCallback, None)
406 self.assertIdentical(decoder.finishCallback, None)
409 def test_earlyConnectionLose(self):
411 L{_IdentityTransferDecoder.noMoreData} raises L{_DataLoss} if it is
412 called and the content length is known but not enough bytes have been
415 self.decoder.dataReceived('x' * (self.contentLength - 1))
416 self.assertRaises(_DataLoss, self.decoder.noMoreData)
417 self._verifyCallbacksUnreferenced(self.decoder)
420 def test_unknownContentLengthConnectionLose(self):
422 L{_IdentityTransferDecoder.noMoreData} calls the finish callback and
423 raises L{PotentialDataLoss} if it is called and the content length is
428 decoder = _IdentityTransferDecoder(None, body.append, finished.append)
429 self.assertRaises(PotentialDataLoss, decoder.noMoreData)
430 self.assertEqual(body, [])
431 self.assertEqual(finished, [''])
432 self._verifyCallbacksUnreferenced(decoder)
435 def test_finishedConnectionLose(self):
437 L{_IdentityTransferDecoder.noMoreData} does not raise any exception if
438 it is called when the content length is known and that many bytes have
441 self.decoder.dataReceived('x' * self.contentLength)
442 self.decoder.noMoreData()
443 self._verifyCallbacksUnreferenced(self.decoder)
447 class ChunkedTransferEncodingTests(unittest.TestCase):
449 Tests for L{_ChunkedTransferDecoder}, which turns a byte stream encoded
450 using HTTP I{chunked} C{Transfer-Encoding} back into the original byte
453 def test_decoding(self):
455 L{_ChunkedTransferDecoder.dataReceived} decodes chunked-encoded data
456 and passes the result to the specified callback.
459 p = http._ChunkedTransferDecoder(L.append, None)
460 p.dataReceived('3\r\nabc\r\n5\r\n12345\r\n')
461 p.dataReceived('a\r\n0123456789\r\n')
462 self.assertEqual(L, ['abc', '12345', '0123456789'])
465 def test_short(self):
467 L{_ChunkedTransferDecoder.dataReceived} decodes chunks broken up and
468 delivered in multiple calls.
472 p = http._ChunkedTransferDecoder(L.append, finished.append)
473 for s in '3\r\nabc\r\n5\r\n12345\r\n0\r\n\r\n':
475 self.assertEqual(L, ['a', 'b', 'c', '1', '2', '3', '4', '5'])
476 self.assertEqual(finished, [''])
479 def test_newlines(self):
481 L{_ChunkedTransferDecoder.dataReceived} doesn't treat CR LF pairs
482 embedded in chunk bodies specially.
485 p = http._ChunkedTransferDecoder(L.append, None)
486 p.dataReceived('2\r\n\r\n\r\n')
487 self.assertEqual(L, ['\r\n'])
490 def test_extensions(self):
492 L{_ChunkedTransferDecoder.dataReceived} disregards chunk-extension
496 p = http._ChunkedTransferDecoder(L.append, None)
497 p.dataReceived('3; x-foo=bar\r\nabc\r\n')
498 self.assertEqual(L, ['abc'])
501 def test_finish(self):
503 L{_ChunkedTransferDecoder.dataReceived} interprets a zero-length
504 chunk as the end of the chunked data stream and calls the completion
508 p = http._ChunkedTransferDecoder(None, finished.append)
509 p.dataReceived('0\r\n\r\n')
510 self.assertEqual(finished, [''])
513 def test_extra(self):
515 L{_ChunkedTransferDecoder.dataReceived} passes any bytes which come
516 after the terminating zero-length chunk to the completion callback.
519 p = http._ChunkedTransferDecoder(None, finished.append)
520 p.dataReceived('0\r\n\r\nhello')
521 self.assertEqual(finished, ['hello'])
524 def test_afterFinished(self):
526 L{_ChunkedTransferDecoder.dataReceived} raises L{RuntimeError} if it
527 is called after it has seen the last chunk.
529 p = http._ChunkedTransferDecoder(None, lambda bytes: None)
530 p.dataReceived('0\r\n\r\n')
531 self.assertRaises(RuntimeError, p.dataReceived, 'hello')
534 def test_earlyConnectionLose(self):
536 L{_ChunkedTransferDecoder.noMoreData} raises L{_DataLoss} if it is
537 called and the end of the last trailer has not yet been received.
539 parser = http._ChunkedTransferDecoder(None, lambda bytes: None)
540 parser.dataReceived('0\r\n\r')
541 exc = self.assertRaises(_DataLoss, parser.noMoreData)
544 "Chunked decoder in 'trailer' state, still expecting more data "
545 "to get to finished state.")
548 def test_finishedConnectionLose(self):
550 L{_ChunkedTransferDecoder.noMoreData} does not raise any exception if
551 it is called after the terminal zero length chunk is received.
553 parser = http._ChunkedTransferDecoder(None, lambda bytes: None)
554 parser.dataReceived('0\r\n\r\n')
558 def test_reentrantFinishedNoMoreData(self):
560 L{_ChunkedTransferDecoder.noMoreData} can be called from the finished
561 callback without raising an exception.
569 errors.append(Failure())
571 successes.append(True)
572 parser = http._ChunkedTransferDecoder(None, finished)
573 parser.dataReceived('0\r\n\r\n')
574 self.assertEqual(errors, [])
575 self.assertEqual(successes, [True])
579 class ChunkingTestCase(unittest.TestCase):
581 strings = ["abcv", "", "fdfsd423", "Ffasfas\r\n",
582 "523523\n\rfsdf", "4234"]
584 def testChunks(self):
585 for s in self.strings:
586 self.assertEqual((s, ''), http.fromChunk(''.join(http.toChunk(s))))
587 self.assertRaises(ValueError, http.fromChunk, '-5\r\nmalformed!\r\n')
589 def testConcatenatedChunks(self):
590 chunked = ''.join([''.join(http.toChunk(t)) for t in self.strings])
596 data, buffer = http.fromChunk(buffer)
600 self.assertEqual(result, self.strings)
604 class ParsingTestCase(unittest.TestCase):
606 Tests for protocol parsing in L{HTTPChannel}.
608 def runRequest(self, httpRequest, requestClass, success=1):
609 httpRequest = httpRequest.replace("\n", "\r\n")
610 b = StringTransport()
611 a = http.HTTPChannel()
612 a.requestFactory = requestClass
614 # one byte at a time, to stress it.
615 for byte in httpRequest:
616 if a.transport.disconnecting:
619 a.connectionLost(IOError("all done"))
621 self.assertEqual(self.didRequest, 1)
624 self.assert_(not hasattr(self, "didRequest"))
628 def test_basicAuth(self):
630 L{HTTPChannel} provides username and password information supplied in
631 an I{Authorization} header to the L{Request} which makes it available
632 via its C{getUser} and C{getPassword} methods.
635 class Request(http.Request):
638 testcase.assertEqual(self.getUser(), self.l[0])
639 testcase.assertEqual(self.getPassword(), self.l[1])
640 for u, p in [("foo", "bar"), ("hello", "there:z")]:
641 Request.l[:] = [u, p]
643 f = "GET / HTTP/1.0\nAuthorization: Basic %s\n\n" % (s.encode("base64").strip(), )
644 self.runRequest(f, Request, 0)
647 def test_headers(self):
649 Headers received by L{HTTPChannel} in a request are made available to
653 class MyRequest(http.Request):
655 processed.append(self)
666 self.runRequest('\n'.join(requestLines), MyRequest, 0)
667 [request] = processed
669 request.requestHeaders.getRawHeaders('foo'), ['bar'])
671 request.requestHeaders.getRawHeaders('bAz'), ['Quux', 'quux'])
674 def test_tooManyHeaders(self):
676 L{HTTPChannel} enforces a limit of C{HTTPChannel.maxHeaders} on the
677 number of headers received per request.
680 class MyRequest(http.Request):
682 processed.append(self)
684 requestLines = ["GET / HTTP/1.0"]
685 for i in range(http.HTTPChannel.maxHeaders + 2):
686 requestLines.append("%s: foo" % (i,))
687 requestLines.extend(["", ""])
689 channel = self.runRequest("\n".join(requestLines), MyRequest, 0)
690 self.assertEqual(processed, [])
692 channel.transport.value(),
693 "HTTP/1.1 400 Bad Request\r\n\r\n")
696 def test_headerLimitPerRequest(self):
698 L{HTTPChannel} enforces the limit of C{HTTPChannel.maxHeaders} per
699 request so that headers received in an earlier request do not count
700 towards the limit when processing a later request.
703 class MyRequest(http.Request):
705 processed.append(self)
708 self.patch(http.HTTPChannel, 'maxHeaders', 1)
719 channel = self.runRequest("\n".join(requestLines), MyRequest, 0)
720 [first, second] = processed
721 self.assertEqual(first.getHeader('foo'), 'bar')
722 self.assertEqual(second.getHeader('bar'), 'baz')
724 channel.transport.value(),
725 'HTTP/1.1 200 OK\r\n'
726 'Transfer-Encoding: chunked\r\n'
730 'HTTP/1.1 200 OK\r\n'
731 'Transfer-Encoding: chunked\r\n'
737 def testCookies(self):
739 Test cookies parsing and reading.
743 Cookie: rabbit="eat carrot"; ninja=secret; spam="hey 1=1!"
748 class MyRequest(http.Request):
750 testcase.assertEqual(self.getCookie('rabbit'), '"eat carrot"')
751 testcase.assertEqual(self.getCookie('ninja'), 'secret')
752 testcase.assertEqual(self.getCookie('spam'), '"hey 1=1!"')
753 testcase.didRequest = 1
756 self.runRequest(httpRequest, MyRequest)
760 GET /?key=value&multiple=two+words&multiple=more%20words&empty= HTTP/1.0
764 class MyRequest(http.Request):
766 testcase.assertEqual(self.method, "GET")
767 testcase.assertEqual(self.args["key"], ["value"])
768 testcase.assertEqual(self.args["empty"], [""])
769 testcase.assertEqual(self.args["multiple"], ["two words", "more words"])
770 testcase.didRequest = 1
773 self.runRequest(httpRequest, MyRequest)
776 def test_extraQuestionMark(self):
778 While only a single '?' is allowed in an URL, several other servers
779 allow several and pass all after the first through as part of the
780 query arguments. Test that we emulate this behavior.
782 httpRequest = 'GET /foo?bar=?&baz=quux HTTP/1.0\n\n'
785 class MyRequest(http.Request):
787 testcase.assertEqual(self.method, 'GET')
788 testcase.assertEqual(self.path, '/foo')
789 testcase.assertEqual(self.args['bar'], ['?'])
790 testcase.assertEqual(self.args['baz'], ['quux'])
791 testcase.didRequest = 1
794 self.runRequest(httpRequest, MyRequest)
797 def test_formPOSTRequest(self):
799 The request body of a I{POST} request with a I{Content-Type} header
800 of I{application/x-www-form-urlencoded} is parsed according to that
801 content type and made available in the C{args} attribute of the
802 request object. The original bytes of the request may still be read
803 from the C{content} attribute.
805 query = 'key=value&multiple=two+words&multiple=more%20words&empty='
809 Content-Type: application/x-www-form-urlencoded
811 %s''' % (len(query), query)
814 class MyRequest(http.Request):
816 testcase.assertEqual(self.method, "POST")
817 testcase.assertEqual(self.args["key"], ["value"])
818 testcase.assertEqual(self.args["empty"], [""])
819 testcase.assertEqual(self.args["multiple"], ["two words", "more words"])
821 # Reading from the content file-like must produce the entire
823 testcase.assertEqual(self.content.read(), query)
824 testcase.didRequest = 1
827 self.runRequest(httpRequest, MyRequest)
829 def testMissingContentDisposition(self):
832 Content-Type: multipart/form-data; boundary=AaB03x
836 Content-Type: text/plain
837 Content-Transfer-Encoding: quoted-printable
842 self.runRequest(req, http.Request, success=False)
844 def test_chunkedEncoding(self):
846 If a request uses the I{chunked} transfer encoding, the request body is
847 decoded accordingly before it is made available on the request.
851 Content-Type: text/plain
852 Transfer-Encoding: chunked
862 class MyRequest(http.Request):
864 # The tempfile API used to create content returns an
865 # instance of a different type depending on what platform
866 # we're running on. The point here is to verify that the
867 # request body is in a file that's on the filesystem.
868 # Having a fileno method that returns an int is a somewhat
869 # close approximation of this. -exarkun
870 testcase.assertIsInstance(self.content.fileno(), int)
871 testcase.assertEqual(self.method, 'GET')
872 testcase.assertEqual(self.path, '/')
873 content = self.content.read()
874 testcase.assertEqual(content, 'Hello, spam,eggs spam spam')
875 testcase.assertIdentical(self.channel._transferDecoder, None)
876 testcase.didRequest = 1
879 self.runRequest(httpRequest, MyRequest)
883 class QueryArgumentsTestCase(unittest.TestCase):
884 def testParseqs(self):
885 self.assertEqual(cgi.parse_qs("a=b&d=c;+=f"),
886 http.parse_qs("a=b&d=c;+=f"))
887 self.failUnlessRaises(ValueError, http.parse_qs, "blah",
889 self.assertEqual(cgi.parse_qs("a=&b=c", keep_blank_values = 1),
890 http.parse_qs("a=&b=c", keep_blank_values = 1))
891 self.assertEqual(cgi.parse_qs("a=&b=c"),
892 http.parse_qs("a=&b=c"))
895 def test_urlparse(self):
897 For a given URL, L{http.urlparse} should behave the same as
898 L{urlparse}, except it should always return C{str}, never C{unicode}.
901 for scheme in ('http', 'https'):
902 for host in ('example.com',):
903 for port in (None, 100):
904 for path in ('', 'path'):
906 host = host + ':' + str(port)
907 yield urlunsplit((scheme, host, path, '', ''))
910 def assertSameParsing(url, decode):
912 Verify that C{url} is parsed into the same objects by both
913 L{http.urlparse} and L{urlparse}.
915 urlToStandardImplementation = url
917 urlToStandardImplementation = url.decode('ascii')
918 standardResult = urlparse(urlToStandardImplementation)
919 scheme, netloc, path, params, query, fragment = http.urlparse(url)
921 (scheme, netloc, path, params, query, fragment),
923 self.assertTrue(isinstance(scheme, str))
924 self.assertTrue(isinstance(netloc, str))
925 self.assertTrue(isinstance(path, str))
926 self.assertTrue(isinstance(params, str))
927 self.assertTrue(isinstance(query, str))
928 self.assertTrue(isinstance(fragment, str))
930 # With caching, unicode then str
933 assertSameParsing(url, True)
934 assertSameParsing(url, False)
936 # With caching, str then unicode
939 assertSameParsing(url, False)
940 assertSameParsing(url, True)
945 assertSameParsing(url, True)
947 assertSameParsing(url, False)
950 def test_urlparseRejectsUnicode(self):
952 L{http.urlparse} should reject unicode input early.
954 self.assertRaises(TypeError, http.urlparse, u'http://example.org/path')
958 class ClientDriver(http.HTTPClient):
959 def handleStatus(self, version, status, message):
960 self.version = version
962 self.message = message
964 class ClientStatusParsing(unittest.TestCase):
965 def testBaseline(self):
967 c.lineReceived('HTTP/1.0 201 foo')
968 self.assertEqual(c.version, 'HTTP/1.0')
969 self.assertEqual(c.status, '201')
970 self.assertEqual(c.message, 'foo')
972 def testNoMessage(self):
974 c.lineReceived('HTTP/1.0 201')
975 self.assertEqual(c.version, 'HTTP/1.0')
976 self.assertEqual(c.status, '201')
977 self.assertEqual(c.message, '')
979 def testNoMessage_trailingSpace(self):
981 c.lineReceived('HTTP/1.0 201 ')
982 self.assertEqual(c.version, 'HTTP/1.0')
983 self.assertEqual(c.status, '201')
984 self.assertEqual(c.message, '')
988 class RequestTests(unittest.TestCase, ResponseTestMixin):
990 Tests for L{http.Request}
992 def _compatHeadersTest(self, oldName, newName):
994 Verify that each of two different attributes which are associated with
995 the same state properly reflect changes made through the other.
997 This is used to test that the C{headers}/C{responseHeaders} and
998 C{received_headers}/C{requestHeaders} pairs interact properly.
1000 req = http.Request(DummyChannel(), None)
1001 getattr(req, newName).setRawHeaders("test", ["lemur"])
1002 self.assertEqual(getattr(req, oldName)["test"], "lemur")
1003 setattr(req, oldName, {"foo": "bar"})
1005 list(getattr(req, newName).getAllRawHeaders()),
1007 setattr(req, newName, http_headers.Headers())
1008 self.assertEqual(getattr(req, oldName), {})
1011 def test_received_headers(self):
1013 L{Request.received_headers} is a backwards compatible API which
1014 accesses and allows mutation of the state at L{Request.requestHeaders}.
1016 self._compatHeadersTest('received_headers', 'requestHeaders')
1019 def test_headers(self):
1021 L{Request.headers} is a backwards compatible API which accesses and
1022 allows mutation of the state at L{Request.responseHeaders}.
1024 self._compatHeadersTest('headers', 'responseHeaders')
1027 def test_getHeader(self):
1029 L{http.Request.getHeader} returns the value of the named request
1032 req = http.Request(DummyChannel(), None)
1033 req.requestHeaders.setRawHeaders("test", ["lemur"])
1034 self.assertEqual(req.getHeader("test"), "lemur")
1037 def test_getHeaderReceivedMultiples(self):
1039 When there are multiple values for a single request header,
1040 L{http.Request.getHeader} returns the last value.
1042 req = http.Request(DummyChannel(), None)
1043 req.requestHeaders.setRawHeaders("test", ["lemur", "panda"])
1044 self.assertEqual(req.getHeader("test"), "panda")
1047 def test_getHeaderNotFound(self):
1049 L{http.Request.getHeader} returns C{None} when asked for the value of a
1050 request header which is not present.
1052 req = http.Request(DummyChannel(), None)
1053 self.assertEqual(req.getHeader("test"), None)
1056 def test_getAllHeaders(self):
1058 L{http.Request.getAllheaders} returns a C{dict} mapping all request
1059 header names to their corresponding values.
1061 req = http.Request(DummyChannel(), None)
1062 req.requestHeaders.setRawHeaders("test", ["lemur"])
1063 self.assertEqual(req.getAllHeaders(), {"test": "lemur"})
1066 def test_getAllHeadersNoHeaders(self):
1068 L{http.Request.getAllHeaders} returns an empty C{dict} if there are no
1071 req = http.Request(DummyChannel(), None)
1072 self.assertEqual(req.getAllHeaders(), {})
1075 def test_getAllHeadersMultipleHeaders(self):
1077 When there are multiple values for a single request header,
1078 L{http.Request.getAllHeaders} returns only the last value.
1080 req = http.Request(DummyChannel(), None)
1081 req.requestHeaders.setRawHeaders("test", ["lemur", "panda"])
1082 self.assertEqual(req.getAllHeaders(), {"test": "panda"})
1085 def test_setResponseCode(self):
1087 L{http.Request.setResponseCode} takes a status code and causes it to be
1088 used as the response status.
1090 channel = DummyChannel()
1091 req = http.Request(channel, None)
1092 req.setResponseCode(201)
1095 channel.transport.written.getvalue().splitlines()[0],
1096 '%s 201 Created' % (req.clientproto,))
1099 def test_setResponseCodeAndMessage(self):
1101 L{http.Request.setResponseCode} takes a status code and a message and
1102 causes them to be used as the response status.
1104 channel = DummyChannel()
1105 req = http.Request(channel, None)
1106 req.setResponseCode(202, "happily accepted")
1109 channel.transport.written.getvalue().splitlines()[0],
1110 '%s 202 happily accepted' % (req.clientproto,))
1113 def test_setResponseCodeAcceptsIntegers(self):
1115 L{http.Request.setResponseCode} accepts C{int} or C{long} for the code
1116 parameter and raises L{TypeError} if passed anything else.
1118 req = http.Request(DummyChannel(), None)
1119 req.setResponseCode(1)
1120 req.setResponseCode(1L)
1121 self.assertRaises(TypeError, req.setResponseCode, "1")
1124 def test_setHost(self):
1126 L{http.Request.setHost} sets the value of the host request header.
1127 The port should not be added because it is the default.
1129 req = http.Request(DummyChannel(), None)
1130 req.setHost("example.com", 80)
1132 req.requestHeaders.getRawHeaders("host"), ["example.com"])
1135 def test_setHostSSL(self):
1137 L{http.Request.setHost} sets the value of the host request header.
1138 The port should not be added because it is the default.
1141 d.transport = DummyChannel.SSL()
1142 req = http.Request(d, None)
1143 req.setHost("example.com", 443)
1145 req.requestHeaders.getRawHeaders("host"), ["example.com"])
1148 def test_setHostNonDefaultPort(self):
1150 L{http.Request.setHost} sets the value of the host request header.
1151 The port should be added because it is not the default.
1153 req = http.Request(DummyChannel(), None)
1154 req.setHost("example.com", 81)
1156 req.requestHeaders.getRawHeaders("host"), ["example.com:81"])
1159 def test_setHostSSLNonDefaultPort(self):
1161 L{http.Request.setHost} sets the value of the host request header.
1162 The port should be added because it is not the default.
1165 d.transport = DummyChannel.SSL()
1166 req = http.Request(d, None)
1167 req.setHost("example.com", 81)
1169 req.requestHeaders.getRawHeaders("host"), ["example.com:81"])
1172 def test_setHeader(self):
1174 L{http.Request.setHeader} sets the value of the given response header.
1176 req = http.Request(DummyChannel(), None)
1177 req.setHeader("test", "lemur")
1178 self.assertEqual(req.responseHeaders.getRawHeaders("test"), ["lemur"])
1181 def test_firstWrite(self):
1183 For an HTTP 1.0 request, L{http.Request.write} sends an HTTP 1.0
1184 Response-Line and whatever response headers are set.
1186 req = http.Request(DummyChannel(), None)
1187 trans = StringTransport()
1189 req.transport = trans
1191 req.setResponseCode(200)
1192 req.clientproto = "HTTP/1.0"
1193 req.responseHeaders.setRawHeaders("test", ["lemur"])
1196 self.assertResponseEquals(
1198 [("HTTP/1.0 200 OK",
1203 def test_firstWriteHTTP11Chunked(self):
1205 For an HTTP 1.1 request, L{http.Request.write} sends an HTTP 1.1
1206 Response-Line, whatever response headers are set, and uses chunked
1207 encoding for the response body.
1209 req = http.Request(DummyChannel(), None)
1210 trans = StringTransport()
1212 req.transport = trans
1214 req.setResponseCode(200)
1215 req.clientproto = "HTTP/1.1"
1216 req.responseHeaders.setRawHeaders("test", ["lemur"])
1220 self.assertResponseEquals(
1222 [("HTTP/1.1 200 OK",
1224 "Transfer-Encoding: chunked",
1225 "5\r\nHello\r\n6\r\nWorld!\r\n")])
1228 def test_firstWriteLastModified(self):
1230 For an HTTP 1.0 request for a resource with a known last modified time,
1231 L{http.Request.write} sends an HTTP Response-Line, whatever response
1232 headers are set, and a last-modified header with that time.
1234 req = http.Request(DummyChannel(), None)
1235 trans = StringTransport()
1237 req.transport = trans
1239 req.setResponseCode(200)
1240 req.clientproto = "HTTP/1.0"
1241 req.lastModified = 0
1242 req.responseHeaders.setRawHeaders("test", ["lemur"])
1245 self.assertResponseEquals(
1247 [("HTTP/1.0 200 OK",
1249 "Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT",
1253 def test_parseCookies(self):
1255 L{http.Request.parseCookies} extracts cookies from C{requestHeaders}
1256 and adds them to C{received_cookies}.
1258 req = http.Request(DummyChannel(), None)
1259 req.requestHeaders.setRawHeaders(
1260 "cookie", ['test="lemur"; test2="panda"'])
1262 self.assertEqual(req.received_cookies, {"test": '"lemur"',
1263 "test2": '"panda"'})
1266 def test_parseCookiesMultipleHeaders(self):
1268 L{http.Request.parseCookies} can extract cookies from multiple Cookie
1271 req = http.Request(DummyChannel(), None)
1272 req.requestHeaders.setRawHeaders(
1273 "cookie", ['test="lemur"', 'test2="panda"'])
1275 self.assertEqual(req.received_cookies, {"test": '"lemur"',
1276 "test2": '"panda"'})
1279 def test_connectionLost(self):
1281 L{http.Request.connectionLost} closes L{Request.content} and drops the
1282 reference to the L{HTTPChannel} to assist with garbage collection.
1284 req = http.Request(DummyChannel(), None)
1286 # Cause Request.content to be created at all.
1289 # Grab a reference to content in case the Request drops it later on.
1290 content = req.content
1292 # Put some bytes into it
1293 req.handleContentChunk("hello")
1295 # Then something goes wrong and content should get closed.
1296 req.connectionLost(Failure(ConnectionLost("Finished")))
1297 self.assertTrue(content.closed)
1298 self.assertIdentical(req.channel, None)
1301 def test_registerProducerTwiceFails(self):
1303 Calling L{Request.registerProducer} when a producer is already
1304 registered raises ValueError.
1306 req = http.Request(DummyChannel(), None)
1307 req.registerProducer(DummyProducer(), True)
1309 ValueError, req.registerProducer, DummyProducer(), True)
1312 def test_registerProducerWhenQueuedPausesPushProducer(self):
1314 Calling L{Request.registerProducer} with an IPushProducer when the
1315 request is queued pauses the producer.
1317 req = http.Request(DummyChannel(), True)
1318 producer = DummyProducer()
1319 req.registerProducer(producer, True)
1320 self.assertEqual(['pause'], producer.events)
1323 def test_registerProducerWhenQueuedDoesntPausePullProducer(self):
1325 Calling L{Request.registerProducer} with an IPullProducer when the
1326 request is queued does not pause the producer, because it doesn't make
1327 sense to pause a pull producer.
1329 req = http.Request(DummyChannel(), True)
1330 producer = DummyProducer()
1331 req.registerProducer(producer, False)
1332 self.assertEqual([], producer.events)
1335 def test_registerProducerWhenQueuedDoesntRegisterPushProducer(self):
1337 Calling L{Request.registerProducer} with an IPushProducer when the
1338 request is queued does not register the producer on the request's
1341 self.assertIdentical(
1342 None, getattr(http.StringTransport, 'registerProducer', None),
1343 "StringTransport cannot implement registerProducer for this test "
1345 req = http.Request(DummyChannel(), True)
1346 producer = DummyProducer()
1347 req.registerProducer(producer, True)
1348 # This is a roundabout assertion: http.StringTransport doesn't
1349 # implement registerProducer, so Request.registerProducer can't have
1350 # tried to call registerProducer on the transport.
1351 self.assertIsInstance(req.transport, http.StringTransport)
1354 def test_registerProducerWhenQueuedDoesntRegisterPullProducer(self):
1356 Calling L{Request.registerProducer} with an IPullProducer when the
1357 request is queued does not register the producer on the request's
1360 self.assertIdentical(
1361 None, getattr(http.StringTransport, 'registerProducer', None),
1362 "StringTransport cannot implement registerProducer for this test "
1364 req = http.Request(DummyChannel(), True)
1365 producer = DummyProducer()
1366 req.registerProducer(producer, False)
1367 # This is a roundabout assertion: http.StringTransport doesn't
1368 # implement registerProducer, so Request.registerProducer can't have
1369 # tried to call registerProducer on the transport.
1370 self.assertIsInstance(req.transport, http.StringTransport)
1373 def test_registerProducerWhenNotQueuedRegistersPushProducer(self):
1375 Calling L{Request.registerProducer} with an IPushProducer when the
1376 request is not queued registers the producer as a push producer on the
1377 request's transport.
1379 req = http.Request(DummyChannel(), False)
1380 producer = DummyProducer()
1381 req.registerProducer(producer, True)
1382 self.assertEqual([(producer, True)], req.transport.producers)
1385 def test_registerProducerWhenNotQueuedRegistersPullProducer(self):
1387 Calling L{Request.registerProducer} with an IPullProducer when the
1388 request is not queued registers the producer as a pull producer on the
1389 request's transport.
1391 req = http.Request(DummyChannel(), False)
1392 producer = DummyProducer()
1393 req.registerProducer(producer, False)
1394 self.assertEqual([(producer, False)], req.transport.producers)
1397 def test_connectionLostNotification(self):
1399 L{Request.connectionLost} triggers all finish notification Deferreds
1400 and cleans up per-request state.
1403 request = http.Request(d, True)
1404 finished = request.notifyFinish()
1405 request.connectionLost(Failure(ConnectionLost("Connection done")))
1406 self.assertIdentical(request.channel, None)
1407 return self.assertFailure(finished, ConnectionLost)
1410 def test_finishNotification(self):
1412 L{Request.finish} triggers all finish notification Deferreds.
1414 request = http.Request(DummyChannel(), False)
1415 finished = request.notifyFinish()
1416 # Force the request to have a non-None content attribute. This is
1417 # probably a bug in Request.
1418 request.gotLength(1)
1423 def test_writeAfterFinish(self):
1425 Calling L{Request.write} after L{Request.finish} has been called results
1426 in a L{RuntimeError} being raised.
1428 request = http.Request(DummyChannel(), False)
1429 finished = request.notifyFinish()
1430 # Force the request to have a non-None content attribute. This is
1431 # probably a bug in Request.
1432 request.gotLength(1)
1433 request.write('foobar')
1435 self.assertRaises(RuntimeError, request.write, 'foobar')
1439 def test_finishAfterConnectionLost(self):
1441 Calling L{Request.finish} after L{Request.connectionLost} has been
1442 called results in a L{RuntimeError} being raised.
1444 channel = DummyChannel()
1445 transport = channel.transport
1446 req = http.Request(channel, False)
1447 req.connectionLost(Failure(ConnectionLost("The end.")))
1448 self.assertRaises(RuntimeError, req.finish)
1452 class MultilineHeadersTestCase(unittest.TestCase):
1454 Tests to exercise handling of multiline headers by L{HTTPClient}. RFCs 1945
1455 (HTTP 1.0) and 2616 (HTTP 1.1) state that HTTP message header fields can
1456 span multiple lines if each extra line is preceded by at least one space or
1461 Initialize variables used to verify that the header-processing functions
1464 self.handleHeaderCalled = False
1465 self.handleEndHeadersCalled = False
1467 # Dictionary of sample complete HTTP header key/value pairs, including
1468 # multiline headers.
1469 expectedHeaders = {'Content-Length': '10',
1470 'X-Multiline' : 'line-0\tline-1',
1471 'X-Multiline2' : 'line-2 line-3'}
1473 def ourHandleHeader(self, key, val):
1475 Dummy implementation of L{HTTPClient.handleHeader}.
1477 self.handleHeaderCalled = True
1478 self.assertEqual(val, self.expectedHeaders[key])
1481 def ourHandleEndHeaders(self):
1483 Dummy implementation of L{HTTPClient.handleEndHeaders}.
1485 self.handleEndHeadersCalled = True
1488 def test_extractHeader(self):
1490 A header isn't processed by L{HTTPClient.extractHeader} until it is
1491 confirmed in L{HTTPClient.lineReceived} that the header has been
1492 received completely.
1495 c.handleHeader = self.ourHandleHeader
1496 c.handleEndHeaders = self.ourHandleEndHeaders
1498 c.lineReceived('HTTP/1.0 201')
1499 c.lineReceived('Content-Length: 10')
1500 self.assertIdentical(c.length, None)
1501 self.assertFalse(self.handleHeaderCalled)
1502 self.assertFalse(self.handleEndHeadersCalled)
1504 # Signal end of headers.
1506 self.assertTrue(self.handleHeaderCalled)
1507 self.assertTrue(self.handleEndHeadersCalled)
1509 self.assertEqual(c.length, 10)
1512 def test_noHeaders(self):
1514 An HTTP request with no headers will not cause any calls to
1515 L{handleHeader} but will cause L{handleEndHeaders} to be called on
1516 L{HTTPClient} subclasses.
1519 c.handleHeader = self.ourHandleHeader
1520 c.handleEndHeaders = self.ourHandleEndHeaders
1521 c.lineReceived('HTTP/1.0 201')
1523 # Signal end of headers.
1525 self.assertFalse(self.handleHeaderCalled)
1526 self.assertTrue(self.handleEndHeadersCalled)
1528 self.assertEqual(c.version, 'HTTP/1.0')
1529 self.assertEqual(c.status, '201')
1532 def test_multilineHeaders(self):
1534 L{HTTPClient} parses multiline headers by buffering header lines until
1535 an empty line or a line that does not start with whitespace hits
1536 lineReceived, confirming that the header has been received completely.
1539 c.handleHeader = self.ourHandleHeader
1540 c.handleEndHeaders = self.ourHandleEndHeaders
1542 c.lineReceived('HTTP/1.0 201')
1543 c.lineReceived('X-Multiline: line-0')
1544 self.assertFalse(self.handleHeaderCalled)
1545 # Start continuing line with a tab.
1546 c.lineReceived('\tline-1')
1547 c.lineReceived('X-Multiline2: line-2')
1548 # The previous header must be complete, so now it can be processed.
1549 self.assertTrue(self.handleHeaderCalled)
1550 # Start continuing line with a space.
1551 c.lineReceived(' line-3')
1552 c.lineReceived('Content-Length: 10')
1554 # Signal end of headers.
1556 self.assertTrue(self.handleEndHeadersCalled)
1558 self.assertEqual(c.version, 'HTTP/1.0')
1559 self.assertEqual(c.status, '201')
1560 self.assertEqual(c.length, 10)
1564 class Expect100ContinueServerTests(unittest.TestCase):
1566 Test that the HTTP server handles 'Expect: 100-continue' header correctly.
1568 The tests in this class all assume a simplistic behavior where user code
1569 cannot choose to deny a request. Once ticket #288 is implemented and user
1570 code can run before the body of a POST is processed this should be
1571 extended to support overriding this behavior.
1574 def test_HTTP10(self):
1576 HTTP/1.0 requests do not get 100-continue returned, even if 'Expect:
1577 100-continue' is included (RFC 2616 10.1.1).
1579 transport = StringTransport()
1580 channel = http.HTTPChannel()
1581 channel.requestFactory = DummyHTTPHandler
1582 channel.makeConnection(transport)
1583 channel.dataReceived("GET / HTTP/1.0\r\n")
1584 channel.dataReceived("Host: www.example.com\r\n")
1585 channel.dataReceived("Content-Length: 3\r\n")
1586 channel.dataReceived("Expect: 100-continue\r\n")
1587 channel.dataReceived("\r\n")
1588 self.assertEqual(transport.value(), "")
1589 channel.dataReceived("abc")
1590 self.assertEqual(transport.value(),
1591 "HTTP/1.0 200 OK\r\n"
1593 "Content-Length: 13\r\n"
1594 "Version: HTTP/1.0\r\n"
1595 "Request: /\r\n\r\n'''\n3\nabc'''\n")
1598 def test_expect100ContinueHeader(self):
1600 If a HTTP/1.1 client sends a 'Expect: 100-continue' header, the server
1601 responds with a 100 response code before handling the request body, if
1602 any. The normal resource rendering code will then be called, which
1603 will send an additional response code.
1605 transport = StringTransport()
1606 channel = http.HTTPChannel()
1607 channel.requestFactory = DummyHTTPHandler
1608 channel.makeConnection(transport)
1609 channel.dataReceived("GET / HTTP/1.1\r\n")
1610 channel.dataReceived("Host: www.example.com\r\n")
1611 channel.dataReceived("Expect: 100-continue\r\n")
1612 channel.dataReceived("Content-Length: 3\r\n")
1613 # The 100 continue response is not sent until all headers are
1615 self.assertEqual(transport.value(), "")
1616 channel.dataReceived("\r\n")
1617 # The 100 continue response is sent *before* the body is even
1619 self.assertEqual(transport.value(), "HTTP/1.1 100 Continue\r\n\r\n")
1620 channel.dataReceived("abc")
1621 self.assertEqual(transport.value(),
1622 "HTTP/1.1 100 Continue\r\n\r\n"
1623 "HTTP/1.1 200 OK\r\n"
1625 "Content-Length: 13\r\n"
1626 "Version: HTTP/1.1\r\n"
1627 "Request: /\r\n\r\n'''\n3\nabc'''\n")
1630 def test_expect100ContinueWithPipelining(self):
1632 If a HTTP/1.1 client sends a 'Expect: 100-continue' header, followed
1633 by another pipelined request, the 100 response does not interfere with
1634 the response to the second request.
1636 transport = StringTransport()
1637 channel = http.HTTPChannel()
1638 channel.requestFactory = DummyHTTPHandler
1639 channel.makeConnection(transport)
1640 channel.dataReceived(
1641 "GET / HTTP/1.1\r\n"
1642 "Host: www.example.com\r\n"
1643 "Expect: 100-continue\r\n"
1644 "Content-Length: 3\r\n"
1646 "POST /foo HTTP/1.1\r\n"
1647 "Host: www.example.com\r\n"
1648 "Content-Length: 4\r\n"
1650 self.assertEqual(transport.value(),
1651 "HTTP/1.1 100 Continue\r\n\r\n"
1652 "HTTP/1.1 200 OK\r\n"
1654 "Content-Length: 13\r\n"
1655 "Version: HTTP/1.1\r\n"
1656 "Request: /\r\n\r\n"
1658 "HTTP/1.1 200 OK\r\n"
1660 "Content-Length: 14\r\n"
1661 "Version: HTTP/1.1\r\n"
1662 "Request: /foo\r\n\r\n"
1663 "'''\n4\ndefg'''\n")