/Lib/test/test_httplib.py

http://unladen-swallow.googlecode.com/ · Python · 300 lines · 233 code · 43 blank · 24 comment · 29 complexity · 8f0f4c9c0f87b0c622bcc3e4e3c81a2c MD5 · raw file

  1. import array
  2. import httplib
  3. import StringIO
  4. import socket
  5. from unittest import TestCase
  6. from test import test_support
  7. HOST = test_support.HOST
  8. class FakeSocket:
  9. def __init__(self, text, fileclass=StringIO.StringIO):
  10. self.text = text
  11. self.fileclass = fileclass
  12. self.data = ''
  13. def sendall(self, data):
  14. self.data += ''.join(data)
  15. def makefile(self, mode, bufsize=None):
  16. if mode != 'r' and mode != 'rb':
  17. raise httplib.UnimplementedFileMode()
  18. return self.fileclass(self.text)
  19. class NoEOFStringIO(StringIO.StringIO):
  20. """Like StringIO, but raises AssertionError on EOF.
  21. This is used below to test that httplib doesn't try to read
  22. more from the underlying file than it should.
  23. """
  24. def read(self, n=-1):
  25. data = StringIO.StringIO.read(self, n)
  26. if data == '':
  27. raise AssertionError('caller tried to read past EOF')
  28. return data
  29. def readline(self, length=None):
  30. data = StringIO.StringIO.readline(self, length)
  31. if data == '':
  32. raise AssertionError('caller tried to read past EOF')
  33. return data
  34. class HeaderTests(TestCase):
  35. def test_auto_headers(self):
  36. # Some headers are added automatically, but should not be added by
  37. # .request() if they are explicitly set.
  38. import httplib
  39. class HeaderCountingBuffer(list):
  40. def __init__(self):
  41. self.count = {}
  42. def append(self, item):
  43. kv = item.split(':')
  44. if len(kv) > 1:
  45. # item is a 'Key: Value' header string
  46. lcKey = kv[0].lower()
  47. self.count.setdefault(lcKey, 0)
  48. self.count[lcKey] += 1
  49. list.append(self, item)
  50. for explicit_header in True, False:
  51. for header in 'Content-length', 'Host', 'Accept-encoding':
  52. conn = httplib.HTTPConnection('example.com')
  53. conn.sock = FakeSocket('blahblahblah')
  54. conn._buffer = HeaderCountingBuffer()
  55. body = 'spamspamspam'
  56. headers = {}
  57. if explicit_header:
  58. headers[header] = str(len(body))
  59. conn.request('POST', '/', body, headers)
  60. self.assertEqual(conn._buffer.count[header.lower()], 1)
  61. class BasicTest(TestCase):
  62. def test_status_lines(self):
  63. # Test HTTP status lines
  64. body = "HTTP/1.1 200 Ok\r\n\r\nText"
  65. sock = FakeSocket(body)
  66. resp = httplib.HTTPResponse(sock)
  67. resp.begin()
  68. self.assertEqual(resp.read(), 'Text')
  69. self.assertTrue(resp.isclosed())
  70. body = "HTTP/1.1 400.100 Not Ok\r\n\r\nText"
  71. sock = FakeSocket(body)
  72. resp = httplib.HTTPResponse(sock)
  73. self.assertRaises(httplib.BadStatusLine, resp.begin)
  74. def test_partial_reads(self):
  75. # if we have a lenght, the system knows when to close itself
  76. # same behaviour than when we read the whole thing with read()
  77. body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
  78. sock = FakeSocket(body)
  79. resp = httplib.HTTPResponse(sock)
  80. resp.begin()
  81. self.assertEqual(resp.read(2), 'Te')
  82. self.assertFalse(resp.isclosed())
  83. self.assertEqual(resp.read(2), 'xt')
  84. self.assertTrue(resp.isclosed())
  85. def test_host_port(self):
  86. # Check invalid host_port
  87. for hp in ("www.python.org:abc", "www.python.org:"):
  88. self.assertRaises(httplib.InvalidURL, httplib.HTTP, hp)
  89. for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000", "fe80::207:e9ff:fe9b",
  90. 8000),
  91. ("www.python.org:80", "www.python.org", 80),
  92. ("www.python.org", "www.python.org", 80),
  93. ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 80)):
  94. http = httplib.HTTP(hp)
  95. c = http._conn
  96. if h != c.host:
  97. self.fail("Host incorrectly parsed: %s != %s" % (h, c.host))
  98. if p != c.port:
  99. self.fail("Port incorrectly parsed: %s != %s" % (p, c.host))
  100. def test_response_headers(self):
  101. # test response with multiple message headers with the same field name.
  102. text = ('HTTP/1.1 200 OK\r\n'
  103. 'Set-Cookie: Customer="WILE_E_COYOTE";'
  104. ' Version="1"; Path="/acme"\r\n'
  105. 'Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";'
  106. ' Path="/acme"\r\n'
  107. '\r\n'
  108. 'No body\r\n')
  109. hdr = ('Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"'
  110. ', '
  111. 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"')
  112. s = FakeSocket(text)
  113. r = httplib.HTTPResponse(s)
  114. r.begin()
  115. cookies = r.getheader("Set-Cookie")
  116. if cookies != hdr:
  117. self.fail("multiple headers not combined properly")
  118. def test_read_head(self):
  119. # Test that the library doesn't attempt to read any data
  120. # from a HEAD request. (Tickles SF bug #622042.)
  121. sock = FakeSocket(
  122. 'HTTP/1.1 200 OK\r\n'
  123. 'Content-Length: 14432\r\n'
  124. '\r\n',
  125. NoEOFStringIO)
  126. resp = httplib.HTTPResponse(sock, method="HEAD")
  127. resp.begin()
  128. if resp.read() != "":
  129. self.fail("Did not expect response from HEAD request")
  130. def test_send_file(self):
  131. expected = 'GET /foo HTTP/1.1\r\nHost: example.com\r\n' \
  132. 'Accept-Encoding: identity\r\nContent-Length:'
  133. body = open(__file__, 'rb')
  134. conn = httplib.HTTPConnection('example.com')
  135. sock = FakeSocket(body)
  136. conn.sock = sock
  137. conn.request('GET', '/foo', body)
  138. self.assertTrue(sock.data.startswith(expected))
  139. def test_send(self):
  140. expected = 'this is a test this is only a test'
  141. conn = httplib.HTTPConnection('example.com')
  142. sock = FakeSocket(None)
  143. conn.sock = sock
  144. conn.send(expected)
  145. self.assertEquals(expected, sock.data)
  146. sock.data = ''
  147. conn.send(array.array('c', expected))
  148. self.assertEquals(expected, sock.data)
  149. sock.data = ''
  150. conn.send(StringIO.StringIO(expected))
  151. self.assertEquals(expected, sock.data)
  152. def test_chunked(self):
  153. chunked_start = (
  154. 'HTTP/1.1 200 OK\r\n'
  155. 'Transfer-Encoding: chunked\r\n\r\n'
  156. 'a\r\n'
  157. 'hello worl\r\n'
  158. '1\r\n'
  159. 'd\r\n'
  160. )
  161. sock = FakeSocket(chunked_start + '0\r\n')
  162. resp = httplib.HTTPResponse(sock, method="GET")
  163. resp.begin()
  164. self.assertEquals(resp.read(), 'hello world')
  165. resp.close()
  166. for x in ('', 'foo\r\n'):
  167. sock = FakeSocket(chunked_start + x)
  168. resp = httplib.HTTPResponse(sock, method="GET")
  169. resp.begin()
  170. try:
  171. resp.read()
  172. except httplib.IncompleteRead, i:
  173. self.assertEquals(i.partial, 'hello world')
  174. self.assertEqual(repr(i),'IncompleteRead(11 bytes read)')
  175. self.assertEqual(str(i),'IncompleteRead(11 bytes read)')
  176. else:
  177. self.fail('IncompleteRead expected')
  178. finally:
  179. resp.close()
  180. def test_negative_content_length(self):
  181. sock = FakeSocket('HTTP/1.1 200 OK\r\n'
  182. 'Content-Length: -1\r\n\r\nHello\r\n')
  183. resp = httplib.HTTPResponse(sock, method="GET")
  184. resp.begin()
  185. self.assertEquals(resp.read(), 'Hello\r\n')
  186. resp.close()
  187. def test_incomplete_read(self):
  188. sock = FakeSocket('HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHello\r\n')
  189. resp = httplib.HTTPResponse(sock, method="GET")
  190. resp.begin()
  191. try:
  192. resp.read()
  193. except httplib.IncompleteRead as i:
  194. self.assertEquals(i.partial, 'Hello\r\n')
  195. self.assertEqual(repr(i),
  196. "IncompleteRead(7 bytes read, 3 more expected)")
  197. self.assertEqual(str(i),
  198. "IncompleteRead(7 bytes read, 3 more expected)")
  199. else:
  200. self.fail('IncompleteRead expected')
  201. finally:
  202. resp.close()
  203. class OfflineTest(TestCase):
  204. def test_responses(self):
  205. self.assertEquals(httplib.responses[httplib.NOT_FOUND], "Not Found")
  206. class TimeoutTest(TestCase):
  207. PORT = None
  208. def setUp(self):
  209. self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  210. TimeoutTest.PORT = test_support.bind_port(self.serv)
  211. self.serv.listen(5)
  212. def tearDown(self):
  213. self.serv.close()
  214. self.serv = None
  215. def testTimeoutAttribute(self):
  216. '''This will prove that the timeout gets through
  217. HTTPConnection and into the socket.
  218. '''
  219. # default -- use global socket timeout
  220. self.assert_(socket.getdefaulttimeout() is None)
  221. socket.setdefaulttimeout(30)
  222. try:
  223. httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT)
  224. httpConn.connect()
  225. finally:
  226. socket.setdefaulttimeout(None)
  227. self.assertEqual(httpConn.sock.gettimeout(), 30)
  228. httpConn.close()
  229. # no timeout -- do not use global socket default
  230. self.assert_(socket.getdefaulttimeout() is None)
  231. socket.setdefaulttimeout(30)
  232. try:
  233. httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT,
  234. timeout=None)
  235. httpConn.connect()
  236. finally:
  237. socket.setdefaulttimeout(None)
  238. self.assertEqual(httpConn.sock.gettimeout(), None)
  239. httpConn.close()
  240. # a value
  241. httpConn = httplib.HTTPConnection(HOST, TimeoutTest.PORT, timeout=30)
  242. httpConn.connect()
  243. self.assertEqual(httpConn.sock.gettimeout(), 30)
  244. httpConn.close()
  245. class HTTPSTimeoutTest(TestCase):
  246. # XXX Here should be tests for HTTPS, there isn't any right now!
  247. def test_attributes(self):
  248. # simple test to check it's storing it
  249. if hasattr(httplib, 'HTTPSConnection'):
  250. h = httplib.HTTPSConnection(HOST, TimeoutTest.PORT, timeout=30)
  251. self.assertEqual(h.timeout, 30)
  252. def test_main(verbose=None):
  253. test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
  254. HTTPSTimeoutTest)
  255. if __name__ == '__main__':
  256. test_main()