Minor doc updates
[services/python-requests.git] / test_requests.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """Tests for Requests."""
5
6 from __future__ import division
7 import json
8 import os
9 import unittest
10 import pickle
11
12 import requests
13 import pytest
14 from requests.auth import HTTPDigestAuth
15 from requests.adapters import HTTPAdapter
16 from requests.compat import str, cookielib, getproxies, urljoin, urlparse
17 from requests.cookies import cookiejar_from_dict
18 from requests.exceptions import InvalidURL, MissingSchema
19 from requests.structures import CaseInsensitiveDict
20
21 try:
22     import StringIO
23 except ImportError:
24     import io as StringIO
25
26 HTTPBIN = os.environ.get('HTTPBIN_URL', 'http://httpbin.org/')
27 # Issue #1483: Make sure the URL always has a trailing slash
28 HTTPBIN = HTTPBIN.rstrip('/') + '/'
29
30
31 def httpbin(*suffix):
32     """Returns url for HTTPBIN resource."""
33     return urljoin(HTTPBIN, '/'.join(suffix))
34
35
36 class RequestsTestCase(unittest.TestCase):
37
38     _multiprocess_can_split_ = True
39
40     def setUp(self):
41         """Create simple data set with headers."""
42         pass
43
44     def tearDown(self):
45         """Teardown."""
46         pass
47
48     def test_entry_points(self):
49
50         requests.session
51         requests.session().get
52         requests.session().head
53         requests.get
54         requests.head
55         requests.put
56         requests.patch
57         requests.post
58
59     def test_invalid_url(self):
60         self.assertRaises(MissingSchema, requests.get, 'hiwpefhipowhefopw')
61         self.assertRaises(InvalidURL, requests.get, 'http://')
62
63     def test_basic_building(self):
64         req = requests.Request()
65         req.url = 'http://kennethreitz.org/'
66         req.data = {'life': '42'}
67
68         pr = req.prepare()
69         assert pr.url == req.url
70         assert pr.body == 'life=42'
71
72     def test_no_content_length(self):
73         get_req = requests.Request('GET', httpbin('get')).prepare()
74         self.assertTrue('Content-Length' not in get_req.headers)
75         head_req = requests.Request('HEAD', httpbin('head')).prepare()
76         self.assertTrue('Content-Length' not in head_req.headers)
77
78     def test_path_is_not_double_encoded(self):
79         request = requests.Request('GET', "http://0.0.0.0/get/test case").prepare()
80
81         self.assertEqual(request.path_url, "/get/test%20case")
82
83     def test_params_are_added_before_fragment(self):
84         request = requests.Request('GET',
85             "http://example.com/path#fragment", params={"a": "b"}).prepare()
86         self.assertEqual(request.url,
87             "http://example.com/path?a=b#fragment")
88         request = requests.Request('GET',
89             "http://example.com/path?key=value#fragment", params={"a": "b"}).prepare()
90         self.assertEqual(request.url,
91             "http://example.com/path?key=value&a=b#fragment")
92
93     def test_mixed_case_scheme_acceptable(self):
94         s = requests.Session()
95         s.proxies = getproxies()
96         parts = urlparse(httpbin('get'))
97         schemes = ['http://', 'HTTP://', 'hTTp://', 'HttP://',
98                    'https://', 'HTTPS://', 'hTTps://', 'HttPs://']
99         for scheme in schemes:
100             url = scheme + parts.netloc + parts.path
101             r = requests.Request('GET', url)
102             r = s.send(r.prepare())
103             self.assertEqual(r.status_code, 200,
104                              "failed for scheme %s" % scheme)
105
106     def test_HTTP_200_OK_GET_ALTERNATIVE(self):
107         r = requests.Request('GET', httpbin('get'))
108         s = requests.Session()
109         s.proxies = getproxies()
110
111         r = s.send(r.prepare())
112
113         self.assertEqual(r.status_code, 200)
114
115     def test_HTTP_302_ALLOW_REDIRECT_GET(self):
116         r = requests.get(httpbin('redirect', '1'))
117         self.assertEqual(r.status_code, 200)
118
119     # def test_HTTP_302_ALLOW_REDIRECT_POST(self):
120     #     r = requests.post(httpbin('status', '302'), data={'some': 'data'})
121     #     self.assertEqual(r.status_code, 200)
122
123     def test_HTTP_200_OK_GET_WITH_PARAMS(self):
124         heads = {'User-agent': 'Mozilla/5.0'}
125
126         r = requests.get(httpbin('user-agent'), headers=heads)
127
128         self.assertTrue(heads['User-agent'] in r.text)
129         self.assertEqual(r.status_code, 200)
130
131     def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
132         heads = {'User-agent': 'Mozilla/5.0'}
133
134         r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
135         self.assertEqual(r.status_code, 200)
136
137     def test_set_cookie_on_301(self):
138         s = requests.session()
139         url = httpbin('cookies/set?foo=bar')
140         r = s.get(url)
141         self.assertTrue(s.cookies['foo'] == 'bar')
142
143     def test_cookie_sent_on_redirect(self):
144         s = requests.session()
145         s.get(httpbin('cookies/set?foo=bar'))
146         r = s.get(httpbin('redirect/1'))  # redirects to httpbin('get')
147         self.assertTrue("Cookie" in r.json()["headers"])
148
149     def test_cookie_removed_on_expire(self):
150         s = requests.session()
151         s.get(httpbin('cookies/set?foo=bar'))
152         self.assertTrue(s.cookies['foo'] == 'bar')
153         s.get(
154             httpbin('response-headers'),
155             params={
156                 'Set-Cookie':
157                     'foo=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT'
158             }
159         )
160         assert 'foo' not in s.cookies
161
162     def test_cookie_quote_wrapped(self):
163         s = requests.session()
164         s.get(httpbin('cookies/set?foo="bar:baz"'))
165         self.assertTrue(s.cookies['foo'] == '"bar:baz"')
166
167     def test_request_cookie_overrides_session_cookie(self):
168         s = requests.session()
169         s.cookies['foo'] = 'bar'
170         r = s.get(httpbin('cookies'), cookies={'foo': 'baz'})
171         assert r.json()['cookies']['foo'] == 'baz'
172         # Session cookie should not be modified
173         assert s.cookies['foo'] == 'bar'
174
175     def test_generic_cookiejar_works(self):
176         cj = cookielib.CookieJar()
177         cookiejar_from_dict({'foo': 'bar'}, cj)
178         s = requests.session()
179         s.cookies = cj
180         r = s.get(httpbin('cookies'))
181         # Make sure the cookie was sent
182         assert r.json()['cookies']['foo'] == 'bar'
183         # Make sure the session cj is still the custom one
184         assert s.cookies is cj
185
186     def test_requests_in_history_are_not_overridden(self):
187         resp = requests.get(httpbin('redirect/3'))
188         urls = [r.url for r in resp.history]
189         req_urls = [r.request.url for r in resp.history]
190         self.assertEquals(urls, req_urls)
191
192     def test_user_agent_transfers(self):
193
194         heads = {
195             'User-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
196         }
197
198         r = requests.get(httpbin('user-agent'), headers=heads)
199         self.assertTrue(heads['User-agent'] in r.text)
200
201         heads = {
202             'user-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
203         }
204
205         r = requests.get(httpbin('user-agent'), headers=heads)
206         self.assertTrue(heads['user-agent'] in r.text)
207
208     def test_HTTP_200_OK_HEAD(self):
209         r = requests.head(httpbin('get'))
210         self.assertEqual(r.status_code, 200)
211
212     def test_HTTP_200_OK_PUT(self):
213         r = requests.put(httpbin('put'))
214         self.assertEqual(r.status_code, 200)
215
216     def test_BASICAUTH_TUPLE_HTTP_200_OK_GET(self):
217         auth = ('user', 'pass')
218         url = httpbin('basic-auth', 'user', 'pass')
219
220         r = requests.get(url, auth=auth)
221         self.assertEqual(r.status_code, 200)
222
223         r = requests.get(url)
224         self.assertEqual(r.status_code, 401)
225
226         s = requests.session()
227         s.auth = auth
228         r = s.get(url)
229         self.assertEqual(r.status_code, 200)
230
231     def test_basicauth_with_netrc(self):
232         auth = ('user', 'pass')
233         wrong_auth = ('wronguser', 'wrongpass')
234         url = httpbin('basic-auth', 'user', 'pass')
235
236         def get_netrc_auth_mock(url):
237             return auth
238         requests.sessions.get_netrc_auth = get_netrc_auth_mock
239
240         # Should use netrc and work.
241         r = requests.get(url)
242         self.assertEqual(r.status_code, 200)
243
244         # Given auth should override and fail.
245         r = requests.get(url, auth=wrong_auth)
246         self.assertEqual(r.status_code, 401)
247
248         s = requests.session()
249
250         # Should use netrc and work.
251         r = s.get(url)
252         self.assertEqual(r.status_code, 200)
253
254         # Given auth should override and fail.
255         s.auth = wrong_auth
256         r = s.get(url)
257         self.assertEqual(r.status_code, 401)
258
259     def test_DIGEST_HTTP_200_OK_GET(self):
260
261         auth = HTTPDigestAuth('user', 'pass')
262         url = httpbin('digest-auth', 'auth', 'user', 'pass')
263
264         r = requests.get(url, auth=auth)
265         self.assertEqual(r.status_code, 200)
266
267         r = requests.get(url)
268         self.assertEqual(r.status_code, 401)
269
270         s = requests.session()
271         s.auth = HTTPDigestAuth('user', 'pass')
272         r = s.get(url)
273         self.assertEqual(r.status_code, 200)
274
275     def test_DIGEST_AUTH_RETURNS_COOKIE(self):
276         url = httpbin('digest-auth', 'auth', 'user', 'pass')
277         auth = HTTPDigestAuth('user', 'pass')
278         r = requests.get(url)
279         assert r.cookies['fake'] == 'fake_value'
280
281         r = requests.get(url, auth=auth)
282         assert r.status_code == 200
283
284     def test_DIGEST_AUTH_SETS_SESSION_COOKIES(self):
285         url = httpbin('digest-auth', 'auth', 'user', 'pass')
286         auth = HTTPDigestAuth('user', 'pass')
287         s = requests.Session()
288         s.get(url, auth=auth)
289         assert s.cookies['fake'] == 'fake_value'
290
291     def test_DIGEST_STREAM(self):
292
293         auth = HTTPDigestAuth('user', 'pass')
294         url = httpbin('digest-auth', 'auth', 'user', 'pass')
295
296         r = requests.get(url, auth=auth, stream=True)
297         self.assertNotEqual(r.raw.read(), b'')
298
299         r = requests.get(url, auth=auth, stream=False)
300         self.assertEqual(r.raw.read(), b'')
301
302
303     def test_DIGESTAUTH_WRONG_HTTP_401_GET(self):
304
305         auth = HTTPDigestAuth('user', 'wrongpass')
306         url = httpbin('digest-auth', 'auth', 'user', 'pass')
307
308         r = requests.get(url, auth=auth)
309         self.assertEqual(r.status_code, 401)
310
311         r = requests.get(url)
312         self.assertEqual(r.status_code, 401)
313
314         s = requests.session()
315         s.auth = auth
316         r = s.get(url)
317         self.assertEqual(r.status_code, 401)
318
319     def test_POSTBIN_GET_POST_FILES(self):
320
321         url = httpbin('post')
322         post1 = requests.post(url).raise_for_status()
323
324         post1 = requests.post(url, data={'some': 'data'})
325         self.assertEqual(post1.status_code, 200)
326
327         with open('requirements.txt') as f:
328             post2 = requests.post(url, files={'some': f})
329         self.assertEqual(post2.status_code, 200)
330
331         post4 = requests.post(url, data='[{"some": "json"}]')
332         self.assertEqual(post4.status_code, 200)
333
334         try:
335             requests.post(url, files=['bad file data'])
336         except ValueError:
337             pass
338
339     def test_POSTBIN_GET_POST_FILES_WITH_DATA(self):
340
341         url = httpbin('post')
342         post1 = requests.post(url).raise_for_status()
343
344         post1 = requests.post(url, data={'some': 'data'})
345         self.assertEqual(post1.status_code, 200)
346
347         with open('requirements.txt') as f:
348             post2 = requests.post(url, data={'some': 'data'}, files={'some': f})
349         self.assertEqual(post2.status_code, 200)
350
351         post4 = requests.post(url, data='[{"some": "json"}]')
352         self.assertEqual(post4.status_code, 200)
353
354         try:
355             requests.post(url, files=['bad file data'])
356         except ValueError:
357             pass
358
359     def test_conflicting_post_params(self):
360         url = httpbin('post')
361         with open('requirements.txt') as f:
362             pytest.raises(ValueError, "requests.post(url, data='[{\"some\": \"data\"}]', files={'some': f})")
363             pytest.raises(ValueError, "requests.post(url, data=u'[{\"some\": \"data\"}]', files={'some': f})")
364
365     def test_request_ok_set(self):
366         r = requests.get(httpbin('status', '404'))
367         self.assertEqual(r.ok, False)
368
369     def test_status_raising(self):
370         r = requests.get(httpbin('status', '404'))
371         self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
372
373         r = requests.get(httpbin('status', '500'))
374         self.assertFalse(r.ok)
375
376     def test_decompress_gzip(self):
377         r = requests.get(httpbin('gzip'))
378         r.content.decode('ascii')
379
380     def test_unicode_get(self):
381         url = httpbin('/get')
382         requests.get(url, params={'foo': 'føø'})
383         requests.get(url, params={'føø': 'føø'})
384         requests.get(url, params={'føø': 'føø'})
385         requests.get(url, params={'foo': 'foo'})
386         requests.get(httpbin('ø'), params={'foo': 'foo'})
387
388     def test_unicode_header_name(self):
389         requests.put(httpbin('put'), headers={str('Content-Type'): 'application/octet-stream'}, data='\xff') # compat.str is unicode.
390
391     def test_urlencoded_get_query_multivalued_param(self):
392
393         r = requests.get(httpbin('get'), params=dict(test=['foo', 'baz']))
394         self.assertEqual(r.status_code, 200)
395         self.assertEqual(r.url, httpbin('get?test=foo&test=baz'))
396
397     def test_different_encodings_dont_break_post(self):
398         r = requests.post(httpbin('post'),
399                           data={'stuff': json.dumps({'a': 123})},
400                           params={'blah': 'asdf1234'},
401                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
402         self.assertEqual(r.status_code, 200)
403
404     def test_unicode_multipart_post(self):
405         r = requests.post(httpbin('post'),
406                           data={'stuff': u'ëlïxr'},
407                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
408         self.assertEqual(r.status_code, 200)
409
410         r = requests.post(httpbin('post'),
411                           data={'stuff': u'ëlïxr'.encode('utf-8')},
412                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
413         self.assertEqual(r.status_code, 200)
414
415         r = requests.post(httpbin('post'),
416                           data={'stuff': 'elixr'},
417                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
418         self.assertEqual(r.status_code, 200)
419
420         r = requests.post(httpbin('post'),
421                           data={'stuff': 'elixr'.encode('utf-8')},
422                           files={'file': ('test_requests.py', open(__file__, 'rb'))})
423         self.assertEqual(r.status_code, 200)
424
425     def test_unicode_multipart_post_fieldnames(self):
426         filename = os.path.splitext(__file__)[0] + '.py'
427         r = requests.Request(method='POST',
428                              url=httpbin('post'),
429                              data={'stuff'.encode('utf-8'): 'elixr'},
430                              files={'file': ('test_requests.py',
431                                              open(filename, 'rb'))})
432         prep = r.prepare()
433         self.assertTrue(b'name="stuff"' in prep.body)
434         self.assertFalse(b'name="b\'stuff\'"' in prep.body)
435
436     def test_custom_content_type(self):
437         r = requests.post(httpbin('post'),
438                           data={'stuff': json.dumps({'a': 123})},
439                           files={'file1': ('test_requests.py', open(__file__, 'rb')),
440                                  'file2': ('test_requests', open(__file__, 'rb'),
441                                            'text/py-content-type')})
442         self.assertEqual(r.status_code, 200)
443         self.assertTrue(b"text/py-content-type" in r.request.body)
444
445     def test_hook_receives_request_arguments(self):
446         def hook(resp, **kwargs):
447             assert resp is not None
448             assert kwargs != {}
449
450         requests.Request('GET', HTTPBIN, hooks={'response': hook})
451
452     def test_prepared_request_hook(self):
453         def hook(resp, **kwargs):
454             resp.hook_working = True
455             return resp
456
457         req = requests.Request('GET', HTTPBIN, hooks={'response': hook})
458         prep = req.prepare()
459
460         s = requests.Session()
461         s.proxies = getproxies()
462         resp = s.send(prep)
463
464         self.assertTrue(hasattr(resp, 'hook_working'))
465
466     def test_prepared_from_session(self):
467         class DummyAuth(requests.auth.AuthBase):
468             def __call__(self, r):
469                 r.headers['Dummy-Auth-Test'] = 'dummy-auth-test-ok'
470                 return r
471
472         req = requests.Request('GET', httpbin('headers'))
473         self.assertEqual(req.auth, None)
474
475         s = requests.Session()
476         s.auth = DummyAuth()
477
478         prep = s.prepare_request(req)
479         resp = s.send(prep)
480
481         self.assertTrue(resp.json()['headers']['Dummy-Auth-Test'], 'dummy-auth-test-ok')
482
483     def test_links(self):
484         r = requests.Response()
485         r.headers = {
486             'cache-control': 'public, max-age=60, s-maxage=60',
487             'connection': 'keep-alive',
488             'content-encoding': 'gzip',
489             'content-type': 'application/json; charset=utf-8',
490             'date': 'Sat, 26 Jan 2013 16:47:56 GMT',
491             'etag': '"6ff6a73c0e446c1f61614769e3ceb778"',
492             'last-modified': 'Sat, 26 Jan 2013 16:22:39 GMT',
493             'link': ('<https://api.github.com/users/kennethreitz/repos?'
494                      'page=2&per_page=10>; rel="next", <https://api.github.'
495                      'com/users/kennethreitz/repos?page=7&per_page=10>; '
496                      ' rel="last"'),
497             'server': 'GitHub.com',
498             'status': '200 OK',
499             'vary': 'Accept',
500             'x-content-type-options': 'nosniff',
501             'x-github-media-type': 'github.beta',
502             'x-ratelimit-limit': '60',
503             'x-ratelimit-remaining': '57'
504         }
505         self.assertEqual(r.links['next']['rel'], 'next')
506
507     def test_cookie_parameters(self):
508         key = 'some_cookie'
509         value = 'some_value'
510         secure = True
511         domain = 'test.com'
512         rest = {'HttpOnly': True}
513
514         jar = requests.cookies.RequestsCookieJar()
515         jar.set(key, value, secure=secure, domain=domain, rest=rest)
516
517         self.assertEqual(len(jar), 1)
518         self.assertTrue('some_cookie' in jar)
519
520         cookie = list(jar)[0]
521         self.assertEqual(cookie.secure, secure)
522         self.assertEqual(cookie.domain, domain)
523         self.assertEqual(cookie._rest['HttpOnly'], rest['HttpOnly'])
524
525     def test_time_elapsed_blank(self):
526         r = requests.get(httpbin('get'))
527         td = r.elapsed
528         total_seconds = ((td.microseconds + (td.seconds + td.days * 24 * 3600)
529                          * 10**6) / 10**6)
530         self.assertTrue(total_seconds > 0.0)
531
532     def test_response_is_iterable(self):
533         r = requests.Response()
534         io = StringIO.StringIO('abc')
535         read_ = io.read
536
537         def read_mock(amt, decode_content=None):
538             return read_(amt)
539         setattr(io, 'read', read_mock)
540         r.raw = io
541         self.assertTrue(next(iter(r)))
542         io.close()
543
544     def test_get_auth_from_url(self):
545         url = 'http://user:pass@complex.url.com/path?query=yes'
546         self.assertEqual(('user', 'pass'),
547                          requests.utils.get_auth_from_url(url))
548
549     def test_cannot_send_unprepared_requests(self):
550         r = requests.Request(url=HTTPBIN)
551         self.assertRaises(ValueError, requests.Session().send, r)
552
553     def test_http_error(self):
554         error = requests.exceptions.HTTPError()
555         self.assertEqual(error.response, None)
556         response = requests.Response()
557         error = requests.exceptions.HTTPError(response=response)
558         self.assertEqual(error.response, response)
559         error = requests.exceptions.HTTPError('message', response=response)
560         self.assertEqual(str(error), 'message')
561         self.assertEqual(error.response, response)
562
563     def test_session_pickling(self):
564         r = requests.Request('GET', httpbin('get'))
565         s = requests.Session()
566
567         s = pickle.loads(pickle.dumps(s))
568         s.proxies = getproxies()
569
570         r = s.send(r.prepare())
571         self.assertEqual(r.status_code, 200)
572
573     def test_fixes_1329(self):
574         """
575         Ensure that header updates are done case-insensitively.
576         """
577         s = requests.Session()
578         s.headers.update({'ACCEPT': 'BOGUS'})
579         s.headers.update({'accept': 'application/json'})
580         r = s.get(httpbin('get'))
581         headers = r.request.headers
582         self.assertEqual(
583             headers['accept'],
584             'application/json'
585         )
586         self.assertEqual(
587             headers['Accept'],
588             'application/json'
589         )
590         self.assertEqual(
591             headers['ACCEPT'],
592             'application/json'
593         )
594
595     def test_uppercase_scheme_redirect(self):
596         parts = urlparse(httpbin('html'))
597         url = "HTTP://" + parts.netloc + parts.path
598         r = requests.get(httpbin('redirect-to'), params={'url': url})
599         self.assertEqual(r.status_code, 200)
600         self.assertEqual(r.url.lower(), url.lower())
601
602     def test_transport_adapter_ordering(self):
603         s = requests.Session()
604         order = ['https://', 'http://']
605         self.assertEqual(order, list(s.adapters))
606         s.mount('http://git', HTTPAdapter())
607         s.mount('http://github', HTTPAdapter())
608         s.mount('http://github.com', HTTPAdapter())
609         s.mount('http://github.com/about/', HTTPAdapter())
610         order = [
611             'http://github.com/about/',
612             'http://github.com',
613             'http://github',
614             'http://git',
615             'https://',
616             'http://',
617         ]
618         self.assertEqual(order, list(s.adapters))
619         s.mount('http://gittip', HTTPAdapter())
620         s.mount('http://gittip.com', HTTPAdapter())
621         s.mount('http://gittip.com/about/', HTTPAdapter())
622         order = [
623             'http://github.com/about/',
624             'http://gittip.com/about/',
625             'http://github.com',
626             'http://gittip.com',
627             'http://github',
628             'http://gittip',
629             'http://git',
630             'https://',
631             'http://',
632         ]
633         self.assertEqual(order, list(s.adapters))
634         s2 = requests.Session()
635         s2.adapters = {'http://': HTTPAdapter()}
636         s2.mount('https://', HTTPAdapter())
637         self.assertTrue('http://' in s2.adapters)
638         self.assertTrue('https://' in s2.adapters)
639
640     def test_header_remove_is_case_insensitive(self):
641         # From issue #1321
642         s = requests.Session()
643         s.headers['foo'] = 'bar'
644         r = s.get(httpbin('get'), headers={'FOO': None})
645         assert 'foo' not in r.request.headers
646
647     def test_params_are_merged_case_sensitive(self):
648         s = requests.Session()
649         s.params['foo'] = 'bar'
650         r = s.get(httpbin('get'), params={'FOO': 'bar'})
651         assert r.json()['args'] == {'foo': 'bar', 'FOO': 'bar'}
652
653
654     def test_long_authinfo_in_url(self):
655         url = 'http://{0}:{1}@{2}:9000/path?query#frag'.format(
656             'E8A3BE87-9E3F-4620-8858-95478E385B5B',
657             'EA770032-DA4D-4D84-8CE9-29C6D910BF1E',
658             'exactly-------------sixty-----------three------------characters',
659         )
660         r = requests.Request('GET', url).prepare()
661         self.assertEqual(r.url, url)
662
663     def test_header_keys_are_native(self):
664         headers = {u'unicode': 'blah', 'byte'.encode('ascii'): 'blah'}
665         r = requests.Request('GET', httpbin('get'), headers=headers)
666         p = r.prepare()
667
668         # This is testing that they are builtin strings. A bit weird, but there
669         # we go.
670         self.assertTrue('unicode' in p.headers.keys())
671         self.assertTrue('byte' in p.headers.keys())
672
673     def test_can_send_nonstring_objects_with_files(self):
674         data = {'a': 0.0}
675         files = {'b': 'foo'}
676         r = requests.Request('POST', httpbin('post'), data=data, files=files)
677         p = r.prepare()
678
679         self.assertTrue('multipart/form-data' in p.headers['Content-Type'])
680
681
682 class TestContentEncodingDetection(unittest.TestCase):
683
684     def test_none(self):
685         encodings = requests.utils.get_encodings_from_content('')
686         self.assertEqual(len(encodings), 0)
687
688     def test_html_charset(self):
689         """HTML5 meta charset attribute"""
690         content = '<meta charset="UTF-8">'
691         encodings = requests.utils.get_encodings_from_content(content)
692         self.assertEqual(len(encodings), 1)
693         self.assertEqual(encodings[0], 'UTF-8')
694
695     def test_html4_pragma(self):
696         """HTML4 pragma directive"""
697         content = '<meta http-equiv="Content-type" content="text/html;charset=UTF-8">'
698         encodings = requests.utils.get_encodings_from_content(content)
699         self.assertEqual(len(encodings), 1)
700         self.assertEqual(encodings[0], 'UTF-8')
701
702     def test_xhtml_pragma(self):
703         """XHTML 1.x served with text/html MIME type"""
704         content = '<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />'
705         encodings = requests.utils.get_encodings_from_content(content)
706         self.assertEqual(len(encodings), 1)
707         self.assertEqual(encodings[0], 'UTF-8')
708
709     def test_xml(self):
710         """XHTML 1.x served as XML"""
711         content = '<?xml version="1.0" encoding="UTF-8"?>'
712         encodings = requests.utils.get_encodings_from_content(content)
713         self.assertEqual(len(encodings), 1)
714         self.assertEqual(encodings[0], 'UTF-8')
715
716     def test_precedence(self):
717         content = '''
718         <?xml version="1.0" encoding="XML"?>
719         <meta charset="HTML5">
720         <meta http-equiv="Content-type" content="text/html;charset=HTML4" />
721         '''.strip()
722         encodings = requests.utils.get_encodings_from_content(content)
723         self.assertEqual(encodings, ['HTML5', 'HTML4', 'XML'])
724
725
726 class TestCaseInsensitiveDict(unittest.TestCase):
727
728     def test_mapping_init(self):
729         cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
730         self.assertEqual(len(cid), 2)
731         self.assertTrue('foo' in cid)
732         self.assertTrue('bar' in cid)
733
734     def test_iterable_init(self):
735         cid = CaseInsensitiveDict([('Foo', 'foo'), ('BAr', 'bar')])
736         self.assertEqual(len(cid), 2)
737         self.assertTrue('foo' in cid)
738         self.assertTrue('bar' in cid)
739
740     def test_kwargs_init(self):
741         cid = CaseInsensitiveDict(FOO='foo', BAr='bar')
742         self.assertEqual(len(cid), 2)
743         self.assertTrue('foo' in cid)
744         self.assertTrue('bar' in cid)
745
746     def test_docstring_example(self):
747         cid = CaseInsensitiveDict()
748         cid['Accept'] = 'application/json'
749         self.assertEqual(cid['aCCEPT'], 'application/json')
750         self.assertEqual(list(cid), ['Accept'])
751
752     def test_len(self):
753         cid = CaseInsensitiveDict({'a': 'a', 'b': 'b'})
754         cid['A'] = 'a'
755         self.assertEqual(len(cid), 2)
756
757     def test_getitem(self):
758         cid = CaseInsensitiveDict({'Spam': 'blueval'})
759         self.assertEqual(cid['spam'], 'blueval')
760         self.assertEqual(cid['SPAM'], 'blueval')
761
762     def test_fixes_649(self):
763         """__setitem__ should behave case-insensitively."""
764         cid = CaseInsensitiveDict()
765         cid['spam'] = 'oneval'
766         cid['Spam'] = 'twoval'
767         cid['sPAM'] = 'redval'
768         cid['SPAM'] = 'blueval'
769         self.assertEqual(cid['spam'], 'blueval')
770         self.assertEqual(cid['SPAM'], 'blueval')
771         self.assertEqual(list(cid.keys()), ['SPAM'])
772
773     def test_delitem(self):
774         cid = CaseInsensitiveDict()
775         cid['Spam'] = 'someval'
776         del cid['sPam']
777         self.assertFalse('spam' in cid)
778         self.assertEqual(len(cid), 0)
779
780     def test_contains(self):
781         cid = CaseInsensitiveDict()
782         cid['Spam'] = 'someval'
783         self.assertTrue('Spam' in cid)
784         self.assertTrue('spam' in cid)
785         self.assertTrue('SPAM' in cid)
786         self.assertTrue('sPam' in cid)
787         self.assertFalse('notspam' in cid)
788
789     def test_get(self):
790         cid = CaseInsensitiveDict()
791         cid['spam'] = 'oneval'
792         cid['SPAM'] = 'blueval'
793         self.assertEqual(cid.get('spam'), 'blueval')
794         self.assertEqual(cid.get('SPAM'), 'blueval')
795         self.assertEqual(cid.get('sPam'), 'blueval')
796         self.assertEqual(cid.get('notspam', 'default'), 'default')
797
798     def test_update(self):
799         cid = CaseInsensitiveDict()
800         cid['spam'] = 'blueval'
801         cid.update({'sPam': 'notblueval'})
802         self.assertEqual(cid['spam'], 'notblueval')
803         cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
804         cid.update({'fOO': 'anotherfoo', 'bAR': 'anotherbar'})
805         self.assertEqual(len(cid), 2)
806         self.assertEqual(cid['foo'], 'anotherfoo')
807         self.assertEqual(cid['bar'], 'anotherbar')
808
809     def test_update_retains_unchanged(self):
810         cid = CaseInsensitiveDict({'foo': 'foo', 'bar': 'bar'})
811         cid.update({'foo': 'newfoo'})
812         self.assertEquals(cid['bar'], 'bar')
813
814     def test_iter(self):
815         cid = CaseInsensitiveDict({'Spam': 'spam', 'Eggs': 'eggs'})
816         keys = frozenset(['Spam', 'Eggs'])
817         self.assertEqual(frozenset(iter(cid)), keys)
818
819     def test_equality(self):
820         cid = CaseInsensitiveDict({'SPAM': 'blueval', 'Eggs': 'redval'})
821         othercid = CaseInsensitiveDict({'spam': 'blueval', 'eggs': 'redval'})
822         self.assertEqual(cid, othercid)
823         del othercid['spam']
824         self.assertNotEqual(cid, othercid)
825         self.assertEqual(cid, {'spam': 'blueval', 'eggs': 'redval'})
826
827     def test_setdefault(self):
828         cid = CaseInsensitiveDict({'Spam': 'blueval'})
829         self.assertEqual(
830             cid.setdefault('spam', 'notblueval'),
831             'blueval'
832         )
833         self.assertEqual(
834             cid.setdefault('notspam', 'notblueval'),
835             'notblueval'
836         )
837
838     def test_lower_items(self):
839         cid = CaseInsensitiveDict({
840             'Accept': 'application/json',
841             'user-Agent': 'requests',
842         })
843         keyset = frozenset(lowerkey for lowerkey, v in cid.lower_items())
844         lowerkeyset = frozenset(['accept', 'user-agent'])
845         self.assertEqual(keyset, lowerkeyset)
846
847     def test_preserve_key_case(self):
848         cid = CaseInsensitiveDict({
849             'Accept': 'application/json',
850             'user-Agent': 'requests',
851         })
852         keyset = frozenset(['Accept', 'user-Agent'])
853         self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
854         self.assertEqual(frozenset(cid.keys()), keyset)
855         self.assertEqual(frozenset(cid), keyset)
856
857     def test_preserve_last_key_case(self):
858         cid = CaseInsensitiveDict({
859             'Accept': 'application/json',
860             'user-Agent': 'requests',
861         })
862         cid.update({'ACCEPT': 'application/json'})
863         cid['USER-AGENT'] = 'requests'
864         keyset = frozenset(['ACCEPT', 'USER-AGENT'])
865         self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
866         self.assertEqual(frozenset(cid.keys()), keyset)
867         self.assertEqual(frozenset(cid), keyset)
868
869
870 if __name__ == '__main__':
871     unittest.main()