Imported Upstream version 12.1.0
[contrib/python-twisted.git] / twisted / web / test / test_http.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Test HTTP support.
6 """
7
8 from urlparse import urlparse, urlunsplit, clear_cache
9 import random, urllib, cgi
10
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
24
25
26 class DateTimeTest(unittest.TestCase):
27     """Test date parsing functions."""
28
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)
35
36
37 class DummyHTTPHandler(http.Request):
38
39     def process(self):
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))
49         self.write(request)
50         self.finish()
51
52
53 class LoopbackHTTPClient(http.HTTPClient):
54
55     def connectionMade(self):
56         self.sendCommand("GET", "/foo/bar")
57         self.sendHeader("Content-Length", 10)
58         self.endHeaders()
59         self.transport.write("0123456789")
60
61
62 class ResponseTestMixin(object):
63     """
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.
66     """
67
68     def assertResponseEquals(self, responses, expected):
69         """
70         Assert that the C{responses} matches the C{expected} responses.
71
72         @type responses: C{str}
73         @param responses: The bytes sent in response to one or more requests.
74
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.
80         """
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)
89
90
91
92 class HTTP1_0TestCase(unittest.TestCase, ResponseTestMixin):
93     requests = (
94         "GET / HTTP/1.0\r\n"
95         "\r\n"
96         "GET / HTTP/1.1\r\n"
97         "Accept: text/html\r\n"
98         "\r\n")
99
100     expected_response = [
101         ("HTTP/1.0 200 OK",
102          "Request: /",
103          "Command: GET",
104          "Version: HTTP/1.0",
105          "Content-Length: 13",
106          "'''\nNone\n'''\n")]
107
108     def test_buffer(self):
109         """
110         Send requests over a channel and check responses match what is expected.
111         """
112         b = StringTransport()
113         a = http.HTTPChannel()
114         a.requestFactory = DummyHTTPHandler
115         a.makeConnection(b)
116         # one byte at a time, to stress it.
117         for byte in self.requests:
118             a.dataReceived(byte)
119         a.connectionLost(IOError("all one"))
120         value = b.value()
121         self.assertResponseEquals(value, self.expected_response)
122
123
124     def test_requestBodyTimeout(self):
125         """
126         L{HTTPChannel} resets its timeout whenever data from a request body is
127         delivered to it.
128         """
129         clock = Clock()
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')
136         clock.advance(99)
137         self.assertFalse(transport.disconnecting)
138         protocol.dataReceived('x')
139         clock.advance(99)
140         self.assertFalse(transport.disconnecting)
141         protocol.dataReceived('x')
142         self.assertEqual(len(protocol.requests), 1)
143
144
145
146 class HTTP1_1TestCase(HTTP1_0TestCase):
147
148     requests = (
149         "GET / HTTP/1.1\r\n"
150         "Accept: text/html\r\n"
151         "\r\n"
152         "POST / HTTP/1.1\r\n"
153         "Content-Length: 10\r\n"
154         "\r\n"
155         "0123456789POST / HTTP/1.1\r\n"
156         "Content-Length: 10\r\n"
157         "\r\n"
158         "0123456789HEAD / HTTP/1.1\r\n"
159         "\r\n")
160
161     expected_response = [
162         ("HTTP/1.1 200 OK",
163          "Request: /",
164          "Command: GET",
165          "Version: HTTP/1.1",
166          "Content-Length: 13",
167          "'''\nNone\n'''\n"),
168         ("HTTP/1.1 200 OK",
169          "Request: /",
170          "Command: POST",
171          "Version: HTTP/1.1",
172          "Content-Length: 21",
173          "'''\n10\n0123456789'''\n"),
174         ("HTTP/1.1 200 OK",
175          "Request: /",
176          "Command: POST",
177          "Version: HTTP/1.1",
178          "Content-Length: 21",
179          "'''\n10\n0123456789'''\n"),
180         ("HTTP/1.1 200 OK",
181          "Request: /",
182          "Command: HEAD",
183          "Version: HTTP/1.1",
184          "Content-Length: 13",
185          "")]
186
187
188
189 class HTTP1_1_close_TestCase(HTTP1_0TestCase):
190
191     requests = (
192         "GET / HTTP/1.1\r\n"
193         "Accept: text/html\r\n"
194         "Connection: close\r\n"
195         "\r\n"
196         "GET / HTTP/1.0\r\n"
197         "\r\n")
198
199     expected_response = [
200         ("HTTP/1.1 200 OK",
201          "Connection: close",
202          "Request: /",
203          "Command: GET",
204          "Version: HTTP/1.1",
205          "Content-Length: 13",
206          "'''\nNone\n'''\n")]
207
208
209
210 class HTTP0_9TestCase(HTTP1_0TestCase):
211
212     requests = (
213         "GET /\r\n")
214
215     expected_response = "HTTP/1.1 400 Bad Request\r\n\r\n"
216
217
218     def assertResponseEquals(self, response, expectedResponse):
219         self.assertEqual(response, expectedResponse)
220
221
222 class HTTPLoopbackTestCase(unittest.TestCase):
223
224     expectedHeaders = {'request' : '/foo/bar',
225                        'command' : 'GET',
226                        'version' : 'HTTP/1.0',
227                        'content-length' : '21'}
228     numHeaders = 0
229     gotStatus = 0
230     gotResponse = 0
231     gotEndHeaders = 0
232
233     def _handleStatus(self, version, status, message):
234         self.gotStatus = 1
235         self.assertEqual(version, "HTTP/1.0")
236         self.assertEqual(status, "200")
237
238     def _handleResponse(self, data):
239         self.gotResponse = 1
240         self.assertEqual(data, "'''\n10\n0123456789'''\n")
241
242     def _handleHeader(self, key, value):
243         self.numHeaders = self.numHeaders + 1
244         self.assertEqual(self.expectedHeaders[key.lower()], value)
245
246     def _handleEndHeaders(self):
247         self.gotEndHeaders = 1
248         self.assertEqual(self.numHeaders, 4)
249
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)
260         return d
261
262     def _cbTestLoopback(self, ignored):
263         if not (self.gotStatus and self.gotResponse and self.gotEndHeaders):
264             raise RuntimeError(
265                 "didn't got all callbacks %s"
266                 % [self.gotStatus, self.gotResponse, self.gotEndHeaders])
267         del self.gotEndHeaders
268         del self.gotResponse
269         del self.gotStatus
270         del self.numHeaders
271
272
273
274 def _prequest(**headers):
275     """
276     Make a request with the given request headers for the persistence tests.
277     """
278     request = http.Request(DummyChannel(), None)
279     for k, v in headers.iteritems():
280         request.requestHeaders.setRawHeaders(k, v)
281     return request
282
283
284 class PersistenceTestCase(unittest.TestCase):
285     """
286     Tests for persistent HTTP connections.
287     """
288
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}),
294               ]
295
296
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])
304
305
306
307 class IdentityTransferEncodingTests(TestCase):
308     """
309     Tests for L{_IdentityTransferDecoder}.
310     """
311     def setUp(self):
312         """
313         Create an L{_IdentityTransferDecoder} with callbacks hooked up so that
314         calls to them can be inspected.
315         """
316         self.data = []
317         self.finish = []
318         self.contentLength = 10
319         self.decoder = _IdentityTransferDecoder(
320             self.contentLength, self.data.append, self.finish.append)
321
322
323     def test_exactAmountReceived(self):
324         """
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
329         string.
330         """
331         self.decoder.dataReceived('x' * self.contentLength)
332         self.assertEqual(self.data, ['x' * self.contentLength])
333         self.assertEqual(self.finish, [''])
334
335
336     def test_shortStrings(self):
337         """
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.
342         """
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, [''])
349
350
351     def test_longString(self):
352         """
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.
357         """
358         self.decoder.dataReceived('x' * self.contentLength + 'y')
359         self.assertEqual(self.data, ['x' * self.contentLength])
360         self.assertEqual(self.finish, ['y'])
361
362
363     def test_rejectDataAfterFinished(self):
364         """
365         If data is passed to L{_IdentityTransferDecoder.dataReceived} after the
366         finish callback has been invoked, L{RuntimeError} is raised.
367         """
368         failures = []
369         def finish(bytes):
370             try:
371                 decoder.dataReceived('foo')
372             except:
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)
379         self.assertEqual(
380             str(failures[0].value),
381             "_IdentityTransferDecoder cannot decode data after finishing")
382
383
384     def test_unknownContentLength(self):
385         """
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
388         callback.
389         """
390         data = []
391         finish = []
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, [])
398
399
400     def _verifyCallbacksUnreferenced(self, decoder):
401         """
402         Check the decoder's data and finish callbacks and make sure they are
403         None in order to help avoid references cycles.
404         """
405         self.assertIdentical(decoder.dataCallback, None)
406         self.assertIdentical(decoder.finishCallback, None)
407
408
409     def test_earlyConnectionLose(self):
410         """
411         L{_IdentityTransferDecoder.noMoreData} raises L{_DataLoss} if it is
412         called and the content length is known but not enough bytes have been
413         delivered.
414         """
415         self.decoder.dataReceived('x' * (self.contentLength - 1))
416         self.assertRaises(_DataLoss, self.decoder.noMoreData)
417         self._verifyCallbacksUnreferenced(self.decoder)
418
419
420     def test_unknownContentLengthConnectionLose(self):
421         """
422         L{_IdentityTransferDecoder.noMoreData} calls the finish callback and
423         raises L{PotentialDataLoss} if it is called and the content length is
424         unknown.
425         """
426         body = []
427         finished = []
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)
433
434
435     def test_finishedConnectionLose(self):
436         """
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
439         been delivered.
440         """
441         self.decoder.dataReceived('x' * self.contentLength)
442         self.decoder.noMoreData()
443         self._verifyCallbacksUnreferenced(self.decoder)
444
445
446
447 class ChunkedTransferEncodingTests(unittest.TestCase):
448     """
449     Tests for L{_ChunkedTransferDecoder}, which turns a byte stream encoded
450     using HTTP I{chunked} C{Transfer-Encoding} back into the original byte
451     stream.
452     """
453     def test_decoding(self):
454         """
455         L{_ChunkedTransferDecoder.dataReceived} decodes chunked-encoded data
456         and passes the result to the specified callback.
457         """
458         L = []
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'])
463
464
465     def test_short(self):
466         """
467         L{_ChunkedTransferDecoder.dataReceived} decodes chunks broken up and
468         delivered in multiple calls.
469         """
470         L = []
471         finished = []
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':
474             p.dataReceived(s)
475         self.assertEqual(L, ['a', 'b', 'c', '1', '2', '3', '4', '5'])
476         self.assertEqual(finished, [''])
477
478
479     def test_newlines(self):
480         """
481         L{_ChunkedTransferDecoder.dataReceived} doesn't treat CR LF pairs
482         embedded in chunk bodies specially.
483         """
484         L = []
485         p = http._ChunkedTransferDecoder(L.append, None)
486         p.dataReceived('2\r\n\r\n\r\n')
487         self.assertEqual(L, ['\r\n'])
488
489
490     def test_extensions(self):
491         """
492         L{_ChunkedTransferDecoder.dataReceived} disregards chunk-extension
493         fields.
494         """
495         L = []
496         p = http._ChunkedTransferDecoder(L.append, None)
497         p.dataReceived('3; x-foo=bar\r\nabc\r\n')
498         self.assertEqual(L, ['abc'])
499
500
501     def test_finish(self):
502         """
503         L{_ChunkedTransferDecoder.dataReceived} interprets a zero-length
504         chunk as the end of the chunked data stream and calls the completion
505         callback.
506         """
507         finished = []
508         p = http._ChunkedTransferDecoder(None, finished.append)
509         p.dataReceived('0\r\n\r\n')
510         self.assertEqual(finished, [''])
511
512
513     def test_extra(self):
514         """
515         L{_ChunkedTransferDecoder.dataReceived} passes any bytes which come
516         after the terminating zero-length chunk to the completion callback.
517         """
518         finished = []
519         p = http._ChunkedTransferDecoder(None, finished.append)
520         p.dataReceived('0\r\n\r\nhello')
521         self.assertEqual(finished, ['hello'])
522
523
524     def test_afterFinished(self):
525         """
526         L{_ChunkedTransferDecoder.dataReceived} raises L{RuntimeError} if it
527         is called after it has seen the last chunk.
528         """
529         p = http._ChunkedTransferDecoder(None, lambda bytes: None)
530         p.dataReceived('0\r\n\r\n')
531         self.assertRaises(RuntimeError, p.dataReceived, 'hello')
532
533
534     def test_earlyConnectionLose(self):
535         """
536         L{_ChunkedTransferDecoder.noMoreData} raises L{_DataLoss} if it is
537         called and the end of the last trailer has not yet been received.
538         """
539         parser = http._ChunkedTransferDecoder(None, lambda bytes: None)
540         parser.dataReceived('0\r\n\r')
541         exc = self.assertRaises(_DataLoss, parser.noMoreData)
542         self.assertEqual(
543             str(exc),
544             "Chunked decoder in 'trailer' state, still expecting more data "
545             "to get to finished state.")
546
547
548     def test_finishedConnectionLose(self):
549         """
550         L{_ChunkedTransferDecoder.noMoreData} does not raise any exception if
551         it is called after the terminal zero length chunk is received.
552         """
553         parser = http._ChunkedTransferDecoder(None, lambda bytes: None)
554         parser.dataReceived('0\r\n\r\n')
555         parser.noMoreData()
556
557
558     def test_reentrantFinishedNoMoreData(self):
559         """
560         L{_ChunkedTransferDecoder.noMoreData} can be called from the finished
561         callback without raising an exception.
562         """
563         errors = []
564         successes = []
565         def finished(extra):
566             try:
567                 parser.noMoreData()
568             except:
569                 errors.append(Failure())
570             else:
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])
576
577
578
579 class ChunkingTestCase(unittest.TestCase):
580
581     strings = ["abcv", "", "fdfsd423", "Ffasfas\r\n",
582                "523523\n\rfsdf", "4234"]
583
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')
588
589     def testConcatenatedChunks(self):
590         chunked = ''.join([''.join(http.toChunk(t)) for t in self.strings])
591         result = []
592         buffer = ""
593         for c in chunked:
594             buffer = buffer + c
595             try:
596                 data, buffer = http.fromChunk(buffer)
597                 result.append(data)
598             except ValueError:
599                 pass
600         self.assertEqual(result, self.strings)
601
602
603
604 class ParsingTestCase(unittest.TestCase):
605     """
606     Tests for protocol parsing in L{HTTPChannel}.
607     """
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
613         a.makeConnection(b)
614         # one byte at a time, to stress it.
615         for byte in httpRequest:
616             if a.transport.disconnecting:
617                 break
618             a.dataReceived(byte)
619         a.connectionLost(IOError("all done"))
620         if success:
621             self.assertEqual(self.didRequest, 1)
622             del self.didRequest
623         else:
624             self.assert_(not hasattr(self, "didRequest"))
625         return a
626
627
628     def test_basicAuth(self):
629         """
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.
633         """
634         testcase = self
635         class Request(http.Request):
636             l = []
637             def process(self):
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]
642             s = "%s:%s" % (u, p)
643             f = "GET / HTTP/1.0\nAuthorization: Basic %s\n\n" % (s.encode("base64").strip(), )
644             self.runRequest(f, Request, 0)
645
646
647     def test_headers(self):
648         """
649         Headers received by L{HTTPChannel} in a request are made available to
650         the L{Request}.
651         """
652         processed = []
653         class MyRequest(http.Request):
654             def process(self):
655                 processed.append(self)
656                 self.finish()
657
658         requestLines = [
659             "GET / HTTP/1.0",
660             "Foo: bar",
661             "baz: Quux",
662             "baz: quux",
663             "",
664             ""]
665
666         self.runRequest('\n'.join(requestLines), MyRequest, 0)
667         [request] = processed
668         self.assertEqual(
669             request.requestHeaders.getRawHeaders('foo'), ['bar'])
670         self.assertEqual(
671             request.requestHeaders.getRawHeaders('bAz'), ['Quux', 'quux'])
672
673
674     def test_tooManyHeaders(self):
675         """
676         L{HTTPChannel} enforces a limit of C{HTTPChannel.maxHeaders} on the
677         number of headers received per request.
678         """
679         processed = []
680         class MyRequest(http.Request):
681             def process(self):
682                 processed.append(self)
683
684         requestLines = ["GET / HTTP/1.0"]
685         for i in range(http.HTTPChannel.maxHeaders + 2):
686             requestLines.append("%s: foo" % (i,))
687         requestLines.extend(["", ""])
688
689         channel = self.runRequest("\n".join(requestLines), MyRequest, 0)
690         self.assertEqual(processed, [])
691         self.assertEqual(
692             channel.transport.value(),
693             "HTTP/1.1 400 Bad Request\r\n\r\n")
694
695
696     def test_headerLimitPerRequest(self):
697         """
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.
701         """
702         processed = []
703         class MyRequest(http.Request):
704             def process(self):
705                 processed.append(self)
706                 self.finish()
707
708         self.patch(http.HTTPChannel, 'maxHeaders', 1)
709         requestLines = [
710             "GET / HTTP/1.1",
711             "Foo: bar",
712             "",
713             "",
714             "GET / HTTP/1.1",
715             "Bar: baz",
716             "",
717             ""]
718
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')
723         self.assertEqual(
724             channel.transport.value(),
725             'HTTP/1.1 200 OK\r\n'
726             'Transfer-Encoding: chunked\r\n'
727             '\r\n'
728             '0\r\n'
729             '\r\n'
730             'HTTP/1.1 200 OK\r\n'
731             'Transfer-Encoding: chunked\r\n'
732             '\r\n'
733             '0\r\n'
734             '\r\n')
735
736
737     def testCookies(self):
738         """
739         Test cookies parsing and reading.
740         """
741         httpRequest = '''\
742 GET / HTTP/1.0
743 Cookie: rabbit="eat carrot"; ninja=secret; spam="hey 1=1!"
744
745 '''
746         testcase = self
747
748         class MyRequest(http.Request):
749             def process(self):
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
754                 self.finish()
755
756         self.runRequest(httpRequest, MyRequest)
757
758     def testGET(self):
759         httpRequest = '''\
760 GET /?key=value&multiple=two+words&multiple=more%20words&empty= HTTP/1.0
761
762 '''
763         testcase = self
764         class MyRequest(http.Request):
765             def process(self):
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
771                 self.finish()
772
773         self.runRequest(httpRequest, MyRequest)
774
775
776     def test_extraQuestionMark(self):
777         """
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.
781         """
782         httpRequest = 'GET /foo?bar=?&baz=quux HTTP/1.0\n\n'
783
784         testcase = self
785         class MyRequest(http.Request):
786             def process(self):
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
792                 self.finish()
793
794         self.runRequest(httpRequest, MyRequest)
795
796
797     def test_formPOSTRequest(self):
798         """
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.
804         """
805         query = 'key=value&multiple=two+words&multiple=more%20words&empty='
806         httpRequest = '''\
807 POST / HTTP/1.0
808 Content-Length: %d
809 Content-Type: application/x-www-form-urlencoded
810
811 %s''' % (len(query), query)
812
813         testcase = self
814         class MyRequest(http.Request):
815             def process(self):
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"])
820
821                 # Reading from the content file-like must produce the entire
822                 # request body.
823                 testcase.assertEqual(self.content.read(), query)
824                 testcase.didRequest = 1
825                 self.finish()
826
827         self.runRequest(httpRequest, MyRequest)
828
829     def testMissingContentDisposition(self):
830         req = '''\
831 POST / HTTP/1.0
832 Content-Type: multipart/form-data; boundary=AaB03x
833 Content-Length: 103
834
835 --AaB03x
836 Content-Type: text/plain
837 Content-Transfer-Encoding: quoted-printable
838
839 abasdfg
840 --AaB03x--
841 '''
842         self.runRequest(req, http.Request, success=False)
843
844     def test_chunkedEncoding(self):
845         """
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.
848         """
849         httpRequest = '''\
850 GET / HTTP/1.0
851 Content-Type: text/plain
852 Transfer-Encoding: chunked
853
854 6
855 Hello,
856 14
857  spam,eggs spam spam
858 0
859
860 '''
861         testcase = self
862         class MyRequest(http.Request):
863             def process(self):
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
877                 self.finish()
878
879         self.runRequest(httpRequest, MyRequest)
880
881
882
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",
888             strict_parsing = 1)
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"))
893
894
895     def test_urlparse(self):
896         """
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}.
899         """
900         def urls():
901             for scheme in ('http', 'https'):
902                 for host in ('example.com',):
903                     for port in (None, 100):
904                         for path in ('', 'path'):
905                             if port is not None:
906                                 host = host + ':' + str(port)
907                                 yield urlunsplit((scheme, host, path, '', ''))
908
909
910         def assertSameParsing(url, decode):
911             """
912             Verify that C{url} is parsed into the same objects by both
913             L{http.urlparse} and L{urlparse}.
914             """
915             urlToStandardImplementation = url
916             if decode:
917                 urlToStandardImplementation = url.decode('ascii')
918             standardResult = urlparse(urlToStandardImplementation)
919             scheme, netloc, path, params, query, fragment = http.urlparse(url)
920             self.assertEqual(
921                 (scheme, netloc, path, params, query, fragment),
922                 standardResult)
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))
929
930         # With caching, unicode then str
931         clear_cache()
932         for url in urls():
933             assertSameParsing(url, True)
934             assertSameParsing(url, False)
935
936         # With caching, str then unicode
937         clear_cache()
938         for url in urls():
939             assertSameParsing(url, False)
940             assertSameParsing(url, True)
941
942         # Without caching
943         for url in urls():
944             clear_cache()
945             assertSameParsing(url, True)
946             clear_cache()
947             assertSameParsing(url, False)
948
949
950     def test_urlparseRejectsUnicode(self):
951         """
952         L{http.urlparse} should reject unicode input early.
953         """
954         self.assertRaises(TypeError, http.urlparse, u'http://example.org/path')
955
956
957
958 class ClientDriver(http.HTTPClient):
959     def handleStatus(self, version, status, message):
960         self.version = version
961         self.status = status
962         self.message = message
963
964 class ClientStatusParsing(unittest.TestCase):
965     def testBaseline(self):
966         c = ClientDriver()
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')
971
972     def testNoMessage(self):
973         c = ClientDriver()
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, '')
978
979     def testNoMessage_trailingSpace(self):
980         c = ClientDriver()
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, '')
985
986
987
988 class RequestTests(unittest.TestCase, ResponseTestMixin):
989     """
990     Tests for L{http.Request}
991     """
992     def _compatHeadersTest(self, oldName, newName):
993         """
994         Verify that each of two different attributes which are associated with
995         the same state properly reflect changes made through the other.
996
997         This is used to test that the C{headers}/C{responseHeaders} and
998         C{received_headers}/C{requestHeaders} pairs interact properly.
999         """
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"})
1004         self.assertEqual(
1005             list(getattr(req, newName).getAllRawHeaders()),
1006             [("Foo", ["bar"])])
1007         setattr(req, newName, http_headers.Headers())
1008         self.assertEqual(getattr(req, oldName), {})
1009
1010
1011     def test_received_headers(self):
1012         """
1013         L{Request.received_headers} is a backwards compatible API which
1014         accesses and allows mutation of the state at L{Request.requestHeaders}.
1015         """
1016         self._compatHeadersTest('received_headers', 'requestHeaders')
1017
1018
1019     def test_headers(self):
1020         """
1021         L{Request.headers} is a backwards compatible API which accesses and
1022         allows mutation of the state at L{Request.responseHeaders}.
1023         """
1024         self._compatHeadersTest('headers', 'responseHeaders')
1025
1026
1027     def test_getHeader(self):
1028         """
1029         L{http.Request.getHeader} returns the value of the named request
1030         header.
1031         """
1032         req = http.Request(DummyChannel(), None)
1033         req.requestHeaders.setRawHeaders("test", ["lemur"])
1034         self.assertEqual(req.getHeader("test"), "lemur")
1035
1036
1037     def test_getHeaderReceivedMultiples(self):
1038         """
1039         When there are multiple values for a single request header,
1040         L{http.Request.getHeader} returns the last value.
1041         """
1042         req = http.Request(DummyChannel(), None)
1043         req.requestHeaders.setRawHeaders("test", ["lemur", "panda"])
1044         self.assertEqual(req.getHeader("test"), "panda")
1045
1046
1047     def test_getHeaderNotFound(self):
1048         """
1049         L{http.Request.getHeader} returns C{None} when asked for the value of a
1050         request header which is not present.
1051         """
1052         req = http.Request(DummyChannel(), None)
1053         self.assertEqual(req.getHeader("test"), None)
1054
1055
1056     def test_getAllHeaders(self):
1057         """
1058         L{http.Request.getAllheaders} returns a C{dict} mapping all request
1059         header names to their corresponding values.
1060         """
1061         req = http.Request(DummyChannel(), None)
1062         req.requestHeaders.setRawHeaders("test", ["lemur"])
1063         self.assertEqual(req.getAllHeaders(), {"test": "lemur"})
1064
1065
1066     def test_getAllHeadersNoHeaders(self):
1067         """
1068         L{http.Request.getAllHeaders} returns an empty C{dict} if there are no
1069         request headers.
1070         """
1071         req = http.Request(DummyChannel(), None)
1072         self.assertEqual(req.getAllHeaders(), {})
1073
1074
1075     def test_getAllHeadersMultipleHeaders(self):
1076         """
1077         When there are multiple values for a single request header,
1078         L{http.Request.getAllHeaders} returns only the last value.
1079         """
1080         req = http.Request(DummyChannel(), None)
1081         req.requestHeaders.setRawHeaders("test", ["lemur", "panda"])
1082         self.assertEqual(req.getAllHeaders(), {"test": "panda"})
1083
1084
1085     def test_setResponseCode(self):
1086         """
1087         L{http.Request.setResponseCode} takes a status code and causes it to be
1088         used as the response status.
1089         """
1090         channel = DummyChannel()
1091         req = http.Request(channel, None)
1092         req.setResponseCode(201)
1093         req.write('')
1094         self.assertEqual(
1095             channel.transport.written.getvalue().splitlines()[0],
1096             '%s 201 Created' % (req.clientproto,))
1097
1098
1099     def test_setResponseCodeAndMessage(self):
1100         """
1101         L{http.Request.setResponseCode} takes a status code and a message and
1102         causes them to be used as the response status.
1103         """
1104         channel = DummyChannel()
1105         req = http.Request(channel, None)
1106         req.setResponseCode(202, "happily accepted")
1107         req.write('')
1108         self.assertEqual(
1109             channel.transport.written.getvalue().splitlines()[0],
1110             '%s 202 happily accepted' % (req.clientproto,))
1111
1112
1113     def test_setResponseCodeAcceptsIntegers(self):
1114         """
1115         L{http.Request.setResponseCode} accepts C{int} or C{long} for the code
1116         parameter and raises L{TypeError} if passed anything else.
1117         """
1118         req = http.Request(DummyChannel(), None)
1119         req.setResponseCode(1)
1120         req.setResponseCode(1L)
1121         self.assertRaises(TypeError, req.setResponseCode, "1")
1122
1123
1124     def test_setHost(self):
1125         """
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.
1128         """
1129         req = http.Request(DummyChannel(), None)
1130         req.setHost("example.com", 80)
1131         self.assertEqual(
1132             req.requestHeaders.getRawHeaders("host"), ["example.com"])
1133
1134
1135     def test_setHostSSL(self):
1136         """
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.
1139         """
1140         d = DummyChannel()
1141         d.transport = DummyChannel.SSL()
1142         req = http.Request(d, None)
1143         req.setHost("example.com", 443)
1144         self.assertEqual(
1145             req.requestHeaders.getRawHeaders("host"), ["example.com"])
1146
1147
1148     def test_setHostNonDefaultPort(self):
1149         """
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.
1152         """
1153         req = http.Request(DummyChannel(), None)
1154         req.setHost("example.com", 81)
1155         self.assertEqual(
1156             req.requestHeaders.getRawHeaders("host"), ["example.com:81"])
1157
1158
1159     def test_setHostSSLNonDefaultPort(self):
1160         """
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.
1163         """
1164         d = DummyChannel()
1165         d.transport = DummyChannel.SSL()
1166         req = http.Request(d, None)
1167         req.setHost("example.com", 81)
1168         self.assertEqual(
1169             req.requestHeaders.getRawHeaders("host"), ["example.com:81"])
1170
1171
1172     def test_setHeader(self):
1173         """
1174         L{http.Request.setHeader} sets the value of the given response header.
1175         """
1176         req = http.Request(DummyChannel(), None)
1177         req.setHeader("test", "lemur")
1178         self.assertEqual(req.responseHeaders.getRawHeaders("test"), ["lemur"])
1179
1180
1181     def test_firstWrite(self):
1182         """
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.
1185         """
1186         req = http.Request(DummyChannel(), None)
1187         trans = StringTransport()
1188
1189         req.transport = trans
1190
1191         req.setResponseCode(200)
1192         req.clientproto = "HTTP/1.0"
1193         req.responseHeaders.setRawHeaders("test", ["lemur"])
1194         req.write('Hello')
1195
1196         self.assertResponseEquals(
1197             trans.value(),
1198             [("HTTP/1.0 200 OK",
1199               "Test: lemur",
1200               "Hello")])
1201
1202
1203     def test_firstWriteHTTP11Chunked(self):
1204         """
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.
1208         """
1209         req = http.Request(DummyChannel(), None)
1210         trans = StringTransport()
1211
1212         req.transport = trans
1213
1214         req.setResponseCode(200)
1215         req.clientproto = "HTTP/1.1"
1216         req.responseHeaders.setRawHeaders("test", ["lemur"])
1217         req.write('Hello')
1218         req.write('World!')
1219
1220         self.assertResponseEquals(
1221             trans.value(),
1222             [("HTTP/1.1 200 OK",
1223               "Test: lemur",
1224               "Transfer-Encoding: chunked",
1225               "5\r\nHello\r\n6\r\nWorld!\r\n")])
1226
1227
1228     def test_firstWriteLastModified(self):
1229         """
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.
1233         """
1234         req = http.Request(DummyChannel(), None)
1235         trans = StringTransport()
1236
1237         req.transport = trans
1238
1239         req.setResponseCode(200)
1240         req.clientproto = "HTTP/1.0"
1241         req.lastModified = 0
1242         req.responseHeaders.setRawHeaders("test", ["lemur"])
1243         req.write('Hello')
1244
1245         self.assertResponseEquals(
1246             trans.value(),
1247             [("HTTP/1.0 200 OK",
1248               "Test: lemur",
1249               "Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT",
1250               "Hello")])
1251
1252
1253     def test_parseCookies(self):
1254         """
1255         L{http.Request.parseCookies} extracts cookies from C{requestHeaders}
1256         and adds them to C{received_cookies}.
1257         """
1258         req = http.Request(DummyChannel(), None)
1259         req.requestHeaders.setRawHeaders(
1260             "cookie", ['test="lemur"; test2="panda"'])
1261         req.parseCookies()
1262         self.assertEqual(req.received_cookies, {"test": '"lemur"',
1263                                                  "test2": '"panda"'})
1264
1265
1266     def test_parseCookiesMultipleHeaders(self):
1267         """
1268         L{http.Request.parseCookies} can extract cookies from multiple Cookie
1269         headers.
1270         """
1271         req = http.Request(DummyChannel(), None)
1272         req.requestHeaders.setRawHeaders(
1273             "cookie", ['test="lemur"', 'test2="panda"'])
1274         req.parseCookies()
1275         self.assertEqual(req.received_cookies, {"test": '"lemur"',
1276                                                  "test2": '"panda"'})
1277
1278
1279     def test_connectionLost(self):
1280         """
1281         L{http.Request.connectionLost} closes L{Request.content} and drops the
1282         reference to the L{HTTPChannel} to assist with garbage collection.
1283         """
1284         req = http.Request(DummyChannel(), None)
1285
1286         # Cause Request.content to be created at all.
1287         req.gotLength(10)
1288
1289         # Grab a reference to content in case the Request drops it later on.
1290         content = req.content
1291
1292         # Put some bytes into it
1293         req.handleContentChunk("hello")
1294
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)
1299
1300
1301     def test_registerProducerTwiceFails(self):
1302         """
1303         Calling L{Request.registerProducer} when a producer is already
1304         registered raises ValueError.
1305         """
1306         req = http.Request(DummyChannel(), None)
1307         req.registerProducer(DummyProducer(), True)
1308         self.assertRaises(
1309             ValueError, req.registerProducer, DummyProducer(), True)
1310
1311
1312     def test_registerProducerWhenQueuedPausesPushProducer(self):
1313         """
1314         Calling L{Request.registerProducer} with an IPushProducer when the
1315         request is queued pauses the producer.
1316         """
1317         req = http.Request(DummyChannel(), True)
1318         producer = DummyProducer()
1319         req.registerProducer(producer, True)
1320         self.assertEqual(['pause'], producer.events)
1321
1322
1323     def test_registerProducerWhenQueuedDoesntPausePullProducer(self):
1324         """
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.
1328         """
1329         req = http.Request(DummyChannel(), True)
1330         producer = DummyProducer()
1331         req.registerProducer(producer, False)
1332         self.assertEqual([], producer.events)
1333
1334
1335     def test_registerProducerWhenQueuedDoesntRegisterPushProducer(self):
1336         """
1337         Calling L{Request.registerProducer} with an IPushProducer when the
1338         request is queued does not register the producer on the request's
1339         transport.
1340         """
1341         self.assertIdentical(
1342             None, getattr(http.StringTransport, 'registerProducer', None),
1343             "StringTransport cannot implement registerProducer for this test "
1344             "to be valid.")
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)
1352
1353
1354     def test_registerProducerWhenQueuedDoesntRegisterPullProducer(self):
1355         """
1356         Calling L{Request.registerProducer} with an IPullProducer when the
1357         request is queued does not register the producer on the request's
1358         transport.
1359         """
1360         self.assertIdentical(
1361             None, getattr(http.StringTransport, 'registerProducer', None),
1362             "StringTransport cannot implement registerProducer for this test "
1363             "to be valid.")
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)
1371
1372
1373     def test_registerProducerWhenNotQueuedRegistersPushProducer(self):
1374         """
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.
1378         """
1379         req = http.Request(DummyChannel(), False)
1380         producer = DummyProducer()
1381         req.registerProducer(producer, True)
1382         self.assertEqual([(producer, True)], req.transport.producers)
1383
1384
1385     def test_registerProducerWhenNotQueuedRegistersPullProducer(self):
1386         """
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.
1390         """
1391         req = http.Request(DummyChannel(), False)
1392         producer = DummyProducer()
1393         req.registerProducer(producer, False)
1394         self.assertEqual([(producer, False)], req.transport.producers)
1395
1396
1397     def test_connectionLostNotification(self):
1398         """
1399         L{Request.connectionLost} triggers all finish notification Deferreds
1400         and cleans up per-request state.
1401         """
1402         d = DummyChannel()
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)
1408
1409
1410     def test_finishNotification(self):
1411         """
1412         L{Request.finish} triggers all finish notification Deferreds.
1413         """
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)
1419         request.finish()
1420         return finished
1421
1422
1423     def test_writeAfterFinish(self):
1424         """
1425         Calling L{Request.write} after L{Request.finish} has been called results
1426         in a L{RuntimeError} being raised.
1427         """
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')
1434         request.finish()
1435         self.assertRaises(RuntimeError, request.write, 'foobar')
1436         return finished
1437
1438
1439     def test_finishAfterConnectionLost(self):
1440         """
1441         Calling L{Request.finish} after L{Request.connectionLost} has been
1442         called results in a L{RuntimeError} being raised.
1443         """
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)
1449
1450
1451
1452 class MultilineHeadersTestCase(unittest.TestCase):
1453     """
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
1457     horizontal tab.
1458     """
1459     def setUp(self):
1460         """
1461         Initialize variables used to verify that the header-processing functions
1462         are getting called.
1463         """
1464         self.handleHeaderCalled = False
1465         self.handleEndHeadersCalled = False
1466
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'}
1472
1473     def ourHandleHeader(self, key, val):
1474         """
1475         Dummy implementation of L{HTTPClient.handleHeader}.
1476         """
1477         self.handleHeaderCalled = True
1478         self.assertEqual(val, self.expectedHeaders[key])
1479
1480
1481     def ourHandleEndHeaders(self):
1482         """
1483         Dummy implementation of L{HTTPClient.handleEndHeaders}.
1484         """
1485         self.handleEndHeadersCalled = True
1486
1487
1488     def test_extractHeader(self):
1489         """
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.
1493         """
1494         c = ClientDriver()
1495         c.handleHeader = self.ourHandleHeader
1496         c.handleEndHeaders = self.ourHandleEndHeaders
1497
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)
1503
1504         # Signal end of headers.
1505         c.lineReceived('')
1506         self.assertTrue(self.handleHeaderCalled)
1507         self.assertTrue(self.handleEndHeadersCalled)
1508
1509         self.assertEqual(c.length, 10)
1510
1511
1512     def test_noHeaders(self):
1513         """
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.
1517         """
1518         c = ClientDriver()
1519         c.handleHeader = self.ourHandleHeader
1520         c.handleEndHeaders = self.ourHandleEndHeaders
1521         c.lineReceived('HTTP/1.0 201')
1522
1523         # Signal end of headers.
1524         c.lineReceived('')
1525         self.assertFalse(self.handleHeaderCalled)
1526         self.assertTrue(self.handleEndHeadersCalled)
1527
1528         self.assertEqual(c.version, 'HTTP/1.0')
1529         self.assertEqual(c.status, '201')
1530
1531
1532     def test_multilineHeaders(self):
1533         """
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.
1537         """
1538         c = ClientDriver()
1539         c.handleHeader = self.ourHandleHeader
1540         c.handleEndHeaders = self.ourHandleEndHeaders
1541
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')
1553
1554         # Signal end of headers.
1555         c.lineReceived('')
1556         self.assertTrue(self.handleEndHeadersCalled)
1557
1558         self.assertEqual(c.version, 'HTTP/1.0')
1559         self.assertEqual(c.status, '201')
1560         self.assertEqual(c.length, 10)
1561
1562
1563
1564 class Expect100ContinueServerTests(unittest.TestCase):
1565     """
1566     Test that the HTTP server handles 'Expect: 100-continue' header correctly.
1567
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.
1572     """
1573
1574     def test_HTTP10(self):
1575         """
1576         HTTP/1.0 requests do not get 100-continue returned, even if 'Expect:
1577         100-continue' is included (RFC 2616 10.1.1).
1578         """
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"
1592                          "Command: GET\r\n"
1593                          "Content-Length: 13\r\n"
1594                          "Version: HTTP/1.0\r\n"
1595                          "Request: /\r\n\r\n'''\n3\nabc'''\n")
1596
1597
1598     def test_expect100ContinueHeader(self):
1599         """
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.
1604         """
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
1614         # received:
1615         self.assertEqual(transport.value(), "")
1616         channel.dataReceived("\r\n")
1617         # The 100 continue response is sent *before* the body is even
1618         # received:
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"
1624                          "Command: GET\r\n"
1625                          "Content-Length: 13\r\n"
1626                          "Version: HTTP/1.1\r\n"
1627                          "Request: /\r\n\r\n'''\n3\nabc'''\n")
1628
1629
1630     def test_expect100ContinueWithPipelining(self):
1631         """
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.
1635         """
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"
1645             "\r\nabc"
1646             "POST /foo HTTP/1.1\r\n"
1647             "Host: www.example.com\r\n"
1648             "Content-Length: 4\r\n"
1649             "\r\ndefg")
1650         self.assertEqual(transport.value(),
1651                          "HTTP/1.1 100 Continue\r\n\r\n"
1652                          "HTTP/1.1 200 OK\r\n"
1653                          "Command: GET\r\n"
1654                          "Content-Length: 13\r\n"
1655                          "Version: HTTP/1.1\r\n"
1656                          "Request: /\r\n\r\n"
1657                          "'''\n3\nabc'''\n"
1658                          "HTTP/1.1 200 OK\r\n"
1659                          "Command: POST\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")