blob: c071411f58cb31e189376f4f324d34bd11fc7e41 [file] [log] [blame]
Senthil Kumaranaa5f49e2010-10-03 18:26:07 +00001import httplib
Antoine Pitrou72481782009-09-29 17:48:18 +00002import array
Jeremy Hylton79fa2b62001-04-13 14:57:44 +00003import StringIO
Facundo Batista07c78be2007-03-23 18:54:07 +00004import socket
Victor Stinner2c6aee92010-07-24 02:46:16 +00005import errno
Benjamin Petersone3e7d402014-11-23 21:02:02 -06006import os
Jeremy Hylton121d34a2003-07-08 12:36:58 +00007
Gregory P. Smith9d325212010-01-03 02:06:07 +00008import unittest
9TestCase = unittest.TestCase
Jeremy Hylton2c178252004-08-07 16:28:14 +000010
11from test import test_support
Jeremy Hylton79fa2b62001-04-13 14:57:44 +000012
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -060013here = os.path.dirname(__file__)
14# Self-signed cert file for 'localhost'
15CERT_localhost = os.path.join(here, 'keycert.pem')
16# Self-signed cert file for 'fakehostname'
17CERT_fakehostname = os.path.join(here, 'keycert2.pem')
18# Self-signed cert file for self-signed.pythontest.net
19CERT_selfsigned_pythontestdotnet = os.path.join(here, 'selfsigned_pythontestdotnet.pem')
20
Trent Nelsone41b0062008-04-08 23:47:30 +000021HOST = test_support.HOST
22
Jeremy Hylton79fa2b62001-04-13 14:57:44 +000023class FakeSocket:
Senthil Kumaran36f28f72014-05-16 18:51:46 -070024 def __init__(self, text, fileclass=StringIO.StringIO, host=None, port=None):
Jeremy Hylton79fa2b62001-04-13 14:57:44 +000025 self.text = text
Jeremy Hylton121d34a2003-07-08 12:36:58 +000026 self.fileclass = fileclass
Martin v. Löwis040a9272006-11-12 10:32:47 +000027 self.data = ''
Serhiy Storchakad862db02014-12-01 13:07:28 +020028 self.file_closed = False
Senthil Kumaran36f28f72014-05-16 18:51:46 -070029 self.host = host
30 self.port = port
Jeremy Hylton79fa2b62001-04-13 14:57:44 +000031
Jeremy Hylton2c178252004-08-07 16:28:14 +000032 def sendall(self, data):
Antoine Pitrou72481782009-09-29 17:48:18 +000033 self.data += ''.join(data)
Jeremy Hylton2c178252004-08-07 16:28:14 +000034
Jeremy Hylton79fa2b62001-04-13 14:57:44 +000035 def makefile(self, mode, bufsize=None):
36 if mode != 'r' and mode != 'rb':
Neal Norwitz28bb5722002-04-01 19:00:50 +000037 raise httplib.UnimplementedFileMode()
Serhiy Storchakad862db02014-12-01 13:07:28 +020038 # keep the file around so we can check how much was read from it
39 self.file = self.fileclass(self.text)
40 self.file.close = self.file_close #nerf close ()
41 return self.file
42
43 def file_close(self):
44 self.file_closed = True
Jeremy Hylton121d34a2003-07-08 12:36:58 +000045
Senthil Kumaran36f28f72014-05-16 18:51:46 -070046 def close(self):
47 pass
48
Victor Stinner2c6aee92010-07-24 02:46:16 +000049class EPipeSocket(FakeSocket):
50
51 def __init__(self, text, pipe_trigger):
52 # When sendall() is called with pipe_trigger, raise EPIPE.
53 FakeSocket.__init__(self, text)
54 self.pipe_trigger = pipe_trigger
55
56 def sendall(self, data):
57 if self.pipe_trigger in data:
58 raise socket.error(errno.EPIPE, "gotcha")
59 self.data += data
60
61 def close(self):
62 pass
63
Jeremy Hylton121d34a2003-07-08 12:36:58 +000064class NoEOFStringIO(StringIO.StringIO):
65 """Like StringIO, but raises AssertionError on EOF.
66
67 This is used below to test that httplib doesn't try to read
68 more from the underlying file than it should.
69 """
70 def read(self, n=-1):
71 data = StringIO.StringIO.read(self, n)
72 if data == '':
73 raise AssertionError('caller tried to read past EOF')
74 return data
75
76 def readline(self, length=None):
77 data = StringIO.StringIO.readline(self, length)
78 if data == '':
79 raise AssertionError('caller tried to read past EOF')
80 return data
Jeremy Hylton79fa2b62001-04-13 14:57:44 +000081
Jeremy Hylton2c178252004-08-07 16:28:14 +000082
83class HeaderTests(TestCase):
84 def test_auto_headers(self):
85 # Some headers are added automatically, but should not be added by
86 # .request() if they are explicitly set.
87
Jeremy Hylton2c178252004-08-07 16:28:14 +000088 class HeaderCountingBuffer(list):
89 def __init__(self):
90 self.count = {}
91 def append(self, item):
92 kv = item.split(':')
93 if len(kv) > 1:
94 # item is a 'Key: Value' header string
95 lcKey = kv[0].lower()
96 self.count.setdefault(lcKey, 0)
97 self.count[lcKey] += 1
98 list.append(self, item)
99
100 for explicit_header in True, False:
101 for header in 'Content-length', 'Host', 'Accept-encoding':
102 conn = httplib.HTTPConnection('example.com')
103 conn.sock = FakeSocket('blahblahblah')
104 conn._buffer = HeaderCountingBuffer()
105
106 body = 'spamspamspam'
107 headers = {}
108 if explicit_header:
109 headers[header] = str(len(body))
110 conn.request('POST', '/', body, headers)
111 self.assertEqual(conn._buffer.count[header.lower()], 1)
112
Senthil Kumaran618802d2012-05-19 16:52:21 +0800113 def test_content_length_0(self):
114
115 class ContentLengthChecker(list):
116 def __init__(self):
117 list.__init__(self)
118 self.content_length = None
119 def append(self, item):
120 kv = item.split(':', 1)
121 if len(kv) > 1 and kv[0].lower() == 'content-length':
122 self.content_length = kv[1].strip()
123 list.append(self, item)
124
125 # POST with empty body
126 conn = httplib.HTTPConnection('example.com')
127 conn.sock = FakeSocket(None)
128 conn._buffer = ContentLengthChecker()
129 conn.request('POST', '/', '')
130 self.assertEqual(conn._buffer.content_length, '0',
131 'Header Content-Length not set')
132
133 # PUT request with empty body
134 conn = httplib.HTTPConnection('example.com')
135 conn.sock = FakeSocket(None)
136 conn._buffer = ContentLengthChecker()
137 conn.request('PUT', '/', '')
138 self.assertEqual(conn._buffer.content_length, '0',
139 'Header Content-Length not set')
140
Senthil Kumaranaa5f49e2010-10-03 18:26:07 +0000141 def test_putheader(self):
142 conn = httplib.HTTPConnection('example.com')
143 conn.sock = FakeSocket(None)
144 conn.putrequest('GET','/')
145 conn.putheader('Content-length',42)
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200146 self.assertIn('Content-length: 42', conn._buffer)
Senthil Kumaranaa5f49e2010-10-03 18:26:07 +0000147
Senthil Kumaran501bfd82010-11-14 03:31:52 +0000148 def test_ipv6host_header(self):
149 # Default host header on IPv6 transaction should wrapped by [] if
150 # its actual IPv6 address
151 expected = 'GET /foo HTTP/1.1\r\nHost: [2001::]:81\r\n' \
152 'Accept-Encoding: identity\r\n\r\n'
153 conn = httplib.HTTPConnection('[2001::]:81')
154 sock = FakeSocket('')
155 conn.sock = sock
156 conn.request('GET', '/foo')
157 self.assertTrue(sock.data.startswith(expected))
158
159 expected = 'GET /foo HTTP/1.1\r\nHost: [2001:102A::]\r\n' \
160 'Accept-Encoding: identity\r\n\r\n'
161 conn = httplib.HTTPConnection('[2001:102A::]')
162 sock = FakeSocket('')
163 conn.sock = sock
164 conn.request('GET', '/foo')
165 self.assertTrue(sock.data.startswith(expected))
166
Benjamin Petersonbfd976f2015-01-25 23:34:42 -0500167 def test_malformed_headers_coped_with(self):
168 # Issue 19996
169 body = "HTTP/1.1 200 OK\r\nFirst: val\r\n: nval\r\nSecond: val\r\n\r\n"
170 sock = FakeSocket(body)
171 resp = httplib.HTTPResponse(sock)
172 resp.begin()
173
174 self.assertEqual(resp.getheader('First'), 'val')
175 self.assertEqual(resp.getheader('Second'), 'val')
176
Senthil Kumaran501bfd82010-11-14 03:31:52 +0000177
Georg Brandl71a20892006-10-29 20:24:01 +0000178class BasicTest(TestCase):
179 def test_status_lines(self):
180 # Test HTTP status lines
Jeremy Hylton79fa2b62001-04-13 14:57:44 +0000181
Georg Brandl71a20892006-10-29 20:24:01 +0000182 body = "HTTP/1.1 200 Ok\r\n\r\nText"
183 sock = FakeSocket(body)
184 resp = httplib.HTTPResponse(sock)
Jeremy Hyltonba603192003-01-23 18:02:20 +0000185 resp.begin()
Serhiy Storchakac97f5ed2013-12-17 21:49:48 +0200186 self.assertEqual(resp.read(0), '') # Issue #20007
187 self.assertFalse(resp.isclosed())
Georg Brandl71a20892006-10-29 20:24:01 +0000188 self.assertEqual(resp.read(), 'Text')
Facundo Batista70665902007-10-18 03:16:03 +0000189 self.assertTrue(resp.isclosed())
Jeremy Hyltonba603192003-01-23 18:02:20 +0000190
Georg Brandl71a20892006-10-29 20:24:01 +0000191 body = "HTTP/1.1 400.100 Not Ok\r\n\r\nText"
192 sock = FakeSocket(body)
193 resp = httplib.HTTPResponse(sock)
194 self.assertRaises(httplib.BadStatusLine, resp.begin)
Jeremy Hyltonba603192003-01-23 18:02:20 +0000195
Dirkjan Ochtmanebc73dc2010-02-24 04:49:00 +0000196 def test_bad_status_repr(self):
197 exc = httplib.BadStatusLine('')
Ezio Melotti2623a372010-11-21 13:34:58 +0000198 self.assertEqual(repr(exc), '''BadStatusLine("\'\'",)''')
Dirkjan Ochtmanebc73dc2010-02-24 04:49:00 +0000199
Facundo Batista70665902007-10-18 03:16:03 +0000200 def test_partial_reads(self):
Antoine Pitrou4113d2b2012-12-15 19:11:54 +0100201 # if we have a length, the system knows when to close itself
Facundo Batista70665902007-10-18 03:16:03 +0000202 # same behaviour than when we read the whole thing with read()
203 body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
204 sock = FakeSocket(body)
205 resp = httplib.HTTPResponse(sock)
206 resp.begin()
207 self.assertEqual(resp.read(2), 'Te')
208 self.assertFalse(resp.isclosed())
209 self.assertEqual(resp.read(2), 'xt')
210 self.assertTrue(resp.isclosed())
211
Antoine Pitrou4113d2b2012-12-15 19:11:54 +0100212 def test_partial_reads_no_content_length(self):
213 # when no length is present, the socket should be gracefully closed when
214 # all data was read
215 body = "HTTP/1.1 200 Ok\r\n\r\nText"
216 sock = FakeSocket(body)
217 resp = httplib.HTTPResponse(sock)
218 resp.begin()
219 self.assertEqual(resp.read(2), 'Te')
220 self.assertFalse(resp.isclosed())
221 self.assertEqual(resp.read(2), 'xt')
222 self.assertEqual(resp.read(1), '')
223 self.assertTrue(resp.isclosed())
224
Antoine Pitroud66c0ee2013-02-02 22:49:34 +0100225 def test_partial_reads_incomplete_body(self):
226 # if the server shuts down the connection before the whole
227 # content-length is delivered, the socket is gracefully closed
228 body = "HTTP/1.1 200 Ok\r\nContent-Length: 10\r\n\r\nText"
229 sock = FakeSocket(body)
230 resp = httplib.HTTPResponse(sock)
231 resp.begin()
232 self.assertEqual(resp.read(2), 'Te')
233 self.assertFalse(resp.isclosed())
234 self.assertEqual(resp.read(2), 'xt')
235 self.assertEqual(resp.read(1), '')
236 self.assertTrue(resp.isclosed())
237
Georg Brandl71a20892006-10-29 20:24:01 +0000238 def test_host_port(self):
239 # Check invalid host_port
Jeremy Hyltonba603192003-01-23 18:02:20 +0000240
Łukasz Langa7a153902011-10-18 17:16:00 +0200241 # Note that httplib does not accept user:password@ in the host-port.
242 for hp in ("www.python.org:abc", "user:password@www.python.org"):
Georg Brandl71a20892006-10-29 20:24:01 +0000243 self.assertRaises(httplib.InvalidURL, httplib.HTTP, hp)
244
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000245 for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000", "fe80::207:e9ff:fe9b",
246 8000),
Georg Brandl71a20892006-10-29 20:24:01 +0000247 ("www.python.org:80", "www.python.org", 80),
248 ("www.python.org", "www.python.org", 80),
Łukasz Langa7a153902011-10-18 17:16:00 +0200249 ("www.python.org:", "www.python.org", 80),
Georg Brandl71a20892006-10-29 20:24:01 +0000250 ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 80)):
Martin v. Löwis74a249e2004-09-14 21:45:36 +0000251 http = httplib.HTTP(hp)
Georg Brandl71a20892006-10-29 20:24:01 +0000252 c = http._conn
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000253 if h != c.host:
254 self.fail("Host incorrectly parsed: %s != %s" % (h, c.host))
255 if p != c.port:
256 self.fail("Port incorrectly parsed: %s != %s" % (p, c.host))
Skip Montanaro10e6e0e2004-09-14 16:32:02 +0000257
Georg Brandl71a20892006-10-29 20:24:01 +0000258 def test_response_headers(self):
259 # test response with multiple message headers with the same field name.
260 text = ('HTTP/1.1 200 OK\r\n'
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000261 'Set-Cookie: Customer="WILE_E_COYOTE";'
262 ' Version="1"; Path="/acme"\r\n'
Georg Brandl71a20892006-10-29 20:24:01 +0000263 'Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";'
264 ' Path="/acme"\r\n'
265 '\r\n'
266 'No body\r\n')
267 hdr = ('Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"'
268 ', '
269 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"')
270 s = FakeSocket(text)
271 r = httplib.HTTPResponse(s)
272 r.begin()
273 cookies = r.getheader("Set-Cookie")
274 if cookies != hdr:
275 self.fail("multiple headers not combined properly")
Jeremy Hyltonba603192003-01-23 18:02:20 +0000276
Georg Brandl71a20892006-10-29 20:24:01 +0000277 def test_read_head(self):
278 # Test that the library doesn't attempt to read any data
279 # from a HEAD request. (Tickles SF bug #622042.)
280 sock = FakeSocket(
281 'HTTP/1.1 200 OK\r\n'
282 'Content-Length: 14432\r\n'
283 '\r\n',
284 NoEOFStringIO)
285 resp = httplib.HTTPResponse(sock, method="HEAD")
286 resp.begin()
287 if resp.read() != "":
288 self.fail("Did not expect response from HEAD request")
Jeremy Hyltonc1b2cb92003-05-05 16:13:58 +0000289
Berker Peksagb7414e02014-08-05 07:15:57 +0300290 def test_too_many_headers(self):
291 headers = '\r\n'.join('Header%d: foo' % i for i in xrange(200)) + '\r\n'
292 text = ('HTTP/1.1 200 OK\r\n' + headers)
293 s = FakeSocket(text)
294 r = httplib.HTTPResponse(s)
295 self.assertRaises(httplib.HTTPException, r.begin)
296
Martin v. Löwis040a9272006-11-12 10:32:47 +0000297 def test_send_file(self):
298 expected = 'GET /foo HTTP/1.1\r\nHost: example.com\r\n' \
299 'Accept-Encoding: identity\r\nContent-Length:'
300
301 body = open(__file__, 'rb')
302 conn = httplib.HTTPConnection('example.com')
303 sock = FakeSocket(body)
304 conn.sock = sock
305 conn.request('GET', '/foo', body)
306 self.assertTrue(sock.data.startswith(expected))
Jeremy Hylton2c178252004-08-07 16:28:14 +0000307
Antoine Pitrou72481782009-09-29 17:48:18 +0000308 def test_send(self):
309 expected = 'this is a test this is only a test'
310 conn = httplib.HTTPConnection('example.com')
311 sock = FakeSocket(None)
312 conn.sock = sock
313 conn.send(expected)
Ezio Melotti2623a372010-11-21 13:34:58 +0000314 self.assertEqual(expected, sock.data)
Antoine Pitrou72481782009-09-29 17:48:18 +0000315 sock.data = ''
316 conn.send(array.array('c', expected))
Ezio Melotti2623a372010-11-21 13:34:58 +0000317 self.assertEqual(expected, sock.data)
Antoine Pitrou72481782009-09-29 17:48:18 +0000318 sock.data = ''
319 conn.send(StringIO.StringIO(expected))
Ezio Melotti2623a372010-11-21 13:34:58 +0000320 self.assertEqual(expected, sock.data)
Antoine Pitrou72481782009-09-29 17:48:18 +0000321
Georg Brandl23635032008-02-24 00:03:22 +0000322 def test_chunked(self):
323 chunked_start = (
324 'HTTP/1.1 200 OK\r\n'
325 'Transfer-Encoding: chunked\r\n\r\n'
326 'a\r\n'
327 'hello worl\r\n'
328 '1\r\n'
329 'd\r\n'
330 )
331 sock = FakeSocket(chunked_start + '0\r\n')
332 resp = httplib.HTTPResponse(sock, method="GET")
333 resp.begin()
Ezio Melotti2623a372010-11-21 13:34:58 +0000334 self.assertEqual(resp.read(), 'hello world')
Georg Brandl23635032008-02-24 00:03:22 +0000335 resp.close()
336
337 for x in ('', 'foo\r\n'):
338 sock = FakeSocket(chunked_start + x)
339 resp = httplib.HTTPResponse(sock, method="GET")
340 resp.begin()
341 try:
342 resp.read()
343 except httplib.IncompleteRead, i:
Ezio Melotti2623a372010-11-21 13:34:58 +0000344 self.assertEqual(i.partial, 'hello world')
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000345 self.assertEqual(repr(i),'IncompleteRead(11 bytes read)')
346 self.assertEqual(str(i),'IncompleteRead(11 bytes read)')
Georg Brandl23635032008-02-24 00:03:22 +0000347 else:
348 self.fail('IncompleteRead expected')
349 finally:
350 resp.close()
351
Senthil Kumaraned9204342010-04-28 17:20:43 +0000352 def test_chunked_head(self):
353 chunked_start = (
354 'HTTP/1.1 200 OK\r\n'
355 'Transfer-Encoding: chunked\r\n\r\n'
356 'a\r\n'
357 'hello world\r\n'
358 '1\r\n'
359 'd\r\n'
360 )
361 sock = FakeSocket(chunked_start + '0\r\n')
362 resp = httplib.HTTPResponse(sock, method="HEAD")
363 resp.begin()
Ezio Melotti2623a372010-11-21 13:34:58 +0000364 self.assertEqual(resp.read(), '')
365 self.assertEqual(resp.status, 200)
366 self.assertEqual(resp.reason, 'OK')
Senthil Kumaranfb695012010-06-04 17:17:09 +0000367 self.assertTrue(resp.isclosed())
Senthil Kumaraned9204342010-04-28 17:20:43 +0000368
Georg Brandl8c460d52008-02-24 00:14:24 +0000369 def test_negative_content_length(self):
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000370 sock = FakeSocket('HTTP/1.1 200 OK\r\n'
371 'Content-Length: -1\r\n\r\nHello\r\n')
Georg Brandl8c460d52008-02-24 00:14:24 +0000372 resp = httplib.HTTPResponse(sock, method="GET")
373 resp.begin()
Ezio Melotti2623a372010-11-21 13:34:58 +0000374 self.assertEqual(resp.read(), 'Hello\r\n')
Antoine Pitroud66c0ee2013-02-02 22:49:34 +0100375 self.assertTrue(resp.isclosed())
Georg Brandl8c460d52008-02-24 00:14:24 +0000376
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000377 def test_incomplete_read(self):
378 sock = FakeSocket('HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHello\r\n')
379 resp = httplib.HTTPResponse(sock, method="GET")
380 resp.begin()
381 try:
382 resp.read()
383 except httplib.IncompleteRead as i:
Ezio Melotti2623a372010-11-21 13:34:58 +0000384 self.assertEqual(i.partial, 'Hello\r\n')
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000385 self.assertEqual(repr(i),
386 "IncompleteRead(7 bytes read, 3 more expected)")
387 self.assertEqual(str(i),
388 "IncompleteRead(7 bytes read, 3 more expected)")
Antoine Pitroud66c0ee2013-02-02 22:49:34 +0100389 self.assertTrue(resp.isclosed())
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000390 else:
391 self.fail('IncompleteRead expected')
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000392
Victor Stinner2c6aee92010-07-24 02:46:16 +0000393 def test_epipe(self):
394 sock = EPipeSocket(
395 "HTTP/1.0 401 Authorization Required\r\n"
396 "Content-type: text/html\r\n"
397 "WWW-Authenticate: Basic realm=\"example\"\r\n",
398 b"Content-Length")
399 conn = httplib.HTTPConnection("example.com")
400 conn.sock = sock
401 self.assertRaises(socket.error,
402 lambda: conn.request("PUT", "/url", "body"))
403 resp = conn.getresponse()
404 self.assertEqual(401, resp.status)
405 self.assertEqual("Basic realm=\"example\"",
406 resp.getheader("www-authenticate"))
407
Senthil Kumarand389cb52010-09-21 01:38:15 +0000408 def test_filenoattr(self):
409 # Just test the fileno attribute in the HTTPResponse Object.
410 body = "HTTP/1.1 200 Ok\r\n\r\nText"
411 sock = FakeSocket(body)
412 resp = httplib.HTTPResponse(sock)
413 self.assertTrue(hasattr(resp,'fileno'),
414 'HTTPResponse should expose a fileno attribute')
Georg Brandl23635032008-02-24 00:03:22 +0000415
Antoine Pitroud7b6ac62010-12-18 18:18:21 +0000416 # Test lines overflowing the max line size (_MAXLINE in http.client)
417
418 def test_overflowing_status_line(self):
419 self.skipTest("disabled for HTTP 0.9 support")
420 body = "HTTP/1.1 200 Ok" + "k" * 65536 + "\r\n"
421 resp = httplib.HTTPResponse(FakeSocket(body))
422 self.assertRaises((httplib.LineTooLong, httplib.BadStatusLine), resp.begin)
423
424 def test_overflowing_header_line(self):
425 body = (
426 'HTTP/1.1 200 OK\r\n'
427 'X-Foo: bar' + 'r' * 65536 + '\r\n\r\n'
428 )
429 resp = httplib.HTTPResponse(FakeSocket(body))
430 self.assertRaises(httplib.LineTooLong, resp.begin)
431
432 def test_overflowing_chunked_line(self):
433 body = (
434 'HTTP/1.1 200 OK\r\n'
435 'Transfer-Encoding: chunked\r\n\r\n'
436 + '0' * 65536 + 'a\r\n'
437 'hello world\r\n'
438 '0\r\n'
439 )
440 resp = httplib.HTTPResponse(FakeSocket(body))
441 resp.begin()
442 self.assertRaises(httplib.LineTooLong, resp.read)
443
Senthil Kumaranf5aaf6f2012-04-29 10:15:31 +0800444 def test_early_eof(self):
445 # Test httpresponse with no \r\n termination,
446 body = "HTTP/1.1 200 Ok"
447 sock = FakeSocket(body)
448 resp = httplib.HTTPResponse(sock)
449 resp.begin()
450 self.assertEqual(resp.read(), '')
451 self.assertTrue(resp.isclosed())
Antoine Pitroud7b6ac62010-12-18 18:18:21 +0000452
Serhiy Storchakad862db02014-12-01 13:07:28 +0200453 def test_error_leak(self):
454 # Test that the socket is not leaked if getresponse() fails
455 conn = httplib.HTTPConnection('example.com')
456 response = []
457 class Response(httplib.HTTPResponse):
458 def __init__(self, *pos, **kw):
459 response.append(self) # Avoid garbage collector closing the socket
460 httplib.HTTPResponse.__init__(self, *pos, **kw)
461 conn.response_class = Response
462 conn.sock = FakeSocket('') # Emulate server dropping connection
463 conn.request('GET', '/')
464 self.assertRaises(httplib.BadStatusLine, conn.getresponse)
465 self.assertTrue(response)
466 #self.assertTrue(response[0].closed)
467 self.assertTrue(conn.sock.file_closed)
468
Georg Brandl4cbd1e32006-02-17 22:01:08 +0000469class OfflineTest(TestCase):
470 def test_responses(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000471 self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")
Georg Brandl4cbd1e32006-02-17 22:01:08 +0000472
Gregory P. Smith9d325212010-01-03 02:06:07 +0000473
Senthil Kumaran812b9752015-01-24 12:58:10 -0800474class TestServerMixin:
475 """A limited socket server mixin.
476
477 This is used by test cases for testing http connection end points.
478 """
Gregory P. Smith9d325212010-01-03 02:06:07 +0000479 def setUp(self):
480 self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
481 self.port = test_support.bind_port(self.serv)
482 self.source_port = test_support.find_unused_port()
483 self.serv.listen(5)
484 self.conn = None
485
486 def tearDown(self):
487 if self.conn:
488 self.conn.close()
489 self.conn = None
490 self.serv.close()
491 self.serv = None
492
Senthil Kumaran812b9752015-01-24 12:58:10 -0800493class SourceAddressTest(TestServerMixin, TestCase):
Gregory P. Smith9d325212010-01-03 02:06:07 +0000494 def testHTTPConnectionSourceAddress(self):
495 self.conn = httplib.HTTPConnection(HOST, self.port,
496 source_address=('', self.source_port))
497 self.conn.connect()
498 self.assertEqual(self.conn.sock.getsockname()[1], self.source_port)
499
500 @unittest.skipIf(not hasattr(httplib, 'HTTPSConnection'),
501 'httplib.HTTPSConnection not defined')
502 def testHTTPSConnectionSourceAddress(self):
503 self.conn = httplib.HTTPSConnection(HOST, self.port,
504 source_address=('', self.source_port))
505 # We don't test anything here other the constructor not barfing as
506 # this code doesn't deal with setting up an active running SSL server
507 # for an ssl_wrapped connect() to actually return from.
508
509
Senthil Kumaran812b9752015-01-24 12:58:10 -0800510class HTTPTest(TestServerMixin, TestCase):
511 def testHTTPConnection(self):
512 self.conn = httplib.HTTP(host=HOST, port=self.port, strict=None)
513 self.conn.connect()
514 self.assertEqual(self.conn._conn.host, HOST)
515 self.assertEqual(self.conn._conn.port, self.port)
516
517 def testHTTPWithConnectHostPort(self):
518 testhost = 'unreachable.test.domain'
519 testport = '80'
520 self.conn = httplib.HTTP(host=testhost, port=testport)
521 self.conn.connect(host=HOST, port=self.port)
522 self.assertNotEqual(self.conn._conn.host, testhost)
523 self.assertNotEqual(self.conn._conn.port, testport)
524 self.assertEqual(self.conn._conn.host, HOST)
525 self.assertEqual(self.conn._conn.port, self.port)
526
527
Facundo Batista07c78be2007-03-23 18:54:07 +0000528class TimeoutTest(TestCase):
Trent Nelsone41b0062008-04-08 23:47:30 +0000529 PORT = None
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000530
Facundo Batista07c78be2007-03-23 18:54:07 +0000531 def setUp(self):
532 self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Trent Nelson6c4a7c62008-04-09 00:34:53 +0000533 TimeoutTest.PORT = test_support.bind_port(self.serv)
Facundo Batistaf1966292007-03-25 03:20:05 +0000534 self.serv.listen(5)
Facundo Batista07c78be2007-03-23 18:54:07 +0000535
536 def tearDown(self):
537 self.serv.close()
538 self.serv = None
539
540 def testTimeoutAttribute(self):
541 '''This will prove that the timeout gets through
542 HTTPConnection and into the socket.
543 '''
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000544 # default -- use global socket timeout
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200545 self.assertIsNone(socket.getdefaulttimeout())
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000546 socket.setdefaulttimeout(30)
547 try:
548 httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT)
549 httpConn.connect()
550 finally:
551 socket.setdefaulttimeout(None)
Facundo Batista14553b02007-03-23 20:23:08 +0000552 self.assertEqual(httpConn.sock.gettimeout(), 30)
Facundo Batistaf1966292007-03-25 03:20:05 +0000553 httpConn.close()
Facundo Batista07c78be2007-03-23 18:54:07 +0000554
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000555 # no timeout -- do not use global socket default
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200556 self.assertIsNone(socket.getdefaulttimeout())
Facundo Batista14553b02007-03-23 20:23:08 +0000557 socket.setdefaulttimeout(30)
558 try:
Trent Nelson6c4a7c62008-04-09 00:34:53 +0000559 httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT,
560 timeout=None)
Facundo Batista14553b02007-03-23 20:23:08 +0000561 httpConn.connect()
562 finally:
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000563 socket.setdefaulttimeout(None)
564 self.assertEqual(httpConn.sock.gettimeout(), None)
565 httpConn.close()
566
567 # a value
568 httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT, timeout=30)
569 httpConn.connect()
Facundo Batista14553b02007-03-23 20:23:08 +0000570 self.assertEqual(httpConn.sock.gettimeout(), 30)
Facundo Batistaf1966292007-03-25 03:20:05 +0000571 httpConn.close()
Facundo Batista07c78be2007-03-23 18:54:07 +0000572
Senthil Kumaran812b9752015-01-24 12:58:10 -0800573
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600574class HTTPSTest(TestCase):
Facundo Batista07c78be2007-03-23 18:54:07 +0000575
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600576 def setUp(self):
577 if not hasattr(httplib, 'HTTPSConnection'):
578 self.skipTest('ssl support required')
579
580 def make_server(self, certfile):
581 from test.ssl_servers import make_https_server
582 return make_https_server(self, certfile=certfile)
Facundo Batista70f996b2007-05-21 17:32:32 +0000583
584 def test_attributes(self):
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600585 # simple test to check it's storing the timeout
586 h = httplib.HTTPSConnection(HOST, TimeoutTest.PORT, timeout=30)
587 self.assertEqual(h.timeout, 30)
Facundo Batista70f996b2007-05-21 17:32:32 +0000588
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600589 def test_networked(self):
590 # Default settings: requires a valid cert from a trusted CA
591 import ssl
592 test_support.requires('network')
593 with test_support.transient_internet('self-signed.pythontest.net'):
594 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443)
595 with self.assertRaises(ssl.SSLError) as exc_info:
596 h.request('GET', '/')
597 self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
598
599 def test_networked_noverification(self):
600 # Switch off cert verification
601 import ssl
602 test_support.requires('network')
603 with test_support.transient_internet('self-signed.pythontest.net'):
604 context = ssl._create_stdlib_context()
605 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443,
606 context=context)
607 h.request('GET', '/')
608 resp = h.getresponse()
609 self.assertIn('nginx', resp.getheader('server'))
610
Benjamin Petersonf671de42014-11-25 15:16:55 -0600611 @test_support.system_must_validate_cert
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600612 def test_networked_trusted_by_default_cert(self):
613 # Default settings: requires a valid cert from a trusted CA
614 test_support.requires('network')
615 with test_support.transient_internet('www.python.org'):
616 h = httplib.HTTPSConnection('www.python.org', 443)
617 h.request('GET', '/')
618 resp = h.getresponse()
619 content_type = resp.getheader('content-type')
620 self.assertIn('text/html', content_type)
621
622 def test_networked_good_cert(self):
623 # We feed the server's cert as a validating cert
624 import ssl
625 test_support.requires('network')
626 with test_support.transient_internet('self-signed.pythontest.net'):
627 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
628 context.verify_mode = ssl.CERT_REQUIRED
629 context.load_verify_locations(CERT_selfsigned_pythontestdotnet)
630 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443, context=context)
631 h.request('GET', '/')
632 resp = h.getresponse()
633 server_string = resp.getheader('server')
634 self.assertIn('nginx', server_string)
635
636 def test_networked_bad_cert(self):
637 # We feed a "CA" cert that is unrelated to the server's cert
638 import ssl
639 test_support.requires('network')
640 with test_support.transient_internet('self-signed.pythontest.net'):
641 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
642 context.verify_mode = ssl.CERT_REQUIRED
643 context.load_verify_locations(CERT_localhost)
644 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443, context=context)
645 with self.assertRaises(ssl.SSLError) as exc_info:
646 h.request('GET', '/')
647 self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
648
649 def test_local_unknown_cert(self):
650 # The custom cert isn't known to the default trust bundle
651 import ssl
652 server = self.make_server(CERT_localhost)
653 h = httplib.HTTPSConnection('localhost', server.port)
654 with self.assertRaises(ssl.SSLError) as exc_info:
655 h.request('GET', '/')
656 self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
657
658 def test_local_good_hostname(self):
659 # The (valid) cert validates the HTTP hostname
660 import ssl
661 server = self.make_server(CERT_localhost)
662 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
663 context.verify_mode = ssl.CERT_REQUIRED
664 context.load_verify_locations(CERT_localhost)
665 h = httplib.HTTPSConnection('localhost', server.port, context=context)
666 h.request('GET', '/nonexistent')
667 resp = h.getresponse()
668 self.assertEqual(resp.status, 404)
669
670 def test_local_bad_hostname(self):
671 # The (valid) cert doesn't validate the HTTP hostname
672 import ssl
673 server = self.make_server(CERT_fakehostname)
674 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
675 context.verify_mode = ssl.CERT_REQUIRED
Benjamin Peterson227f6e02014-12-07 13:41:26 -0500676 context.check_hostname = True
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600677 context.load_verify_locations(CERT_fakehostname)
678 h = httplib.HTTPSConnection('localhost', server.port, context=context)
679 with self.assertRaises(ssl.CertificateError):
680 h.request('GET', '/')
Benjamin Peterson227f6e02014-12-07 13:41:26 -0500681 h.close()
682 # With context.check_hostname=False, the mismatching is ignored
683 context.check_hostname = False
684 h = httplib.HTTPSConnection('localhost', server.port, context=context)
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600685 h.request('GET', '/nonexistent')
686 resp = h.getresponse()
687 self.assertEqual(resp.status, 404)
688
Łukasz Langa7a153902011-10-18 17:16:00 +0200689 def test_host_port(self):
690 # Check invalid host_port
691
Łukasz Langa7a153902011-10-18 17:16:00 +0200692 for hp in ("www.python.org:abc", "user:password@www.python.org"):
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600693 self.assertRaises(httplib.InvalidURL, httplib.HTTPSConnection, hp)
Łukasz Langa7a153902011-10-18 17:16:00 +0200694
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600695 for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000",
696 "fe80::207:e9ff:fe9b", 8000),
697 ("www.python.org:443", "www.python.org", 443),
698 ("www.python.org:", "www.python.org", 443),
699 ("www.python.org", "www.python.org", 443),
700 ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 443),
701 ("[fe80::207:e9ff:fe9b]:", "fe80::207:e9ff:fe9b",
702 443)):
703 c = httplib.HTTPSConnection(hp)
704 self.assertEqual(h, c.host)
705 self.assertEqual(p, c.port)
Łukasz Langa7a153902011-10-18 17:16:00 +0200706
707
Senthil Kumaran36f28f72014-05-16 18:51:46 -0700708class TunnelTests(TestCase):
709 def test_connect(self):
710 response_text = (
711 'HTTP/1.0 200 OK\r\n\r\n' # Reply to CONNECT
712 'HTTP/1.1 200 OK\r\n' # Reply to HEAD
713 'Content-Length: 42\r\n\r\n'
714 )
715
716 def create_connection(address, timeout=None, source_address=None):
717 return FakeSocket(response_text, host=address[0], port=address[1])
718
719 conn = httplib.HTTPConnection('proxy.com')
720 conn._create_connection = create_connection
721
722 # Once connected, we should not be able to tunnel anymore
723 conn.connect()
724 self.assertRaises(RuntimeError, conn.set_tunnel, 'destination.com')
725
726 # But if close the connection, we are good.
727 conn.close()
728 conn.set_tunnel('destination.com')
729 conn.request('HEAD', '/', '')
730
731 self.assertEqual(conn.sock.host, 'proxy.com')
732 self.assertEqual(conn.sock.port, 80)
733 self.assertTrue('CONNECT destination.com' in conn.sock.data)
734 self.assertTrue('Host: destination.com' in conn.sock.data)
735
736 self.assertTrue('Host: proxy.com' not in conn.sock.data)
737
738 conn.close()
739
740 conn.request('PUT', '/', '')
741 self.assertEqual(conn.sock.host, 'proxy.com')
742 self.assertEqual(conn.sock.port, 80)
743 self.assertTrue('CONNECT destination.com' in conn.sock.data)
744 self.assertTrue('Host: destination.com' in conn.sock.data)
745
746
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600747@test_support.reap_threads
Jeremy Hylton2c178252004-08-07 16:28:14 +0000748def test_main(verbose=None):
Trent Nelsone41b0062008-04-08 23:47:30 +0000749 test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
Senthil Kumaran812b9752015-01-24 12:58:10 -0800750 HTTPTest, HTTPSTest, SourceAddressTest,
751 TunnelTests)
Jeremy Hylton2c178252004-08-07 16:28:14 +0000752
Georg Brandl71a20892006-10-29 20:24:01 +0000753if __name__ == '__main__':
754 test_main()