test_requests.py 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """Tests for Requests."""
  4. from __future__ import division
  5. import json
  6. import os
  7. import pickle
  8. import unittest
  9. import collections
  10. import contextlib
  11. import io
  12. import requests
  13. import pytest
  14. from requests.adapters import HTTPAdapter
  15. from requests.auth import HTTPDigestAuth, _basic_auth_str
  16. from requests.compat import (
  17. Morsel, cookielib, getproxies, str, urljoin, urlparse, is_py3,
  18. builtin_str, OrderedDict
  19. )
  20. from requests.cookies import cookiejar_from_dict, morsel_to_cookie
  21. from requests.exceptions import (ConnectionError, ConnectTimeout,
  22. InvalidSchema, InvalidURL, MissingSchema,
  23. ReadTimeout, Timeout, RetryError)
  24. from requests.models import PreparedRequest
  25. from requests.structures import CaseInsensitiveDict
  26. from requests.sessions import SessionRedirectMixin
  27. from requests.models import urlencode
  28. from requests.hooks import default_hooks
  29. try:
  30. import StringIO
  31. except ImportError:
  32. import io as StringIO
  33. try:
  34. from multiprocessing.pool import ThreadPool
  35. except ImportError:
  36. ThreadPool = None
  37. if is_py3:
  38. def u(s):
  39. return s
  40. else:
  41. def u(s):
  42. return s.decode('unicode-escape')
  43. @pytest.fixture
  44. def httpbin(httpbin):
  45. # Issue #1483: Make sure the URL always has a trailing slash
  46. httpbin_url = httpbin.url.rstrip('/') + '/'
  47. def inner(*suffix):
  48. return urljoin(httpbin_url, '/'.join(suffix))
  49. return inner
  50. @pytest.fixture
  51. def httpsbin_url(httpbin_secure):
  52. # Issue #1483: Make sure the URL always has a trailing slash
  53. httpbin_url = httpbin_secure.url.rstrip('/') + '/'
  54. def inner(*suffix):
  55. return urljoin(httpbin_url, '/'.join(suffix))
  56. return inner
  57. # Requests to this URL should always fail with a connection timeout (nothing
  58. # listening on that port)
  59. TARPIT = "http://10.255.255.1"
  60. class TestRequests(object):
  61. _multiprocess_can_split_ = True
  62. def setUp(self):
  63. """Create simple data set with headers."""
  64. pass
  65. def tearDown(self):
  66. """Teardown."""
  67. pass
  68. def test_entry_points(self):
  69. requests.session
  70. requests.session().get
  71. requests.session().head
  72. requests.get
  73. requests.head
  74. requests.put
  75. requests.patch
  76. requests.post
  77. def test_invalid_url(self):
  78. with pytest.raises(MissingSchema):
  79. requests.get('hiwpefhipowhefopw')
  80. with pytest.raises(InvalidSchema):
  81. requests.get('localhost:3128')
  82. with pytest.raises(InvalidSchema):
  83. requests.get('localhost.localdomain:3128/')
  84. with pytest.raises(InvalidSchema):
  85. requests.get('10.122.1.1:3128/')
  86. with pytest.raises(InvalidURL):
  87. requests.get('http://')
  88. def test_basic_building(self):
  89. req = requests.Request()
  90. req.url = 'http://kennethreitz.org/'
  91. req.data = {'life': '42'}
  92. pr = req.prepare()
  93. assert pr.url == req.url
  94. assert pr.body == 'life=42'
  95. def test_no_content_length(self, httpbin):
  96. get_req = requests.Request('GET', httpbin('get')).prepare()
  97. assert 'Content-Length' not in get_req.headers
  98. head_req = requests.Request('HEAD', httpbin('head')).prepare()
  99. assert 'Content-Length' not in head_req.headers
  100. def test_override_content_length(self, httpbin):
  101. headers = {
  102. 'Content-Length': 'not zero'
  103. }
  104. r = requests.Request('POST', httpbin('post'), headers=headers).prepare()
  105. assert 'Content-Length' in r.headers
  106. assert r.headers['Content-Length'] == 'not zero'
  107. def test_path_is_not_double_encoded(self):
  108. request = requests.Request('GET', "http://0.0.0.0/get/test case").prepare()
  109. assert request.path_url == '/get/test%20case'
  110. def test_params_are_added_before_fragment(self):
  111. request = requests.Request('GET',
  112. "http://example.com/path#fragment", params={"a": "b"}).prepare()
  113. assert request.url == "http://example.com/path?a=b#fragment"
  114. request = requests.Request('GET',
  115. "http://example.com/path?key=value#fragment", params={"a": "b"}).prepare()
  116. assert request.url == "http://example.com/path?key=value&a=b#fragment"
  117. def test_params_original_order_is_preserved_by_default(self):
  118. param_ordered_dict = OrderedDict((('z', 1), ('a', 1), ('k', 1), ('d', 1)))
  119. session = requests.Session()
  120. request = requests.Request('GET', 'http://example.com/', params=param_ordered_dict)
  121. prep = session.prepare_request(request)
  122. assert prep.url == 'http://example.com/?z=1&a=1&k=1&d=1'
  123. def test_params_bytes_are_encoded(self):
  124. request = requests.Request('GET', 'http://example.com',
  125. params=b'test=foo').prepare()
  126. assert request.url == 'http://example.com/?test=foo'
  127. def test_binary_put(self):
  128. request = requests.Request('PUT', 'http://example.com',
  129. data=u"ööö".encode("utf-8")).prepare()
  130. assert isinstance(request.body, bytes)
  131. def test_mixed_case_scheme_acceptable(self, httpbin):
  132. s = requests.Session()
  133. s.proxies = getproxies()
  134. parts = urlparse(httpbin('get'))
  135. schemes = ['http://', 'HTTP://', 'hTTp://', 'HttP://']
  136. for scheme in schemes:
  137. url = scheme + parts.netloc + parts.path
  138. r = requests.Request('GET', url)
  139. r = s.send(r.prepare())
  140. assert r.status_code == 200, 'failed for scheme {0}'.format(scheme)
  141. def test_HTTP_200_OK_GET_ALTERNATIVE(self, httpbin):
  142. r = requests.Request('GET', httpbin('get'))
  143. s = requests.Session()
  144. s.proxies = getproxies()
  145. r = s.send(r.prepare())
  146. assert r.status_code == 200
  147. def test_HTTP_302_ALLOW_REDIRECT_GET(self, httpbin):
  148. r = requests.get(httpbin('redirect', '1'))
  149. assert r.status_code == 200
  150. assert r.history[0].status_code == 302
  151. assert r.history[0].is_redirect
  152. # def test_HTTP_302_ALLOW_REDIRECT_POST(self):
  153. # r = requests.post(httpbin('status', '302'), data={'some': 'data'})
  154. # self.assertEqual(r.status_code, 200)
  155. def test_HTTP_200_OK_GET_WITH_PARAMS(self, httpbin):
  156. heads = {'User-agent': 'Mozilla/5.0'}
  157. r = requests.get(httpbin('user-agent'), headers=heads)
  158. assert heads['User-agent'] in r.text
  159. assert r.status_code == 200
  160. def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self, httpbin):
  161. heads = {'User-agent': 'Mozilla/5.0'}
  162. r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
  163. assert r.status_code == 200
  164. def test_set_cookie_on_301(self, httpbin):
  165. s = requests.session()
  166. url = httpbin('cookies/set?foo=bar')
  167. s.get(url)
  168. assert s.cookies['foo'] == 'bar'
  169. def test_cookie_sent_on_redirect(self, httpbin):
  170. s = requests.session()
  171. s.get(httpbin('cookies/set?foo=bar'))
  172. r = s.get(httpbin('redirect/1')) # redirects to httpbin('get')
  173. assert 'Cookie' in r.json()['headers']
  174. def test_cookie_removed_on_expire(self, httpbin):
  175. s = requests.session()
  176. s.get(httpbin('cookies/set?foo=bar'))
  177. assert s.cookies['foo'] == 'bar'
  178. s.get(
  179. httpbin('response-headers'),
  180. params={
  181. 'Set-Cookie':
  182. 'foo=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT'
  183. }
  184. )
  185. assert 'foo' not in s.cookies
  186. def test_cookie_quote_wrapped(self, httpbin):
  187. s = requests.session()
  188. s.get(httpbin('cookies/set?foo="bar:baz"'))
  189. assert s.cookies['foo'] == '"bar:baz"'
  190. def test_cookie_persists_via_api(self, httpbin):
  191. s = requests.session()
  192. r = s.get(httpbin('redirect/1'), cookies={'foo': 'bar'})
  193. assert 'foo' in r.request.headers['Cookie']
  194. assert 'foo' in r.history[0].request.headers['Cookie']
  195. def test_request_cookie_overrides_session_cookie(self, httpbin):
  196. s = requests.session()
  197. s.cookies['foo'] = 'bar'
  198. r = s.get(httpbin('cookies'), cookies={'foo': 'baz'})
  199. assert r.json()['cookies']['foo'] == 'baz'
  200. # Session cookie should not be modified
  201. assert s.cookies['foo'] == 'bar'
  202. def test_request_cookies_not_persisted(self, httpbin):
  203. s = requests.session()
  204. s.get(httpbin('cookies'), cookies={'foo': 'baz'})
  205. # Sending a request with cookies should not add cookies to the session
  206. assert not s.cookies
  207. def test_generic_cookiejar_works(self, httpbin):
  208. cj = cookielib.CookieJar()
  209. cookiejar_from_dict({'foo': 'bar'}, cj)
  210. s = requests.session()
  211. s.cookies = cj
  212. r = s.get(httpbin('cookies'))
  213. # Make sure the cookie was sent
  214. assert r.json()['cookies']['foo'] == 'bar'
  215. # Make sure the session cj is still the custom one
  216. assert s.cookies is cj
  217. def test_param_cookiejar_works(self, httpbin):
  218. cj = cookielib.CookieJar()
  219. cookiejar_from_dict({'foo': 'bar'}, cj)
  220. s = requests.session()
  221. r = s.get(httpbin('cookies'), cookies=cj)
  222. # Make sure the cookie was sent
  223. assert r.json()['cookies']['foo'] == 'bar'
  224. def test_requests_in_history_are_not_overridden(self, httpbin):
  225. resp = requests.get(httpbin('redirect/3'))
  226. urls = [r.url for r in resp.history]
  227. req_urls = [r.request.url for r in resp.history]
  228. assert urls == req_urls
  229. def test_history_is_always_a_list(self, httpbin):
  230. """
  231. Show that even with redirects, Response.history is always a list.
  232. """
  233. resp = requests.get(httpbin('get'))
  234. assert isinstance(resp.history, list)
  235. resp = requests.get(httpbin('redirect/1'))
  236. assert isinstance(resp.history, list)
  237. assert not isinstance(resp.history, tuple)
  238. def test_headers_on_session_with_None_are_not_sent(self, httpbin):
  239. """Do not send headers in Session.headers with None values."""
  240. ses = requests.Session()
  241. ses.headers['Accept-Encoding'] = None
  242. req = requests.Request('GET', httpbin('get'))
  243. prep = ses.prepare_request(req)
  244. assert 'Accept-Encoding' not in prep.headers
  245. def test_user_agent_transfers(self, httpbin):
  246. heads = {
  247. 'User-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
  248. }
  249. r = requests.get(httpbin('user-agent'), headers=heads)
  250. assert heads['User-agent'] in r.text
  251. heads = {
  252. 'user-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
  253. }
  254. r = requests.get(httpbin('user-agent'), headers=heads)
  255. assert heads['user-agent'] in r.text
  256. def test_HTTP_200_OK_HEAD(self, httpbin):
  257. r = requests.head(httpbin('get'))
  258. assert r.status_code == 200
  259. def test_HTTP_200_OK_PUT(self, httpbin):
  260. r = requests.put(httpbin('put'))
  261. assert r.status_code == 200
  262. def test_BASICAUTH_TUPLE_HTTP_200_OK_GET(self, httpbin):
  263. auth = ('user', 'pass')
  264. url = httpbin('basic-auth', 'user', 'pass')
  265. r = requests.get(url, auth=auth)
  266. assert r.status_code == 200
  267. r = requests.get(url)
  268. assert r.status_code == 401
  269. s = requests.session()
  270. s.auth = auth
  271. r = s.get(url)
  272. assert r.status_code == 200
  273. def test_connection_error_invalid_domain(self):
  274. """Connecting to an unknown domain should raise a ConnectionError"""
  275. with pytest.raises(ConnectionError):
  276. requests.get("http://doesnotexist.google.com")
  277. def test_connection_error_invalid_port(self):
  278. """Connecting to an invalid port should raise a ConnectionError"""
  279. with pytest.raises(ConnectionError):
  280. requests.get("http://localhost:1", timeout=1)
  281. def test_LocationParseError(self):
  282. """Inputing a URL that cannot be parsed should raise an InvalidURL error"""
  283. with pytest.raises(InvalidURL):
  284. requests.get("http://fe80::5054:ff:fe5a:fc0")
  285. def test_basicauth_with_netrc(self, httpbin):
  286. auth = ('user', 'pass')
  287. wrong_auth = ('wronguser', 'wrongpass')
  288. url = httpbin('basic-auth', 'user', 'pass')
  289. old_auth = requests.sessions.get_netrc_auth
  290. try:
  291. def get_netrc_auth_mock(url):
  292. return auth
  293. requests.sessions.get_netrc_auth = get_netrc_auth_mock
  294. # Should use netrc and work.
  295. r = requests.get(url)
  296. assert r.status_code == 200
  297. # Given auth should override and fail.
  298. r = requests.get(url, auth=wrong_auth)
  299. assert r.status_code == 401
  300. s = requests.session()
  301. # Should use netrc and work.
  302. r = s.get(url)
  303. assert r.status_code == 200
  304. # Given auth should override and fail.
  305. s.auth = wrong_auth
  306. r = s.get(url)
  307. assert r.status_code == 401
  308. finally:
  309. requests.sessions.get_netrc_auth = old_auth
  310. def test_DIGEST_HTTP_200_OK_GET(self, httpbin):
  311. auth = HTTPDigestAuth('user', 'pass')
  312. url = httpbin('digest-auth', 'auth', 'user', 'pass')
  313. r = requests.get(url, auth=auth)
  314. assert r.status_code == 200
  315. r = requests.get(url)
  316. assert r.status_code == 401
  317. s = requests.session()
  318. s.auth = HTTPDigestAuth('user', 'pass')
  319. r = s.get(url)
  320. assert r.status_code == 200
  321. def test_DIGEST_AUTH_RETURNS_COOKIE(self, httpbin):
  322. url = httpbin('digest-auth', 'auth', 'user', 'pass')
  323. auth = HTTPDigestAuth('user', 'pass')
  324. r = requests.get(url)
  325. assert r.cookies['fake'] == 'fake_value'
  326. r = requests.get(url, auth=auth)
  327. assert r.status_code == 200
  328. def test_DIGEST_AUTH_SETS_SESSION_COOKIES(self, httpbin):
  329. url = httpbin('digest-auth', 'auth', 'user', 'pass')
  330. auth = HTTPDigestAuth('user', 'pass')
  331. s = requests.Session()
  332. s.get(url, auth=auth)
  333. assert s.cookies['fake'] == 'fake_value'
  334. def test_DIGEST_STREAM(self, httpbin):
  335. auth = HTTPDigestAuth('user', 'pass')
  336. url = httpbin('digest-auth', 'auth', 'user', 'pass')
  337. r = requests.get(url, auth=auth, stream=True)
  338. assert r.raw.read() != b''
  339. r = requests.get(url, auth=auth, stream=False)
  340. assert r.raw.read() == b''
  341. def test_DIGESTAUTH_WRONG_HTTP_401_GET(self, httpbin):
  342. auth = HTTPDigestAuth('user', 'wrongpass')
  343. url = httpbin('digest-auth', 'auth', 'user', 'pass')
  344. r = requests.get(url, auth=auth)
  345. assert r.status_code == 401
  346. r = requests.get(url)
  347. assert r.status_code == 401
  348. s = requests.session()
  349. s.auth = auth
  350. r = s.get(url)
  351. assert r.status_code == 401
  352. def test_DIGESTAUTH_QUOTES_QOP_VALUE(self, httpbin):
  353. auth = HTTPDigestAuth('user', 'pass')
  354. url = httpbin('digest-auth', 'auth', 'user', 'pass')
  355. r = requests.get(url, auth=auth)
  356. assert '"auth"' in r.request.headers['Authorization']
  357. def test_POSTBIN_GET_POST_FILES(self, httpbin):
  358. url = httpbin('post')
  359. post1 = requests.post(url).raise_for_status()
  360. post1 = requests.post(url, data={'some': 'data'})
  361. assert post1.status_code == 200
  362. with open('requirements.txt') as f:
  363. post2 = requests.post(url, files={'some': f})
  364. assert post2.status_code == 200
  365. post4 = requests.post(url, data='[{"some": "json"}]')
  366. assert post4.status_code == 200
  367. with pytest.raises(ValueError):
  368. requests.post(url, files=['bad file data'])
  369. def test_POSTBIN_GET_POST_FILES_WITH_DATA(self, httpbin):
  370. url = httpbin('post')
  371. post1 = requests.post(url).raise_for_status()
  372. post1 = requests.post(url, data={'some': 'data'})
  373. assert post1.status_code == 200
  374. with open('requirements.txt') as f:
  375. post2 = requests.post(url,
  376. data={'some': 'data'}, files={'some': f})
  377. assert post2.status_code == 200
  378. post4 = requests.post(url, data='[{"some": "json"}]')
  379. assert post4.status_code == 200
  380. with pytest.raises(ValueError):
  381. requests.post(url, files=['bad file data'])
  382. def test_conflicting_post_params(self, httpbin):
  383. url = httpbin('post')
  384. with open('requirements.txt') as f:
  385. pytest.raises(ValueError, "requests.post(url, data='[{\"some\": \"data\"}]', files={'some': f})")
  386. pytest.raises(ValueError, "requests.post(url, data=u('[{\"some\": \"data\"}]'), files={'some': f})")
  387. def test_request_ok_set(self, httpbin):
  388. r = requests.get(httpbin('status', '404'))
  389. assert not r.ok
  390. def test_status_raising(self, httpbin):
  391. r = requests.get(httpbin('status', '404'))
  392. with pytest.raises(requests.exceptions.HTTPError):
  393. r.raise_for_status()
  394. r = requests.get(httpbin('status', '500'))
  395. assert not r.ok
  396. def test_decompress_gzip(self, httpbin):
  397. r = requests.get(httpbin('gzip'))
  398. r.content.decode('ascii')
  399. def test_unicode_get(self, httpbin):
  400. url = httpbin('/get')
  401. requests.get(url, params={'foo': 'føø'})
  402. requests.get(url, params={'føø': 'føø'})
  403. requests.get(url, params={'føø': 'føø'})
  404. requests.get(url, params={'foo': 'foo'})
  405. requests.get(httpbin('ø'), params={'foo': 'foo'})
  406. def test_unicode_header_name(self, httpbin):
  407. requests.put(
  408. httpbin('put'),
  409. headers={str('Content-Type'): 'application/octet-stream'},
  410. data='\xff') # compat.str is unicode.
  411. def test_pyopenssl_redirect(self, httpsbin_url, httpbin_ca_bundle):
  412. requests.get(httpsbin_url('status', '301'), verify=httpbin_ca_bundle)
  413. def test_urlencoded_get_query_multivalued_param(self, httpbin):
  414. r = requests.get(httpbin('get'), params=dict(test=['foo', 'baz']))
  415. assert r.status_code == 200
  416. assert r.url == httpbin('get?test=foo&test=baz')
  417. def test_different_encodings_dont_break_post(self, httpbin):
  418. r = requests.post(httpbin('post'),
  419. data={'stuff': json.dumps({'a': 123})},
  420. params={'blah': 'asdf1234'},
  421. files={'file': ('test_requests.py', open(__file__, 'rb'))})
  422. assert r.status_code == 200
  423. def test_unicode_multipart_post(self, httpbin):
  424. r = requests.post(httpbin('post'),
  425. data={'stuff': u('ëlïxr')},
  426. files={'file': ('test_requests.py', open(__file__, 'rb'))})
  427. assert r.status_code == 200
  428. r = requests.post(httpbin('post'),
  429. data={'stuff': u('ëlïxr').encode('utf-8')},
  430. files={'file': ('test_requests.py', open(__file__, 'rb'))})
  431. assert r.status_code == 200
  432. r = requests.post(httpbin('post'),
  433. data={'stuff': 'elixr'},
  434. files={'file': ('test_requests.py', open(__file__, 'rb'))})
  435. assert r.status_code == 200
  436. r = requests.post(httpbin('post'),
  437. data={'stuff': 'elixr'.encode('utf-8')},
  438. files={'file': ('test_requests.py', open(__file__, 'rb'))})
  439. assert r.status_code == 200
  440. def test_unicode_multipart_post_fieldnames(self, httpbin):
  441. filename = os.path.splitext(__file__)[0] + '.py'
  442. r = requests.Request(method='POST',
  443. url=httpbin('post'),
  444. data={'stuff'.encode('utf-8'): 'elixr'},
  445. files={'file': ('test_requests.py',
  446. open(filename, 'rb'))})
  447. prep = r.prepare()
  448. assert b'name="stuff"' in prep.body
  449. assert b'name="b\'stuff\'"' not in prep.body
  450. def test_unicode_method_name(self, httpbin):
  451. files = {'file': open('test_requests.py', 'rb')}
  452. r = requests.request(
  453. method=u('POST'), url=httpbin('post'), files=files)
  454. assert r.status_code == 200
  455. def test_unicode_method_name_with_request_object(self, httpbin):
  456. files = {'file': open('test_requests.py', 'rb')}
  457. s = requests.Session()
  458. req = requests.Request(u("POST"), httpbin('post'), files=files)
  459. prep = s.prepare_request(req)
  460. assert isinstance(prep.method, builtin_str)
  461. assert prep.method == "POST"
  462. resp = s.send(prep)
  463. assert resp.status_code == 200
  464. def test_custom_content_type(self, httpbin):
  465. r = requests.post(
  466. httpbin('post'),
  467. data={'stuff': json.dumps({'a': 123})},
  468. files={'file1': ('test_requests.py', open(__file__, 'rb')),
  469. 'file2': ('test_requests', open(__file__, 'rb'),
  470. 'text/py-content-type')})
  471. assert r.status_code == 200
  472. assert b"text/py-content-type" in r.request.body
  473. def test_hook_receives_request_arguments(self, httpbin):
  474. def hook(resp, **kwargs):
  475. assert resp is not None
  476. assert kwargs != {}
  477. requests.Request('GET', httpbin(), hooks={'response': hook})
  478. def test_session_hooks_are_used_with_no_request_hooks(self, httpbin):
  479. hook = lambda x, *args, **kwargs: x
  480. s = requests.Session()
  481. s.hooks['response'].append(hook)
  482. r = requests.Request('GET', httpbin())
  483. prep = s.prepare_request(r)
  484. assert prep.hooks['response'] != []
  485. assert prep.hooks['response'] == [hook]
  486. def test_session_hooks_are_overridden_by_request_hooks(self, httpbin):
  487. hook1 = lambda x, *args, **kwargs: x
  488. hook2 = lambda x, *args, **kwargs: x
  489. assert hook1 is not hook2
  490. s = requests.Session()
  491. s.hooks['response'].append(hook2)
  492. r = requests.Request('GET', httpbin(), hooks={'response': [hook1]})
  493. prep = s.prepare_request(r)
  494. assert prep.hooks['response'] == [hook1]
  495. def test_prepared_request_hook(self, httpbin):
  496. def hook(resp, **kwargs):
  497. resp.hook_working = True
  498. return resp
  499. req = requests.Request('GET', httpbin(), hooks={'response': hook})
  500. prep = req.prepare()
  501. s = requests.Session()
  502. s.proxies = getproxies()
  503. resp = s.send(prep)
  504. assert hasattr(resp, 'hook_working')
  505. def test_prepared_from_session(self, httpbin):
  506. class DummyAuth(requests.auth.AuthBase):
  507. def __call__(self, r):
  508. r.headers['Dummy-Auth-Test'] = 'dummy-auth-test-ok'
  509. return r
  510. req = requests.Request('GET', httpbin('headers'))
  511. assert not req.auth
  512. s = requests.Session()
  513. s.auth = DummyAuth()
  514. prep = s.prepare_request(req)
  515. resp = s.send(prep)
  516. assert resp.json()['headers'][
  517. 'Dummy-Auth-Test'] == 'dummy-auth-test-ok'
  518. def test_prepare_request_with_bytestring_url(self):
  519. req = requests.Request('GET', b'https://httpbin.org/')
  520. s = requests.Session()
  521. prep = s.prepare_request(req)
  522. assert prep.url == "https://httpbin.org/"
  523. def test_links(self):
  524. r = requests.Response()
  525. r.headers = {
  526. 'cache-control': 'public, max-age=60, s-maxage=60',
  527. 'connection': 'keep-alive',
  528. 'content-encoding': 'gzip',
  529. 'content-type': 'application/json; charset=utf-8',
  530. 'date': 'Sat, 26 Jan 2013 16:47:56 GMT',
  531. 'etag': '"6ff6a73c0e446c1f61614769e3ceb778"',
  532. 'last-modified': 'Sat, 26 Jan 2013 16:22:39 GMT',
  533. 'link': ('<https://api.github.com/users/kennethreitz/repos?'
  534. 'page=2&per_page=10>; rel="next", <https://api.github.'
  535. 'com/users/kennethreitz/repos?page=7&per_page=10>; '
  536. ' rel="last"'),
  537. 'server': 'GitHub.com',
  538. 'status': '200 OK',
  539. 'vary': 'Accept',
  540. 'x-content-type-options': 'nosniff',
  541. 'x-github-media-type': 'github.beta',
  542. 'x-ratelimit-limit': '60',
  543. 'x-ratelimit-remaining': '57'
  544. }
  545. assert r.links['next']['rel'] == 'next'
  546. def test_cookie_parameters(self):
  547. key = 'some_cookie'
  548. value = 'some_value'
  549. secure = True
  550. domain = 'test.com'
  551. rest = {'HttpOnly': True}
  552. jar = requests.cookies.RequestsCookieJar()
  553. jar.set(key, value, secure=secure, domain=domain, rest=rest)
  554. assert len(jar) == 1
  555. assert 'some_cookie' in jar
  556. cookie = list(jar)[0]
  557. assert cookie.secure == secure
  558. assert cookie.domain == domain
  559. assert cookie._rest['HttpOnly'] == rest['HttpOnly']
  560. def test_cookie_as_dict_keeps_len(self):
  561. key = 'some_cookie'
  562. value = 'some_value'
  563. key1 = 'some_cookie1'
  564. value1 = 'some_value1'
  565. jar = requests.cookies.RequestsCookieJar()
  566. jar.set(key, value)
  567. jar.set(key1, value1)
  568. d1 = dict(jar)
  569. d2 = dict(jar.iteritems())
  570. d3 = dict(jar.items())
  571. assert len(jar) == 2
  572. assert len(d1) == 2
  573. assert len(d2) == 2
  574. assert len(d3) == 2
  575. def test_cookie_as_dict_keeps_items(self):
  576. key = 'some_cookie'
  577. value = 'some_value'
  578. key1 = 'some_cookie1'
  579. value1 = 'some_value1'
  580. jar = requests.cookies.RequestsCookieJar()
  581. jar.set(key, value)
  582. jar.set(key1, value1)
  583. d1 = dict(jar)
  584. d2 = dict(jar.iteritems())
  585. d3 = dict(jar.items())
  586. assert d1['some_cookie'] == 'some_value'
  587. assert d2['some_cookie'] == 'some_value'
  588. assert d3['some_cookie1'] == 'some_value1'
  589. def test_cookie_as_dict_keys(self):
  590. key = 'some_cookie'
  591. value = 'some_value'
  592. key1 = 'some_cookie1'
  593. value1 = 'some_value1'
  594. jar = requests.cookies.RequestsCookieJar()
  595. jar.set(key, value)
  596. jar.set(key1, value1)
  597. keys = jar.keys()
  598. assert keys == list(keys)
  599. # make sure one can use keys multiple times
  600. assert list(keys) == list(keys)
  601. def test_cookie_as_dict_values(self):
  602. key = 'some_cookie'
  603. value = 'some_value'
  604. key1 = 'some_cookie1'
  605. value1 = 'some_value1'
  606. jar = requests.cookies.RequestsCookieJar()
  607. jar.set(key, value)
  608. jar.set(key1, value1)
  609. values = jar.values()
  610. assert values == list(values)
  611. # make sure one can use values multiple times
  612. assert list(values) == list(values)
  613. def test_cookie_as_dict_items(self):
  614. key = 'some_cookie'
  615. value = 'some_value'
  616. key1 = 'some_cookie1'
  617. value1 = 'some_value1'
  618. jar = requests.cookies.RequestsCookieJar()
  619. jar.set(key, value)
  620. jar.set(key1, value1)
  621. items = jar.items()
  622. assert items == list(items)
  623. # make sure one can use items multiple times
  624. assert list(items) == list(items)
  625. def test_time_elapsed_blank(self, httpbin):
  626. r = requests.get(httpbin('get'))
  627. td = r.elapsed
  628. total_seconds = ((td.microseconds + (td.seconds + td.days * 24 * 3600)
  629. * 10**6) / 10**6)
  630. assert total_seconds > 0.0
  631. def test_response_is_iterable(self):
  632. r = requests.Response()
  633. io = StringIO.StringIO('abc')
  634. read_ = io.read
  635. def read_mock(amt, decode_content=None):
  636. return read_(amt)
  637. setattr(io, 'read', read_mock)
  638. r.raw = io
  639. assert next(iter(r))
  640. io.close()
  641. def test_response_decode_unicode(self):
  642. """
  643. When called with decode_unicode, Response.iter_content should always
  644. return unicode.
  645. """
  646. r = requests.Response()
  647. r._content_consumed = True
  648. r._content = b'the content'
  649. r.encoding = 'ascii'
  650. chunks = r.iter_content(decode_unicode=True)
  651. assert all(isinstance(chunk, str) for chunk in chunks)
  652. # also for streaming
  653. r = requests.Response()
  654. r.raw = io.BytesIO(b'the content')
  655. r.encoding = 'ascii'
  656. chunks = r.iter_content(decode_unicode=True)
  657. assert all(isinstance(chunk, str) for chunk in chunks)
  658. def test_request_and_response_are_pickleable(self, httpbin):
  659. r = requests.get(httpbin('get'))
  660. # verify we can pickle the original request
  661. assert pickle.loads(pickle.dumps(r.request))
  662. # verify we can pickle the response and that we have access to
  663. # the original request.
  664. pr = pickle.loads(pickle.dumps(r))
  665. assert r.request.url == pr.request.url
  666. assert r.request.headers == pr.request.headers
  667. def test_get_auth_from_url(self):
  668. url = 'http://user:pass@complex.url.com/path?query=yes'
  669. assert ('user', 'pass') == requests.utils.get_auth_from_url(url)
  670. def test_get_auth_from_url_encoded_spaces(self):
  671. url = 'http://user:pass%20pass@complex.url.com/path?query=yes'
  672. assert ('user', 'pass pass') == requests.utils.get_auth_from_url(url)
  673. def test_get_auth_from_url_not_encoded_spaces(self):
  674. url = 'http://user:pass pass@complex.url.com/path?query=yes'
  675. assert ('user', 'pass pass') == requests.utils.get_auth_from_url(url)
  676. def test_get_auth_from_url_percent_chars(self):
  677. url = 'http://user%25user:pass@complex.url.com/path?query=yes'
  678. assert ('user%user', 'pass') == requests.utils.get_auth_from_url(url)
  679. def test_get_auth_from_url_encoded_hashes(self):
  680. url = 'http://user:pass%23pass@complex.url.com/path?query=yes'
  681. assert ('user', 'pass#pass') == requests.utils.get_auth_from_url(url)
  682. def test_cannot_send_unprepared_requests(self, httpbin):
  683. r = requests.Request(url=httpbin())
  684. with pytest.raises(ValueError):
  685. requests.Session().send(r)
  686. def test_http_error(self):
  687. error = requests.exceptions.HTTPError()
  688. assert not error.response
  689. response = requests.Response()
  690. error = requests.exceptions.HTTPError(response=response)
  691. assert error.response == response
  692. error = requests.exceptions.HTTPError('message', response=response)
  693. assert str(error) == 'message'
  694. assert error.response == response
  695. def test_session_pickling(self, httpbin):
  696. r = requests.Request('GET', httpbin('get'))
  697. s = requests.Session()
  698. s = pickle.loads(pickle.dumps(s))
  699. s.proxies = getproxies()
  700. r = s.send(r.prepare())
  701. assert r.status_code == 200
  702. def test_fixes_1329(self, httpbin):
  703. """
  704. Ensure that header updates are done case-insensitively.
  705. """
  706. s = requests.Session()
  707. s.headers.update({'ACCEPT': 'BOGUS'})
  708. s.headers.update({'accept': 'application/json'})
  709. r = s.get(httpbin('get'))
  710. headers = r.request.headers
  711. assert headers['accept'] == 'application/json'
  712. assert headers['Accept'] == 'application/json'
  713. assert headers['ACCEPT'] == 'application/json'
  714. def test_uppercase_scheme_redirect(self, httpbin):
  715. parts = urlparse(httpbin('html'))
  716. url = "HTTP://" + parts.netloc + parts.path
  717. r = requests.get(httpbin('redirect-to'), params={'url': url})
  718. assert r.status_code == 200
  719. assert r.url.lower() == url.lower()
  720. def test_transport_adapter_ordering(self):
  721. s = requests.Session()
  722. order = ['https://', 'http://']
  723. assert order == list(s.adapters)
  724. s.mount('http://git', HTTPAdapter())
  725. s.mount('http://github', HTTPAdapter())
  726. s.mount('http://github.com', HTTPAdapter())
  727. s.mount('http://github.com/about/', HTTPAdapter())
  728. order = [
  729. 'http://github.com/about/',
  730. 'http://github.com',
  731. 'http://github',
  732. 'http://git',
  733. 'https://',
  734. 'http://',
  735. ]
  736. assert order == list(s.adapters)
  737. s.mount('http://gittip', HTTPAdapter())
  738. s.mount('http://gittip.com', HTTPAdapter())
  739. s.mount('http://gittip.com/about/', HTTPAdapter())
  740. order = [
  741. 'http://github.com/about/',
  742. 'http://gittip.com/about/',
  743. 'http://github.com',
  744. 'http://gittip.com',
  745. 'http://github',
  746. 'http://gittip',
  747. 'http://git',
  748. 'https://',
  749. 'http://',
  750. ]
  751. assert order == list(s.adapters)
  752. s2 = requests.Session()
  753. s2.adapters = {'http://': HTTPAdapter()}
  754. s2.mount('https://', HTTPAdapter())
  755. assert 'http://' in s2.adapters
  756. assert 'https://' in s2.adapters
  757. def test_header_remove_is_case_insensitive(self, httpbin):
  758. # From issue #1321
  759. s = requests.Session()
  760. s.headers['foo'] = 'bar'
  761. r = s.get(httpbin('get'), headers={'FOO': None})
  762. assert 'foo' not in r.request.headers
  763. def test_params_are_merged_case_sensitive(self, httpbin):
  764. s = requests.Session()
  765. s.params['foo'] = 'bar'
  766. r = s.get(httpbin('get'), params={'FOO': 'bar'})
  767. assert r.json()['args'] == {'foo': 'bar', 'FOO': 'bar'}
  768. def test_long_authinfo_in_url(self):
  769. url = 'http://{0}:{1}@{2}:9000/path?query#frag'.format(
  770. 'E8A3BE87-9E3F-4620-8858-95478E385B5B',
  771. 'EA770032-DA4D-4D84-8CE9-29C6D910BF1E',
  772. 'exactly-------------sixty-----------three------------characters',
  773. )
  774. r = requests.Request('GET', url).prepare()
  775. assert r.url == url
  776. def test_header_keys_are_native(self, httpbin):
  777. headers = {u('unicode'): 'blah', 'byte'.encode('ascii'): 'blah'}
  778. r = requests.Request('GET', httpbin('get'), headers=headers)
  779. p = r.prepare()
  780. # This is testing that they are builtin strings. A bit weird, but there
  781. # we go.
  782. assert 'unicode' in p.headers.keys()
  783. assert 'byte' in p.headers.keys()
  784. def test_can_send_nonstring_objects_with_files(self, httpbin):
  785. data = {'a': 0.0}
  786. files = {'b': 'foo'}
  787. r = requests.Request('POST', httpbin('post'), data=data, files=files)
  788. p = r.prepare()
  789. assert 'multipart/form-data' in p.headers['Content-Type']
  790. def test_can_send_bytes_bytearray_objects_with_files(self, httpbin):
  791. # Test bytes:
  792. data = {'a': 'this is a string'}
  793. files = {'b': b'foo'}
  794. r = requests.Request('POST', httpbin('post'), data=data, files=files)
  795. p = r.prepare()
  796. assert 'multipart/form-data' in p.headers['Content-Type']
  797. # Test bytearrays:
  798. files = {'b': bytearray(b'foo')}
  799. r = requests.Request('POST', httpbin('post'), data=data, files=files)
  800. p = r.prepare()
  801. assert 'multipart/form-data' in p.headers['Content-Type']
  802. def test_can_send_file_object_with_non_string_filename(self, httpbin):
  803. f = io.BytesIO()
  804. f.name = 2
  805. r = requests.Request('POST', httpbin('post'), files={'f': f})
  806. p = r.prepare()
  807. assert 'multipart/form-data' in p.headers['Content-Type']
  808. def test_autoset_header_values_are_native(self, httpbin):
  809. data = 'this is a string'
  810. length = '16'
  811. req = requests.Request('POST', httpbin('post'), data=data)
  812. p = req.prepare()
  813. assert p.headers['Content-Length'] == length
  814. def test_nonhttp_schemes_dont_check_URLs(self):
  815. test_urls = (
  816. 'data:image/gif;base64,R0lGODlhAQABAHAAACH5BAUAAAAALAAAAAABAAEAAAICRAEAOw==',
  817. 'file:///etc/passwd',
  818. 'magnet:?xt=urn:btih:be08f00302bc2d1d3cfa3af02024fa647a271431',
  819. )
  820. for test_url in test_urls:
  821. req = requests.Request('GET', test_url)
  822. preq = req.prepare()
  823. assert test_url == preq.url
  824. def test_auth_is_stripped_on_redirect_off_host(self, httpbin):
  825. r = requests.get(
  826. httpbin('redirect-to'),
  827. params={'url': 'http://www.google.co.uk'},
  828. auth=('user', 'pass'),
  829. )
  830. assert r.history[0].request.headers['Authorization']
  831. assert not r.request.headers.get('Authorization', '')
  832. def test_auth_is_retained_for_redirect_on_host(self, httpbin):
  833. r = requests.get(httpbin('redirect/1'), auth=('user', 'pass'))
  834. h1 = r.history[0].request.headers['Authorization']
  835. h2 = r.request.headers['Authorization']
  836. assert h1 == h2
  837. def test_manual_redirect_with_partial_body_read(self, httpbin):
  838. s = requests.Session()
  839. r1 = s.get(httpbin('redirect/2'), allow_redirects=False, stream=True)
  840. assert r1.is_redirect
  841. rg = s.resolve_redirects(r1, r1.request, stream=True)
  842. # read only the first eight bytes of the response body,
  843. # then follow the redirect
  844. r1.iter_content(8)
  845. r2 = next(rg)
  846. assert r2.is_redirect
  847. # read all of the response via iter_content,
  848. # then follow the redirect
  849. for _ in r2.iter_content():
  850. pass
  851. r3 = next(rg)
  852. assert not r3.is_redirect
  853. def _patch_adapter_gzipped_redirect(self, session, url):
  854. adapter = session.get_adapter(url=url)
  855. org_build_response = adapter.build_response
  856. self._patched_response = False
  857. def build_response(*args, **kwargs):
  858. resp = org_build_response(*args, **kwargs)
  859. if not self._patched_response:
  860. resp.raw.headers['content-encoding'] = 'gzip'
  861. self._patched_response = True
  862. return resp
  863. adapter.build_response = build_response
  864. def test_redirect_with_wrong_gzipped_header(self, httpbin):
  865. s = requests.Session()
  866. url = httpbin('redirect/1')
  867. self._patch_adapter_gzipped_redirect(s, url)
  868. s.get(url)
  869. def test_basic_auth_str_is_always_native(self):
  870. s = _basic_auth_str("test", "test")
  871. assert isinstance(s, builtin_str)
  872. assert s == "Basic dGVzdDp0ZXN0"
  873. def test_requests_history_is_saved(self, httpbin):
  874. r = requests.get(httpbin('redirect/5'))
  875. total = r.history[-1].history
  876. i = 0
  877. for item in r.history:
  878. assert item.history == total[0:i]
  879. i = i + 1
  880. def test_json_param_post_content_type_works(self, httpbin):
  881. r = requests.post(
  882. httpbin('post'),
  883. json={'life': 42}
  884. )
  885. assert r.status_code == 200
  886. assert 'application/json' in r.request.headers['Content-Type']
  887. assert {'life': 42} == r.json()['json']
  888. def test_json_param_post_should_not_override_data_param(self, httpbin):
  889. r = requests.Request(method='POST', url=httpbin('post'),
  890. data={'stuff': 'elixr'},
  891. json={'music': 'flute'})
  892. prep = r.prepare()
  893. assert 'stuff=elixr' == prep.body
  894. def test_response_iter_lines(self, httpbin):
  895. r = requests.get(httpbin('stream/4'), stream=True)
  896. assert r.status_code == 200
  897. it = r.iter_lines()
  898. next(it)
  899. assert len(list(it)) == 3
  900. def test_unconsumed_session_response_closes_connection(self, httpbin):
  901. s = requests.session()
  902. with contextlib.closing(s.get(httpbin('stream/4'), stream=True)) as response:
  903. pass
  904. assert response._content_consumed is False
  905. assert response.raw.closed
  906. @pytest.mark.xfail
  907. def test_response_iter_lines_reentrant(self, httpbin):
  908. """Response.iter_lines() is not reentrant safe"""
  909. r = requests.get(httpbin('stream/4'), stream=True)
  910. assert r.status_code == 200
  911. next(r.iter_lines())
  912. assert len(list(r.iter_lines())) == 3
  913. class TestContentEncodingDetection(unittest.TestCase):
  914. def test_none(self):
  915. encodings = requests.utils.get_encodings_from_content('')
  916. assert not len(encodings)
  917. def test_html_charset(self):
  918. """HTML5 meta charset attribute"""
  919. content = '<meta charset="UTF-8">'
  920. encodings = requests.utils.get_encodings_from_content(content)
  921. assert len(encodings) == 1
  922. assert encodings[0] == 'UTF-8'
  923. def test_html4_pragma(self):
  924. """HTML4 pragma directive"""
  925. content = '<meta http-equiv="Content-type" content="text/html;charset=UTF-8">'
  926. encodings = requests.utils.get_encodings_from_content(content)
  927. assert len(encodings) == 1
  928. assert encodings[0] == 'UTF-8'
  929. def test_xhtml_pragma(self):
  930. """XHTML 1.x served with text/html MIME type"""
  931. content = '<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />'
  932. encodings = requests.utils.get_encodings_from_content(content)
  933. assert len(encodings) == 1
  934. assert encodings[0] == 'UTF-8'
  935. def test_xml(self):
  936. """XHTML 1.x served as XML"""
  937. content = '<?xml version="1.0" encoding="UTF-8"?>'
  938. encodings = requests.utils.get_encodings_from_content(content)
  939. assert len(encodings) == 1
  940. assert encodings[0] == 'UTF-8'
  941. def test_precedence(self):
  942. content = '''
  943. <?xml version="1.0" encoding="XML"?>
  944. <meta charset="HTML5">
  945. <meta http-equiv="Content-type" content="text/html;charset=HTML4" />
  946. '''.strip()
  947. encodings = requests.utils.get_encodings_from_content(content)
  948. assert encodings == ['HTML5', 'HTML4', 'XML']
  949. class TestCaseInsensitiveDict(unittest.TestCase):
  950. def test_mapping_init(self):
  951. cid = CaseInsensitiveDict({'Foo': 'foo', 'BAr': 'bar'})
  952. assert len(cid) == 2
  953. assert 'foo' in cid
  954. assert 'bar' in cid
  955. def test_iterable_init(self):
  956. cid = CaseInsensitiveDict([('Foo', 'foo'), ('BAr', 'bar')])
  957. assert len(cid) == 2
  958. assert 'foo' in cid
  959. assert 'bar' in cid
  960. def test_kwargs_init(self):
  961. cid = CaseInsensitiveDict(FOO='foo', BAr='bar')
  962. assert len(cid) == 2
  963. assert 'foo' in cid
  964. assert 'bar' in cid
  965. def test_docstring_example(self):
  966. cid = CaseInsensitiveDict()
  967. cid['Accept'] = 'application/json'
  968. assert cid['aCCEPT'] == 'application/json'
  969. assert list(cid) == ['Accept']
  970. def test_len(self):
  971. cid = CaseInsensitiveDict({'a': 'a', 'b': 'b'})
  972. cid['A'] = 'a'
  973. assert len(cid) == 2
  974. def test_getitem(self):
  975. cid = CaseInsensitiveDict({'Spam': 'blueval'})
  976. assert cid['spam'] == 'blueval'
  977. assert cid['SPAM'] == 'blueval'
  978. def test_fixes_649(self):
  979. """__setitem__ should behave case-insensitively."""
  980. cid = CaseInsensitiveDict()
  981. cid['spam'] = 'oneval'
  982. cid['Spam'] = 'twoval'
  983. cid['sPAM'] = 'redval'
  984. cid['SPAM'] = 'blueval'
  985. assert cid['spam'] == 'blueval'
  986. assert cid['SPAM'] == 'blueval'
  987. assert list(cid.keys()) == ['SPAM']
  988. def test_delitem(self):
  989. cid = CaseInsensitiveDict()
  990. cid['Spam'] = 'someval'
  991. del cid['sPam']
  992. assert 'spam' not in cid
  993. assert len(cid) == 0
  994. def test_contains(self):
  995. cid = CaseInsensitiveDict()
  996. cid['Spam'] = 'someval'
  997. assert 'Spam' in cid
  998. assert 'spam' in cid
  999. assert 'SPAM' in cid
  1000. assert 'sPam' in cid
  1001. assert 'notspam' not in cid
  1002. def test_get(self):
  1003. cid = CaseInsensitiveDict()
  1004. cid['spam'] = 'oneval'
  1005. cid['SPAM'] = 'blueval'
  1006. assert cid.get('spam') == 'blueval'
  1007. assert cid.get('SPAM') == 'blueval'
  1008. assert cid.get('sPam') == 'blueval'
  1009. assert cid.get('notspam', 'default') == 'default'
  1010. def test_update(self):
  1011. cid = CaseInsensitiveDict()
  1012. cid['spam'] = 'blueval'
  1013. cid.update({'sPam': 'notblueval'})
  1014. assert cid['spam'] == 'notblueval'
  1015. cid = CaseInsensitiveDict({'Foo': 'foo', 'BAr': 'bar'})
  1016. cid.update({'fOO': 'anotherfoo', 'bAR': 'anotherbar'})
  1017. assert len(cid) == 2
  1018. assert cid['foo'] == 'anotherfoo'
  1019. assert cid['bar'] == 'anotherbar'
  1020. def test_update_retains_unchanged(self):
  1021. cid = CaseInsensitiveDict({'foo': 'foo', 'bar': 'bar'})
  1022. cid.update({'foo': 'newfoo'})
  1023. assert cid['bar'] == 'bar'
  1024. def test_iter(self):
  1025. cid = CaseInsensitiveDict({'Spam': 'spam', 'Eggs': 'eggs'})
  1026. keys = frozenset(['Spam', 'Eggs'])
  1027. assert frozenset(iter(cid)) == keys
  1028. def test_equality(self):
  1029. cid = CaseInsensitiveDict({'SPAM': 'blueval', 'Eggs': 'redval'})
  1030. othercid = CaseInsensitiveDict({'spam': 'blueval', 'eggs': 'redval'})
  1031. assert cid == othercid
  1032. del othercid['spam']
  1033. assert cid != othercid
  1034. assert cid == {'spam': 'blueval', 'eggs': 'redval'}
  1035. assert cid != object()
  1036. def test_setdefault(self):
  1037. cid = CaseInsensitiveDict({'Spam': 'blueval'})
  1038. assert cid.setdefault('spam', 'notblueval') == 'blueval'
  1039. assert cid.setdefault('notspam', 'notblueval') == 'notblueval'
  1040. def test_lower_items(self):
  1041. cid = CaseInsensitiveDict({
  1042. 'Accept': 'application/json',
  1043. 'user-Agent': 'requests',
  1044. })
  1045. keyset = frozenset(lowerkey for lowerkey, v in cid.lower_items())
  1046. lowerkeyset = frozenset(['accept', 'user-agent'])
  1047. assert keyset == lowerkeyset
  1048. def test_preserve_key_case(self):
  1049. cid = CaseInsensitiveDict({
  1050. 'Accept': 'application/json',
  1051. 'user-Agent': 'requests',
  1052. })
  1053. keyset = frozenset(['Accept', 'user-Agent'])
  1054. assert frozenset(i[0] for i in cid.items()) == keyset
  1055. assert frozenset(cid.keys()) == keyset
  1056. assert frozenset(cid) == keyset
  1057. def test_preserve_last_key_case(self):
  1058. cid = CaseInsensitiveDict({
  1059. 'Accept': 'application/json',
  1060. 'user-Agent': 'requests',
  1061. })
  1062. cid.update({'ACCEPT': 'application/json'})
  1063. cid['USER-AGENT'] = 'requests'
  1064. keyset = frozenset(['ACCEPT', 'USER-AGENT'])
  1065. assert frozenset(i[0] for i in cid.items()) == keyset
  1066. assert frozenset(cid.keys()) == keyset
  1067. assert frozenset(cid) == keyset
  1068. def test_copy(self):
  1069. cid = CaseInsensitiveDict({
  1070. 'Accept': 'application/json',
  1071. 'user-Agent': 'requests',
  1072. })
  1073. cid_copy = cid.copy()
  1074. assert cid == cid_copy
  1075. cid['changed'] = True
  1076. assert cid != cid_copy
  1077. class UtilsTestCase(unittest.TestCase):
  1078. def test_super_len_io_streams(self):
  1079. """ Ensures that we properly deal with different kinds of IO streams. """
  1080. # uses StringIO or io.StringIO (see import above)
  1081. from io import BytesIO
  1082. from requests.utils import super_len
  1083. assert super_len(StringIO.StringIO()) == 0
  1084. assert super_len(
  1085. StringIO.StringIO('with so much drama in the LBC')) == 29
  1086. assert super_len(BytesIO()) == 0
  1087. assert super_len(
  1088. BytesIO(b"it's kinda hard bein' snoop d-o-double-g")) == 40
  1089. try:
  1090. import cStringIO
  1091. except ImportError:
  1092. pass
  1093. else:
  1094. assert super_len(
  1095. cStringIO.StringIO('but some how, some way...')) == 25
  1096. def test_super_len_correctly_calculates_len_of_partially_read_file(self):
  1097. """Ensure that we handle partially consumed file like objects."""
  1098. from requests.utils import super_len
  1099. s = StringIO.StringIO()
  1100. s.write('foobarbogus')
  1101. assert super_len(s) == 0
  1102. def test_get_environ_proxies_ip_ranges(self):
  1103. """Ensures that IP addresses are correctly matches with ranges
  1104. in no_proxy variable."""
  1105. from requests.utils import get_environ_proxies
  1106. os.environ['no_proxy'] = "192.168.0.0/24,127.0.0.1,localhost.localdomain,172.16.1.1"
  1107. assert get_environ_proxies('http://192.168.0.1:5000/') == {}
  1108. assert get_environ_proxies('http://192.168.0.1/') == {}
  1109. assert get_environ_proxies('http://172.16.1.1/') == {}
  1110. assert get_environ_proxies('http://172.16.1.1:5000/') == {}
  1111. assert get_environ_proxies('http://192.168.1.1:5000/') != {}
  1112. assert get_environ_proxies('http://192.168.1.1/') != {}
  1113. def test_get_environ_proxies(self):
  1114. """Ensures that IP addresses are correctly matches with ranges
  1115. in no_proxy variable."""
  1116. from requests.utils import get_environ_proxies
  1117. os.environ['no_proxy'] = "127.0.0.1,localhost.localdomain,192.168.0.0/24,172.16.1.1"
  1118. assert get_environ_proxies(
  1119. 'http://localhost.localdomain:5000/v1.0/') == {}
  1120. assert get_environ_proxies('http://www.requests.com/') != {}
  1121. def test_select_proxies(self):
  1122. """Make sure we can select per-host proxies correctly."""
  1123. from requests.utils import select_proxy
  1124. proxies = {'http': 'http://http.proxy',
  1125. 'http://some.host': 'http://some.host.proxy'}
  1126. assert select_proxy('hTTp://u:p@Some.Host/path', proxies) == 'http://some.host.proxy'
  1127. assert select_proxy('hTTp://u:p@Other.Host/path', proxies) == 'http://http.proxy'
  1128. assert select_proxy('hTTps://Other.Host', proxies) is None
  1129. def test_guess_filename_when_int(self):
  1130. from requests.utils import guess_filename
  1131. assert None is guess_filename(1)
  1132. def test_guess_filename_when_filename_is_an_int(self):
  1133. from requests.utils import guess_filename
  1134. fake = type('Fake', (object,), {'name': 1})()
  1135. assert None is guess_filename(fake)
  1136. def test_guess_filename_with_file_like_obj(self):
  1137. from requests.utils import guess_filename
  1138. from requests import compat
  1139. fake = type('Fake', (object,), {'name': b'value'})()
  1140. guessed_name = guess_filename(fake)
  1141. assert b'value' == guessed_name
  1142. assert isinstance(guessed_name, compat.bytes)
  1143. def test_guess_filename_with_unicode_name(self):
  1144. from requests.utils import guess_filename
  1145. from requests import compat
  1146. filename = b'value'.decode('utf-8')
  1147. fake = type('Fake', (object,), {'name': filename})()
  1148. guessed_name = guess_filename(fake)
  1149. assert filename == guessed_name
  1150. assert isinstance(guessed_name, compat.str)
  1151. def test_is_ipv4_address(self):
  1152. from requests.utils import is_ipv4_address
  1153. assert is_ipv4_address('8.8.8.8')
  1154. assert not is_ipv4_address('8.8.8.8.8')
  1155. assert not is_ipv4_address('localhost.localdomain')
  1156. def test_is_valid_cidr(self):
  1157. from requests.utils import is_valid_cidr
  1158. assert not is_valid_cidr('8.8.8.8')
  1159. assert is_valid_cidr('192.168.1.0/24')
  1160. def test_dotted_netmask(self):
  1161. from requests.utils import dotted_netmask
  1162. assert dotted_netmask(8) == '255.0.0.0'
  1163. assert dotted_netmask(24) == '255.255.255.0'
  1164. assert dotted_netmask(25) == '255.255.255.128'
  1165. def test_address_in_network(self):
  1166. from requests.utils import address_in_network
  1167. assert address_in_network('192.168.1.1', '192.168.1.0/24')
  1168. assert not address_in_network('172.16.0.1', '192.168.1.0/24')
  1169. def test_get_auth_from_url(self):
  1170. """Ensures that username and password in well-encoded URI as per
  1171. RFC 3986 are correclty extracted."""
  1172. from requests.utils import get_auth_from_url
  1173. from requests.compat import quote
  1174. percent_encoding_test_chars = "%!*'();:@&=+$,/?#[] "
  1175. url_address = "request.com/url.html#test"
  1176. url = "http://" + quote(
  1177. percent_encoding_test_chars, '') + ':' + quote(
  1178. percent_encoding_test_chars, '') + '@' + url_address
  1179. (username, password) = get_auth_from_url(url)
  1180. assert username == percent_encoding_test_chars
  1181. assert password == percent_encoding_test_chars
  1182. def test_requote_uri_with_unquoted_percents(self):
  1183. """Ensure we handle unquoted percent signs in redirects.
  1184. See: https://github.com/kennethreitz/requests/issues/2356
  1185. """
  1186. from requests.utils import requote_uri
  1187. bad_uri = 'http://example.com/fiz?buz=%ppicture'
  1188. quoted = 'http://example.com/fiz?buz=%25ppicture'
  1189. assert quoted == requote_uri(bad_uri)
  1190. def test_requote_uri_properly_requotes(self):
  1191. """Ensure requoting doesn't break expectations."""
  1192. from requests.utils import requote_uri
  1193. quoted = 'http://example.com/fiz?buz=%25ppicture'
  1194. assert quoted == requote_uri(quoted)
  1195. class TestMorselToCookieExpires(unittest.TestCase):
  1196. """Tests for morsel_to_cookie when morsel contains expires."""
  1197. def test_expires_valid_str(self):
  1198. """Test case where we convert expires from string time."""
  1199. morsel = Morsel()
  1200. morsel['expires'] = 'Thu, 01-Jan-1970 00:00:01 GMT'
  1201. cookie = morsel_to_cookie(morsel)
  1202. assert cookie.expires == 1
  1203. def test_expires_invalid_int(self):
  1204. """Test case where an invalid type is passed for expires."""
  1205. morsel = Morsel()
  1206. morsel['expires'] = 100
  1207. with pytest.raises(TypeError):
  1208. morsel_to_cookie(morsel)
  1209. def test_expires_invalid_str(self):
  1210. """Test case where an invalid string is input."""
  1211. morsel = Morsel()
  1212. morsel['expires'] = 'woops'
  1213. with pytest.raises(ValueError):
  1214. morsel_to_cookie(morsel)
  1215. def test_expires_none(self):
  1216. """Test case where expires is None."""
  1217. morsel = Morsel()
  1218. morsel['expires'] = None
  1219. cookie = morsel_to_cookie(morsel)
  1220. assert cookie.expires is None
  1221. class TestMorselToCookieMaxAge(unittest.TestCase):
  1222. """Tests for morsel_to_cookie when morsel contains max-age."""
  1223. def test_max_age_valid_int(self):
  1224. """Test case where a valid max age in seconds is passed."""
  1225. morsel = Morsel()
  1226. morsel['max-age'] = 60
  1227. cookie = morsel_to_cookie(morsel)
  1228. assert isinstance(cookie.expires, int)
  1229. def test_max_age_invalid_str(self):
  1230. """Test case where a invalid max age is passed."""
  1231. morsel = Morsel()
  1232. morsel['max-age'] = 'woops'
  1233. with pytest.raises(TypeError):
  1234. morsel_to_cookie(morsel)
  1235. class TestTimeout:
  1236. def test_stream_timeout(self, httpbin):
  1237. try:
  1238. requests.get(httpbin('delay/10'), timeout=2.0)
  1239. except requests.exceptions.Timeout as e:
  1240. assert 'Read timed out' in e.args[0].args[0]
  1241. def test_invalid_timeout(self, httpbin):
  1242. with pytest.raises(ValueError) as e:
  1243. requests.get(httpbin('get'), timeout=(3, 4, 5))
  1244. assert '(connect, read)' in str(e)
  1245. with pytest.raises(ValueError) as e:
  1246. requests.get(httpbin('get'), timeout="foo")
  1247. assert 'must be an int or float' in str(e)
  1248. def test_none_timeout(self, httpbin):
  1249. """ Check that you can set None as a valid timeout value.
  1250. To actually test this behavior, we'd want to check that setting the
  1251. timeout to None actually lets the request block past the system default
  1252. timeout. However, this would make the test suite unbearably slow.
  1253. Instead we verify that setting the timeout to None does not prevent the
  1254. request from succeeding.
  1255. """
  1256. r = requests.get(httpbin('get'), timeout=None)
  1257. assert r.status_code == 200
  1258. def test_read_timeout(self, httpbin):
  1259. try:
  1260. requests.get(httpbin('delay/10'), timeout=(None, 0.1))
  1261. assert False, "The recv() request should time out."
  1262. except ReadTimeout:
  1263. pass
  1264. def test_connect_timeout(self):
  1265. try:
  1266. requests.get(TARPIT, timeout=(0.1, None))
  1267. assert False, "The connect() request should time out."
  1268. except ConnectTimeout as e:
  1269. assert isinstance(e, ConnectionError)
  1270. assert isinstance(e, Timeout)
  1271. def test_total_timeout_connect(self):
  1272. try:
  1273. requests.get(TARPIT, timeout=(0.1, 0.1))
  1274. assert False, "The connect() request should time out."
  1275. except ConnectTimeout:
  1276. pass
  1277. def test_encoded_methods(self, httpbin):
  1278. """See: https://github.com/kennethreitz/requests/issues/2316"""
  1279. r = requests.request(b'GET', httpbin('get'))
  1280. assert r.ok
  1281. SendCall = collections.namedtuple('SendCall', ('args', 'kwargs'))
  1282. class RedirectSession(SessionRedirectMixin):
  1283. def __init__(self, order_of_redirects):
  1284. self.redirects = order_of_redirects
  1285. self.calls = []
  1286. self.max_redirects = 30
  1287. self.cookies = {}
  1288. self.trust_env = False
  1289. def send(self, *args, **kwargs):
  1290. self.calls.append(SendCall(args, kwargs))
  1291. return self.build_response()
  1292. def build_response(self):
  1293. request = self.calls[-1].args[0]
  1294. r = requests.Response()
  1295. try:
  1296. r.status_code = int(self.redirects.pop(0))
  1297. except IndexError:
  1298. r.status_code = 200
  1299. r.headers = CaseInsensitiveDict({'Location': '/'})
  1300. r.raw = self._build_raw()
  1301. r.request = request
  1302. return r
  1303. def _build_raw(self):
  1304. string = StringIO.StringIO('')
  1305. setattr(string, 'release_conn', lambda *args: args)
  1306. return string
  1307. class TestRedirects:
  1308. default_keyword_args = {
  1309. 'stream': False,
  1310. 'verify': True,
  1311. 'cert': None,
  1312. 'timeout': None,
  1313. 'allow_redirects': False,
  1314. 'proxies': {},
  1315. }
  1316. def test_requests_are_updated_each_time(self, httpbin):
  1317. session = RedirectSession([303, 307])
  1318. prep = requests.Request('POST', httpbin('post')).prepare()
  1319. r0 = session.send(prep)
  1320. assert r0.request.method == 'POST'
  1321. assert session.calls[-1] == SendCall((r0.request,), {})
  1322. redirect_generator = session.resolve_redirects(r0, prep)
  1323. for response in redirect_generator:
  1324. assert response.request.method == 'GET'
  1325. send_call = SendCall((response.request,),
  1326. TestRedirects.default_keyword_args)
  1327. assert session.calls[-1] == send_call
  1328. @pytest.fixture
  1329. def list_of_tuples():
  1330. return [
  1331. (('a', 'b'), ('c', 'd')),
  1332. (('c', 'd'), ('a', 'b')),
  1333. (('a', 'b'), ('c', 'd'), ('e', 'f')),
  1334. ]
  1335. def test_data_argument_accepts_tuples(list_of_tuples):
  1336. """
  1337. Ensure that the data argument will accept tuples of strings
  1338. and properly encode them.
  1339. """
  1340. for data in list_of_tuples:
  1341. p = PreparedRequest()
  1342. p.prepare(
  1343. method='GET',
  1344. url='http://www.example.com',
  1345. data=data,
  1346. hooks=default_hooks()
  1347. )
  1348. assert p.body == urlencode(data)
  1349. def assert_copy(p, p_copy):
  1350. for attr in ('method', 'url', 'headers', '_cookies', 'body', 'hooks'):
  1351. assert getattr(p, attr) == getattr(p_copy, attr)
  1352. def test_prepared_request_empty_copy():
  1353. p = PreparedRequest()
  1354. assert_copy(p, p.copy())
  1355. def test_prepared_request_no_cookies_copy():
  1356. p = PreparedRequest()
  1357. p.prepare(
  1358. method='GET',
  1359. url='http://www.example.com',
  1360. data='foo=bar',
  1361. hooks=default_hooks()
  1362. )
  1363. assert_copy(p, p.copy())
  1364. def test_prepared_request_complete_copy():
  1365. p = PreparedRequest()
  1366. p.prepare(
  1367. method='GET',
  1368. url='http://www.example.com',
  1369. data='foo=bar',
  1370. hooks=default_hooks(),
  1371. cookies={'foo': 'bar'}
  1372. )
  1373. assert_copy(p, p.copy())
  1374. def test_prepare_unicode_url():
  1375. p = PreparedRequest()
  1376. p.prepare(
  1377. method='GET',
  1378. url=u('http://www.example.com/üniçø∂é'),
  1379. )
  1380. assert_copy(p, p.copy())
  1381. def test_urllib3_retries(httpbin):
  1382. from requests.packages.urllib3.util import Retry
  1383. s = requests.Session()
  1384. s.mount('http://', HTTPAdapter(max_retries=Retry(
  1385. total=2, status_forcelist=[500]
  1386. )))
  1387. with pytest.raises(RetryError):
  1388. s.get(httpbin('status/500'))
  1389. def test_urllib3_pool_connection_closed(httpbin):
  1390. s = requests.Session()
  1391. s.mount('http://', HTTPAdapter(pool_connections=0, pool_maxsize=0))
  1392. try:
  1393. s.get(httpbin('status/200'))
  1394. except ConnectionError as e:
  1395. assert u"Pool is closed." in str(e)
  1396. def test_vendor_aliases():
  1397. from requests.packages import urllib3
  1398. from requests.packages import chardet
  1399. with pytest.raises(ImportError):
  1400. from requests.packages import webbrowser
  1401. if __name__ == '__main__':
  1402. unittest.main()