test_httplib.py 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254
  1. import enum
  2. import errno
  3. from http import client, HTTPStatus
  4. import io
  5. import itertools
  6. import os
  7. import array
  8. import re
  9. import socket
  10. import threading
  11. import warnings
  12. import unittest
  13. from unittest import mock
  14. TestCase = unittest.TestCase
  15. from test import support
  16. from test.support import os_helper
  17. from test.support import socket_helper
  18. from test.support import warnings_helper
  19. support.requires_working_socket(module=True)
  20. here = os.path.dirname(__file__)
  21. # Self-signed cert file for 'localhost'
  22. CERT_localhost = os.path.join(here, 'keycert.pem')
  23. # Self-signed cert file for 'fakehostname'
  24. CERT_fakehostname = os.path.join(here, 'keycert2.pem')
  25. # Self-signed cert file for self-signed.pythontest.net
  26. CERT_selfsigned_pythontestdotnet = os.path.join(here, 'selfsigned_pythontestdotnet.pem')
  27. # constants for testing chunked encoding
  28. chunked_start = (
  29. 'HTTP/1.1 200 OK\r\n'
  30. 'Transfer-Encoding: chunked\r\n\r\n'
  31. 'a\r\n'
  32. 'hello worl\r\n'
  33. '3\r\n'
  34. 'd! \r\n'
  35. '8\r\n'
  36. 'and now \r\n'
  37. '22\r\n'
  38. 'for something completely different\r\n'
  39. )
  40. chunked_expected = b'hello world! and now for something completely different'
  41. chunk_extension = ";foo=bar"
  42. last_chunk = "0\r\n"
  43. last_chunk_extended = "0" + chunk_extension + "\r\n"
  44. trailers = "X-Dummy: foo\r\nX-Dumm2: bar\r\n"
  45. chunked_end = "\r\n"
  46. HOST = socket_helper.HOST
  47. class FakeSocket:
  48. def __init__(self, text, fileclass=io.BytesIO, host=None, port=None):
  49. if isinstance(text, str):
  50. text = text.encode("ascii")
  51. self.text = text
  52. self.fileclass = fileclass
  53. self.data = b''
  54. self.sendall_calls = 0
  55. self.file_closed = False
  56. self.host = host
  57. self.port = port
  58. def sendall(self, data):
  59. self.sendall_calls += 1
  60. self.data += data
  61. def makefile(self, mode, bufsize=None):
  62. if mode != 'r' and mode != 'rb':
  63. raise client.UnimplementedFileMode()
  64. # keep the file around so we can check how much was read from it
  65. self.file = self.fileclass(self.text)
  66. self.file.close = self.file_close #nerf close ()
  67. return self.file
  68. def file_close(self):
  69. self.file_closed = True
  70. def close(self):
  71. pass
  72. def setsockopt(self, level, optname, value):
  73. pass
  74. class EPipeSocket(FakeSocket):
  75. def __init__(self, text, pipe_trigger):
  76. # When sendall() is called with pipe_trigger, raise EPIPE.
  77. FakeSocket.__init__(self, text)
  78. self.pipe_trigger = pipe_trigger
  79. def sendall(self, data):
  80. if self.pipe_trigger in data:
  81. raise OSError(errno.EPIPE, "gotcha")
  82. self.data += data
  83. def close(self):
  84. pass
  85. class NoEOFBytesIO(io.BytesIO):
  86. """Like BytesIO, but raises AssertionError on EOF.
  87. This is used below to test that http.client doesn't try to read
  88. more from the underlying file than it should.
  89. """
  90. def read(self, n=-1):
  91. data = io.BytesIO.read(self, n)
  92. if data == b'':
  93. raise AssertionError('caller tried to read past EOF')
  94. return data
  95. def readline(self, length=None):
  96. data = io.BytesIO.readline(self, length)
  97. if data == b'':
  98. raise AssertionError('caller tried to read past EOF')
  99. return data
  100. class FakeSocketHTTPConnection(client.HTTPConnection):
  101. """HTTPConnection subclass using FakeSocket; counts connect() calls"""
  102. def __init__(self, *args):
  103. self.connections = 0
  104. super().__init__('example.com')
  105. self.fake_socket_args = args
  106. self._create_connection = self.create_connection
  107. def connect(self):
  108. """Count the number of times connect() is invoked"""
  109. self.connections += 1
  110. return super().connect()
  111. def create_connection(self, *pos, **kw):
  112. return FakeSocket(*self.fake_socket_args)
  113. class HeaderTests(TestCase):
  114. def test_auto_headers(self):
  115. # Some headers are added automatically, but should not be added by
  116. # .request() if they are explicitly set.
  117. class HeaderCountingBuffer(list):
  118. def __init__(self):
  119. self.count = {}
  120. def append(self, item):
  121. kv = item.split(b':')
  122. if len(kv) > 1:
  123. # item is a 'Key: Value' header string
  124. lcKey = kv[0].decode('ascii').lower()
  125. self.count.setdefault(lcKey, 0)
  126. self.count[lcKey] += 1
  127. list.append(self, item)
  128. for explicit_header in True, False:
  129. for header in 'Content-length', 'Host', 'Accept-encoding':
  130. conn = client.HTTPConnection('example.com')
  131. conn.sock = FakeSocket('blahblahblah')
  132. conn._buffer = HeaderCountingBuffer()
  133. body = 'spamspamspam'
  134. headers = {}
  135. if explicit_header:
  136. headers[header] = str(len(body))
  137. conn.request('POST', '/', body, headers)
  138. self.assertEqual(conn._buffer.count[header.lower()], 1)
  139. def test_content_length_0(self):
  140. class ContentLengthChecker(list):
  141. def __init__(self):
  142. list.__init__(self)
  143. self.content_length = None
  144. def append(self, item):
  145. kv = item.split(b':', 1)
  146. if len(kv) > 1 and kv[0].lower() == b'content-length':
  147. self.content_length = kv[1].strip()
  148. list.append(self, item)
  149. # Here, we're testing that methods expecting a body get a
  150. # content-length set to zero if the body is empty (either None or '')
  151. bodies = (None, '')
  152. methods_with_body = ('PUT', 'POST', 'PATCH')
  153. for method, body in itertools.product(methods_with_body, bodies):
  154. conn = client.HTTPConnection('example.com')
  155. conn.sock = FakeSocket(None)
  156. conn._buffer = ContentLengthChecker()
  157. conn.request(method, '/', body)
  158. self.assertEqual(
  159. conn._buffer.content_length, b'0',
  160. 'Header Content-Length incorrect on {}'.format(method)
  161. )
  162. # For these methods, we make sure that content-length is not set when
  163. # the body is None because it might cause unexpected behaviour on the
  164. # server.
  165. methods_without_body = (
  166. 'GET', 'CONNECT', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE',
  167. )
  168. for method in methods_without_body:
  169. conn = client.HTTPConnection('example.com')
  170. conn.sock = FakeSocket(None)
  171. conn._buffer = ContentLengthChecker()
  172. conn.request(method, '/', None)
  173. self.assertEqual(
  174. conn._buffer.content_length, None,
  175. 'Header Content-Length set for empty body on {}'.format(method)
  176. )
  177. # If the body is set to '', that's considered to be "present but
  178. # empty" rather than "missing", so content length would be set, even
  179. # for methods that don't expect a body.
  180. for method in methods_without_body:
  181. conn = client.HTTPConnection('example.com')
  182. conn.sock = FakeSocket(None)
  183. conn._buffer = ContentLengthChecker()
  184. conn.request(method, '/', '')
  185. self.assertEqual(
  186. conn._buffer.content_length, b'0',
  187. 'Header Content-Length incorrect on {}'.format(method)
  188. )
  189. # If the body is set, make sure Content-Length is set.
  190. for method in itertools.chain(methods_without_body, methods_with_body):
  191. conn = client.HTTPConnection('example.com')
  192. conn.sock = FakeSocket(None)
  193. conn._buffer = ContentLengthChecker()
  194. conn.request(method, '/', ' ')
  195. self.assertEqual(
  196. conn._buffer.content_length, b'1',
  197. 'Header Content-Length incorrect on {}'.format(method)
  198. )
  199. def test_putheader(self):
  200. conn = client.HTTPConnection('example.com')
  201. conn.sock = FakeSocket(None)
  202. conn.putrequest('GET','/')
  203. conn.putheader('Content-length', 42)
  204. self.assertIn(b'Content-length: 42', conn._buffer)
  205. conn.putheader('Foo', ' bar ')
  206. self.assertIn(b'Foo: bar ', conn._buffer)
  207. conn.putheader('Bar', '\tbaz\t')
  208. self.assertIn(b'Bar: \tbaz\t', conn._buffer)
  209. conn.putheader('Authorization', 'Bearer mytoken')
  210. self.assertIn(b'Authorization: Bearer mytoken', conn._buffer)
  211. conn.putheader('IterHeader', 'IterA', 'IterB')
  212. self.assertIn(b'IterHeader: IterA\r\n\tIterB', conn._buffer)
  213. conn.putheader('LatinHeader', b'\xFF')
  214. self.assertIn(b'LatinHeader: \xFF', conn._buffer)
  215. conn.putheader('Utf8Header', b'\xc3\x80')
  216. self.assertIn(b'Utf8Header: \xc3\x80', conn._buffer)
  217. conn.putheader('C1-Control', b'next\x85line')
  218. self.assertIn(b'C1-Control: next\x85line', conn._buffer)
  219. conn.putheader('Embedded-Fold-Space', 'is\r\n allowed')
  220. self.assertIn(b'Embedded-Fold-Space: is\r\n allowed', conn._buffer)
  221. conn.putheader('Embedded-Fold-Tab', 'is\r\n\tallowed')
  222. self.assertIn(b'Embedded-Fold-Tab: is\r\n\tallowed', conn._buffer)
  223. conn.putheader('Key Space', 'value')
  224. self.assertIn(b'Key Space: value', conn._buffer)
  225. conn.putheader('KeySpace ', 'value')
  226. self.assertIn(b'KeySpace : value', conn._buffer)
  227. conn.putheader(b'Nonbreak\xa0Space', 'value')
  228. self.assertIn(b'Nonbreak\xa0Space: value', conn._buffer)
  229. conn.putheader(b'\xa0NonbreakSpace', 'value')
  230. self.assertIn(b'\xa0NonbreakSpace: value', conn._buffer)
  231. def test_ipv6host_header(self):
  232. # Default host header on IPv6 transaction should be wrapped by [] if
  233. # it is an IPv6 address
  234. expected = b'GET /foo HTTP/1.1\r\nHost: [2001::]:81\r\n' \
  235. b'Accept-Encoding: identity\r\n\r\n'
  236. conn = client.HTTPConnection('[2001::]:81')
  237. sock = FakeSocket('')
  238. conn.sock = sock
  239. conn.request('GET', '/foo')
  240. self.assertTrue(sock.data.startswith(expected))
  241. expected = b'GET /foo HTTP/1.1\r\nHost: [2001:102A::]\r\n' \
  242. b'Accept-Encoding: identity\r\n\r\n'
  243. conn = client.HTTPConnection('[2001:102A::]')
  244. sock = FakeSocket('')
  245. conn.sock = sock
  246. conn.request('GET', '/foo')
  247. self.assertTrue(sock.data.startswith(expected))
  248. def test_malformed_headers_coped_with(self):
  249. # Issue 19996
  250. body = "HTTP/1.1 200 OK\r\nFirst: val\r\n: nval\r\nSecond: val\r\n\r\n"
  251. sock = FakeSocket(body)
  252. resp = client.HTTPResponse(sock)
  253. resp.begin()
  254. self.assertEqual(resp.getheader('First'), 'val')
  255. self.assertEqual(resp.getheader('Second'), 'val')
  256. def test_parse_all_octets(self):
  257. # Ensure no valid header field octet breaks the parser
  258. body = (
  259. b'HTTP/1.1 200 OK\r\n'
  260. b"!#$%&'*+-.^_`|~: value\r\n" # Special token characters
  261. b'VCHAR: ' + bytes(range(0x21, 0x7E + 1)) + b'\r\n'
  262. b'obs-text: ' + bytes(range(0x80, 0xFF + 1)) + b'\r\n'
  263. b'obs-fold: text\r\n'
  264. b' folded with space\r\n'
  265. b'\tfolded with tab\r\n'
  266. b'Content-Length: 0\r\n'
  267. b'\r\n'
  268. )
  269. sock = FakeSocket(body)
  270. resp = client.HTTPResponse(sock)
  271. resp.begin()
  272. self.assertEqual(resp.getheader('Content-Length'), '0')
  273. self.assertEqual(resp.msg['Content-Length'], '0')
  274. self.assertEqual(resp.getheader("!#$%&'*+-.^_`|~"), 'value')
  275. self.assertEqual(resp.msg["!#$%&'*+-.^_`|~"], 'value')
  276. vchar = ''.join(map(chr, range(0x21, 0x7E + 1)))
  277. self.assertEqual(resp.getheader('VCHAR'), vchar)
  278. self.assertEqual(resp.msg['VCHAR'], vchar)
  279. self.assertIsNotNone(resp.getheader('obs-text'))
  280. self.assertIn('obs-text', resp.msg)
  281. for folded in (resp.getheader('obs-fold'), resp.msg['obs-fold']):
  282. self.assertTrue(folded.startswith('text'))
  283. self.assertIn(' folded with space', folded)
  284. self.assertTrue(folded.endswith('folded with tab'))
  285. def test_invalid_headers(self):
  286. conn = client.HTTPConnection('example.com')
  287. conn.sock = FakeSocket('')
  288. conn.putrequest('GET', '/')
  289. # http://tools.ietf.org/html/rfc7230#section-3.2.4, whitespace is no
  290. # longer allowed in header names
  291. cases = (
  292. (b'Invalid\r\nName', b'ValidValue'),
  293. (b'Invalid\rName', b'ValidValue'),
  294. (b'Invalid\nName', b'ValidValue'),
  295. (b'\r\nInvalidName', b'ValidValue'),
  296. (b'\rInvalidName', b'ValidValue'),
  297. (b'\nInvalidName', b'ValidValue'),
  298. (b' InvalidName', b'ValidValue'),
  299. (b'\tInvalidName', b'ValidValue'),
  300. (b'Invalid:Name', b'ValidValue'),
  301. (b':InvalidName', b'ValidValue'),
  302. (b'ValidName', b'Invalid\r\nValue'),
  303. (b'ValidName', b'Invalid\rValue'),
  304. (b'ValidName', b'Invalid\nValue'),
  305. (b'ValidName', b'InvalidValue\r\n'),
  306. (b'ValidName', b'InvalidValue\r'),
  307. (b'ValidName', b'InvalidValue\n'),
  308. )
  309. for name, value in cases:
  310. with self.subTest((name, value)):
  311. with self.assertRaisesRegex(ValueError, 'Invalid header'):
  312. conn.putheader(name, value)
  313. def test_headers_debuglevel(self):
  314. body = (
  315. b'HTTP/1.1 200 OK\r\n'
  316. b'First: val\r\n'
  317. b'Second: val1\r\n'
  318. b'Second: val2\r\n'
  319. )
  320. sock = FakeSocket(body)
  321. resp = client.HTTPResponse(sock, debuglevel=1)
  322. with support.captured_stdout() as output:
  323. resp.begin()
  324. lines = output.getvalue().splitlines()
  325. self.assertEqual(lines[0], "reply: 'HTTP/1.1 200 OK\\r\\n'")
  326. self.assertEqual(lines[1], "header: First: val")
  327. self.assertEqual(lines[2], "header: Second: val1")
  328. self.assertEqual(lines[3], "header: Second: val2")
  329. class HttpMethodTests(TestCase):
  330. def test_invalid_method_names(self):
  331. methods = (
  332. 'GET\r',
  333. 'POST\n',
  334. 'PUT\n\r',
  335. 'POST\nValue',
  336. 'POST\nHOST:abc',
  337. 'GET\nrHost:abc\n',
  338. 'POST\rRemainder:\r',
  339. 'GET\rHOST:\n',
  340. '\nPUT'
  341. )
  342. for method in methods:
  343. with self.assertRaisesRegex(
  344. ValueError, "method can't contain control characters"):
  345. conn = client.HTTPConnection('example.com')
  346. conn.sock = FakeSocket(None)
  347. conn.request(method=method, url="/")
  348. class TransferEncodingTest(TestCase):
  349. expected_body = b"It's just a flesh wound"
  350. def test_endheaders_chunked(self):
  351. conn = client.HTTPConnection('example.com')
  352. conn.sock = FakeSocket(b'')
  353. conn.putrequest('POST', '/')
  354. conn.endheaders(self._make_body(), encode_chunked=True)
  355. _, _, body = self._parse_request(conn.sock.data)
  356. body = self._parse_chunked(body)
  357. self.assertEqual(body, self.expected_body)
  358. def test_explicit_headers(self):
  359. # explicit chunked
  360. conn = client.HTTPConnection('example.com')
  361. conn.sock = FakeSocket(b'')
  362. # this shouldn't actually be automatically chunk-encoded because the
  363. # calling code has explicitly stated that it's taking care of it
  364. conn.request(
  365. 'POST', '/', self._make_body(), {'Transfer-Encoding': 'chunked'})
  366. _, headers, body = self._parse_request(conn.sock.data)
  367. self.assertNotIn('content-length', [k.lower() for k in headers.keys()])
  368. self.assertEqual(headers['Transfer-Encoding'], 'chunked')
  369. self.assertEqual(body, self.expected_body)
  370. # explicit chunked, string body
  371. conn = client.HTTPConnection('example.com')
  372. conn.sock = FakeSocket(b'')
  373. conn.request(
  374. 'POST', '/', self.expected_body.decode('latin-1'),
  375. {'Transfer-Encoding': 'chunked'})
  376. _, headers, body = self._parse_request(conn.sock.data)
  377. self.assertNotIn('content-length', [k.lower() for k in headers.keys()])
  378. self.assertEqual(headers['Transfer-Encoding'], 'chunked')
  379. self.assertEqual(body, self.expected_body)
  380. # User-specified TE, but request() does the chunk encoding
  381. conn = client.HTTPConnection('example.com')
  382. conn.sock = FakeSocket(b'')
  383. conn.request('POST', '/',
  384. headers={'Transfer-Encoding': 'gzip, chunked'},
  385. encode_chunked=True,
  386. body=self._make_body())
  387. _, headers, body = self._parse_request(conn.sock.data)
  388. self.assertNotIn('content-length', [k.lower() for k in headers])
  389. self.assertEqual(headers['Transfer-Encoding'], 'gzip, chunked')
  390. self.assertEqual(self._parse_chunked(body), self.expected_body)
  391. def test_request(self):
  392. for empty_lines in (False, True,):
  393. conn = client.HTTPConnection('example.com')
  394. conn.sock = FakeSocket(b'')
  395. conn.request(
  396. 'POST', '/', self._make_body(empty_lines=empty_lines))
  397. _, headers, body = self._parse_request(conn.sock.data)
  398. body = self._parse_chunked(body)
  399. self.assertEqual(body, self.expected_body)
  400. self.assertEqual(headers['Transfer-Encoding'], 'chunked')
  401. # Content-Length and Transfer-Encoding SHOULD not be sent in the
  402. # same request
  403. self.assertNotIn('content-length', [k.lower() for k in headers])
  404. def test_empty_body(self):
  405. # Zero-length iterable should be treated like any other iterable
  406. conn = client.HTTPConnection('example.com')
  407. conn.sock = FakeSocket(b'')
  408. conn.request('POST', '/', ())
  409. _, headers, body = self._parse_request(conn.sock.data)
  410. self.assertEqual(headers['Transfer-Encoding'], 'chunked')
  411. self.assertNotIn('content-length', [k.lower() for k in headers])
  412. self.assertEqual(body, b"0\r\n\r\n")
  413. def _make_body(self, empty_lines=False):
  414. lines = self.expected_body.split(b' ')
  415. for idx, line in enumerate(lines):
  416. # for testing handling empty lines
  417. if empty_lines and idx % 2:
  418. yield b''
  419. if idx < len(lines) - 1:
  420. yield line + b' '
  421. else:
  422. yield line
  423. def _parse_request(self, data):
  424. lines = data.split(b'\r\n')
  425. request = lines[0]
  426. headers = {}
  427. n = 1
  428. while n < len(lines) and len(lines[n]) > 0:
  429. key, val = lines[n].split(b':')
  430. key = key.decode('latin-1').strip()
  431. headers[key] = val.decode('latin-1').strip()
  432. n += 1
  433. return request, headers, b'\r\n'.join(lines[n + 1:])
  434. def _parse_chunked(self, data):
  435. body = []
  436. trailers = {}
  437. n = 0
  438. lines = data.split(b'\r\n')
  439. # parse body
  440. while True:
  441. size, chunk = lines[n:n+2]
  442. size = int(size, 16)
  443. if size == 0:
  444. n += 1
  445. break
  446. self.assertEqual(size, len(chunk))
  447. body.append(chunk)
  448. n += 2
  449. # we /should/ hit the end chunk, but check against the size of
  450. # lines so we're not stuck in an infinite loop should we get
  451. # malformed data
  452. if n > len(lines):
  453. break
  454. return b''.join(body)
  455. class BasicTest(TestCase):
  456. def test_dir_with_added_behavior_on_status(self):
  457. # see issue40084
  458. self.assertTrue({'description', 'name', 'phrase', 'value'} <= set(dir(HTTPStatus(404))))
  459. def test_simple_httpstatus(self):
  460. class CheckedHTTPStatus(enum.IntEnum):
  461. """HTTP status codes and reason phrases
  462. Status codes from the following RFCs are all observed:
  463. * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616
  464. * RFC 6585: Additional HTTP Status Codes
  465. * RFC 3229: Delta encoding in HTTP
  466. * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518
  467. * RFC 5842: Binding Extensions to WebDAV
  468. * RFC 7238: Permanent Redirect
  469. * RFC 2295: Transparent Content Negotiation in HTTP
  470. * RFC 2774: An HTTP Extension Framework
  471. * RFC 7725: An HTTP Status Code to Report Legal Obstacles
  472. * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2)
  473. * RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)
  474. * RFC 8297: An HTTP Status Code for Indicating Hints
  475. * RFC 8470: Using Early Data in HTTP
  476. """
  477. def __new__(cls, value, phrase, description=''):
  478. obj = int.__new__(cls, value)
  479. obj._value_ = value
  480. obj.phrase = phrase
  481. obj.description = description
  482. return obj
  483. # informational
  484. CONTINUE = 100, 'Continue', 'Request received, please continue'
  485. SWITCHING_PROTOCOLS = (101, 'Switching Protocols',
  486. 'Switching to new protocol; obey Upgrade header')
  487. PROCESSING = 102, 'Processing'
  488. EARLY_HINTS = 103, 'Early Hints'
  489. # success
  490. OK = 200, 'OK', 'Request fulfilled, document follows'
  491. CREATED = 201, 'Created', 'Document created, URL follows'
  492. ACCEPTED = (202, 'Accepted',
  493. 'Request accepted, processing continues off-line')
  494. NON_AUTHORITATIVE_INFORMATION = (203,
  495. 'Non-Authoritative Information', 'Request fulfilled from cache')
  496. NO_CONTENT = 204, 'No Content', 'Request fulfilled, nothing follows'
  497. RESET_CONTENT = 205, 'Reset Content', 'Clear input form for further input'
  498. PARTIAL_CONTENT = 206, 'Partial Content', 'Partial content follows'
  499. MULTI_STATUS = 207, 'Multi-Status'
  500. ALREADY_REPORTED = 208, 'Already Reported'
  501. IM_USED = 226, 'IM Used'
  502. # redirection
  503. MULTIPLE_CHOICES = (300, 'Multiple Choices',
  504. 'Object has several resources -- see URI list')
  505. MOVED_PERMANENTLY = (301, 'Moved Permanently',
  506. 'Object moved permanently -- see URI list')
  507. FOUND = 302, 'Found', 'Object moved temporarily -- see URI list'
  508. SEE_OTHER = 303, 'See Other', 'Object moved -- see Method and URL list'
  509. NOT_MODIFIED = (304, 'Not Modified',
  510. 'Document has not changed since given time')
  511. USE_PROXY = (305, 'Use Proxy',
  512. 'You must use proxy specified in Location to access this resource')
  513. TEMPORARY_REDIRECT = (307, 'Temporary Redirect',
  514. 'Object moved temporarily -- see URI list')
  515. PERMANENT_REDIRECT = (308, 'Permanent Redirect',
  516. 'Object moved permanently -- see URI list')
  517. # client error
  518. BAD_REQUEST = (400, 'Bad Request',
  519. 'Bad request syntax or unsupported method')
  520. UNAUTHORIZED = (401, 'Unauthorized',
  521. 'No permission -- see authorization schemes')
  522. PAYMENT_REQUIRED = (402, 'Payment Required',
  523. 'No payment -- see charging schemes')
  524. FORBIDDEN = (403, 'Forbidden',
  525. 'Request forbidden -- authorization will not help')
  526. NOT_FOUND = (404, 'Not Found',
  527. 'Nothing matches the given URI')
  528. METHOD_NOT_ALLOWED = (405, 'Method Not Allowed',
  529. 'Specified method is invalid for this resource')
  530. NOT_ACCEPTABLE = (406, 'Not Acceptable',
  531. 'URI not available in preferred format')
  532. PROXY_AUTHENTICATION_REQUIRED = (407,
  533. 'Proxy Authentication Required',
  534. 'You must authenticate with this proxy before proceeding')
  535. REQUEST_TIMEOUT = (408, 'Request Timeout',
  536. 'Request timed out; try again later')
  537. CONFLICT = 409, 'Conflict', 'Request conflict'
  538. GONE = (410, 'Gone',
  539. 'URI no longer exists and has been permanently removed')
  540. LENGTH_REQUIRED = (411, 'Length Required',
  541. 'Client must specify Content-Length')
  542. PRECONDITION_FAILED = (412, 'Precondition Failed',
  543. 'Precondition in headers is false')
  544. REQUEST_ENTITY_TOO_LARGE = (413, 'Request Entity Too Large',
  545. 'Entity is too large')
  546. REQUEST_URI_TOO_LONG = (414, 'Request-URI Too Long',
  547. 'URI is too long')
  548. UNSUPPORTED_MEDIA_TYPE = (415, 'Unsupported Media Type',
  549. 'Entity body in unsupported format')
  550. REQUESTED_RANGE_NOT_SATISFIABLE = (416,
  551. 'Requested Range Not Satisfiable',
  552. 'Cannot satisfy request range')
  553. EXPECTATION_FAILED = (417, 'Expectation Failed',
  554. 'Expect condition could not be satisfied')
  555. IM_A_TEAPOT = (418, 'I\'m a Teapot',
  556. 'Server refuses to brew coffee because it is a teapot.')
  557. MISDIRECTED_REQUEST = (421, 'Misdirected Request',
  558. 'Server is not able to produce a response')
  559. UNPROCESSABLE_ENTITY = 422, 'Unprocessable Entity'
  560. LOCKED = 423, 'Locked'
  561. FAILED_DEPENDENCY = 424, 'Failed Dependency'
  562. TOO_EARLY = 425, 'Too Early'
  563. UPGRADE_REQUIRED = 426, 'Upgrade Required'
  564. PRECONDITION_REQUIRED = (428, 'Precondition Required',
  565. 'The origin server requires the request to be conditional')
  566. TOO_MANY_REQUESTS = (429, 'Too Many Requests',
  567. 'The user has sent too many requests in '
  568. 'a given amount of time ("rate limiting")')
  569. REQUEST_HEADER_FIELDS_TOO_LARGE = (431,
  570. 'Request Header Fields Too Large',
  571. 'The server is unwilling to process the request because its header '
  572. 'fields are too large')
  573. UNAVAILABLE_FOR_LEGAL_REASONS = (451,
  574. 'Unavailable For Legal Reasons',
  575. 'The server is denying access to the '
  576. 'resource as a consequence of a legal demand')
  577. # server errors
  578. INTERNAL_SERVER_ERROR = (500, 'Internal Server Error',
  579. 'Server got itself in trouble')
  580. NOT_IMPLEMENTED = (501, 'Not Implemented',
  581. 'Server does not support this operation')
  582. BAD_GATEWAY = (502, 'Bad Gateway',
  583. 'Invalid responses from another server/proxy')
  584. SERVICE_UNAVAILABLE = (503, 'Service Unavailable',
  585. 'The server cannot process the request due to a high load')
  586. GATEWAY_TIMEOUT = (504, 'Gateway Timeout',
  587. 'The gateway server did not receive a timely response')
  588. HTTP_VERSION_NOT_SUPPORTED = (505, 'HTTP Version Not Supported',
  589. 'Cannot fulfill request')
  590. VARIANT_ALSO_NEGOTIATES = 506, 'Variant Also Negotiates'
  591. INSUFFICIENT_STORAGE = 507, 'Insufficient Storage'
  592. LOOP_DETECTED = 508, 'Loop Detected'
  593. NOT_EXTENDED = 510, 'Not Extended'
  594. NETWORK_AUTHENTICATION_REQUIRED = (511,
  595. 'Network Authentication Required',
  596. 'The client needs to authenticate to gain network access')
  597. enum._test_simple_enum(CheckedHTTPStatus, HTTPStatus)
  598. def test_status_lines(self):
  599. # Test HTTP status lines
  600. body = "HTTP/1.1 200 Ok\r\n\r\nText"
  601. sock = FakeSocket(body)
  602. resp = client.HTTPResponse(sock)
  603. resp.begin()
  604. self.assertEqual(resp.read(0), b'') # Issue #20007
  605. self.assertFalse(resp.isclosed())
  606. self.assertFalse(resp.closed)
  607. self.assertEqual(resp.read(), b"Text")
  608. self.assertTrue(resp.isclosed())
  609. self.assertFalse(resp.closed)
  610. resp.close()
  611. self.assertTrue(resp.closed)
  612. body = "HTTP/1.1 400.100 Not Ok\r\n\r\nText"
  613. sock = FakeSocket(body)
  614. resp = client.HTTPResponse(sock)
  615. self.assertRaises(client.BadStatusLine, resp.begin)
  616. def test_bad_status_repr(self):
  617. exc = client.BadStatusLine('')
  618. self.assertEqual(repr(exc), '''BadStatusLine("''")''')
  619. def test_partial_reads(self):
  620. # if we have Content-Length, HTTPResponse knows when to close itself,
  621. # the same behaviour as when we read the whole thing with read()
  622. body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
  623. sock = FakeSocket(body)
  624. resp = client.HTTPResponse(sock)
  625. resp.begin()
  626. self.assertEqual(resp.read(2), b'Te')
  627. self.assertFalse(resp.isclosed())
  628. self.assertEqual(resp.read(2), b'xt')
  629. self.assertTrue(resp.isclosed())
  630. self.assertFalse(resp.closed)
  631. resp.close()
  632. self.assertTrue(resp.closed)
  633. def test_mixed_reads(self):
  634. # readline() should update the remaining length, so that read() knows
  635. # how much data is left and does not raise IncompleteRead
  636. body = "HTTP/1.1 200 Ok\r\nContent-Length: 13\r\n\r\nText\r\nAnother"
  637. sock = FakeSocket(body)
  638. resp = client.HTTPResponse(sock)
  639. resp.begin()
  640. self.assertEqual(resp.readline(), b'Text\r\n')
  641. self.assertFalse(resp.isclosed())
  642. self.assertEqual(resp.read(), b'Another')
  643. self.assertTrue(resp.isclosed())
  644. self.assertFalse(resp.closed)
  645. resp.close()
  646. self.assertTrue(resp.closed)
  647. def test_partial_readintos(self):
  648. # if we have Content-Length, HTTPResponse knows when to close itself,
  649. # the same behaviour as when we read the whole thing with read()
  650. body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
  651. sock = FakeSocket(body)
  652. resp = client.HTTPResponse(sock)
  653. resp.begin()
  654. b = bytearray(2)
  655. n = resp.readinto(b)
  656. self.assertEqual(n, 2)
  657. self.assertEqual(bytes(b), b'Te')
  658. self.assertFalse(resp.isclosed())
  659. n = resp.readinto(b)
  660. self.assertEqual(n, 2)
  661. self.assertEqual(bytes(b), b'xt')
  662. self.assertTrue(resp.isclosed())
  663. self.assertFalse(resp.closed)
  664. resp.close()
  665. self.assertTrue(resp.closed)
  666. def test_partial_reads_past_end(self):
  667. # if we have Content-Length, clip reads to the end
  668. body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
  669. sock = FakeSocket(body)
  670. resp = client.HTTPResponse(sock)
  671. resp.begin()
  672. self.assertEqual(resp.read(10), b'Text')
  673. self.assertTrue(resp.isclosed())
  674. self.assertFalse(resp.closed)
  675. resp.close()
  676. self.assertTrue(resp.closed)
  677. def test_partial_readintos_past_end(self):
  678. # if we have Content-Length, clip readintos to the end
  679. body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
  680. sock = FakeSocket(body)
  681. resp = client.HTTPResponse(sock)
  682. resp.begin()
  683. b = bytearray(10)
  684. n = resp.readinto(b)
  685. self.assertEqual(n, 4)
  686. self.assertEqual(bytes(b)[:4], b'Text')
  687. self.assertTrue(resp.isclosed())
  688. self.assertFalse(resp.closed)
  689. resp.close()
  690. self.assertTrue(resp.closed)
  691. def test_partial_reads_no_content_length(self):
  692. # when no length is present, the socket should be gracefully closed when
  693. # all data was read
  694. body = "HTTP/1.1 200 Ok\r\n\r\nText"
  695. sock = FakeSocket(body)
  696. resp = client.HTTPResponse(sock)
  697. resp.begin()
  698. self.assertEqual(resp.read(2), b'Te')
  699. self.assertFalse(resp.isclosed())
  700. self.assertEqual(resp.read(2), b'xt')
  701. self.assertEqual(resp.read(1), b'')
  702. self.assertTrue(resp.isclosed())
  703. self.assertFalse(resp.closed)
  704. resp.close()
  705. self.assertTrue(resp.closed)
  706. def test_partial_readintos_no_content_length(self):
  707. # when no length is present, the socket should be gracefully closed when
  708. # all data was read
  709. body = "HTTP/1.1 200 Ok\r\n\r\nText"
  710. sock = FakeSocket(body)
  711. resp = client.HTTPResponse(sock)
  712. resp.begin()
  713. b = bytearray(2)
  714. n = resp.readinto(b)
  715. self.assertEqual(n, 2)
  716. self.assertEqual(bytes(b), b'Te')
  717. self.assertFalse(resp.isclosed())
  718. n = resp.readinto(b)
  719. self.assertEqual(n, 2)
  720. self.assertEqual(bytes(b), b'xt')
  721. n = resp.readinto(b)
  722. self.assertEqual(n, 0)
  723. self.assertTrue(resp.isclosed())
  724. def test_partial_reads_incomplete_body(self):
  725. # if the server shuts down the connection before the whole
  726. # content-length is delivered, the socket is gracefully closed
  727. body = "HTTP/1.1 200 Ok\r\nContent-Length: 10\r\n\r\nText"
  728. sock = FakeSocket(body)
  729. resp = client.HTTPResponse(sock)
  730. resp.begin()
  731. self.assertEqual(resp.read(2), b'Te')
  732. self.assertFalse(resp.isclosed())
  733. self.assertEqual(resp.read(2), b'xt')
  734. self.assertEqual(resp.read(1), b'')
  735. self.assertTrue(resp.isclosed())
  736. def test_partial_readintos_incomplete_body(self):
  737. # if the server shuts down the connection before the whole
  738. # content-length is delivered, the socket is gracefully closed
  739. body = "HTTP/1.1 200 Ok\r\nContent-Length: 10\r\n\r\nText"
  740. sock = FakeSocket(body)
  741. resp = client.HTTPResponse(sock)
  742. resp.begin()
  743. b = bytearray(2)
  744. n = resp.readinto(b)
  745. self.assertEqual(n, 2)
  746. self.assertEqual(bytes(b), b'Te')
  747. self.assertFalse(resp.isclosed())
  748. n = resp.readinto(b)
  749. self.assertEqual(n, 2)
  750. self.assertEqual(bytes(b), b'xt')
  751. n = resp.readinto(b)
  752. self.assertEqual(n, 0)
  753. self.assertTrue(resp.isclosed())
  754. self.assertFalse(resp.closed)
  755. resp.close()
  756. self.assertTrue(resp.closed)
  757. def test_host_port(self):
  758. # Check invalid host_port
  759. for hp in ("www.python.org:abc", "user:password@www.python.org"):
  760. self.assertRaises(client.InvalidURL, client.HTTPConnection, hp)
  761. for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000",
  762. "fe80::207:e9ff:fe9b", 8000),
  763. ("www.python.org:80", "www.python.org", 80),
  764. ("www.python.org:", "www.python.org", 80),
  765. ("www.python.org", "www.python.org", 80),
  766. ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 80),
  767. ("[fe80::207:e9ff:fe9b]:", "fe80::207:e9ff:fe9b", 80)):
  768. c = client.HTTPConnection(hp)
  769. self.assertEqual(h, c.host)
  770. self.assertEqual(p, c.port)
  771. def test_response_headers(self):
  772. # test response with multiple message headers with the same field name.
  773. text = ('HTTP/1.1 200 OK\r\n'
  774. 'Set-Cookie: Customer="WILE_E_COYOTE"; '
  775. 'Version="1"; Path="/acme"\r\n'
  776. 'Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";'
  777. ' Path="/acme"\r\n'
  778. '\r\n'
  779. 'No body\r\n')
  780. hdr = ('Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"'
  781. ', '
  782. 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"')
  783. s = FakeSocket(text)
  784. r = client.HTTPResponse(s)
  785. r.begin()
  786. cookies = r.getheader("Set-Cookie")
  787. self.assertEqual(cookies, hdr)
  788. def test_read_head(self):
  789. # Test that the library doesn't attempt to read any data
  790. # from a HEAD request. (Tickles SF bug #622042.)
  791. sock = FakeSocket(
  792. 'HTTP/1.1 200 OK\r\n'
  793. 'Content-Length: 14432\r\n'
  794. '\r\n',
  795. NoEOFBytesIO)
  796. resp = client.HTTPResponse(sock, method="HEAD")
  797. resp.begin()
  798. if resp.read():
  799. self.fail("Did not expect response from HEAD request")
  800. def test_readinto_head(self):
  801. # Test that the library doesn't attempt to read any data
  802. # from a HEAD request. (Tickles SF bug #622042.)
  803. sock = FakeSocket(
  804. 'HTTP/1.1 200 OK\r\n'
  805. 'Content-Length: 14432\r\n'
  806. '\r\n',
  807. NoEOFBytesIO)
  808. resp = client.HTTPResponse(sock, method="HEAD")
  809. resp.begin()
  810. b = bytearray(5)
  811. if resp.readinto(b) != 0:
  812. self.fail("Did not expect response from HEAD request")
  813. self.assertEqual(bytes(b), b'\x00'*5)
  814. def test_too_many_headers(self):
  815. headers = '\r\n'.join('Header%d: foo' % i
  816. for i in range(client._MAXHEADERS + 1)) + '\r\n'
  817. text = ('HTTP/1.1 200 OK\r\n' + headers)
  818. s = FakeSocket(text)
  819. r = client.HTTPResponse(s)
  820. self.assertRaisesRegex(client.HTTPException,
  821. r"got more than \d+ headers", r.begin)
  822. def test_send_file(self):
  823. expected = (b'GET /foo HTTP/1.1\r\nHost: example.com\r\n'
  824. b'Accept-Encoding: identity\r\n'
  825. b'Transfer-Encoding: chunked\r\n'
  826. b'\r\n')
  827. with open(__file__, 'rb') as body:
  828. conn = client.HTTPConnection('example.com')
  829. sock = FakeSocket(body)
  830. conn.sock = sock
  831. conn.request('GET', '/foo', body)
  832. self.assertTrue(sock.data.startswith(expected), '%r != %r' %
  833. (sock.data[:len(expected)], expected))
  834. def test_send(self):
  835. expected = b'this is a test this is only a test'
  836. conn = client.HTTPConnection('example.com')
  837. sock = FakeSocket(None)
  838. conn.sock = sock
  839. conn.send(expected)
  840. self.assertEqual(expected, sock.data)
  841. sock.data = b''
  842. conn.send(array.array('b', expected))
  843. self.assertEqual(expected, sock.data)
  844. sock.data = b''
  845. conn.send(io.BytesIO(expected))
  846. self.assertEqual(expected, sock.data)
  847. def test_send_updating_file(self):
  848. def data():
  849. yield 'data'
  850. yield None
  851. yield 'data_two'
  852. class UpdatingFile(io.TextIOBase):
  853. mode = 'r'
  854. d = data()
  855. def read(self, blocksize=-1):
  856. return next(self.d)
  857. expected = b'data'
  858. conn = client.HTTPConnection('example.com')
  859. sock = FakeSocket("")
  860. conn.sock = sock
  861. conn.send(UpdatingFile())
  862. self.assertEqual(sock.data, expected)
  863. def test_send_iter(self):
  864. expected = b'GET /foo HTTP/1.1\r\nHost: example.com\r\n' \
  865. b'Accept-Encoding: identity\r\nContent-Length: 11\r\n' \
  866. b'\r\nonetwothree'
  867. def body():
  868. yield b"one"
  869. yield b"two"
  870. yield b"three"
  871. conn = client.HTTPConnection('example.com')
  872. sock = FakeSocket("")
  873. conn.sock = sock
  874. conn.request('GET', '/foo', body(), {'Content-Length': '11'})
  875. self.assertEqual(sock.data, expected)
  876. def test_blocksize_request(self):
  877. """Check that request() respects the configured block size."""
  878. blocksize = 8 # For easy debugging.
  879. conn = client.HTTPConnection('example.com', blocksize=blocksize)
  880. sock = FakeSocket(None)
  881. conn.sock = sock
  882. expected = b"a" * blocksize + b"b"
  883. conn.request("PUT", "/", io.BytesIO(expected), {"Content-Length": "9"})
  884. self.assertEqual(sock.sendall_calls, 3)
  885. body = sock.data.split(b"\r\n\r\n", 1)[1]
  886. self.assertEqual(body, expected)
  887. def test_blocksize_send(self):
  888. """Check that send() respects the configured block size."""
  889. blocksize = 8 # For easy debugging.
  890. conn = client.HTTPConnection('example.com', blocksize=blocksize)
  891. sock = FakeSocket(None)
  892. conn.sock = sock
  893. expected = b"a" * blocksize + b"b"
  894. conn.send(io.BytesIO(expected))
  895. self.assertEqual(sock.sendall_calls, 2)
  896. self.assertEqual(sock.data, expected)
  897. def test_send_type_error(self):
  898. # See: Issue #12676
  899. conn = client.HTTPConnection('example.com')
  900. conn.sock = FakeSocket('')
  901. with self.assertRaises(TypeError):
  902. conn.request('POST', 'test', conn)
  903. def test_chunked(self):
  904. expected = chunked_expected
  905. sock = FakeSocket(chunked_start + last_chunk + chunked_end)
  906. resp = client.HTTPResponse(sock, method="GET")
  907. resp.begin()
  908. self.assertEqual(resp.read(), expected)
  909. resp.close()
  910. # Various read sizes
  911. for n in range(1, 12):
  912. sock = FakeSocket(chunked_start + last_chunk + chunked_end)
  913. resp = client.HTTPResponse(sock, method="GET")
  914. resp.begin()
  915. self.assertEqual(resp.read(n) + resp.read(n) + resp.read(), expected)
  916. resp.close()
  917. for x in ('', 'foo\r\n'):
  918. sock = FakeSocket(chunked_start + x)
  919. resp = client.HTTPResponse(sock, method="GET")
  920. resp.begin()
  921. try:
  922. resp.read()
  923. except client.IncompleteRead as i:
  924. self.assertEqual(i.partial, expected)
  925. expected_message = 'IncompleteRead(%d bytes read)' % len(expected)
  926. self.assertEqual(repr(i), expected_message)
  927. self.assertEqual(str(i), expected_message)
  928. else:
  929. self.fail('IncompleteRead expected')
  930. finally:
  931. resp.close()
  932. def test_readinto_chunked(self):
  933. expected = chunked_expected
  934. nexpected = len(expected)
  935. b = bytearray(128)
  936. sock = FakeSocket(chunked_start + last_chunk + chunked_end)
  937. resp = client.HTTPResponse(sock, method="GET")
  938. resp.begin()
  939. n = resp.readinto(b)
  940. self.assertEqual(b[:nexpected], expected)
  941. self.assertEqual(n, nexpected)
  942. resp.close()
  943. # Various read sizes
  944. for n in range(1, 12):
  945. sock = FakeSocket(chunked_start + last_chunk + chunked_end)
  946. resp = client.HTTPResponse(sock, method="GET")
  947. resp.begin()
  948. m = memoryview(b)
  949. i = resp.readinto(m[0:n])
  950. i += resp.readinto(m[i:n + i])
  951. i += resp.readinto(m[i:])
  952. self.assertEqual(b[:nexpected], expected)
  953. self.assertEqual(i, nexpected)
  954. resp.close()
  955. for x in ('', 'foo\r\n'):
  956. sock = FakeSocket(chunked_start + x)
  957. resp = client.HTTPResponse(sock, method="GET")
  958. resp.begin()
  959. try:
  960. n = resp.readinto(b)
  961. except client.IncompleteRead as i:
  962. self.assertEqual(i.partial, expected)
  963. expected_message = 'IncompleteRead(%d bytes read)' % len(expected)
  964. self.assertEqual(repr(i), expected_message)
  965. self.assertEqual(str(i), expected_message)
  966. else:
  967. self.fail('IncompleteRead expected')
  968. finally:
  969. resp.close()
  970. def test_chunked_head(self):
  971. chunked_start = (
  972. 'HTTP/1.1 200 OK\r\n'
  973. 'Transfer-Encoding: chunked\r\n\r\n'
  974. 'a\r\n'
  975. 'hello world\r\n'
  976. '1\r\n'
  977. 'd\r\n'
  978. )
  979. sock = FakeSocket(chunked_start + last_chunk + chunked_end)
  980. resp = client.HTTPResponse(sock, method="HEAD")
  981. resp.begin()
  982. self.assertEqual(resp.read(), b'')
  983. self.assertEqual(resp.status, 200)
  984. self.assertEqual(resp.reason, 'OK')
  985. self.assertTrue(resp.isclosed())
  986. self.assertFalse(resp.closed)
  987. resp.close()
  988. self.assertTrue(resp.closed)
  989. def test_readinto_chunked_head(self):
  990. chunked_start = (
  991. 'HTTP/1.1 200 OK\r\n'
  992. 'Transfer-Encoding: chunked\r\n\r\n'
  993. 'a\r\n'
  994. 'hello world\r\n'
  995. '1\r\n'
  996. 'd\r\n'
  997. )
  998. sock = FakeSocket(chunked_start + last_chunk + chunked_end)
  999. resp = client.HTTPResponse(sock, method="HEAD")
  1000. resp.begin()
  1001. b = bytearray(5)
  1002. n = resp.readinto(b)
  1003. self.assertEqual(n, 0)
  1004. self.assertEqual(bytes(b), b'\x00'*5)
  1005. self.assertEqual(resp.status, 200)
  1006. self.assertEqual(resp.reason, 'OK')
  1007. self.assertTrue(resp.isclosed())
  1008. self.assertFalse(resp.closed)
  1009. resp.close()
  1010. self.assertTrue(resp.closed)
  1011. def test_negative_content_length(self):
  1012. sock = FakeSocket(
  1013. 'HTTP/1.1 200 OK\r\nContent-Length: -1\r\n\r\nHello\r\n')
  1014. resp = client.HTTPResponse(sock, method="GET")
  1015. resp.begin()
  1016. self.assertEqual(resp.read(), b'Hello\r\n')
  1017. self.assertTrue(resp.isclosed())
  1018. def test_incomplete_read(self):
  1019. sock = FakeSocket('HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHello\r\n')
  1020. resp = client.HTTPResponse(sock, method="GET")
  1021. resp.begin()
  1022. try:
  1023. resp.read()
  1024. except client.IncompleteRead as i:
  1025. self.assertEqual(i.partial, b'Hello\r\n')
  1026. self.assertEqual(repr(i),
  1027. "IncompleteRead(7 bytes read, 3 more expected)")
  1028. self.assertEqual(str(i),
  1029. "IncompleteRead(7 bytes read, 3 more expected)")
  1030. self.assertTrue(resp.isclosed())
  1031. else:
  1032. self.fail('IncompleteRead expected')
  1033. def test_epipe(self):
  1034. sock = EPipeSocket(
  1035. "HTTP/1.0 401 Authorization Required\r\n"
  1036. "Content-type: text/html\r\n"
  1037. "WWW-Authenticate: Basic realm=\"example\"\r\n",
  1038. b"Content-Length")
  1039. conn = client.HTTPConnection("example.com")
  1040. conn.sock = sock
  1041. self.assertRaises(OSError,
  1042. lambda: conn.request("PUT", "/url", "body"))
  1043. resp = conn.getresponse()
  1044. self.assertEqual(401, resp.status)
  1045. self.assertEqual("Basic realm=\"example\"",
  1046. resp.getheader("www-authenticate"))
  1047. # Test lines overflowing the max line size (_MAXLINE in http.client)
  1048. def test_overflowing_status_line(self):
  1049. body = "HTTP/1.1 200 Ok" + "k" * 65536 + "\r\n"
  1050. resp = client.HTTPResponse(FakeSocket(body))
  1051. self.assertRaises((client.LineTooLong, client.BadStatusLine), resp.begin)
  1052. def test_overflowing_header_line(self):
  1053. body = (
  1054. 'HTTP/1.1 200 OK\r\n'
  1055. 'X-Foo: bar' + 'r' * 65536 + '\r\n\r\n'
  1056. )
  1057. resp = client.HTTPResponse(FakeSocket(body))
  1058. self.assertRaises(client.LineTooLong, resp.begin)
  1059. def test_overflowing_header_limit_after_100(self):
  1060. body = (
  1061. 'HTTP/1.1 100 OK\r\n'
  1062. 'r\n' * 32768
  1063. )
  1064. resp = client.HTTPResponse(FakeSocket(body))
  1065. with self.assertRaises(client.HTTPException) as cm:
  1066. resp.begin()
  1067. # We must assert more because other reasonable errors that we
  1068. # do not want can also be HTTPException derived.
  1069. self.assertIn('got more than ', str(cm.exception))
  1070. self.assertIn('headers', str(cm.exception))
  1071. def test_overflowing_chunked_line(self):
  1072. body = (
  1073. 'HTTP/1.1 200 OK\r\n'
  1074. 'Transfer-Encoding: chunked\r\n\r\n'
  1075. + '0' * 65536 + 'a\r\n'
  1076. 'hello world\r\n'
  1077. '0\r\n'
  1078. '\r\n'
  1079. )
  1080. resp = client.HTTPResponse(FakeSocket(body))
  1081. resp.begin()
  1082. self.assertRaises(client.LineTooLong, resp.read)
  1083. def test_early_eof(self):
  1084. # Test httpresponse with no \r\n termination,
  1085. body = "HTTP/1.1 200 Ok"
  1086. sock = FakeSocket(body)
  1087. resp = client.HTTPResponse(sock)
  1088. resp.begin()
  1089. self.assertEqual(resp.read(), b'')
  1090. self.assertTrue(resp.isclosed())
  1091. self.assertFalse(resp.closed)
  1092. resp.close()
  1093. self.assertTrue(resp.closed)
  1094. def test_error_leak(self):
  1095. # Test that the socket is not leaked if getresponse() fails
  1096. conn = client.HTTPConnection('example.com')
  1097. response = None
  1098. class Response(client.HTTPResponse):
  1099. def __init__(self, *pos, **kw):
  1100. nonlocal response
  1101. response = self # Avoid garbage collector closing the socket
  1102. client.HTTPResponse.__init__(self, *pos, **kw)
  1103. conn.response_class = Response
  1104. conn.sock = FakeSocket('Invalid status line')
  1105. conn.request('GET', '/')
  1106. self.assertRaises(client.BadStatusLine, conn.getresponse)
  1107. self.assertTrue(response.closed)
  1108. self.assertTrue(conn.sock.file_closed)
  1109. def test_chunked_extension(self):
  1110. extra = '3;foo=bar\r\n' + 'abc\r\n'
  1111. expected = chunked_expected + b'abc'
  1112. sock = FakeSocket(chunked_start + extra + last_chunk_extended + chunked_end)
  1113. resp = client.HTTPResponse(sock, method="GET")
  1114. resp.begin()
  1115. self.assertEqual(resp.read(), expected)
  1116. resp.close()
  1117. def test_chunked_missing_end(self):
  1118. """some servers may serve up a short chunked encoding stream"""
  1119. expected = chunked_expected
  1120. sock = FakeSocket(chunked_start + last_chunk) #no terminating crlf
  1121. resp = client.HTTPResponse(sock, method="GET")
  1122. resp.begin()
  1123. self.assertEqual(resp.read(), expected)
  1124. resp.close()
  1125. def test_chunked_trailers(self):
  1126. """See that trailers are read and ignored"""
  1127. expected = chunked_expected
  1128. sock = FakeSocket(chunked_start + last_chunk + trailers + chunked_end)
  1129. resp = client.HTTPResponse(sock, method="GET")
  1130. resp.begin()
  1131. self.assertEqual(resp.read(), expected)
  1132. # we should have reached the end of the file
  1133. self.assertEqual(sock.file.read(), b"") #we read to the end
  1134. resp.close()
  1135. def test_chunked_sync(self):
  1136. """Check that we don't read past the end of the chunked-encoding stream"""
  1137. expected = chunked_expected
  1138. extradata = "extradata"
  1139. sock = FakeSocket(chunked_start + last_chunk + trailers + chunked_end + extradata)
  1140. resp = client.HTTPResponse(sock, method="GET")
  1141. resp.begin()
  1142. self.assertEqual(resp.read(), expected)
  1143. # the file should now have our extradata ready to be read
  1144. self.assertEqual(sock.file.read(), extradata.encode("ascii")) #we read to the end
  1145. resp.close()
  1146. def test_content_length_sync(self):
  1147. """Check that we don't read past the end of the Content-Length stream"""
  1148. extradata = b"extradata"
  1149. expected = b"Hello123\r\n"
  1150. sock = FakeSocket(b'HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n' + expected + extradata)
  1151. resp = client.HTTPResponse(sock, method="GET")
  1152. resp.begin()
  1153. self.assertEqual(resp.read(), expected)
  1154. # the file should now have our extradata ready to be read
  1155. self.assertEqual(sock.file.read(), extradata) #we read to the end
  1156. resp.close()
  1157. def test_readlines_content_length(self):
  1158. extradata = b"extradata"
  1159. expected = b"Hello123\r\n"
  1160. sock = FakeSocket(b'HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n' + expected + extradata)
  1161. resp = client.HTTPResponse(sock, method="GET")
  1162. resp.begin()
  1163. self.assertEqual(resp.readlines(2000), [expected])
  1164. # the file should now have our extradata ready to be read
  1165. self.assertEqual(sock.file.read(), extradata) #we read to the end
  1166. resp.close()
  1167. def test_read1_content_length(self):
  1168. extradata = b"extradata"
  1169. expected = b"Hello123\r\n"
  1170. sock = FakeSocket(b'HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n' + expected + extradata)
  1171. resp = client.HTTPResponse(sock, method="GET")
  1172. resp.begin()
  1173. self.assertEqual(resp.read1(2000), expected)
  1174. # the file should now have our extradata ready to be read
  1175. self.assertEqual(sock.file.read(), extradata) #we read to the end
  1176. resp.close()
  1177. def test_readline_bound_content_length(self):
  1178. extradata = b"extradata"
  1179. expected = b"Hello123\r\n"
  1180. sock = FakeSocket(b'HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n' + expected + extradata)
  1181. resp = client.HTTPResponse(sock, method="GET")
  1182. resp.begin()
  1183. self.assertEqual(resp.readline(10), expected)
  1184. self.assertEqual(resp.readline(10), b"")
  1185. # the file should now have our extradata ready to be read
  1186. self.assertEqual(sock.file.read(), extradata) #we read to the end
  1187. resp.close()
  1188. def test_read1_bound_content_length(self):
  1189. extradata = b"extradata"
  1190. expected = b"Hello123\r\n"
  1191. sock = FakeSocket(b'HTTP/1.1 200 OK\r\nContent-Length: 30\r\n\r\n' + expected*3 + extradata)
  1192. resp = client.HTTPResponse(sock, method="GET")
  1193. resp.begin()
  1194. self.assertEqual(resp.read1(20), expected*2)
  1195. self.assertEqual(resp.read(), expected)
  1196. # the file should now have our extradata ready to be read
  1197. self.assertEqual(sock.file.read(), extradata) #we read to the end
  1198. resp.close()
  1199. def test_response_fileno(self):
  1200. # Make sure fd returned by fileno is valid.
  1201. serv = socket.create_server((HOST, 0))
  1202. self.addCleanup(serv.close)
  1203. result = None
  1204. def run_server():
  1205. [conn, address] = serv.accept()
  1206. with conn, conn.makefile("rb") as reader:
  1207. # Read the request header until a blank line
  1208. while True:
  1209. line = reader.readline()
  1210. if not line.rstrip(b"\r\n"):
  1211. break
  1212. conn.sendall(b"HTTP/1.1 200 Connection established\r\n\r\n")
  1213. nonlocal result
  1214. result = reader.read()
  1215. thread = threading.Thread(target=run_server)
  1216. thread.start()
  1217. self.addCleanup(thread.join, float(1))
  1218. conn = client.HTTPConnection(*serv.getsockname())
  1219. conn.request("CONNECT", "dummy:1234")
  1220. response = conn.getresponse()
  1221. try:
  1222. self.assertEqual(response.status, client.OK)
  1223. s = socket.socket(fileno=response.fileno())
  1224. try:
  1225. s.sendall(b"proxied data\n")
  1226. finally:
  1227. s.detach()
  1228. finally:
  1229. response.close()
  1230. conn.close()
  1231. thread.join()
  1232. self.assertEqual(result, b"proxied data\n")
  1233. def test_putrequest_override_domain_validation(self):
  1234. """
  1235. It should be possible to override the default validation
  1236. behavior in putrequest (bpo-38216).
  1237. """
  1238. class UnsafeHTTPConnection(client.HTTPConnection):
  1239. def _validate_path(self, url):
  1240. pass
  1241. conn = UnsafeHTTPConnection('example.com')
  1242. conn.sock = FakeSocket('')
  1243. conn.putrequest('GET', '/\x00')
  1244. def test_putrequest_override_host_validation(self):
  1245. class UnsafeHTTPConnection(client.HTTPConnection):
  1246. def _validate_host(self, url):
  1247. pass
  1248. conn = UnsafeHTTPConnection('example.com\r\n')
  1249. conn.sock = FakeSocket('')
  1250. # set skip_host so a ValueError is not raised upon adding the
  1251. # invalid URL as the value of the "Host:" header
  1252. conn.putrequest('GET', '/', skip_host=1)
  1253. def test_putrequest_override_encoding(self):
  1254. """
  1255. It should be possible to override the default encoding
  1256. to transmit bytes in another encoding even if invalid
  1257. (bpo-36274).
  1258. """
  1259. class UnsafeHTTPConnection(client.HTTPConnection):
  1260. def _encode_request(self, str_url):
  1261. return str_url.encode('utf-8')
  1262. conn = UnsafeHTTPConnection('example.com')
  1263. conn.sock = FakeSocket('')
  1264. conn.putrequest('GET', '/☃')
  1265. class ExtendedReadTest(TestCase):
  1266. """
  1267. Test peek(), read1(), readline()
  1268. """
  1269. lines = (
  1270. 'HTTP/1.1 200 OK\r\n'
  1271. '\r\n'
  1272. 'hello world!\n'
  1273. 'and now \n'
  1274. 'for something completely different\n'
  1275. 'foo'
  1276. )
  1277. lines_expected = lines[lines.find('hello'):].encode("ascii")
  1278. lines_chunked = (
  1279. 'HTTP/1.1 200 OK\r\n'
  1280. 'Transfer-Encoding: chunked\r\n\r\n'
  1281. 'a\r\n'
  1282. 'hello worl\r\n'
  1283. '3\r\n'
  1284. 'd!\n\r\n'
  1285. '9\r\n'
  1286. 'and now \n\r\n'
  1287. '23\r\n'
  1288. 'for something completely different\n\r\n'
  1289. '3\r\n'
  1290. 'foo\r\n'
  1291. '0\r\n' # terminating chunk
  1292. '\r\n' # end of trailers
  1293. )
  1294. def setUp(self):
  1295. sock = FakeSocket(self.lines)
  1296. resp = client.HTTPResponse(sock, method="GET")
  1297. resp.begin()
  1298. resp.fp = io.BufferedReader(resp.fp)
  1299. self.resp = resp
  1300. def test_peek(self):
  1301. resp = self.resp
  1302. # patch up the buffered peek so that it returns not too much stuff
  1303. oldpeek = resp.fp.peek
  1304. def mypeek(n=-1):
  1305. p = oldpeek(n)
  1306. if n >= 0:
  1307. return p[:n]
  1308. return p[:10]
  1309. resp.fp.peek = mypeek
  1310. all = []
  1311. while True:
  1312. # try a short peek
  1313. p = resp.peek(3)
  1314. if p:
  1315. self.assertGreater(len(p), 0)
  1316. # then unbounded peek
  1317. p2 = resp.peek()
  1318. self.assertGreaterEqual(len(p2), len(p))
  1319. self.assertTrue(p2.startswith(p))
  1320. next = resp.read(len(p2))
  1321. self.assertEqual(next, p2)
  1322. else:
  1323. next = resp.read()
  1324. self.assertFalse(next)
  1325. all.append(next)
  1326. if not next:
  1327. break
  1328. self.assertEqual(b"".join(all), self.lines_expected)
  1329. def test_readline(self):
  1330. resp = self.resp
  1331. self._verify_readline(self.resp.readline, self.lines_expected)
  1332. def _verify_readline(self, readline, expected):
  1333. all = []
  1334. while True:
  1335. # short readlines
  1336. line = readline(5)
  1337. if line and line != b"foo":
  1338. if len(line) < 5:
  1339. self.assertTrue(line.endswith(b"\n"))
  1340. all.append(line)
  1341. if not line:
  1342. break
  1343. self.assertEqual(b"".join(all), expected)
  1344. def test_read1(self):
  1345. resp = self.resp
  1346. def r():
  1347. res = resp.read1(4)
  1348. self.assertLessEqual(len(res), 4)
  1349. return res
  1350. readliner = Readliner(r)
  1351. self._verify_readline(readliner.readline, self.lines_expected)
  1352. def test_read1_unbounded(self):
  1353. resp = self.resp
  1354. all = []
  1355. while True:
  1356. data = resp.read1()
  1357. if not data:
  1358. break
  1359. all.append(data)
  1360. self.assertEqual(b"".join(all), self.lines_expected)
  1361. def test_read1_bounded(self):
  1362. resp = self.resp
  1363. all = []
  1364. while True:
  1365. data = resp.read1(10)
  1366. if not data:
  1367. break
  1368. self.assertLessEqual(len(data), 10)
  1369. all.append(data)
  1370. self.assertEqual(b"".join(all), self.lines_expected)
  1371. def test_read1_0(self):
  1372. self.assertEqual(self.resp.read1(0), b"")
  1373. def test_peek_0(self):
  1374. p = self.resp.peek(0)
  1375. self.assertLessEqual(0, len(p))
  1376. class ExtendedReadTestChunked(ExtendedReadTest):
  1377. """
  1378. Test peek(), read1(), readline() in chunked mode
  1379. """
  1380. lines = (
  1381. 'HTTP/1.1 200 OK\r\n'
  1382. 'Transfer-Encoding: chunked\r\n\r\n'
  1383. 'a\r\n'
  1384. 'hello worl\r\n'
  1385. '3\r\n'
  1386. 'd!\n\r\n'
  1387. '9\r\n'
  1388. 'and now \n\r\n'
  1389. '23\r\n'
  1390. 'for something completely different\n\r\n'
  1391. '3\r\n'
  1392. 'foo\r\n'
  1393. '0\r\n' # terminating chunk
  1394. '\r\n' # end of trailers
  1395. )
  1396. class Readliner:
  1397. """
  1398. a simple readline class that uses an arbitrary read function and buffering
  1399. """
  1400. def __init__(self, readfunc):
  1401. self.readfunc = readfunc
  1402. self.remainder = b""
  1403. def readline(self, limit):
  1404. data = []
  1405. datalen = 0
  1406. read = self.remainder
  1407. try:
  1408. while True:
  1409. idx = read.find(b'\n')
  1410. if idx != -1:
  1411. break
  1412. if datalen + len(read) >= limit:
  1413. idx = limit - datalen - 1
  1414. # read more data
  1415. data.append(read)
  1416. read = self.readfunc()
  1417. if not read:
  1418. idx = 0 #eof condition
  1419. break
  1420. idx += 1
  1421. data.append(read[:idx])
  1422. self.remainder = read[idx:]
  1423. return b"".join(data)
  1424. except:
  1425. self.remainder = b"".join(data)
  1426. raise
  1427. class OfflineTest(TestCase):
  1428. def test_all(self):
  1429. # Documented objects defined in the module should be in __all__
  1430. expected = {"responses"} # Allowlist documented dict() object
  1431. # HTTPMessage, parse_headers(), and the HTTP status code constants are
  1432. # intentionally omitted for simplicity
  1433. denylist = {"HTTPMessage", "parse_headers"}
  1434. for name in dir(client):
  1435. if name.startswith("_") or name in denylist:
  1436. continue
  1437. module_object = getattr(client, name)
  1438. if getattr(module_object, "__module__", None) == "http.client":
  1439. expected.add(name)
  1440. self.assertCountEqual(client.__all__, expected)
  1441. def test_responses(self):
  1442. self.assertEqual(client.responses[client.NOT_FOUND], "Not Found")
  1443. def test_client_constants(self):
  1444. # Make sure we don't break backward compatibility with 3.4
  1445. expected = [
  1446. 'CONTINUE',
  1447. 'SWITCHING_PROTOCOLS',
  1448. 'PROCESSING',
  1449. 'OK',
  1450. 'CREATED',
  1451. 'ACCEPTED',
  1452. 'NON_AUTHORITATIVE_INFORMATION',
  1453. 'NO_CONTENT',
  1454. 'RESET_CONTENT',
  1455. 'PARTIAL_CONTENT',
  1456. 'MULTI_STATUS',
  1457. 'IM_USED',
  1458. 'MULTIPLE_CHOICES',
  1459. 'MOVED_PERMANENTLY',
  1460. 'FOUND',
  1461. 'SEE_OTHER',
  1462. 'NOT_MODIFIED',
  1463. 'USE_PROXY',
  1464. 'TEMPORARY_REDIRECT',
  1465. 'BAD_REQUEST',
  1466. 'UNAUTHORIZED',
  1467. 'PAYMENT_REQUIRED',
  1468. 'FORBIDDEN',
  1469. 'NOT_FOUND',
  1470. 'METHOD_NOT_ALLOWED',
  1471. 'NOT_ACCEPTABLE',
  1472. 'PROXY_AUTHENTICATION_REQUIRED',
  1473. 'REQUEST_TIMEOUT',
  1474. 'CONFLICT',
  1475. 'GONE',
  1476. 'LENGTH_REQUIRED',
  1477. 'PRECONDITION_FAILED',
  1478. 'REQUEST_ENTITY_TOO_LARGE',
  1479. 'REQUEST_URI_TOO_LONG',
  1480. 'UNSUPPORTED_MEDIA_TYPE',
  1481. 'REQUESTED_RANGE_NOT_SATISFIABLE',
  1482. 'EXPECTATION_FAILED',
  1483. 'IM_A_TEAPOT',
  1484. 'MISDIRECTED_REQUEST',
  1485. 'UNPROCESSABLE_ENTITY',
  1486. 'LOCKED',
  1487. 'FAILED_DEPENDENCY',
  1488. 'UPGRADE_REQUIRED',
  1489. 'PRECONDITION_REQUIRED',
  1490. 'TOO_MANY_REQUESTS',
  1491. 'REQUEST_HEADER_FIELDS_TOO_LARGE',
  1492. 'UNAVAILABLE_FOR_LEGAL_REASONS',
  1493. 'INTERNAL_SERVER_ERROR',
  1494. 'NOT_IMPLEMENTED',
  1495. 'BAD_GATEWAY',
  1496. 'SERVICE_UNAVAILABLE',
  1497. 'GATEWAY_TIMEOUT',
  1498. 'HTTP_VERSION_NOT_SUPPORTED',
  1499. 'INSUFFICIENT_STORAGE',
  1500. 'NOT_EXTENDED',
  1501. 'NETWORK_AUTHENTICATION_REQUIRED',
  1502. 'EARLY_HINTS',
  1503. 'TOO_EARLY'
  1504. ]
  1505. for const in expected:
  1506. with self.subTest(constant=const):
  1507. self.assertTrue(hasattr(client, const))
  1508. class SourceAddressTest(TestCase):
  1509. def setUp(self):
  1510. self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  1511. self.port = socket_helper.bind_port(self.serv)
  1512. self.source_port = socket_helper.find_unused_port()
  1513. self.serv.listen()
  1514. self.conn = None
  1515. def tearDown(self):
  1516. if self.conn:
  1517. self.conn.close()
  1518. self.conn = None
  1519. self.serv.close()
  1520. self.serv = None
  1521. def testHTTPConnectionSourceAddress(self):
  1522. self.conn = client.HTTPConnection(HOST, self.port,
  1523. source_address=('', self.source_port))
  1524. self.conn.connect()
  1525. self.assertEqual(self.conn.sock.getsockname()[1], self.source_port)
  1526. @unittest.skipIf(not hasattr(client, 'HTTPSConnection'),
  1527. 'http.client.HTTPSConnection not defined')
  1528. def testHTTPSConnectionSourceAddress(self):
  1529. self.conn = client.HTTPSConnection(HOST, self.port,
  1530. source_address=('', self.source_port))
  1531. # We don't test anything here other than the constructor not barfing as
  1532. # this code doesn't deal with setting up an active running SSL server
  1533. # for an ssl_wrapped connect() to actually return from.
  1534. class TimeoutTest(TestCase):
  1535. PORT = None
  1536. def setUp(self):
  1537. self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  1538. TimeoutTest.PORT = socket_helper.bind_port(self.serv)
  1539. self.serv.listen()
  1540. def tearDown(self):
  1541. self.serv.close()
  1542. self.serv = None
  1543. def testTimeoutAttribute(self):
  1544. # This will prove that the timeout gets through HTTPConnection
  1545. # and into the socket.
  1546. # default -- use global socket timeout
  1547. self.assertIsNone(socket.getdefaulttimeout())
  1548. socket.setdefaulttimeout(30)
  1549. try:
  1550. httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT)
  1551. httpConn.connect()
  1552. finally:
  1553. socket.setdefaulttimeout(None)
  1554. self.assertEqual(httpConn.sock.gettimeout(), 30)
  1555. httpConn.close()
  1556. # no timeout -- do not use global socket default
  1557. self.assertIsNone(socket.getdefaulttimeout())
  1558. socket.setdefaulttimeout(30)
  1559. try:
  1560. httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT,
  1561. timeout=None)
  1562. httpConn.connect()
  1563. finally:
  1564. socket.setdefaulttimeout(None)
  1565. self.assertEqual(httpConn.sock.gettimeout(), None)
  1566. httpConn.close()
  1567. # a value
  1568. httpConn = client.HTTPConnection(HOST, TimeoutTest.PORT, timeout=30)
  1569. httpConn.connect()
  1570. self.assertEqual(httpConn.sock.gettimeout(), 30)
  1571. httpConn.close()
  1572. class PersistenceTest(TestCase):
  1573. def test_reuse_reconnect(self):
  1574. # Should reuse or reconnect depending on header from server
  1575. tests = (
  1576. ('1.0', '', False),
  1577. ('1.0', 'Connection: keep-alive\r\n', True),
  1578. ('1.1', '', True),
  1579. ('1.1', 'Connection: close\r\n', False),
  1580. ('1.0', 'Connection: keep-ALIVE\r\n', True),
  1581. ('1.1', 'Connection: cloSE\r\n', False),
  1582. )
  1583. for version, header, reuse in tests:
  1584. with self.subTest(version=version, header=header):
  1585. msg = (
  1586. 'HTTP/{} 200 OK\r\n'
  1587. '{}'
  1588. 'Content-Length: 12\r\n'
  1589. '\r\n'
  1590. 'Dummy body\r\n'
  1591. ).format(version, header)
  1592. conn = FakeSocketHTTPConnection(msg)
  1593. self.assertIsNone(conn.sock)
  1594. conn.request('GET', '/open-connection')
  1595. with conn.getresponse() as response:
  1596. self.assertEqual(conn.sock is None, not reuse)
  1597. response.read()
  1598. self.assertEqual(conn.sock is None, not reuse)
  1599. self.assertEqual(conn.connections, 1)
  1600. conn.request('GET', '/subsequent-request')
  1601. self.assertEqual(conn.connections, 1 if reuse else 2)
  1602. def test_disconnected(self):
  1603. def make_reset_reader(text):
  1604. """Return BufferedReader that raises ECONNRESET at EOF"""
  1605. stream = io.BytesIO(text)
  1606. def readinto(buffer):
  1607. size = io.BytesIO.readinto(stream, buffer)
  1608. if size == 0:
  1609. raise ConnectionResetError()
  1610. return size
  1611. stream.readinto = readinto
  1612. return io.BufferedReader(stream)
  1613. tests = (
  1614. (io.BytesIO, client.RemoteDisconnected),
  1615. (make_reset_reader, ConnectionResetError),
  1616. )
  1617. for stream_factory, exception in tests:
  1618. with self.subTest(exception=exception):
  1619. conn = FakeSocketHTTPConnection(b'', stream_factory)
  1620. conn.request('GET', '/eof-response')
  1621. self.assertRaises(exception, conn.getresponse)
  1622. self.assertIsNone(conn.sock)
  1623. # HTTPConnection.connect() should be automatically invoked
  1624. conn.request('GET', '/reconnect')
  1625. self.assertEqual(conn.connections, 2)
  1626. def test_100_close(self):
  1627. conn = FakeSocketHTTPConnection(
  1628. b'HTTP/1.1 100 Continue\r\n'
  1629. b'\r\n'
  1630. # Missing final response
  1631. )
  1632. conn.request('GET', '/', headers={'Expect': '100-continue'})
  1633. self.assertRaises(client.RemoteDisconnected, conn.getresponse)
  1634. self.assertIsNone(conn.sock)
  1635. conn.request('GET', '/reconnect')
  1636. self.assertEqual(conn.connections, 2)
  1637. class HTTPSTest(TestCase):
  1638. def setUp(self):
  1639. if not hasattr(client, 'HTTPSConnection'):
  1640. self.skipTest('ssl support required')
  1641. def make_server(self, certfile):
  1642. from test.ssl_servers import make_https_server
  1643. return make_https_server(self, certfile=certfile)
  1644. def test_attributes(self):
  1645. # simple test to check it's storing the timeout
  1646. h = client.HTTPSConnection(HOST, TimeoutTest.PORT, timeout=30)
  1647. self.assertEqual(h.timeout, 30)
  1648. def test_networked(self):
  1649. # Default settings: requires a valid cert from a trusted CA
  1650. import ssl
  1651. support.requires('network')
  1652. with socket_helper.transient_internet('self-signed.pythontest.net'):
  1653. h = client.HTTPSConnection('self-signed.pythontest.net', 443)
  1654. with self.assertRaises(ssl.SSLError) as exc_info:
  1655. h.request('GET', '/')
  1656. self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
  1657. def test_networked_noverification(self):
  1658. # Switch off cert verification
  1659. import ssl
  1660. support.requires('network')
  1661. with socket_helper.transient_internet('self-signed.pythontest.net'):
  1662. context = ssl._create_unverified_context()
  1663. h = client.HTTPSConnection('self-signed.pythontest.net', 443,
  1664. context=context)
  1665. h.request('GET', '/')
  1666. resp = h.getresponse()
  1667. h.close()
  1668. self.assertIn('nginx', resp.getheader('server'))
  1669. resp.close()
  1670. @support.system_must_validate_cert
  1671. def test_networked_trusted_by_default_cert(self):
  1672. # Default settings: requires a valid cert from a trusted CA
  1673. support.requires('network')
  1674. with socket_helper.transient_internet('www.python.org'):
  1675. h = client.HTTPSConnection('www.python.org', 443)
  1676. h.request('GET', '/')
  1677. resp = h.getresponse()
  1678. content_type = resp.getheader('content-type')
  1679. resp.close()
  1680. h.close()
  1681. self.assertIn('text/html', content_type)
  1682. def test_networked_good_cert(self):
  1683. # We feed the server's cert as a validating cert
  1684. import ssl
  1685. support.requires('network')
  1686. selfsigned_pythontestdotnet = 'self-signed.pythontest.net'
  1687. with socket_helper.transient_internet(selfsigned_pythontestdotnet):
  1688. context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
  1689. self.assertEqual(context.verify_mode, ssl.CERT_REQUIRED)
  1690. self.assertEqual(context.check_hostname, True)
  1691. context.load_verify_locations(CERT_selfsigned_pythontestdotnet)
  1692. try:
  1693. h = client.HTTPSConnection(selfsigned_pythontestdotnet, 443,
  1694. context=context)
  1695. h.request('GET', '/')
  1696. resp = h.getresponse()
  1697. except ssl.SSLError as ssl_err:
  1698. ssl_err_str = str(ssl_err)
  1699. # In the error message of [SSL: CERTIFICATE_VERIFY_FAILED] on
  1700. # modern Linux distros (Debian Buster, etc) default OpenSSL
  1701. # configurations it'll fail saying "key too weak" until we
  1702. # address https://bugs.python.org/issue36816 to use a proper
  1703. # key size on self-signed.pythontest.net.
  1704. if re.search(r'(?i)key.too.weak', ssl_err_str):
  1705. raise unittest.SkipTest(
  1706. f'Got {ssl_err_str} trying to connect '
  1707. f'to {selfsigned_pythontestdotnet}. '
  1708. 'See https://bugs.python.org/issue36816.')
  1709. raise
  1710. server_string = resp.getheader('server')
  1711. resp.close()
  1712. h.close()
  1713. self.assertIn('nginx', server_string)
  1714. def test_networked_bad_cert(self):
  1715. # We feed a "CA" cert that is unrelated to the server's cert
  1716. import ssl
  1717. support.requires('network')
  1718. with socket_helper.transient_internet('self-signed.pythontest.net'):
  1719. context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
  1720. context.load_verify_locations(CERT_localhost)
  1721. h = client.HTTPSConnection('self-signed.pythontest.net', 443, context=context)
  1722. with self.assertRaises(ssl.SSLError) as exc_info:
  1723. h.request('GET', '/')
  1724. self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
  1725. def test_local_unknown_cert(self):
  1726. # The custom cert isn't known to the default trust bundle
  1727. import ssl
  1728. server = self.make_server(CERT_localhost)
  1729. h = client.HTTPSConnection('localhost', server.port)
  1730. with self.assertRaises(ssl.SSLError) as exc_info:
  1731. h.request('GET', '/')
  1732. self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
  1733. def test_local_good_hostname(self):
  1734. # The (valid) cert validates the HTTP hostname
  1735. import ssl
  1736. server = self.make_server(CERT_localhost)
  1737. context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
  1738. context.load_verify_locations(CERT_localhost)
  1739. h = client.HTTPSConnection('localhost', server.port, context=context)
  1740. self.addCleanup(h.close)
  1741. h.request('GET', '/nonexistent')
  1742. resp = h.getresponse()
  1743. self.addCleanup(resp.close)
  1744. self.assertEqual(resp.status, 404)
  1745. def test_local_bad_hostname(self):
  1746. # The (valid) cert doesn't validate the HTTP hostname
  1747. import ssl
  1748. server = self.make_server(CERT_fakehostname)
  1749. context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
  1750. context.load_verify_locations(CERT_fakehostname)
  1751. h = client.HTTPSConnection('localhost', server.port, context=context)
  1752. with self.assertRaises(ssl.CertificateError):
  1753. h.request('GET', '/')
  1754. # Same with explicit check_hostname=True
  1755. with warnings_helper.check_warnings(('', DeprecationWarning)):
  1756. h = client.HTTPSConnection('localhost', server.port,
  1757. context=context, check_hostname=True)
  1758. with self.assertRaises(ssl.CertificateError):
  1759. h.request('GET', '/')
  1760. # With check_hostname=False, the mismatching is ignored
  1761. context.check_hostname = False
  1762. with warnings_helper.check_warnings(('', DeprecationWarning)):
  1763. h = client.HTTPSConnection('localhost', server.port,
  1764. context=context, check_hostname=False)
  1765. h.request('GET', '/nonexistent')
  1766. resp = h.getresponse()
  1767. resp.close()
  1768. h.close()
  1769. self.assertEqual(resp.status, 404)
  1770. # The context's check_hostname setting is used if one isn't passed to
  1771. # HTTPSConnection.
  1772. context.check_hostname = False
  1773. h = client.HTTPSConnection('localhost', server.port, context=context)
  1774. h.request('GET', '/nonexistent')
  1775. resp = h.getresponse()
  1776. self.assertEqual(resp.status, 404)
  1777. resp.close()
  1778. h.close()
  1779. # Passing check_hostname to HTTPSConnection should override the
  1780. # context's setting.
  1781. with warnings_helper.check_warnings(('', DeprecationWarning)):
  1782. h = client.HTTPSConnection('localhost', server.port,
  1783. context=context, check_hostname=True)
  1784. with self.assertRaises(ssl.CertificateError):
  1785. h.request('GET', '/')
  1786. @unittest.skipIf(not hasattr(client, 'HTTPSConnection'),
  1787. 'http.client.HTTPSConnection not available')
  1788. def test_host_port(self):
  1789. # Check invalid host_port
  1790. for hp in ("www.python.org:abc", "user:password@www.python.org"):
  1791. self.assertRaises(client.InvalidURL, client.HTTPSConnection, hp)
  1792. for hp, h, p in (("[fe80::207:e9ff:fe9b]:8000",
  1793. "fe80::207:e9ff:fe9b", 8000),
  1794. ("www.python.org:443", "www.python.org", 443),
  1795. ("www.python.org:", "www.python.org", 443),
  1796. ("www.python.org", "www.python.org", 443),
  1797. ("[fe80::207:e9ff:fe9b]", "fe80::207:e9ff:fe9b", 443),
  1798. ("[fe80::207:e9ff:fe9b]:", "fe80::207:e9ff:fe9b",
  1799. 443)):
  1800. c = client.HTTPSConnection(hp)
  1801. self.assertEqual(h, c.host)
  1802. self.assertEqual(p, c.port)
  1803. def test_tls13_pha(self):
  1804. import ssl
  1805. if not ssl.HAS_TLSv1_3:
  1806. self.skipTest('TLS 1.3 support required')
  1807. # just check status of PHA flag
  1808. h = client.HTTPSConnection('localhost', 443)
  1809. self.assertTrue(h._context.post_handshake_auth)
  1810. context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
  1811. self.assertFalse(context.post_handshake_auth)
  1812. h = client.HTTPSConnection('localhost', 443, context=context)
  1813. self.assertIs(h._context, context)
  1814. self.assertFalse(h._context.post_handshake_auth)
  1815. with warnings.catch_warnings():
  1816. warnings.filterwarnings('ignore', 'key_file, cert_file and check_hostname are deprecated',
  1817. DeprecationWarning)
  1818. h = client.HTTPSConnection('localhost', 443, context=context,
  1819. cert_file=CERT_localhost)
  1820. self.assertTrue(h._context.post_handshake_auth)
  1821. class RequestBodyTest(TestCase):
  1822. """Test cases where a request includes a message body."""
  1823. def setUp(self):
  1824. self.conn = client.HTTPConnection('example.com')
  1825. self.conn.sock = self.sock = FakeSocket("")
  1826. self.conn.sock = self.sock
  1827. def get_headers_and_fp(self):
  1828. f = io.BytesIO(self.sock.data)
  1829. f.readline() # read the request line
  1830. message = client.parse_headers(f)
  1831. return message, f
  1832. def test_list_body(self):
  1833. # Note that no content-length is automatically calculated for
  1834. # an iterable. The request will fall back to send chunked
  1835. # transfer encoding.
  1836. cases = (
  1837. ([b'foo', b'bar'], b'3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n'),
  1838. ((b'foo', b'bar'), b'3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n'),
  1839. )
  1840. for body, expected in cases:
  1841. with self.subTest(body):
  1842. self.conn = client.HTTPConnection('example.com')
  1843. self.conn.sock = self.sock = FakeSocket('')
  1844. self.conn.request('PUT', '/url', body)
  1845. msg, f = self.get_headers_and_fp()
  1846. self.assertNotIn('Content-Type', msg)
  1847. self.assertNotIn('Content-Length', msg)
  1848. self.assertEqual(msg.get('Transfer-Encoding'), 'chunked')
  1849. self.assertEqual(expected, f.read())
  1850. def test_manual_content_length(self):
  1851. # Set an incorrect content-length so that we can verify that
  1852. # it will not be over-ridden by the library.
  1853. self.conn.request("PUT", "/url", "body",
  1854. {"Content-Length": "42"})
  1855. message, f = self.get_headers_and_fp()
  1856. self.assertEqual("42", message.get("content-length"))
  1857. self.assertEqual(4, len(f.read()))
  1858. def test_ascii_body(self):
  1859. self.conn.request("PUT", "/url", "body")
  1860. message, f = self.get_headers_and_fp()
  1861. self.assertEqual("text/plain", message.get_content_type())
  1862. self.assertIsNone(message.get_charset())
  1863. self.assertEqual("4", message.get("content-length"))
  1864. self.assertEqual(b'body', f.read())
  1865. def test_latin1_body(self):
  1866. self.conn.request("PUT", "/url", "body\xc1")
  1867. message, f = self.get_headers_and_fp()
  1868. self.assertEqual("text/plain", message.get_content_type())
  1869. self.assertIsNone(message.get_charset())
  1870. self.assertEqual("5", message.get("content-length"))
  1871. self.assertEqual(b'body\xc1', f.read())
  1872. def test_bytes_body(self):
  1873. self.conn.request("PUT", "/url", b"body\xc1")
  1874. message, f = self.get_headers_and_fp()
  1875. self.assertEqual("text/plain", message.get_content_type())
  1876. self.assertIsNone(message.get_charset())
  1877. self.assertEqual("5", message.get("content-length"))
  1878. self.assertEqual(b'body\xc1', f.read())
  1879. def test_text_file_body(self):
  1880. self.addCleanup(os_helper.unlink, os_helper.TESTFN)
  1881. with open(os_helper.TESTFN, "w", encoding="utf-8") as f:
  1882. f.write("body")
  1883. with open(os_helper.TESTFN, encoding="utf-8") as f:
  1884. self.conn.request("PUT", "/url", f)
  1885. message, f = self.get_headers_and_fp()
  1886. self.assertEqual("text/plain", message.get_content_type())
  1887. self.assertIsNone(message.get_charset())
  1888. # No content-length will be determined for files; the body
  1889. # will be sent using chunked transfer encoding instead.
  1890. self.assertIsNone(message.get("content-length"))
  1891. self.assertEqual("chunked", message.get("transfer-encoding"))
  1892. self.assertEqual(b'4\r\nbody\r\n0\r\n\r\n', f.read())
  1893. def test_binary_file_body(self):
  1894. self.addCleanup(os_helper.unlink, os_helper.TESTFN)
  1895. with open(os_helper.TESTFN, "wb") as f:
  1896. f.write(b"body\xc1")
  1897. with open(os_helper.TESTFN, "rb") as f:
  1898. self.conn.request("PUT", "/url", f)
  1899. message, f = self.get_headers_and_fp()
  1900. self.assertEqual("text/plain", message.get_content_type())
  1901. self.assertIsNone(message.get_charset())
  1902. self.assertEqual("chunked", message.get("Transfer-Encoding"))
  1903. self.assertNotIn("Content-Length", message)
  1904. self.assertEqual(b'5\r\nbody\xc1\r\n0\r\n\r\n', f.read())
  1905. class HTTPResponseTest(TestCase):
  1906. def setUp(self):
  1907. body = "HTTP/1.1 200 Ok\r\nMy-Header: first-value\r\nMy-Header: \
  1908. second-value\r\n\r\nText"
  1909. sock = FakeSocket(body)
  1910. self.resp = client.HTTPResponse(sock)
  1911. self.resp.begin()
  1912. def test_getting_header(self):
  1913. header = self.resp.getheader('My-Header')
  1914. self.assertEqual(header, 'first-value, second-value')
  1915. header = self.resp.getheader('My-Header', 'some default')
  1916. self.assertEqual(header, 'first-value, second-value')
  1917. def test_getting_nonexistent_header_with_string_default(self):
  1918. header = self.resp.getheader('No-Such-Header', 'default-value')
  1919. self.assertEqual(header, 'default-value')
  1920. def test_getting_nonexistent_header_with_iterable_default(self):
  1921. header = self.resp.getheader('No-Such-Header', ['default', 'values'])
  1922. self.assertEqual(header, 'default, values')
  1923. header = self.resp.getheader('No-Such-Header', ('default', 'values'))
  1924. self.assertEqual(header, 'default, values')
  1925. def test_getting_nonexistent_header_without_default(self):
  1926. header = self.resp.getheader('No-Such-Header')
  1927. self.assertEqual(header, None)
  1928. def test_getting_header_defaultint(self):
  1929. header = self.resp.getheader('No-Such-Header',default=42)
  1930. self.assertEqual(header, 42)
  1931. class TunnelTests(TestCase):
  1932. def setUp(self):
  1933. response_text = (
  1934. 'HTTP/1.0 200 OK\r\n\r\n' # Reply to CONNECT
  1935. 'HTTP/1.1 200 OK\r\n' # Reply to HEAD
  1936. 'Content-Length: 42\r\n\r\n'
  1937. )
  1938. self.host = 'proxy.com'
  1939. self.conn = client.HTTPConnection(self.host)
  1940. self.conn._create_connection = self._create_connection(response_text)
  1941. def tearDown(self):
  1942. self.conn.close()
  1943. def _create_connection(self, response_text):
  1944. def create_connection(address, timeout=None, source_address=None):
  1945. return FakeSocket(response_text, host=address[0], port=address[1])
  1946. return create_connection
  1947. def test_set_tunnel_host_port_headers(self):
  1948. tunnel_host = 'destination.com'
  1949. tunnel_port = 8888
  1950. tunnel_headers = {'User-Agent': 'Mozilla/5.0 (compatible, MSIE 11)'}
  1951. self.conn.set_tunnel(tunnel_host, port=tunnel_port,
  1952. headers=tunnel_headers)
  1953. self.conn.request('HEAD', '/', '')
  1954. self.assertEqual(self.conn.sock.host, self.host)
  1955. self.assertEqual(self.conn.sock.port, client.HTTP_PORT)
  1956. self.assertEqual(self.conn._tunnel_host, tunnel_host)
  1957. self.assertEqual(self.conn._tunnel_port, tunnel_port)
  1958. self.assertEqual(self.conn._tunnel_headers, tunnel_headers)
  1959. def test_disallow_set_tunnel_after_connect(self):
  1960. # Once connected, we shouldn't be able to tunnel anymore
  1961. self.conn.connect()
  1962. self.assertRaises(RuntimeError, self.conn.set_tunnel,
  1963. 'destination.com')
  1964. def test_connect_with_tunnel(self):
  1965. self.conn.set_tunnel('destination.com')
  1966. self.conn.request('HEAD', '/', '')
  1967. self.assertEqual(self.conn.sock.host, self.host)
  1968. self.assertEqual(self.conn.sock.port, client.HTTP_PORT)
  1969. self.assertIn(b'CONNECT destination.com', self.conn.sock.data)
  1970. # issue22095
  1971. self.assertNotIn(b'Host: destination.com:None', self.conn.sock.data)
  1972. self.assertIn(b'Host: destination.com', self.conn.sock.data)
  1973. # This test should be removed when CONNECT gets the HTTP/1.1 blessing
  1974. self.assertNotIn(b'Host: proxy.com', self.conn.sock.data)
  1975. def test_tunnel_connect_single_send_connection_setup(self):
  1976. """Regresstion test for https://bugs.python.org/issue43332."""
  1977. with mock.patch.object(self.conn, 'send') as mock_send:
  1978. self.conn.set_tunnel('destination.com')
  1979. self.conn.connect()
  1980. self.conn.request('GET', '/')
  1981. mock_send.assert_called()
  1982. # Likely 2, but this test only cares about the first.
  1983. self.assertGreater(
  1984. len(mock_send.mock_calls), 1,
  1985. msg=f'unexpected number of send calls: {mock_send.mock_calls}')
  1986. proxy_setup_data_sent = mock_send.mock_calls[0][1][0]
  1987. self.assertIn(b'CONNECT destination.com', proxy_setup_data_sent)
  1988. self.assertTrue(
  1989. proxy_setup_data_sent.endswith(b'\r\n\r\n'),
  1990. msg=f'unexpected proxy data sent {proxy_setup_data_sent!r}')
  1991. def test_connect_put_request(self):
  1992. self.conn.set_tunnel('destination.com')
  1993. self.conn.request('PUT', '/', '')
  1994. self.assertEqual(self.conn.sock.host, self.host)
  1995. self.assertEqual(self.conn.sock.port, client.HTTP_PORT)
  1996. self.assertIn(b'CONNECT destination.com', self.conn.sock.data)
  1997. self.assertIn(b'Host: destination.com', self.conn.sock.data)
  1998. def test_tunnel_debuglog(self):
  1999. expected_header = 'X-Dummy: 1'
  2000. response_text = 'HTTP/1.0 200 OK\r\n{}\r\n\r\n'.format(expected_header)
  2001. self.conn.set_debuglevel(1)
  2002. self.conn._create_connection = self._create_connection(response_text)
  2003. self.conn.set_tunnel('destination.com')
  2004. with support.captured_stdout() as output:
  2005. self.conn.request('PUT', '/', '')
  2006. lines = output.getvalue().splitlines()
  2007. self.assertIn('header: {}'.format(expected_header), lines)
  2008. if __name__ == '__main__':
  2009. unittest.main(verbosity=2)