blob: 0892d5ab63bbab770b990b53d384b2a52edfd406 [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
167
Georg Brandl71a20892006-10-29 20:24:01 +0000168class BasicTest(TestCase):
169 def test_status_lines(self):
170 # Test HTTP status lines
Jeremy Hylton79fa2b62001-04-13 14:57:44 +0000171
Georg Brandl71a20892006-10-29 20:24:01 +0000172 body = "HTTP/1.1 200 Ok\r\n\r\nText"
173 sock = FakeSocket(body)
174 resp = httplib.HTTPResponse(sock)
Jeremy Hyltonba603192003-01-23 18:02:20 +0000175 resp.begin()
Serhiy Storchakac97f5ed2013-12-17 21:49:48 +0200176 self.assertEqual(resp.read(0), '') # Issue #20007
177 self.assertFalse(resp.isclosed())
Georg Brandl71a20892006-10-29 20:24:01 +0000178 self.assertEqual(resp.read(), 'Text')
Facundo Batista70665902007-10-18 03:16:03 +0000179 self.assertTrue(resp.isclosed())
Jeremy Hyltonba603192003-01-23 18:02:20 +0000180
Georg Brandl71a20892006-10-29 20:24:01 +0000181 body = "HTTP/1.1 400.100 Not Ok\r\n\r\nText"
182 sock = FakeSocket(body)
183 resp = httplib.HTTPResponse(sock)
184 self.assertRaises(httplib.BadStatusLine, resp.begin)
Jeremy Hyltonba603192003-01-23 18:02:20 +0000185
Dirkjan Ochtmanebc73dc2010-02-24 04:49:00 +0000186 def test_bad_status_repr(self):
187 exc = httplib.BadStatusLine('')
Ezio Melotti2623a372010-11-21 13:34:58 +0000188 self.assertEqual(repr(exc), '''BadStatusLine("\'\'",)''')
Dirkjan Ochtmanebc73dc2010-02-24 04:49:00 +0000189
Facundo Batista70665902007-10-18 03:16:03 +0000190 def test_partial_reads(self):
Antoine Pitrou4113d2b2012-12-15 19:11:54 +0100191 # if we have a length, the system knows when to close itself
Facundo Batista70665902007-10-18 03:16:03 +0000192 # same behaviour than when we read the whole thing with read()
193 body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
194 sock = FakeSocket(body)
195 resp = httplib.HTTPResponse(sock)
196 resp.begin()
197 self.assertEqual(resp.read(2), 'Te')
198 self.assertFalse(resp.isclosed())
199 self.assertEqual(resp.read(2), 'xt')
200 self.assertTrue(resp.isclosed())
201
Antoine Pitrou4113d2b2012-12-15 19:11:54 +0100202 def test_partial_reads_no_content_length(self):
203 # when no length is present, the socket should be gracefully closed when
204 # all data was read
205 body = "HTTP/1.1 200 Ok\r\n\r\nText"
206 sock = FakeSocket(body)
207 resp = httplib.HTTPResponse(sock)
208 resp.begin()
209 self.assertEqual(resp.read(2), 'Te')
210 self.assertFalse(resp.isclosed())
211 self.assertEqual(resp.read(2), 'xt')
212 self.assertEqual(resp.read(1), '')
213 self.assertTrue(resp.isclosed())
214
Antoine Pitroud66c0ee2013-02-02 22:49:34 +0100215 def test_partial_reads_incomplete_body(self):
216 # if the server shuts down the connection before the whole
217 # content-length is delivered, the socket is gracefully closed
218 body = "HTTP/1.1 200 Ok\r\nContent-Length: 10\r\n\r\nText"
219 sock = FakeSocket(body)
220 resp = httplib.HTTPResponse(sock)
221 resp.begin()
222 self.assertEqual(resp.read(2), 'Te')
223 self.assertFalse(resp.isclosed())
224 self.assertEqual(resp.read(2), 'xt')
225 self.assertEqual(resp.read(1), '')
226 self.assertTrue(resp.isclosed())
227
Georg Brandl71a20892006-10-29 20:24:01 +0000228 def test_host_port(self):
229 # Check invalid host_port
Jeremy Hyltonba603192003-01-23 18:02:20 +0000230
Łukasz Langa7a153902011-10-18 17:16:00 +0200231 # Note that httplib does not accept user:password@ in the host-port.
232 for hp in ("www.python.org:abc", "user:password@www.python.org"):
Georg Brandl71a20892006-10-29 20:24:01 +0000233 self.assertRaises(httplib.InvalidURL, httplib.HTTP, hp)
234
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000235 for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000", "fe80::207:e9ff:fe9b",
236 8000),
Georg Brandl71a20892006-10-29 20:24:01 +0000237 ("www.python.org:80", "www.python.org", 80),
238 ("www.python.org", "www.python.org", 80),
Łukasz Langa7a153902011-10-18 17:16:00 +0200239 ("www.python.org:", "www.python.org", 80),
Georg Brandl71a20892006-10-29 20:24:01 +0000240 ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 80)):
Martin v. Löwis74a249e2004-09-14 21:45:36 +0000241 http = httplib.HTTP(hp)
Georg Brandl71a20892006-10-29 20:24:01 +0000242 c = http._conn
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000243 if h != c.host:
244 self.fail("Host incorrectly parsed: %s != %s" % (h, c.host))
245 if p != c.port:
246 self.fail("Port incorrectly parsed: %s != %s" % (p, c.host))
Skip Montanaro10e6e0e2004-09-14 16:32:02 +0000247
Georg Brandl71a20892006-10-29 20:24:01 +0000248 def test_response_headers(self):
249 # test response with multiple message headers with the same field name.
250 text = ('HTTP/1.1 200 OK\r\n'
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000251 'Set-Cookie: Customer="WILE_E_COYOTE";'
252 ' Version="1"; Path="/acme"\r\n'
Georg Brandl71a20892006-10-29 20:24:01 +0000253 'Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";'
254 ' Path="/acme"\r\n'
255 '\r\n'
256 'No body\r\n')
257 hdr = ('Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"'
258 ', '
259 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"')
260 s = FakeSocket(text)
261 r = httplib.HTTPResponse(s)
262 r.begin()
263 cookies = r.getheader("Set-Cookie")
264 if cookies != hdr:
265 self.fail("multiple headers not combined properly")
Jeremy Hyltonba603192003-01-23 18:02:20 +0000266
Georg Brandl71a20892006-10-29 20:24:01 +0000267 def test_read_head(self):
268 # Test that the library doesn't attempt to read any data
269 # from a HEAD request. (Tickles SF bug #622042.)
270 sock = FakeSocket(
271 'HTTP/1.1 200 OK\r\n'
272 'Content-Length: 14432\r\n'
273 '\r\n',
274 NoEOFStringIO)
275 resp = httplib.HTTPResponse(sock, method="HEAD")
276 resp.begin()
277 if resp.read() != "":
278 self.fail("Did not expect response from HEAD request")
Jeremy Hyltonc1b2cb92003-05-05 16:13:58 +0000279
Berker Peksagb7414e02014-08-05 07:15:57 +0300280 def test_too_many_headers(self):
281 headers = '\r\n'.join('Header%d: foo' % i for i in xrange(200)) + '\r\n'
282 text = ('HTTP/1.1 200 OK\r\n' + headers)
283 s = FakeSocket(text)
284 r = httplib.HTTPResponse(s)
285 self.assertRaises(httplib.HTTPException, r.begin)
286
Martin v. Löwis040a9272006-11-12 10:32:47 +0000287 def test_send_file(self):
288 expected = 'GET /foo HTTP/1.1\r\nHost: example.com\r\n' \
289 'Accept-Encoding: identity\r\nContent-Length:'
290
291 body = open(__file__, 'rb')
292 conn = httplib.HTTPConnection('example.com')
293 sock = FakeSocket(body)
294 conn.sock = sock
295 conn.request('GET', '/foo', body)
296 self.assertTrue(sock.data.startswith(expected))
Jeremy Hylton2c178252004-08-07 16:28:14 +0000297
Antoine Pitrou72481782009-09-29 17:48:18 +0000298 def test_send(self):
299 expected = 'this is a test this is only a test'
300 conn = httplib.HTTPConnection('example.com')
301 sock = FakeSocket(None)
302 conn.sock = sock
303 conn.send(expected)
Ezio Melotti2623a372010-11-21 13:34:58 +0000304 self.assertEqual(expected, sock.data)
Antoine Pitrou72481782009-09-29 17:48:18 +0000305 sock.data = ''
306 conn.send(array.array('c', expected))
Ezio Melotti2623a372010-11-21 13:34:58 +0000307 self.assertEqual(expected, sock.data)
Antoine Pitrou72481782009-09-29 17:48:18 +0000308 sock.data = ''
309 conn.send(StringIO.StringIO(expected))
Ezio Melotti2623a372010-11-21 13:34:58 +0000310 self.assertEqual(expected, sock.data)
Antoine Pitrou72481782009-09-29 17:48:18 +0000311
Georg Brandl23635032008-02-24 00:03:22 +0000312 def test_chunked(self):
313 chunked_start = (
314 'HTTP/1.1 200 OK\r\n'
315 'Transfer-Encoding: chunked\r\n\r\n'
316 'a\r\n'
317 'hello worl\r\n'
318 '1\r\n'
319 'd\r\n'
320 )
321 sock = FakeSocket(chunked_start + '0\r\n')
322 resp = httplib.HTTPResponse(sock, method="GET")
323 resp.begin()
Ezio Melotti2623a372010-11-21 13:34:58 +0000324 self.assertEqual(resp.read(), 'hello world')
Georg Brandl23635032008-02-24 00:03:22 +0000325 resp.close()
326
327 for x in ('', 'foo\r\n'):
328 sock = FakeSocket(chunked_start + x)
329 resp = httplib.HTTPResponse(sock, method="GET")
330 resp.begin()
331 try:
332 resp.read()
333 except httplib.IncompleteRead, i:
Ezio Melotti2623a372010-11-21 13:34:58 +0000334 self.assertEqual(i.partial, 'hello world')
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000335 self.assertEqual(repr(i),'IncompleteRead(11 bytes read)')
336 self.assertEqual(str(i),'IncompleteRead(11 bytes read)')
Georg Brandl23635032008-02-24 00:03:22 +0000337 else:
338 self.fail('IncompleteRead expected')
339 finally:
340 resp.close()
341
Senthil Kumaraned9204342010-04-28 17:20:43 +0000342 def test_chunked_head(self):
343 chunked_start = (
344 'HTTP/1.1 200 OK\r\n'
345 'Transfer-Encoding: chunked\r\n\r\n'
346 'a\r\n'
347 'hello world\r\n'
348 '1\r\n'
349 'd\r\n'
350 )
351 sock = FakeSocket(chunked_start + '0\r\n')
352 resp = httplib.HTTPResponse(sock, method="HEAD")
353 resp.begin()
Ezio Melotti2623a372010-11-21 13:34:58 +0000354 self.assertEqual(resp.read(), '')
355 self.assertEqual(resp.status, 200)
356 self.assertEqual(resp.reason, 'OK')
Senthil Kumaranfb695012010-06-04 17:17:09 +0000357 self.assertTrue(resp.isclosed())
Senthil Kumaraned9204342010-04-28 17:20:43 +0000358
Georg Brandl8c460d52008-02-24 00:14:24 +0000359 def test_negative_content_length(self):
Jeremy Hylton21d2e592008-11-29 00:09:16 +0000360 sock = FakeSocket('HTTP/1.1 200 OK\r\n'
361 'Content-Length: -1\r\n\r\nHello\r\n')
Georg Brandl8c460d52008-02-24 00:14:24 +0000362 resp = httplib.HTTPResponse(sock, method="GET")
363 resp.begin()
Ezio Melotti2623a372010-11-21 13:34:58 +0000364 self.assertEqual(resp.read(), 'Hello\r\n')
Antoine Pitroud66c0ee2013-02-02 22:49:34 +0100365 self.assertTrue(resp.isclosed())
Georg Brandl8c460d52008-02-24 00:14:24 +0000366
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000367 def test_incomplete_read(self):
368 sock = FakeSocket('HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHello\r\n')
369 resp = httplib.HTTPResponse(sock, method="GET")
370 resp.begin()
371 try:
372 resp.read()
373 except httplib.IncompleteRead as i:
Ezio Melotti2623a372010-11-21 13:34:58 +0000374 self.assertEqual(i.partial, 'Hello\r\n')
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000375 self.assertEqual(repr(i),
376 "IncompleteRead(7 bytes read, 3 more expected)")
377 self.assertEqual(str(i),
378 "IncompleteRead(7 bytes read, 3 more expected)")
Antoine Pitroud66c0ee2013-02-02 22:49:34 +0100379 self.assertTrue(resp.isclosed())
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000380 else:
381 self.fail('IncompleteRead expected')
Benjamin Peterson7d49bba2009-03-02 22:41:42 +0000382
Victor Stinner2c6aee92010-07-24 02:46:16 +0000383 def test_epipe(self):
384 sock = EPipeSocket(
385 "HTTP/1.0 401 Authorization Required\r\n"
386 "Content-type: text/html\r\n"
387 "WWW-Authenticate: Basic realm=\"example\"\r\n",
388 b"Content-Length")
389 conn = httplib.HTTPConnection("example.com")
390 conn.sock = sock
391 self.assertRaises(socket.error,
392 lambda: conn.request("PUT", "/url", "body"))
393 resp = conn.getresponse()
394 self.assertEqual(401, resp.status)
395 self.assertEqual("Basic realm=\"example\"",
396 resp.getheader("www-authenticate"))
397
Senthil Kumarand389cb52010-09-21 01:38:15 +0000398 def test_filenoattr(self):
399 # Just test the fileno attribute in the HTTPResponse Object.
400 body = "HTTP/1.1 200 Ok\r\n\r\nText"
401 sock = FakeSocket(body)
402 resp = httplib.HTTPResponse(sock)
403 self.assertTrue(hasattr(resp,'fileno'),
404 'HTTPResponse should expose a fileno attribute')
Georg Brandl23635032008-02-24 00:03:22 +0000405
Antoine Pitroud7b6ac62010-12-18 18:18:21 +0000406 # Test lines overflowing the max line size (_MAXLINE in http.client)
407
408 def test_overflowing_status_line(self):
409 self.skipTest("disabled for HTTP 0.9 support")
410 body = "HTTP/1.1 200 Ok" + "k" * 65536 + "\r\n"
411 resp = httplib.HTTPResponse(FakeSocket(body))
412 self.assertRaises((httplib.LineTooLong, httplib.BadStatusLine), resp.begin)
413
414 def test_overflowing_header_line(self):
415 body = (
416 'HTTP/1.1 200 OK\r\n'
417 'X-Foo: bar' + 'r' * 65536 + '\r\n\r\n'
418 )
419 resp = httplib.HTTPResponse(FakeSocket(body))
420 self.assertRaises(httplib.LineTooLong, resp.begin)
421
422 def test_overflowing_chunked_line(self):
423 body = (
424 'HTTP/1.1 200 OK\r\n'
425 'Transfer-Encoding: chunked\r\n\r\n'
426 + '0' * 65536 + 'a\r\n'
427 'hello world\r\n'
428 '0\r\n'
429 )
430 resp = httplib.HTTPResponse(FakeSocket(body))
431 resp.begin()
432 self.assertRaises(httplib.LineTooLong, resp.read)
433
Senthil Kumaranf5aaf6f2012-04-29 10:15:31 +0800434 def test_early_eof(self):
435 # Test httpresponse with no \r\n termination,
436 body = "HTTP/1.1 200 Ok"
437 sock = FakeSocket(body)
438 resp = httplib.HTTPResponse(sock)
439 resp.begin()
440 self.assertEqual(resp.read(), '')
441 self.assertTrue(resp.isclosed())
Antoine Pitroud7b6ac62010-12-18 18:18:21 +0000442
Serhiy Storchakad862db02014-12-01 13:07:28 +0200443 def test_error_leak(self):
444 # Test that the socket is not leaked if getresponse() fails
445 conn = httplib.HTTPConnection('example.com')
446 response = []
447 class Response(httplib.HTTPResponse):
448 def __init__(self, *pos, **kw):
449 response.append(self) # Avoid garbage collector closing the socket
450 httplib.HTTPResponse.__init__(self, *pos, **kw)
451 conn.response_class = Response
452 conn.sock = FakeSocket('') # Emulate server dropping connection
453 conn.request('GET', '/')
454 self.assertRaises(httplib.BadStatusLine, conn.getresponse)
455 self.assertTrue(response)
456 #self.assertTrue(response[0].closed)
457 self.assertTrue(conn.sock.file_closed)
458
Georg Brandl4cbd1e32006-02-17 22:01:08 +0000459class OfflineTest(TestCase):
460 def test_responses(self):
Ezio Melotti2623a372010-11-21 13:34:58 +0000461 self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")
Georg Brandl4cbd1e32006-02-17 22:01:08 +0000462
Gregory P. Smith9d325212010-01-03 02:06:07 +0000463
Senthil Kumaran812b9752015-01-24 12:58:10 -0800464class TestServerMixin:
465 """A limited socket server mixin.
466
467 This is used by test cases for testing http connection end points.
468 """
Gregory P. Smith9d325212010-01-03 02:06:07 +0000469 def setUp(self):
470 self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
471 self.port = test_support.bind_port(self.serv)
472 self.source_port = test_support.find_unused_port()
473 self.serv.listen(5)
474 self.conn = None
475
476 def tearDown(self):
477 if self.conn:
478 self.conn.close()
479 self.conn = None
480 self.serv.close()
481 self.serv = None
482
Senthil Kumaran812b9752015-01-24 12:58:10 -0800483class SourceAddressTest(TestServerMixin, TestCase):
Gregory P. Smith9d325212010-01-03 02:06:07 +0000484 def testHTTPConnectionSourceAddress(self):
485 self.conn = httplib.HTTPConnection(HOST, self.port,
486 source_address=('', self.source_port))
487 self.conn.connect()
488 self.assertEqual(self.conn.sock.getsockname()[1], self.source_port)
489
490 @unittest.skipIf(not hasattr(httplib, 'HTTPSConnection'),
491 'httplib.HTTPSConnection not defined')
492 def testHTTPSConnectionSourceAddress(self):
493 self.conn = httplib.HTTPSConnection(HOST, self.port,
494 source_address=('', self.source_port))
495 # We don't test anything here other the constructor not barfing as
496 # this code doesn't deal with setting up an active running SSL server
497 # for an ssl_wrapped connect() to actually return from.
498
499
Senthil Kumaran812b9752015-01-24 12:58:10 -0800500class HTTPTest(TestServerMixin, TestCase):
501 def testHTTPConnection(self):
502 self.conn = httplib.HTTP(host=HOST, port=self.port, strict=None)
503 self.conn.connect()
504 self.assertEqual(self.conn._conn.host, HOST)
505 self.assertEqual(self.conn._conn.port, self.port)
506
507 def testHTTPWithConnectHostPort(self):
508 testhost = 'unreachable.test.domain'
509 testport = '80'
510 self.conn = httplib.HTTP(host=testhost, port=testport)
511 self.conn.connect(host=HOST, port=self.port)
512 self.assertNotEqual(self.conn._conn.host, testhost)
513 self.assertNotEqual(self.conn._conn.port, testport)
514 self.assertEqual(self.conn._conn.host, HOST)
515 self.assertEqual(self.conn._conn.port, self.port)
516
517
Facundo Batista07c78be2007-03-23 18:54:07 +0000518class TimeoutTest(TestCase):
Trent Nelsone41b0062008-04-08 23:47:30 +0000519 PORT = None
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000520
Facundo Batista07c78be2007-03-23 18:54:07 +0000521 def setUp(self):
522 self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Trent Nelson6c4a7c62008-04-09 00:34:53 +0000523 TimeoutTest.PORT = test_support.bind_port(self.serv)
Facundo Batistaf1966292007-03-25 03:20:05 +0000524 self.serv.listen(5)
Facundo Batista07c78be2007-03-23 18:54:07 +0000525
526 def tearDown(self):
527 self.serv.close()
528 self.serv = None
529
530 def testTimeoutAttribute(self):
531 '''This will prove that the timeout gets through
532 HTTPConnection and into the socket.
533 '''
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000534 # default -- use global socket timeout
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200535 self.assertIsNone(socket.getdefaulttimeout())
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000536 socket.setdefaulttimeout(30)
537 try:
538 httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT)
539 httpConn.connect()
540 finally:
541 socket.setdefaulttimeout(None)
Facundo Batista14553b02007-03-23 20:23:08 +0000542 self.assertEqual(httpConn.sock.gettimeout(), 30)
Facundo Batistaf1966292007-03-25 03:20:05 +0000543 httpConn.close()
Facundo Batista07c78be2007-03-23 18:54:07 +0000544
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000545 # no timeout -- do not use global socket default
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200546 self.assertIsNone(socket.getdefaulttimeout())
Facundo Batista14553b02007-03-23 20:23:08 +0000547 socket.setdefaulttimeout(30)
548 try:
Trent Nelson6c4a7c62008-04-09 00:34:53 +0000549 httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT,
550 timeout=None)
Facundo Batista14553b02007-03-23 20:23:08 +0000551 httpConn.connect()
552 finally:
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000553 socket.setdefaulttimeout(None)
554 self.assertEqual(httpConn.sock.gettimeout(), None)
555 httpConn.close()
556
557 # a value
558 httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT, timeout=30)
559 httpConn.connect()
Facundo Batista14553b02007-03-23 20:23:08 +0000560 self.assertEqual(httpConn.sock.gettimeout(), 30)
Facundo Batistaf1966292007-03-25 03:20:05 +0000561 httpConn.close()
Facundo Batista07c78be2007-03-23 18:54:07 +0000562
Senthil Kumaran812b9752015-01-24 12:58:10 -0800563
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600564class HTTPSTest(TestCase):
Facundo Batista07c78be2007-03-23 18:54:07 +0000565
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600566 def setUp(self):
567 if not hasattr(httplib, 'HTTPSConnection'):
568 self.skipTest('ssl support required')
569
570 def make_server(self, certfile):
571 from test.ssl_servers import make_https_server
572 return make_https_server(self, certfile=certfile)
Facundo Batista70f996b2007-05-21 17:32:32 +0000573
574 def test_attributes(self):
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600575 # simple test to check it's storing the timeout
576 h = httplib.HTTPSConnection(HOST, TimeoutTest.PORT, timeout=30)
577 self.assertEqual(h.timeout, 30)
Facundo Batista70f996b2007-05-21 17:32:32 +0000578
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600579 def test_networked(self):
580 # Default settings: requires a valid cert from a trusted CA
581 import ssl
582 test_support.requires('network')
583 with test_support.transient_internet('self-signed.pythontest.net'):
584 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443)
585 with self.assertRaises(ssl.SSLError) as exc_info:
586 h.request('GET', '/')
587 self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
588
589 def test_networked_noverification(self):
590 # Switch off cert verification
591 import ssl
592 test_support.requires('network')
593 with test_support.transient_internet('self-signed.pythontest.net'):
594 context = ssl._create_stdlib_context()
595 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443,
596 context=context)
597 h.request('GET', '/')
598 resp = h.getresponse()
599 self.assertIn('nginx', resp.getheader('server'))
600
Benjamin Petersonf671de42014-11-25 15:16:55 -0600601 @test_support.system_must_validate_cert
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600602 def test_networked_trusted_by_default_cert(self):
603 # Default settings: requires a valid cert from a trusted CA
604 test_support.requires('network')
605 with test_support.transient_internet('www.python.org'):
606 h = httplib.HTTPSConnection('www.python.org', 443)
607 h.request('GET', '/')
608 resp = h.getresponse()
609 content_type = resp.getheader('content-type')
610 self.assertIn('text/html', content_type)
611
612 def test_networked_good_cert(self):
613 # We feed the server's cert as a validating cert
614 import ssl
615 test_support.requires('network')
616 with test_support.transient_internet('self-signed.pythontest.net'):
617 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
618 context.verify_mode = ssl.CERT_REQUIRED
619 context.load_verify_locations(CERT_selfsigned_pythontestdotnet)
620 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443, context=context)
621 h.request('GET', '/')
622 resp = h.getresponse()
623 server_string = resp.getheader('server')
624 self.assertIn('nginx', server_string)
625
626 def test_networked_bad_cert(self):
627 # We feed a "CA" cert that is unrelated to the server's cert
628 import ssl
629 test_support.requires('network')
630 with test_support.transient_internet('self-signed.pythontest.net'):
631 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
632 context.verify_mode = ssl.CERT_REQUIRED
633 context.load_verify_locations(CERT_localhost)
634 h = httplib.HTTPSConnection('self-signed.pythontest.net', 443, context=context)
635 with self.assertRaises(ssl.SSLError) as exc_info:
636 h.request('GET', '/')
637 self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
638
639 def test_local_unknown_cert(self):
640 # The custom cert isn't known to the default trust bundle
641 import ssl
642 server = self.make_server(CERT_localhost)
643 h = httplib.HTTPSConnection('localhost', server.port)
644 with self.assertRaises(ssl.SSLError) as exc_info:
645 h.request('GET', '/')
646 self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
647
648 def test_local_good_hostname(self):
649 # The (valid) cert validates the HTTP hostname
650 import ssl
651 server = self.make_server(CERT_localhost)
652 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
653 context.verify_mode = ssl.CERT_REQUIRED
654 context.load_verify_locations(CERT_localhost)
655 h = httplib.HTTPSConnection('localhost', server.port, context=context)
656 h.request('GET', '/nonexistent')
657 resp = h.getresponse()
658 self.assertEqual(resp.status, 404)
659
660 def test_local_bad_hostname(self):
661 # The (valid) cert doesn't validate the HTTP hostname
662 import ssl
663 server = self.make_server(CERT_fakehostname)
664 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
665 context.verify_mode = ssl.CERT_REQUIRED
Benjamin Peterson227f6e02014-12-07 13:41:26 -0500666 context.check_hostname = True
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600667 context.load_verify_locations(CERT_fakehostname)
668 h = httplib.HTTPSConnection('localhost', server.port, context=context)
669 with self.assertRaises(ssl.CertificateError):
670 h.request('GET', '/')
Benjamin Peterson227f6e02014-12-07 13:41:26 -0500671 h.close()
672 # With context.check_hostname=False, the mismatching is ignored
673 context.check_hostname = False
674 h = httplib.HTTPSConnection('localhost', server.port, context=context)
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600675 h.request('GET', '/nonexistent')
676 resp = h.getresponse()
677 self.assertEqual(resp.status, 404)
678
Łukasz Langa7a153902011-10-18 17:16:00 +0200679 def test_host_port(self):
680 # Check invalid host_port
681
Łukasz Langa7a153902011-10-18 17:16:00 +0200682 for hp in ("www.python.org:abc", "user:password@www.python.org"):
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600683 self.assertRaises(httplib.InvalidURL, httplib.HTTPSConnection, hp)
Łukasz Langa7a153902011-10-18 17:16:00 +0200684
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600685 for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000",
686 "fe80::207:e9ff:fe9b", 8000),
687 ("www.python.org:443", "www.python.org", 443),
688 ("www.python.org:", "www.python.org", 443),
689 ("www.python.org", "www.python.org", 443),
690 ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 443),
691 ("[fe80::207:e9ff:fe9b]:", "fe80::207:e9ff:fe9b",
692 443)):
693 c = httplib.HTTPSConnection(hp)
694 self.assertEqual(h, c.host)
695 self.assertEqual(p, c.port)
Łukasz Langa7a153902011-10-18 17:16:00 +0200696
697
Senthil Kumaran36f28f72014-05-16 18:51:46 -0700698class TunnelTests(TestCase):
699 def test_connect(self):
700 response_text = (
701 'HTTP/1.0 200 OK\r\n\r\n' # Reply to CONNECT
702 'HTTP/1.1 200 OK\r\n' # Reply to HEAD
703 'Content-Length: 42\r\n\r\n'
704 )
705
706 def create_connection(address, timeout=None, source_address=None):
707 return FakeSocket(response_text, host=address[0], port=address[1])
708
709 conn = httplib.HTTPConnection('proxy.com')
710 conn._create_connection = create_connection
711
712 # Once connected, we should not be able to tunnel anymore
713 conn.connect()
714 self.assertRaises(RuntimeError, conn.set_tunnel, 'destination.com')
715
716 # But if close the connection, we are good.
717 conn.close()
718 conn.set_tunnel('destination.com')
719 conn.request('HEAD', '/', '')
720
721 self.assertEqual(conn.sock.host, 'proxy.com')
722 self.assertEqual(conn.sock.port, 80)
723 self.assertTrue('CONNECT destination.com' in conn.sock.data)
724 self.assertTrue('Host: destination.com' in conn.sock.data)
725
726 self.assertTrue('Host: proxy.com' not in conn.sock.data)
727
728 conn.close()
729
730 conn.request('PUT', '/', '')
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
Benjamin Petersonfcfb18e2014-11-23 11:42:45 -0600737@test_support.reap_threads
Jeremy Hylton2c178252004-08-07 16:28:14 +0000738def test_main(verbose=None):
Trent Nelsone41b0062008-04-08 23:47:30 +0000739 test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
Senthil Kumaran812b9752015-01-24 12:58:10 -0800740 HTTPTest, HTTPSTest, SourceAddressTest,
741 TunnelTests)
Jeremy Hylton2c178252004-08-07 16:28:14 +0000742
Georg Brandl71a20892006-10-29 20:24:01 +0000743if __name__ == '__main__':
744 test_main()