Compare commits

..

127 Commits

Author SHA1 Message Date
Kenneth Reitz
3235766d67 updated CA Bundle! 2013-12-05 17:51:10 -05:00
Kenneth Reitz
57c4684035 Merge remote-tracking branch 'origin/master' 2013-12-05 17:45:05 -05:00
Kenneth Reitz
795f18560b last changelog entry 2013-12-05 17:44:56 -05:00
Kenneth Reitz
aae7e39a56 further notes and version bump 2013-12-05 17:41:25 -05:00
Kenneth Reitz
ba3dab5047 Merge pull request #1770 from kmadac/master
Implemetation of IP address ranges for no_proxy environment variable
2013-12-05 14:40:44 -08:00
Kenneth Reitz
f6107bc151 history notes about v2.1 2013-12-05 17:37:48 -05:00
Kenneth Reitz
1cac72bfa9 Merge pull request #1669 from Lukasa/chunked
[WIP] Better chunked handling.
2013-12-05 14:36:10 -08:00
Kenneth Reitz
2dae61c0e3 Merge pull request #1776 from sigmavirus24/fix-1728
Fix 1728 (Fixed up from #1729)
2013-12-05 14:26:58 -08:00
Kenneth Reitz
e851efb29b Merge pull request #1781 from Lukasa/ucliteral
Clean up #1779.
2013-12-05 14:00:58 -08:00
Cory Benfield
57c138e59b Remove the unicode literals. 2013-12-05 21:06:11 +00:00
Kenneth Reitz
2c465de332 Merge pull request #1779 from kracekumar/patch-2
Made default_user_agent reusable
2013-12-05 11:44:06 -08:00
Kamil Madac
1d42d9d274 function is_ipv4_network renamed
more detailed check of cidr format
2013-12-05 11:15:52 +01:00
Kamil Madac
f3bc8b6e1f Typo fixed 2013-12-05 09:36:49 +01:00
Kamil Madac
21c88c829a Broad exception fixed 2013-12-05 09:32:29 +01:00
kracekumar
6c1be607f1 Made default_user_agent reusable
This is not exciting change, but will help for people who wants to change name of user agent alone. Also name can be unicode.
2013-12-05 12:11:35 +05:30
Kamil Madac
59b29d02f8 Tiny comment fix 2013-12-04 15:11:10 +01:00
Kamil Madac
0d52ef7084 Additional tests added 2013-12-04 15:10:24 +01:00
Ian Cordasco
e8bdcdb0a7 Address feedback from #1729
- Make the PreparedRequest's cookie jar an implementation detail
2013-12-04 06:45:18 -06:00
Chase Sterling
8bfe0d873f Store the request cookiejar in PreparedRequest.cookies fix #1728
Conflicts:
	requests/sessions.py
2013-12-04 06:38:58 -06:00
Chase Sterling
5b30e960eb Add a test case for request cookies persisting to session. refs #1728 2013-12-04 06:35:16 -06:00
Kamil Madac
947248129b Better comments 2013-12-04 11:47:40 +01:00
Kamil Madac
0a4e65c0ed Tests added 2013-12-04 10:54:36 +01:00
Kamil Madac
fe945345dd Unnecessary files kmadac.xml deleted 2013-12-04 09:31:56 +01:00
Kamil Madac
dfd3e45ad0 Merge branch 'master' of https://github.com/kmadac/requests 2013-12-04 09:30:01 +01:00
Kenneth Reitz
4bceb312f1 Merge pull request #1768 from Lukasa/credpct
Unencode usernames and passwords taken from URLs.
2013-12-03 17:36:00 -08:00
Kenneth Reitz
b2cee7bee8 Merge pull request #1764 from Lukasa/clog
Prepare new release (2.1.0)?
2013-12-03 17:35:02 -08:00
Kenneth Reitz
a030a58303 Merge pull request #1766 from Lukasa/qop
Quote qop values in digest auth.
2013-12-03 17:33:59 -08:00
Kenneth Reitz
5c915451e7 Merge pull request #1657 from sigmavirus24/fix-session-hooks
Honor session hooks
2013-12-03 17:33:42 -08:00
Kenneth Reitz
8b88549bb7 Merge pull request #1713 from daftshady/master
Fixed #1711
2013-12-03 17:33:33 -08:00
Kenneth Reitz
3e66f08db7 Merge pull request #1775 from sigmavirus24/add-readme-to-packages
Add README to requests/packages
2013-12-03 17:02:25 -08:00
Kamil Madac
6131390345 Revert "Small test added"
This reverts commit f0d3edf409.
2013-12-03 16:41:07 +01:00
Kamil Madac
0eccd7a856 Merge remote-tracking branch 'origin/master'
Conflicts:
	test_requests.py
2013-12-03 16:34:02 +01:00
Kamil Madac
f0d3edf409 Small test added 2013-12-03 16:30:47 +01:00
Kamil Madac
8aff6f5ed0 Redesigned no_proxy ip range imlementation to use only stdlib functions 2013-12-03 16:23:08 +01:00
Ian Cordasco
617bc8cc1c Add README to requests/packages
- Hopefully people will read this before making changes to vendored libraries
2013-12-03 09:17:37 -06:00
Kamil Madac
f9a48e0f0b Added kamil.madac to Authors 2013-12-02 22:42:20 +01:00
Kamil Madac
9ec222c363 Added test for domain name no_proxy matching. 2013-12-02 22:40:26 +01:00
Kamil Madac
2c914126ba Added possibility to use IP ranges (ex. 192.168.1.0/24) to no_proxy environment variable 2013-12-02 22:32:29 +01:00
Cory Benfield
e2961b9f91 Unquote the URL before extracting the auth. 2013-12-01 10:29:14 +00:00
Cory Benfield
4f786e4f62 Tests for #1767. 2013-12-01 10:29:10 +00:00
Cory Benfield
92d3616b02 Merge remote-tracking branch 'upstream/pr/1765' into qop 2013-11-29 16:44:13 +00:00
Cory Benfield
fecb35a08e Quote qop values in digest auth. 2013-11-29 08:37:25 +00:00
佐藤 建太
649dac1029 quote qop options in Digest Auth
Based on RFC2617 (http://tools.ietf.org/html/rfc2617), the value of
'qop-options' directive should be quoted with double quotes:

    qop-options
         This directive is optional, but is made so only for backward
         compatibility with RFC 2069 [6]; it SHOULD be used by all
         implementations compliant with this version of the Digest
         scheme. If present, it is a quoted string of one or more
         tokens indicating the "quality of protection" values supported by
         the server.  The value "auth" indicates authentication; the
         value "auth-int" indicates authentication with
         integrity protection; see the

curl comamnd-line tool also appends these quotes. You can see this
by `curl -v --digest --user user:passwd http://example.com/digest-auth`.
Unfortunately, some minor server-side implementations seem to be sensitive
on this difference.
2013-11-29 15:28:47 +09:00
Cory Benfield
dc31c11339 Missed a couple. 2013-11-28 18:37:45 +00:00
Cory Benfield
e45d425343 Prepare changelog. 2013-11-28 18:32:28 +00:00
Kenneth Reitz
847735553a Merge pull request #1762 from dstufft/update-urllib3
Update urllib3 to 929f1586
2013-11-27 09:23:40 -08:00
Donald Stufft
b825bbd4a2 Update urllib3 to 929f1586 2013-11-26 21:41:15 -05:00
daftshady
c26a6c310c remove unused import 2013-11-26 15:38:48 +09:00
daftshady
8eb40c777e remove unnecessary try 2013-11-26 15:28:59 +09:00
daftshady
8b2255d0f8 double try statement 2013-11-26 15:28:59 +09:00
daftshady
5fcaf45c33 update 'merge_cookies' not to create new instance. 2013-11-26 15:28:59 +09:00
daftshady
1adba3c9e9 modified 'merge_cookies' and update tests.
made sessions.py to use that method when merging cookies.
2013-11-26 15:28:59 +09:00
daftshady
30018e604c update 'merge_cookies' 2013-11-26 15:28:59 +09:00
daftshady
e40c1637c9 added new method 'merge_cookies' and use it when merging session cookies with param cookies. 2013-11-26 15:28:59 +09:00
daftshady
e3c771f36b added test case related to #1711 2013-11-26 15:22:46 +09:00
daftshady
2bbd7ba1c2 Fixed #1711 2013-11-26 15:22:46 +09:00
Kenneth Reitz
340b245903 Merge pull request #1760 from akitada/fix-hangs-on-streaming-uploads
Fix hangs on streaming uploads with HTTPDigestAuth
2013-11-25 11:40:10 -08:00
Kenneth Reitz
e5b9ae1f8e Merge pull request #1754 from Lukasa/verb_change
Match the HTTPbis on HTTP 301.
2013-11-25 11:40:00 -08:00
Akira Kitada
3a9d9f2279 Fix hangs on streaming uploads with HTTPDigestAuth
When using Digest Authentication, the client resends the same request
after the server responds with the 401 "Unauthorized". However, when
doing streaming uploads, it gets stuck because the body data (a
file-like object) is already consumed at the initial request.

The patch fixes this by rewinding the file-like object before
resending the request.
2013-11-25 22:29:53 +09:00
Cory Benfield
3369d87da9 Cleanup the redirect if blocks. 2013-11-24 11:13:49 +00:00
Cory Benfield
470af31f4a Handle 301s 'properly'. 2013-11-24 11:09:00 +00:00
Kenneth Reitz
3327652623 Merge pull request #1747 from daftshady/lowercase
#1746 don't lowercase the whole url
2013-11-20 01:05:15 -08:00
Kenneth Reitz
8a4c83022a Merge pull request #1742 from kevinburke/docs
Add make target to build the documentation
2013-11-20 01:02:38 -08:00
Kenneth Reitz
af921c7f51 Merge pull request #1733 from ionrock/pickleable-responses
Response and Request objects are pickleable.
2013-11-20 01:01:26 -08:00
Kenneth Reitz
248c9ec53f Merge pull request #1745 from nonm/master
Small fix broken cookie parse
2013-11-20 00:58:21 -08:00
daftshady
4972133a9b use geturl to lower scheme 2013-11-19 19:54:46 +09:00
daftshady
8c8938e8cb issue #1746 : Don't lowercase the whole URL! 2013-11-19 18:59:22 +09:00
nonm
cfbbd15de7 Small fix broken cookie parse 2013-11-18 13:32:59 -08:00
Kevin Burke
0c88ae42e6 wording 2013-11-17 18:39:01 -08:00
Kevin Burke
4b69798796 Add target to build the documentation 2013-11-17 18:38:15 -08:00
Kenneth Reitz
947375e982 Merge pull request #1739 from pengfei-xue/devel
add coverage make target
2013-11-15 10:51:47 -08:00
Pengfei Xue
afaed3343a add coverage make target 2013-11-15 22:11:49 +08:00
Kenneth Reitz
42bffb575d Merge pull request #1735 from Lukasa/wheel
Wheel
2013-11-15 01:33:38 -08:00
Kenneth Reitz
bcb10af64f Merge pull request #1717 from jvantuyl/master
loosen URL handling for non-native URL schemes
2013-11-15 01:33:03 -08:00
Jayson Vantuyl
b149be5d86 loosen URL handling for non-native URL schemes 2013-11-15 01:25:32 -08:00
Cory Benfield
abf96670ce Add wheel to dev requirements. 2013-11-14 13:23:45 +00:00
Cory Benfield
86bdd85c4a Wheel support. 2013-11-13 19:09:49 +00:00
Eric Larson
512beb8795 Response and Request objects are pickleable.
Includes a basic test. More could be add to confirm known attributes
that could cause problems.
2013-11-06 20:01:27 -06:00
Kenneth Reitz
a123f8351d Merge pull request #1660 from sigmavirus24/parse-cookies-when-users-set-host-header
Parse cookies when users set custom Host header
2013-11-06 11:19:31 -08:00
Kenneth Reitz
7ccbcd28f0 Merge pull request #1724 from daftshady/dev3
Fixed #1723
2013-11-04 13:58:12 -08:00
Kenneth Reitz
598f977df4 Merge pull request #1726 from t-8ch/update_urllib3
Update urllib3
2013-11-04 13:55:58 -08:00
Thomas Weißschuh
c477242376 add myself to AUTHORS.rst 2013-11-04 18:54:42 +00:00
Thomas Weißschuh
3ef4115349 upate urllib3 to a0c986a24d 2013-11-04 18:53:41 +00:00
daftshady
1511dfa637 moved logic to Session object and added test case 2013-11-04 22:32:17 +09:00
daftshady
495f87363b fixed #1723 2013-11-04 18:33:14 +09:00
Ian Cordasco
1ba83c47ce Merge pull request #1722 from schinckel/patch-1
Fix documentation typo.
2013-11-03 18:16:21 -08:00
Matthew Schinckel
edf7e5f420 Fix documentation typo. 2013-11-04 12:29:24 +10:30
Cory Benfield
9ffcbb82a2 Capitalisation. 2013-11-01 11:14:05 +00:00
Cory Benfield
c86de38780 Merge pull request #1720 from Lukasa/docs
Chardet -> charade.
2013-11-01 02:21:01 -07:00
Cory Benfield
20f8027641 Chardet -> charade. 2013-11-01 09:20:22 +00:00
Kenneth Reitz
0228851f19 Revert "Merge pull request #1710 from jvantuyl/master"
This reverts commit a8300c0ae6, reversing
changes made to 2e196be143.
2013-10-30 15:32:19 -04:00
Kenneth Reitz
a8300c0ae6 Merge pull request #1710 from jvantuyl/master
loosen URL handling for non-native protocols
2013-10-30 10:17:26 -07:00
Jayson Vantuyl
a9ec28a1b4 don't prepare URLs for oddball schemes 2013-10-30 10:04:43 -07:00
Kenneth Reitz
2e196be143 Merge pull request #1709 from samuelhug/test-branch
Fix for AppEngine
2013-10-28 10:40:52 -07:00
Kenneth Reitz
ebdd5cabab further cleanup examples 2013-10-28 13:39:04 -04:00
Kenneth Reitz
a9839dc7db Merge remote-tracking branch 'origin/master' 2013-10-28 13:36:08 -04:00
Kenneth Reitz
2c1f51be0e fix @lukasa's lack of taste :)
#1702
2013-10-28 13:36:04 -04:00
Kenneth Reitz
129ee3510f Merge pull request #1702 from Lukasa/docs_n_stuff
Improve the PreparedRequest docs.
2013-10-28 10:34:07 -07:00
Kenneth Reitz
55ad38964b Merge pull request #1706 from daftshady/dev2
restoring pull request fixing #1623
2013-10-28 10:32:21 -07:00
Kenneth Reitz
2d5542502b Merge pull request #1707 from canibanoglu/tests
Modified the tests so that they use py.test convention
2013-10-28 10:32:09 -07:00
Kenneth Reitz
c4a7760ca6 Merge pull request #1708 from alex/patch-2
close files in setup.py
2013-10-28 10:31:59 -07:00
Samuel Hug
a501b0ca81 Fix for AppEngine 2013-10-28 09:51:56 -07:00
Alex Gaynor
3488f3ade7 close files in setup.py
otherwise python3.4 warns
2013-10-26 16:43:08 -07:00
Can Ibanoglu
aa60b8ea5b Modified the tests so that they use py.test convention 2013-10-26 13:38:11 +03:00
daftshady
6931bd6715 second commit : Fixed #1623. Added 'MD5-sess' algorithm to HTTPDigestAuth 2013-10-26 18:14:19 +09:00
Kenneth Reitz
25f86f1347 clean up sidebar 2013-10-24 18:09:12 -04:00
Kenneth Reitz
1f83ccb308 get support 2013-10-24 18:07:47 -04:00
Kenneth Reitz
592fb34a3d try gumroad overlay 2013-10-24 18:05:24 -04:00
Kenneth Reitz
cb0d4efba2 projects 2013-10-24 18:01:50 -04:00
Kenneth Reitz
3d8d465a90 "get updates" 2013-10-24 18:00:52 -04:00
Kenneth Reitz
ded42e6498 newsletter 2013-10-24 17:58:30 -04:00
Cory Benfield
e8683f00a6 Improve the PreparedRequest docs. 2013-10-24 21:14:46 +01:00
Kenneth Reitz
7b5c11f66b Merge remote-tracking branch 'origin/master' 2013-10-24 10:54:48 -04:00
Kenneth Reitz
afa7594a20 build status isn't useful anyway 2013-10-24 10:54:45 -04:00
Kenneth Reitz
6e52b5fe41 Merge pull request #1689 from Lukasa/unicode
Use builtin_str for all auto-set header values.
2013-10-24 07:38:52 -07:00
Kenneth Reitz
52fe3f790e Merge pull request #1693 from kevinburke/schema-message
Add clarification to MissingSchema error
2013-10-24 07:35:40 -07:00
Kevin Burke
b5d2160ef8 use .format() 2013-10-20 10:17:54 -07:00
Kevin Burke
333ac101ba Add clarification to MissingSchema error 2013-10-20 00:46:41 -07:00
Cory Benfield
8a42d5fb41 Use builtin_str for all auto-set header values. 2013-10-18 18:34:29 +01:00
Cory Benfield
9606f0240b PEP8 fix to make me happier. 2013-10-13 09:54:01 +01:00
Cory Benfield
31c0962e83 Better connection behaviour for chunked upload. 2013-10-13 09:53:44 +01:00
Ian Cordasco
6632fb87c0 Fix typo 2013-10-11 20:42:52 -05:00
Ian Cordasco
df7aece2c8 Comment our reasonsing for the special cases 2013-10-10 20:43:18 -05:00
Ian Cordasco
0bb8be0e5b Parse cookies when users set custom Host header 2013-10-07 21:43:21 -05:00
Ian Cordasco
a4605fe324 Fix failing test to preserve behaviour 2013-10-07 21:17:59 -05:00
Ian Cordasco
f12584b94a Add tests around hook behaviour 2013-10-07 21:17:46 -05:00
Ian Cordasco
5bf396d5d1 Honor session hooks 2013-10-07 12:23:16 -05:00
30 changed files with 940 additions and 446 deletions

View File

@@ -140,3 +140,7 @@ Patches and Suggestions
- Matt Spitz @mattspitz
- Vikram Oberoi @voberoi
- Can Ibanoglu <can.ibanoglu@gmail.com> @canibanoglu
- Thomas Weißschuh <thomas@t-8ch.de> @t-8ch
- Jayson Vantuyl <jayson@aggressive.ly> @kagato
- Pengfei.X <pengphy@gmail.com>
- Kamil Madac <kamil.madac@gmail.com>

View File

@@ -3,6 +3,28 @@
Release History
---------------
2.1.0 (2013-12-05)
++++++++++++++++++
- Updated CA Bundle, of course.
- Clean up connections when we hit problems during chunked upload, rather than leaking them.
- Return connections to the pool when a chunked upload is successful, rather than leaking it.
- Match the HTTPbis recommendation for HTTP 301 redirects.
- Prevent hanging when using streaming uploads and Digest Auth when a 401 is received.
- Values of headers set by Requests are now always the native string type.
- Fix previously broken SNI support.
- Fix accessing HTTP proxies using proxy authentication.
- Unencode HTTP Basic usernames and passwords extracted from URLs.
- Support for IP address ranges for no_proxy environment variable
- Parse headers correctly when users override the default ``Host:`` header.
- Avoid munging the URL in case of case-sensitive servers.
- Looser URL handling for non-HTTP/HTTPS urls.
- Accept unicode methods in Python 2.6 and 2.7.
- More resilient cookie handling.
- Make ``Response`` objects pickleable.
- Actually added MD5-sess to Digest Auth instead of pretending to like last time.
- Updated internal urllib3.
- Fixed @Lukasa's lack of taste.
2.0.1 (2013-10-24)
++++++++++++++++++

View File

@@ -1,9 +1,14 @@
.PHONY: docs
init:
pip install -r requirements.txt
test:
py.test
coverage:
py.test --verbose --cov-report term --cov=requests test_requests.py
ci: init
py.test --junitxml=junit.xml
@@ -23,3 +28,15 @@ charade:
git clone https://github.com/sigmavirus24/charade.git
mv charade/charade requests/packages/
rm -fr charade
publish:
python setup.py sdist upload
python setup.py bdist_wheel upload
docs-init:
pip install -r docs/requirements.txt
docs:
cd docs && make html
@echo "\033[95m\n\nBuild successful! View the docs homepage at docs/_build/html/index.html.\n\033[0m"

View File

@@ -7,9 +7,6 @@ Requests: HTTP for Humans
.. image:: https://pypip.in/d/requests/badge.png
:target: https://crate.io/packages/requests/
.. image:: http://ci.kennethreitz.org/buildStatus/icon?job=requests-trunk
:target: http://ci.kennethreitz.org/job/requests-trunk/
Requests is an Apache2 Licensed HTTP library, written in Python, for human
beings.

View File

@@ -15,19 +15,17 @@
</p>
<h3>Donate</h3>
<p>
If you love Requests, consider supporting the author <a href="https://www.gittip.com/kennethreitz/">on Gittip</a>:
</p>
<p>
<iframe style="border: 0; margin: 0; padding: 0;"
src="https://www.gittip.com/kennethreitz/widget.html"
width="48pt" height="20pt"></iframe>
</p>
<h3>Get Updates</h3>
<p>Receive updates on new releases and upcoming projects.</p>
<p><a href="http://tinyletter.com/kennethreitz">Subscribe to Newsletter</a></p>
<h3>Get Support</h3>
<p>
If your organization uses Requests, consider financial support:
</p>
<p>
<a href="http://gum.co/RRZc" class="gumroad-button">Requests Pro</a><script type="text/javascript" src="https://gumroad.com/js/gumroad-button.js"></script>
@@ -48,9 +46,7 @@
<h3>Useful Links</h3>
<ul>
<li><a href="http://pypi.python.org/pypi/requests">Requests @ PyPI</a></li>
<li><a href="http://github.com/kennethreitz/requests">Requests @ GitHub</a></li>
<li><a href="https://flattr.com/thing/442264/Requests">Requests @ Flattr</a></li>
<li><a href="http://stackoverflow.com/questions/tagged/python-requests" title="Use it for support regarding python-request usage">Requests @ StackOverflow</a></li>
<li><a href="http://pypi.python.org/pypi/requests">Requests @ PyPI</a></li>
<li><a href="http://github.com/kennethreitz/requests/issues">Issue Tracker</a></li>
</ul>

View File

@@ -14,22 +14,17 @@
development release.
</p>
<h3>Donate</h3>
<p>
If you love Requests, consider supporting the author <a href="https://www.gittip.com/kennethreitz/">on Gittip</a>:
</p>
<p>
<iframe style="border: 0; margin: 0; padding: 0;"
src="https://www.gittip.com/kennethreitz/widget.html"
width="48pt" height="20pt"></iframe>
</p>
<h3>Get Updates</h3>
<p>Receive updates on new releases and upcoming projects.</p>
<p><a href="http://tinyletter.com/kennethreitz">Subscribe to Newsletter</a></p>
<h3>Get Support</h3>
<p>
If your organization uses Requests, consider financial support:
</p>
<p>
<a href="http://gum.co/RRZc" class="gumroad-button">Requests Pro</a><script type="text/javascript" src="https://gumroad.com/js/gumroad-button.js"></script>
</p>
</p>

View File

@@ -5,6 +5,7 @@
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
<script type="text/javascript" src="https://gumroad.com/js/gumroad.js"></script>
{% endblock %}
{%- block relbar2 %}{% endblock %}
{%- block footer %}

1
docs/requirements.txt Normal file
View File

@@ -0,0 +1 @@
Sphinx==1.1.3

View File

@@ -89,22 +89,23 @@ request. The simple recipe for this is the following::
from requests import Request, Session
s = Session()
prepped = Request('GET', # or any other method, 'POST', 'PUT', etc.
url,
data=data
headers=headers
# ...
).prepare()
req = Request('GET', url,
data=data,
headers=header
)
prepped = req.prepare()
# do something with prepped.body
# do something with prepped.headers
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout,
# etc.
)
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
Since you are not doing anything special with the ``Request`` object, you
@@ -112,6 +113,37 @@ prepare it immediately and modified the ``PreparedRequest`` object. You then
send that with the other parameters you would have sent to ``requests.*`` or
``Sesssion.*``.
However, the above code will lose some of the advantages of having a Requests
:class:`Session <requests.Session>` object. In particular,
:class:`Session <requests.Session>`-level state such as cookies will
not get applied to your request. To get a
:class:`PreparedRequest <requests.models.PreparedRequest>` with that state
applied, replace the call to ``Request.prepare()`` with a call to
``Session.prepare_request()``, like this::
from requests import Request, Session
s = Session()
req = Request('GET', url,
data=data
headers=headers
)
prepped = s.prepare_request(req)
# do something with prepped.body
# do something with prepped.headers
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
SSL Cert Verification
---------------------

View File

@@ -42,8 +42,8 @@ is at <http://python-requests.org>.
"""
__title__ = 'requests'
__version__ = '2.0.1'
__build__ = 0x020001
__version__ = '2.1.0'
__build__ = 0x020100
__author__ = 'Kenneth Reitz'
__license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2013 Kenneth Reitz'

View File

@@ -206,7 +206,10 @@ class HTTPAdapter(BaseAdapter):
conn = self.proxy_manager[proxy].connection_from_url(url)
else:
conn = self.poolmanager.connection_from_url(url.lower())
# Only scheme should be lower case
parsed = urlparse(url)
url = parsed.geturl()
conn = self.poolmanager.connection_from_url(url)
return conn
@@ -232,7 +235,7 @@ class HTTPAdapter(BaseAdapter):
:param proxies: A dictionary of schemes to proxy URLs.
"""
proxies = proxies or {}
scheme = urlparse(request.url).scheme.lower()
scheme = urlparse(request.url).scheme
proxy = proxies.get(scheme)
if proxy and scheme != 'https':
@@ -289,7 +292,7 @@ class HTTPAdapter(BaseAdapter):
:param stream: (optional) Whether to stream the request content.
:param timeout: (optional) The timeout on the request.
:param verify: (optional) Whether to verify SSL certificates.
:param vert: (optional) Any user-provided SSL certificate to be trusted.
:param cert: (optional) Any user-provided SSL certificate to be trusted.
:param proxies: (optional) The proxies dictionary to apply to the request.
"""
@@ -327,27 +330,40 @@ class HTTPAdapter(BaseAdapter):
conn = conn.proxy_pool
low_conn = conn._get_conn(timeout=timeout)
low_conn.putrequest(request.method, url, skip_accept_encoding=True)
for header, value in request.headers.items():
low_conn.putheader(header, value)
try:
low_conn.putrequest(request.method,
url,
skip_accept_encoding=True)
low_conn.endheaders()
for header, value in request.headers.items():
low_conn.putheader(header, value)
for i in request.body:
low_conn.send(hex(len(i))[2:].encode('utf-8'))
low_conn.send(b'\r\n')
low_conn.send(i)
low_conn.send(b'\r\n')
low_conn.send(b'0\r\n\r\n')
low_conn.endheaders()
r = low_conn.getresponse()
resp = HTTPResponse.from_httplib(r,
pool=conn,
connection=low_conn,
preload_content=False,
decode_content=False
)
for i in request.body:
low_conn.send(hex(len(i))[2:].encode('utf-8'))
low_conn.send(b'\r\n')
low_conn.send(i)
low_conn.send(b'\r\n')
low_conn.send(b'0\r\n\r\n')
r = low_conn.getresponse()
resp = HTTPResponse.from_httplib(
r,
pool=conn,
connection=low_conn,
preload_content=False,
decode_content=False
)
except:
# If we hit any problems here, clean up the connection.
# Then, reraise so that we can handle the actual exception.
low_conn.close()
raise
else:
# All is well, return the connection to the pool.
conn._put_conn(low_conn)
except socket.error as sockerr:
raise ConnectionError(sockerr)

View File

@@ -16,6 +16,7 @@ import logging
from base64 import b64encode
from .compat import urlparse, str
from .cookies import extract_cookies_to_jar
from .utils import parse_dict_header
log = logging.getLogger(__name__)
@@ -63,6 +64,7 @@ class HTTPDigestAuth(AuthBase):
self.last_nonce = ''
self.nonce_count = 0
self.chal = {}
self.pos = None
def build_digest_header(self, method, url):
@@ -77,7 +79,7 @@ class HTTPDigestAuth(AuthBase):
else:
_algorithm = algorithm.upper()
# lambdas assume digest modules are imported at the top level
if _algorithm == 'MD5':
if _algorithm == 'MD5' or _algorithm == 'MD5-SESS':
def md5_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
@@ -89,7 +91,7 @@ class HTTPDigestAuth(AuthBase):
x = x.encode('utf-8')
return hashlib.sha1(x).hexdigest()
hash_utf8 = sha_utf8
# XXX MD5-sess
KD = lambda s, d: hash_utf8("%s:%s" % (s, d))
if hash_utf8 is None:
@@ -105,23 +107,28 @@ class HTTPDigestAuth(AuthBase):
A1 = '%s:%s:%s' % (self.username, realm, self.password)
A2 = '%s:%s' % (method, path)
HA1 = hash_utf8(A1)
HA2 = hash_utf8(A2)
if nonce == self.last_nonce:
self.nonce_count += 1
else:
self.nonce_count = 1
ncvalue = '%08x' % self.nonce_count
s = str(self.nonce_count).encode('utf-8')
s += nonce.encode('utf-8')
s += time.ctime().encode('utf-8')
s += os.urandom(8)
cnonce = (hashlib.sha1(s).hexdigest()[:16])
noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, HA2)
if _algorithm == 'MD5-SESS':
HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce))
if qop is None:
respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2)))
respdig = KD(HA1, "%s:%s" % (nonce, HA2))
elif qop == 'auth' or 'auth' in qop.split(','):
if nonce == self.last_nonce:
self.nonce_count += 1
else:
self.nonce_count = 1
ncvalue = '%08x' % self.nonce_count
s = str(self.nonce_count).encode('utf-8')
s += nonce.encode('utf-8')
s += time.ctime().encode('utf-8')
s += os.urandom(8)
cnonce = (hashlib.sha1(s).hexdigest()[:16])
noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2))
respdig = KD(hash_utf8(A1), noncebit)
respdig = KD(HA1, noncebit)
else:
# XXX handle auth-int.
return None
@@ -138,13 +145,17 @@ class HTTPDigestAuth(AuthBase):
if entdig:
base += ', digest="%s"' % entdig
if qop:
base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce)
return 'Digest %s' % (base)
def handle_401(self, r, **kwargs):
"""Takes the given response and tries digest-auth, if needed."""
if self.pos is not None:
# Rewind the file position indicator of the body to where
# it was to resend the request.
r.request.body.seek(self.pos)
num_401_calls = getattr(self, 'num_401_calls', 1)
s_auth = r.headers.get('www-authenticate', '')
@@ -159,7 +170,8 @@ class HTTPDigestAuth(AuthBase):
r.content
r.raw.release_conn()
prep = r.request.copy()
prep.prepare_cookies(r.cookies)
extract_cookies_to_jar(prep._cookies, r.request, r.raw)
prep.prepare_cookies(prep._cookies)
prep.headers['Authorization'] = self.build_digest_header(
prep.method, prep.url)
@@ -176,5 +188,9 @@ class HTTPDigestAuth(AuthBase):
# If we have a saved nonce, skip the 401
if self.last_nonce:
r.headers['Authorization'] = self.build_digest_header(r.method, r.url)
try:
self.pos = r.body.tell()
except AttributeError:
pass
r.register_hook('response', self.handle_401)
return r

View File

@@ -1791,37 +1791,6 @@ QWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YGVM+h4k0460tQtcsm9MracEpqoeJ5
quGnM/b9Sh/22WA=
-----END CERTIFICATE-----
# Issuer: CN=Wells Fargo Root Certificate Authority O=Wells Fargo OU=Wells Fargo Certification Authority
# Subject: CN=Wells Fargo Root Certificate Authority O=Wells Fargo OU=Wells Fargo Certification Authority
# Label: "Wells Fargo Root CA"
# Serial: 971282334
# MD5 Fingerprint: 20:0b:4a:7a:88:a7:a9:42:86:8a:5f:74:56:7b:88:05
# SHA1 Fingerprint: 93:e6:ab:22:03:03:b5:23:28:dc:da:56:9e:ba:e4:d1:d1:cc:fb:65
# SHA256 Fingerprint: 03:45:8b:6a:be:ec:c2:14:95:3d:97:14:9a:f4:53:91:69:1d:e9:f9:cd:cc:26:47:86:3a:3d:67:c9:5c:24:3b
-----BEGIN CERTIFICATE-----
MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMC
VVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9v
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDAxMDExMTY0MTI4WhcNMjEwMTE0
MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dlbGxzIEZhcmdvMSww
KgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEvMC0G
A1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n13
5zHCLielTWi5MbqNQ1mXx3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHE
SxP9cMIlrCL1dQu3U+SlK93OvRw6esP3E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4O
JgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5OEL8pahbSCOz6+MlsoCu
ltQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4jsNtlAHCE
AQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMB
AAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcB
CzAyMDAGCCsGAQUFBwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRw
b2xpY3kwDQYJKoZIhvcNAQEFBQADggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo
7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrvm+0fazbuSCUlFLZWohDo7qd/
0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0ROhPs7fpvcmR7
nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx
x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ
33ZwmVxwQ023tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=
-----END CERTIFICATE-----
# Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services
# Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services
# Label: "Swisscom Root CA 1"
@@ -4964,3 +4933,94 @@ G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os
zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x
L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
-----END CERTIFICATE-----
# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV
# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV
# Label: "ACCVRAIZ1"
# Serial: 6828503384748696800
# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02
# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17
# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13
-----BEGIN CERTIFICATE-----
MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
-----END CERTIFICATE-----
# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA
# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA
# Label: "TWCA Global Root CA"
# Serial: 3262
# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96
# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65
# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx
EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT
VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5
NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT
B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF
10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz
0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh
MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH
zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc
46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2
yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi
laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP
oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA
BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE
qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm
4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL
1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF
H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo
RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+
nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh
15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW
6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW
nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j
wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz
aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy
KwbQBM0=
-----END CERTIFICATE-----

View File

@@ -8,7 +8,7 @@ requests.utils imports from here, so be careful with imports.
import time
import collections
from .compat import cookielib, urlparse, Morsel
from .compat import cookielib, urlparse, urlunparse, Morsel
try:
import threading
@@ -45,7 +45,18 @@ class MockRequest(object):
return self.get_host()
def get_full_url(self):
return self._r.url
# Only return the response's URL if the user hadn't set the Host
# header
if not self._r.headers.get('Host'):
return self._r.url
# If they did set it, retrieve it and reconstruct the expected domain
host = self._r.headers['Host']
parsed = urlparse(self._r.url)
# Reconstruct the URL as we expect it
return urlunparse([
parsed.scheme, host, parsed.path, parsed.params, parsed.query,
parsed.fragment
])
def is_unverifiable(self):
return True
@@ -267,7 +278,7 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping):
remove_cookie_by_name(self, name)
def set_cookie(self, cookie, *args, **kwargs):
if cookie.value.startswith('"') and cookie.value.endswith('"'):
if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'):
cookie.value = cookie.value.replace('\\"', '')
return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs)
@@ -410,3 +421,25 @@ def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))
return cookiejar
def merge_cookies(cookiejar, cookies):
"""Add cookies to cookiejar and returns a merged CookieJar.
:param cookiejar: CookieJar object to add the cookies to.
:param cookies: Dictionary or CookieJar object to be added.
"""
if not isinstance(cookiejar, cookielib.CookieJar):
raise ValueError('You can only merge into CookieJar')
if isinstance(cookies, dict):
cookiejar = cookiejar_from_dict(
cookies, cookiejar=cookiejar, overwrite=False)
elif isinstance(cookies, cookielib.CookieJar):
try:
cookiejar.update(cookies)
except AttributeError:
for cookie_in_jar in cookies:
cookiejar.set_cookie(cookie_in_jar)
return cookiejar

View File

@@ -270,6 +270,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
self.url = None
#: dictionary of HTTP headers.
self.headers = None
# The `CookieJar` used to create the Cookie header will be stored here
# after prepare_cookies is called
self._cookies = None
#: request body to send to the server.
self.body = None
#: dictionary of callback hooks, for internal usage.
@@ -299,6 +302,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
p.method = self.method
p.url = self.url
p.headers = self.headers.copy()
p._cookies = self._cookies.copy()
p.body = self.body
p.hooks = self.hooks
return p
@@ -320,11 +324,17 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
except UnicodeDecodeError:
pass
# Don't do any URL preparation for oddball schemes
if ':' in url and not url.lower().startswith('http'):
self.url = url
return
# Support for unicode domain names and paths.
scheme, auth, host, port, path, query, fragment = parse_url(url)
if not scheme:
raise MissingSchema("Invalid URL %r: No schema supplied" % url)
raise MissingSchema("Invalid URL {0!r}: No schema supplied. "
"Perhaps you meant http://{0}?".format(url))
if not host:
raise InvalidURL("Invalid URL %r: No host supplied" % url)
@@ -407,7 +417,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
raise NotImplementedError('Streamed bodies and files are mutually exclusive.')
if length is not None:
self.headers['Content-Length'] = str(length)
self.headers['Content-Length'] = builtin_str(length)
else:
self.headers['Transfer-Encoding'] = 'chunked'
else:
@@ -433,12 +443,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
def prepare_content_length(self, body):
if hasattr(body, 'seek') and hasattr(body, 'tell'):
body.seek(0, 2)
self.headers['Content-Length'] = str(body.tell())
self.headers['Content-Length'] = builtin_str(body.tell())
body.seek(0, 0)
elif body is not None:
l = super_len(body)
if l:
self.headers['Content-Length'] = str(l)
self.headers['Content-Length'] = builtin_str(l)
elif self.method not in ('GET', 'HEAD'):
self.headers['Content-Length'] = '0'
@@ -468,14 +478,13 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
"""Prepares the given HTTP cookie data."""
if isinstance(cookies, cookielib.CookieJar):
cookies = cookies
self._cookies = cookies
else:
cookies = cookiejar_from_dict(cookies)
self._cookies = cookiejar_from_dict(cookies)
if 'cookie' not in self.headers:
cookie_header = get_cookie_header(cookies, self)
if cookie_header is not None:
self.headers['Cookie'] = cookie_header
cookie_header = get_cookie_header(self._cookies, self)
if cookie_header is not None:
self.headers['Cookie'] = cookie_header
def prepare_hooks(self, hooks):
"""Prepares the given hooks."""
@@ -488,6 +497,19 @@ class Response(object):
server's response to an HTTP request.
"""
__attrs__ = [
'_content',
'status_code',
'headers',
'url',
'history',
'encoding',
'reason',
'cookies',
'elapsed',
'request',
]
def __init__(self):
super(Response, self).__init__()
@@ -527,6 +549,24 @@ class Response(object):
#: and the arrival of the response (as a timedelta)
self.elapsed = datetime.timedelta(0)
def __getstate__(self):
# Consume everything; accessing the content attribute makes
# sure the content has been fully read.
if not self._content_consumed:
self.content
return dict(
(attr, getattr(self, attr, None))
for attr in self.__attrs__
)
def __setstate__(self, state):
for name, value in state.items():
setattr(self, name, value)
# pickled objects do not have .raw
setattr(self, '_content_consumed', True)
def __repr__(self):
return '<Response [%s]>' % (self.status_code)
@@ -647,8 +687,8 @@ class Response(object):
def text(self):
"""Content of the response, in unicode.
if Response.encoding is None and chardet module is available, encoding
will be guessed.
If Response.encoding is None, encoding will be guessed using
``charade``.
"""
# Try charset from content-type

View File

@@ -0,0 +1,8 @@
If you are planning to submit a pull request to requests with any changes in
this library do not go any further. These are independent libraries which we
vendor into requests. Any changes necessary to these libraries must be made in
them and submitted as separate pull requests to those libraries.
urllib3 pull requests go here: https://github.com/shazow/urllib3
charade pull requests go here: https://github.com/sigmavirus24/charade

View File

@@ -5,7 +5,16 @@
# the MIT License: http://www.opensource.org/licenses/mit-license.php
from collections import MutableMapping
from threading import RLock
try:
from threading import RLock
except ImportError: # Platform-specific: No threads available
class RLock:
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
pass
try: # Python 2.7+
from collections import OrderedDict

View File

@@ -461,6 +461,13 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
conn = None
# Merge the proxy headers. Only do this in HTTP. We have to copy the
# headers dict so we can safely change it without those changes being
# reflected in anyone else's copy.
if self.scheme == 'http':
headers = headers.copy()
headers.update(self.proxy_headers)
try:
# Request a connection from the queue
conn = self._get_conn(timeout=pool_timeout)

View File

@@ -29,7 +29,7 @@ import ssl
import select
from cStringIO import StringIO
from .. import connectionpool
from .. import connection
from .. import util
__all__ = ['inject_into_urllib3', 'extract_from_urllib3']
@@ -52,20 +52,20 @@ _openssl_verify = {
orig_util_HAS_SNI = util.HAS_SNI
orig_connectionpool_ssl_wrap_socket = connectionpool.ssl_wrap_socket
orig_connection_ssl_wrap_socket = connection.ssl_wrap_socket
def inject_into_urllib3():
'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.'
connectionpool.ssl_wrap_socket = ssl_wrap_socket
connection.ssl_wrap_socket = ssl_wrap_socket
util.HAS_SNI = HAS_SNI
def extract_from_urllib3():
'Undo monkey-patching by :func:`inject_into_urllib3`.'
connectionpool.ssl_wrap_socket = orig_connectionpool_ssl_wrap_socket
connection.ssl_wrap_socket = orig_connection_ssl_wrap_socket
util.HAS_SNI = orig_util_HAS_SNI

View File

@@ -1,98 +1,13 @@
"""The match_hostname() function from Python 3.2, essential when using SSL."""
try:
# Python 3.2+
from ssl import CertificateError, match_hostname
except ImportError:
try:
# Backport of the function from a pypi module
from backports.ssl_match_hostname import CertificateError, match_hostname
except ImportError:
# Our vendored copy
from _implementation import CertificateError, match_hostname
import re
__version__ = '3.2.2'
class CertificateError(ValueError):
pass
def _dnsname_match(dn, hostname, max_wildcards=1):
"""Matching according to RFC 6125, section 6.4.3
http://tools.ietf.org/html/rfc6125#section-6.4.3
"""
pats = []
if not dn:
return False
parts = dn.split(r'.')
leftmost = parts[0]
wildcards = leftmost.count('*')
if wildcards > max_wildcards:
# Issue #17980: avoid denials of service by refusing more
# than one wildcard per fragment. A survery of established
# policy among SSL implementations showed it to be a
# reasonable choice.
raise CertificateError(
"too many wildcards in certificate DNS name: " + repr(dn))
# speed up common case w/o wildcards
if not wildcards:
return dn.lower() == hostname.lower()
# RFC 6125, section 6.4.3, subitem 1.
# The client SHOULD NOT attempt to match a presented identifier in which
# the wildcard character comprises a label other than the left-most label.
if leftmost == '*':
# When '*' is a fragment by itself, it matches a non-empty dotless
# fragment.
pats.append('[^.]+')
elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
# RFC 6125, section 6.4.3, subitem 3.
# The client SHOULD NOT attempt to match a presented identifier
# where the wildcard character is embedded within an A-label or
# U-label of an internationalized domain name.
pats.append(re.escape(leftmost))
else:
# Otherwise, '*' matches any dotless string, e.g. www*
pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
# add the remaining fragments, ignore any wildcards
for frag in parts[1:]:
pats.append(re.escape(frag))
pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
return pat.match(hostname)
def match_hostname(cert, hostname):
"""Verify that *cert* (in decoded format as returned by
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
rules are followed, but IP addresses are not accepted for *hostname*.
CertificateError is raised on failure. On success, the function
returns nothing.
"""
if not cert:
raise ValueError("empty or no certificate")
dnsnames = []
san = cert.get('subjectAltName', ())
for key, value in san:
if key == 'DNS':
if _dnsname_match(value, hostname):
return
dnsnames.append(value)
if not dnsnames:
# The subject is only checked when there is no dNSName entry
# in subjectAltName
for sub in cert.get('subject', ()):
for key, value in sub:
# XXX according to RFC 2818, the most specific Common Name
# must be used.
if key == 'commonName':
if _dnsname_match(value, hostname):
return
dnsnames.append(value)
if len(dnsnames) > 1:
raise CertificateError("hostname %r "
"doesn't match either of %s"
% (hostname, ', '.join(map(repr, dnsnames))))
elif len(dnsnames) == 1:
raise CertificateError("hostname %r "
"doesn't match %r"
% (hostname, dnsnames[0]))
else:
raise CertificateError("no appropriate commonName or "
"subjectAltName fields were found")
# Not needed, but documenting what we provide.
__all__ = ('CertificateError', 'match_hostname')

View File

@@ -0,0 +1,105 @@
"""The match_hostname() function from Python 3.3.3, essential when using SSL."""
# Note: This file is under the PSF license as the code comes from the python
# stdlib. http://docs.python.org/3/license.html
import re
__version__ = '3.4.0.2'
class CertificateError(ValueError):
pass
def _dnsname_match(dn, hostname, max_wildcards=1):
"""Matching according to RFC 6125, section 6.4.3
http://tools.ietf.org/html/rfc6125#section-6.4.3
"""
pats = []
if not dn:
return False
# Ported from python3-syntax:
# leftmost, *remainder = dn.split(r'.')
parts = dn.split(r'.')
leftmost = parts[0]
remainder = parts[1:]
wildcards = leftmost.count('*')
if wildcards > max_wildcards:
# Issue #17980: avoid denials of service by refusing more
# than one wildcard per fragment. A survey of established
# policy among SSL implementations showed it to be a
# reasonable choice.
raise CertificateError(
"too many wildcards in certificate DNS name: " + repr(dn))
# speed up common case w/o wildcards
if not wildcards:
return dn.lower() == hostname.lower()
# RFC 6125, section 6.4.3, subitem 1.
# The client SHOULD NOT attempt to match a presented identifier in which
# the wildcard character comprises a label other than the left-most label.
if leftmost == '*':
# When '*' is a fragment by itself, it matches a non-empty dotless
# fragment.
pats.append('[^.]+')
elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
# RFC 6125, section 6.4.3, subitem 3.
# The client SHOULD NOT attempt to match a presented identifier
# where the wildcard character is embedded within an A-label or
# U-label of an internationalized domain name.
pats.append(re.escape(leftmost))
else:
# Otherwise, '*' matches any dotless string, e.g. www*
pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
# add the remaining fragments, ignore any wildcards
for frag in remainder:
pats.append(re.escape(frag))
pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
return pat.match(hostname)
def match_hostname(cert, hostname):
"""Verify that *cert* (in decoded format as returned by
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
rules are followed, but IP addresses are not accepted for *hostname*.
CertificateError is raised on failure. On success, the function
returns nothing.
"""
if not cert:
raise ValueError("empty or no certificate")
dnsnames = []
san = cert.get('subjectAltName', ())
for key, value in san:
if key == 'DNS':
if _dnsname_match(value, hostname):
return
dnsnames.append(value)
if not dnsnames:
# The subject is only checked when there is no dNSName entry
# in subjectAltName
for sub in cert.get('subject', ()):
for key, value in sub:
# XXX according to RFC 2818, the most specific Common Name
# must be used.
if key == 'commonName':
if _dnsname_match(value, hostname):
return
dnsnames.append(value)
if len(dnsnames) > 1:
raise CertificateError("hostname %r "
"doesn't match either of %s"
% (hostname, ', '.join(map(repr, dnsnames))))
elif len(dnsnames) == 1:
raise CertificateError("hostname %r "
"doesn't match %r"
% (hostname, dnsnames[0]))
else:
raise CertificateError("no appropriate commonName or "
"subjectAltName fields were found")

View File

@@ -245,12 +245,11 @@ class ProxyManager(PoolManager):
u = parse_url(url)
if u.scheme == "http":
# It's too late to set proxy headers on per-request basis for
# tunnelled HTTPS connections, should use
# constructor's proxy_headers instead.
# For proxied HTTPS requests, httplib sets the necessary headers
# on the CONNECT to the proxy. For HTTP, we'll definitely
# need to set 'Host' at the very least.
kw['headers'] = self._set_proxy_headers(url, kw.get('headers',
self.headers))
kw['headers'].update(self.proxy_headers)
return super(ProxyManager, self).urlopen(method, url, redirect, **kw)

View File

@@ -90,6 +90,7 @@ class HTTPResponse(io.IOBase):
self._body = body if body and isinstance(body, basestring) else None
self._fp = None
self._original_response = original_response
self._fp_bytes_read = 0
self._pool = pool
self._connection = connection
@@ -129,6 +130,14 @@ class HTTPResponse(io.IOBase):
if self._fp:
return self.read(cache_content=True)
def tell(self):
"""
Obtain the number of bytes pulled over the wire so far. May differ from
the amount of content returned by :meth:``HTTPResponse.read`` if bytes
are encoded on the wire (e.g, compressed).
"""
return self._fp_bytes_read
def read(self, amt=None, decode_content=None, cache_content=False):
"""
Similar to :meth:`httplib.HTTPResponse.read`, but with two additional
@@ -183,6 +192,8 @@ class HTTPResponse(io.IOBase):
self._fp.close()
flush_decoder = True
self._fp_bytes_read += len(data)
try:
if decode_content and self._decoder:
data = self._decoder.decompress(data)

View File

@@ -426,7 +426,7 @@ def get_host(url):
def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
basic_auth=None):
basic_auth=None, proxy_basic_auth=None):
"""
Shortcuts for generating request headers.
@@ -447,6 +447,10 @@ def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
Colon-separated username:password string for 'authorization: basic ...'
auth header.
:param proxy_basic_auth:
Colon-separated username:password string for 'proxy-authorization: basic ...'
auth header.
Example: ::
>>> make_headers(keep_alive=True, user_agent="Batman/1.0")
@@ -474,6 +478,10 @@ def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
headers['authorization'] = 'Basic ' + \
b64encode(six.b(basic_auth)).decode('utf-8')
if proxy_basic_auth:
headers['proxy-authorization'] = 'Basic ' + \
b64encode(six.b(proxy_basic_auth)).decode('utf-8')
return headers

View File

@@ -12,8 +12,9 @@ import os
from collections import Mapping
from datetime import datetime
from .compat import cookielib, OrderedDict, urljoin, urlparse, urlunparse
from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar
from .compat import cookielib, OrderedDict, urljoin, urlparse, builtin_str
from .cookies import (
cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies)
from .models import Request, PreparedRequest
from .hooks import default_hooks, dispatch_hook
from .utils import to_key_val_list, default_headers
@@ -65,6 +66,22 @@ def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
return merged_setting
def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
"""
Properly merges both requests and session hooks.
This is necessary because when request_hooks == {'response': []}, the
merge breaks Session hooks entirely.
"""
if session_hooks is None or session_hooks.get('response') == []:
return request_hooks
if request_hooks is None or request_hooks.get('response') == []:
return session_hooks
return merge_setting(request_hooks, session_hooks, dict_class)
class SessionRedirectMixin(object):
def resolve_redirects(self, resp, req, stream=False, timeout=None,
verify=True, cert=None, proxies=None):
@@ -94,9 +111,7 @@ class SessionRedirectMixin(object):
# The scheme should be lower case...
parsed = urlparse(url)
parsed = (parsed.scheme.lower(), parsed.netloc, parsed.path,
parsed.params, parsed.query, parsed.fragment)
url = urlunparse(parsed)
url = parsed.geturl()
# Facilitate non-RFC2616-compliant 'location' headers
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
@@ -114,8 +129,13 @@ class SessionRedirectMixin(object):
method = 'GET'
# Do what the browsers do, despite standards...
if (resp.status_code in (codes.moved, codes.found) and
method not in ('GET', 'HEAD')):
# First, turn 302s into GETs.
if resp.status_code == codes.found and method != 'HEAD':
method = 'GET'
# Second, if a POST is responded to with a 301, turn it into a GET.
# This bizarre behaviour is explained in Issue 1704.
if resp.status_code == codes.moved and method == 'POST':
method = 'GET'
prepared_request.method = method
@@ -133,7 +153,9 @@ class SessionRedirectMixin(object):
except KeyError:
pass
prepared_request.prepare_cookies(self.cookies)
extract_cookies_to_jar(prepared_request._cookies,
prepared_request, resp.raw)
prepared_request.prepare_cookies(prepared_request._cookies)
resp = self.send(
prepared_request,
@@ -242,9 +264,8 @@ class Session(SessionRedirectMixin):
cookies = cookiejar_from_dict(cookies)
# Merge with session cookies
merged_cookies = RequestsCookieJar()
merged_cookies.update(self.cookies)
merged_cookies.update(cookies)
merged_cookies = merge_cookies(
merge_cookies(RequestsCookieJar(), self.cookies), cookies)
# Set environment's basic authentication if not explicitly set.
@@ -262,7 +283,7 @@ class Session(SessionRedirectMixin):
params=merge_setting(request.params, self.params),
auth=merge_setting(auth, self.auth),
cookies=merged_cookies,
hooks=merge_setting(request.hooks, self.hooks),
hooks=merge_hooks(request.hooks, self.hooks),
)
return p
@@ -309,6 +330,9 @@ class Session(SessionRedirectMixin):
:param cert: (optional) if String, path to ssl client cert file (.pem).
If Tuple, ('cert', 'key') pair.
"""
method = builtin_str(method)
# Create the Request.
req = Request(
method = method.upper(),
@@ -323,9 +347,6 @@ class Session(SessionRedirectMixin):
)
prep = self.prepare_request(req)
# Add param cookies to session cookies
self.cookies = cookiejar_from_dict(cookies, cookiejar=self.cookies, overwrite=False)
proxies = proxies or {}
# Gather clues from the surrounding environment.

View File

@@ -17,13 +17,14 @@ import os
import platform
import re
import sys
from netrc import netrc, NetrcParseError
import socket
import struct
from . import __version__
from . import certs
from .compat import parse_http_list as _parse_list_header
from .compat import (quote, urlparse, bytes, str, OrderedDict, urlunparse,
is_py2, is_py3, builtin_str, getproxies, proxy_bypass)
from .compat import (quote, urlparse, bytes, str, OrderedDict, unquote, is_py2,
builtin_str, getproxies, proxy_bypass)
from .cookies import RequestsCookieJar, cookiejar_from_dict
from .structures import CaseInsensitiveDict
from .exceptions import MissingSchema, InvalidURL
@@ -67,6 +68,8 @@ def get_netrc_auth(url):
"""Returns the Requests tuple auth for a given url from netrc."""
try:
from netrc import netrc, NetrcParseError
locations = (os.path.expanduser('~/{0}'.format(f)) for f in NETRC_FILES)
netrc_path = None
@@ -404,6 +407,56 @@ def requote_uri(uri):
return quote(unquote_unreserved(uri), safe="!#$%&'()*+,/:;=?@[]~")
def address_in_network(ip, net):
"""
This function allows you to check if on IP belongs to a network subnet
Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24
returns False if ip = 192.168.1.1 and net = 192.168.100.0/24
"""
ipaddr = struct.unpack('=L', socket.inet_aton(ip))[0]
netaddr, bits = net.split('/')
netmask = struct.unpack('=L', socket.inet_aton(dotted_netmask(int(bits))))[0]
network = struct.unpack('=L', socket.inet_aton(netaddr))[0] & netmask
return (ipaddr & netmask) == (network & netmask)
def dotted_netmask(mask):
"""
Converts mask from /xx format to xxx.xxx.xxx.xxx
Example: if mask is 24 function returns 255.255.255.0
"""
bits = 0xffffffff ^ (1 << 32 - mask) - 1
return socket.inet_ntoa(struct.pack('>I', bits))
def is_ipv4_address(string_ip):
try:
socket.inet_aton(string_ip)
except socket.error:
return False
return True
def is_valid_cidr(string_network):
"""Very simple check of the cidr format in no_proxy variable"""
if string_network.count('/') == 1:
try:
mask = int(string_network.split('/')[1])
except ValueError:
return False
if mask < 1 or mask > 32:
return False
try:
socket.inet_aton(string_network.split('/')[0])
except socket.error:
return False
else:
return False
return True
def get_environ_proxies(url):
"""Return a dict of environment proxies."""
@@ -419,11 +472,18 @@ def get_environ_proxies(url):
# the end of the netloc, both with and without the port.
no_proxy = no_proxy.replace(' ', '').split(',')
for host in no_proxy:
if netloc.endswith(host) or netloc.split(':')[0].endswith(host):
# The URL does match something in no_proxy, so we don't want
# to apply the proxies on this URL.
return {}
ip = netloc.split(':')[0]
if is_ipv4_address(ip):
for proxy_ip in no_proxy:
if is_valid_cidr(proxy_ip):
if address_in_network(ip, proxy_ip):
return {}
else:
for host in no_proxy:
if netloc.endswith(host) or netloc.split(':')[0].endswith(host):
# The URL does match something in no_proxy, so we don't want
# to apply the proxies on this URL.
return {}
# If the system proxy settings indicate that this URL should be bypassed,
# don't proxy.
@@ -436,7 +496,7 @@ def get_environ_proxies(url):
return getproxies()
def default_user_agent():
def default_user_agent(name="python-requests"):
"""Return a string representing the default user agent."""
_implementation = platform.python_implementation()
@@ -462,7 +522,7 @@ def default_user_agent():
p_system = 'Unknown'
p_release = 'Unknown'
return " ".join(['python-requests/%s' % __version__,
return " ".join(['%s/%s' % (name, __version__),
'%s/%s' % (_implementation, _implementation_version),
'%s/%s' % (p_system, p_release)])
@@ -557,6 +617,7 @@ def get_auth_from_url(url):
"""Given a url with authentication components, extract them into a tuple of
username,password."""
if url:
url = unquote(url)
parsed = urlparse(url)
return (parsed.username, parsed.password)
else:

View File

@@ -1,3 +1,5 @@
py==1.4.12
pytest==2.3.4
invoke==0.2.0
pytest-cov==1.6
invoke==0.2.0
wheel

2
setup.cfg Normal file
View File

@@ -0,0 +1,2 @@
[wheel]
universal = 1

View File

@@ -26,12 +26,18 @@ packages = [
requires = []
with open('README.rst') as f:
readme = f.read()
with open('HISTORY.rst') as f:
history = f.read()
with open('LICENSE') as f:
license = f.read()
setup(
name='requests',
version=requests.__version__,
description='Python HTTP for Humans.',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
long_description=readme + '\n\n' + history,
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='http://python-requests.org',
@@ -40,7 +46,7 @@ setup(
package_dir={'requests': 'requests'},
include_package_data=True,
install_requires=requires,
license=open('LICENSE').read(),
license=license,
zip_safe=False,
classifiers=(
'Development Status :: 5 - Production/Stable',

View File

@@ -57,8 +57,10 @@ class RequestsTestCase(unittest.TestCase):
requests.post
def test_invalid_url(self):
self.assertRaises(MissingSchema, requests.get, 'hiwpefhipowhefopw')
self.assertRaises(InvalidURL, requests.get, 'http://')
with pytest.raises(MissingSchema):
requests.get('hiwpefhipowhefopw')
with pytest.raises(InvalidURL):
requests.get('http://')
def test_basic_building(self):
req = requests.Request()
@@ -71,24 +73,22 @@ class RequestsTestCase(unittest.TestCase):
def test_no_content_length(self):
get_req = requests.Request('GET', httpbin('get')).prepare()
self.assertTrue('Content-Length' not in get_req.headers)
assert 'Content-Length' not in get_req.headers
head_req = requests.Request('HEAD', httpbin('head')).prepare()
self.assertTrue('Content-Length' not in head_req.headers)
assert 'Content-Length' not in head_req.headers
def test_path_is_not_double_encoded(self):
request = requests.Request('GET', "http://0.0.0.0/get/test case").prepare()
self.assertEqual(request.path_url, "/get/test%20case")
assert request.path_url == '/get/test%20case'
def test_params_are_added_before_fragment(self):
request = requests.Request('GET',
"http://example.com/path#fragment", params={"a": "b"}).prepare()
self.assertEqual(request.url,
"http://example.com/path?a=b#fragment")
assert request.url == "http://example.com/path?a=b#fragment"
request = requests.Request('GET',
"http://example.com/path?key=value#fragment", params={"a": "b"}).prepare()
self.assertEqual(request.url,
"http://example.com/path?key=value&a=b#fragment")
assert request.url == "http://example.com/path?key=value&a=b#fragment"
def test_mixed_case_scheme_acceptable(self):
s = requests.Session()
@@ -100,8 +100,7 @@ class RequestsTestCase(unittest.TestCase):
url = scheme + parts.netloc + parts.path
r = requests.Request('GET', url)
r = s.send(r.prepare())
self.assertEqual(r.status_code, 200,
"failed for scheme %s" % scheme)
assert r.status_code == 200, 'failed for scheme {0}'.format(scheme)
def test_HTTP_200_OK_GET_ALTERNATIVE(self):
r = requests.Request('GET', httpbin('get'))
@@ -110,11 +109,11 @@ class RequestsTestCase(unittest.TestCase):
r = s.send(r.prepare())
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_HTTP_302_ALLOW_REDIRECT_GET(self):
r = requests.get(httpbin('redirect', '1'))
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
# def test_HTTP_302_ALLOW_REDIRECT_POST(self):
# r = requests.post(httpbin('status', '302'), data={'some': 'data'})
@@ -125,31 +124,31 @@ class RequestsTestCase(unittest.TestCase):
r = requests.get(httpbin('user-agent'), headers=heads)
self.assertTrue(heads['User-agent'] in r.text)
self.assertEqual(r.status_code, 200)
assert heads['User-agent'] in r.text
assert r.status_code == 200
def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_set_cookie_on_301(self):
s = requests.session()
url = httpbin('cookies/set?foo=bar')
r = s.get(url)
self.assertTrue(s.cookies['foo'] == 'bar')
assert s.cookies['foo'] == 'bar'
def test_cookie_sent_on_redirect(self):
s = requests.session()
s.get(httpbin('cookies/set?foo=bar'))
r = s.get(httpbin('redirect/1')) # redirects to httpbin('get')
self.assertTrue("Cookie" in r.json()["headers"])
assert 'Cookie' in r.json()['headers']
def test_cookie_removed_on_expire(self):
s = requests.session()
s.get(httpbin('cookies/set?foo=bar'))
self.assertTrue(s.cookies['foo'] == 'bar')
assert s.cookies['foo'] == 'bar'
s.get(
httpbin('response-headers'),
params={
@@ -162,13 +161,13 @@ class RequestsTestCase(unittest.TestCase):
def test_cookie_quote_wrapped(self):
s = requests.session()
s.get(httpbin('cookies/set?foo="bar:baz"'))
self.assertTrue(s.cookies['foo'] == '"bar:baz"')
assert s.cookies['foo'] == '"bar:baz"'
def test_cookie_persists_via_api(self):
s = requests.session()
r = s.get(httpbin('redirect/1'), cookies={'foo':'bar'})
self.assertTrue('foo' in r.request.headers['Cookie'])
self.assertTrue('foo' in r.history[0].request.headers['Cookie'])
r = s.get(httpbin('redirect/1'), cookies={'foo': 'bar'})
assert 'foo' in r.request.headers['Cookie']
assert 'foo' in r.history[0].request.headers['Cookie']
def test_request_cookie_overrides_session_cookie(self):
s = requests.session()
@@ -178,6 +177,12 @@ class RequestsTestCase(unittest.TestCase):
# Session cookie should not be modified
assert s.cookies['foo'] == 'bar'
def test_request_cookies_not_persisted(self):
s = requests.session()
s.get(httpbin('cookies'), cookies={'foo': 'baz'})
# Sending a request with cookies should not add cookies to the session
assert not s.cookies
def test_generic_cookiejar_works(self):
cj = cookielib.CookieJar()
cookiejar_from_dict({'foo': 'bar'}, cj)
@@ -188,12 +193,20 @@ class RequestsTestCase(unittest.TestCase):
assert r.json()['cookies']['foo'] == 'bar'
# Make sure the session cj is still the custom one
assert s.cookies is cj
def test_param_cookiejar_works(self):
cj = cookielib.CookieJar()
cookiejar_from_dict({'foo' : 'bar'}, cj)
s = requests.session()
r = s.get(httpbin('cookies'), cookies=cj)
# Make sure the cookie was sent
assert r.json()['cookies']['foo'] == 'bar'
def test_requests_in_history_are_not_overridden(self):
resp = requests.get(httpbin('redirect/3'))
urls = [r.url for r in resp.history]
req_urls = [r.request.url for r in resp.history]
self.assertEquals(urls, req_urls)
assert urls == req_urls
def test_user_agent_transfers(self):
@@ -202,37 +215,37 @@ class RequestsTestCase(unittest.TestCase):
}
r = requests.get(httpbin('user-agent'), headers=heads)
self.assertTrue(heads['User-agent'] in r.text)
assert heads['User-agent'] in r.text
heads = {
'user-agent': 'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads)
self.assertTrue(heads['user-agent'] in r.text)
assert heads['user-agent'] in r.text
def test_HTTP_200_OK_HEAD(self):
r = requests.head(httpbin('get'))
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_HTTP_200_OK_PUT(self):
r = requests.put(httpbin('put'))
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_BASICAUTH_TUPLE_HTTP_200_OK_GET(self):
auth = ('user', 'pass')
url = httpbin('basic-auth', 'user', 'pass')
r = requests.get(url, auth=auth)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
r = requests.get(url)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
s = requests.session()
s.auth = auth
r = s.get(url)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_basicauth_with_netrc(self):
auth = ('user', 'pass')
@@ -245,22 +258,22 @@ class RequestsTestCase(unittest.TestCase):
# Should use netrc and work.
r = requests.get(url)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
# Given auth should override and fail.
r = requests.get(url, auth=wrong_auth)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
s = requests.session()
# Should use netrc and work.
r = s.get(url)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
# Given auth should override and fail.
s.auth = wrong_auth
r = s.get(url)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
def test_DIGEST_HTTP_200_OK_GET(self):
@@ -268,15 +281,15 @@ class RequestsTestCase(unittest.TestCase):
url = httpbin('digest-auth', 'auth', 'user', 'pass')
r = requests.get(url, auth=auth)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
r = requests.get(url)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
s = requests.session()
s.auth = HTTPDigestAuth('user', 'pass')
r = s.get(url)
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_DIGEST_AUTH_RETURNS_COOKIE(self):
url = httpbin('digest-auth', 'auth', 'user', 'pass')
@@ -300,11 +313,10 @@ class RequestsTestCase(unittest.TestCase):
url = httpbin('digest-auth', 'auth', 'user', 'pass')
r = requests.get(url, auth=auth, stream=True)
self.assertNotEqual(r.raw.read(), b'')
assert r.raw.read() != b''
r = requests.get(url, auth=auth, stream=False)
self.assertEqual(r.raw.read(), b'')
assert r.raw.read() == b''
def test_DIGESTAUTH_WRONG_HTTP_401_GET(self):
@@ -312,15 +324,23 @@ class RequestsTestCase(unittest.TestCase):
url = httpbin('digest-auth', 'auth', 'user', 'pass')
r = requests.get(url, auth=auth)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
r = requests.get(url)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
s = requests.session()
s.auth = auth
r = s.get(url)
self.assertEqual(r.status_code, 401)
assert r.status_code == 401
def test_DIGESTAUTH_QUOTES_QOP_VALUE(self):
auth = HTTPDigestAuth('user', 'pass')
url = httpbin('digest-auth', 'auth', 'user', 'pass')
r = requests.get(url, auth=auth)
assert '"auth"' in r.request.headers['Authorization']
def test_POSTBIN_GET_POST_FILES(self):
@@ -328,19 +348,17 @@ class RequestsTestCase(unittest.TestCase):
post1 = requests.post(url).raise_for_status()
post1 = requests.post(url, data={'some': 'data'})
self.assertEqual(post1.status_code, 200)
assert post1.status_code == 200
with open('requirements.txt') as f:
post2 = requests.post(url, files={'some': f})
self.assertEqual(post2.status_code, 200)
assert post2.status_code == 200
post4 = requests.post(url, data='[{"some": "json"}]')
self.assertEqual(post4.status_code, 200)
assert post4.status_code == 200
try:
requests.post(url, files=['bad file data'])
except ValueError:
pass
with pytest.raises(ValueError):
requests.post(url, files = ['bad file data'])
def test_POSTBIN_GET_POST_FILES_WITH_DATA(self):
@@ -348,19 +366,17 @@ class RequestsTestCase(unittest.TestCase):
post1 = requests.post(url).raise_for_status()
post1 = requests.post(url, data={'some': 'data'})
self.assertEqual(post1.status_code, 200)
assert post1.status_code == 200
with open('requirements.txt') as f:
post2 = requests.post(url, data={'some': 'data'}, files={'some': f})
self.assertEqual(post2.status_code, 200)
assert post2.status_code == 200
post4 = requests.post(url, data='[{"some": "json"}]')
self.assertEqual(post4.status_code, 200)
assert post4.status_code == 200
try:
requests.post(url, files=['bad file data'])
except ValueError:
pass
with pytest.raises(ValueError):
requests.post(url, files = ['bad file data'])
def test_conflicting_post_params(self):
url = httpbin('post')
@@ -370,14 +386,15 @@ class RequestsTestCase(unittest.TestCase):
def test_request_ok_set(self):
r = requests.get(httpbin('status', '404'))
self.assertEqual(r.ok, False)
assert not r.ok
def test_status_raising(self):
r = requests.get(httpbin('status', '404'))
self.assertRaises(requests.exceptions.HTTPError, r.raise_for_status)
with pytest.raises(requests.exceptions.HTTPError):
r.raise_for_status()
r = requests.get(httpbin('status', '500'))
self.assertFalse(r.ok)
assert not r.ok
def test_decompress_gzip(self):
r = requests.get(httpbin('gzip'))
@@ -397,36 +414,36 @@ class RequestsTestCase(unittest.TestCase):
def test_urlencoded_get_query_multivalued_param(self):
r = requests.get(httpbin('get'), params=dict(test=['foo', 'baz']))
self.assertEqual(r.status_code, 200)
self.assertEqual(r.url, httpbin('get?test=foo&test=baz'))
assert r.status_code == 200
assert r.url == httpbin('get?test=foo&test=baz')
def test_different_encodings_dont_break_post(self):
r = requests.post(httpbin('post'),
data={'stuff': json.dumps({'a': 123})},
params={'blah': 'asdf1234'},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_unicode_multipart_post(self):
r = requests.post(httpbin('post'),
data={'stuff': u'ëlïxr'},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
r = requests.post(httpbin('post'),
data={'stuff': u'ëlïxr'.encode('utf-8')},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
r = requests.post(httpbin('post'),
data={'stuff': 'elixr'},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
r = requests.post(httpbin('post'),
data={'stuff': 'elixr'.encode('utf-8')},
files={'file': ('test_requests.py', open(__file__, 'rb'))})
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_unicode_multipart_post_fieldnames(self):
filename = os.path.splitext(__file__)[0] + '.py'
@@ -436,8 +453,13 @@ class RequestsTestCase(unittest.TestCase):
files={'file': ('test_requests.py',
open(filename, 'rb'))})
prep = r.prepare()
self.assertTrue(b'name="stuff"' in prep.body)
self.assertFalse(b'name="b\'stuff\'"' in prep.body)
assert b'name="stuff"' in prep.body
assert b'name="b\'stuff\'"' not in prep.body
def test_unicode_method_name(self):
files = {'file': open('test_requests.py', 'rb')}
r = requests.request(method=u'POST', url=httpbin('post'), files=files)
assert r.status_code == 200
def test_custom_content_type(self):
r = requests.post(httpbin('post'),
@@ -445,8 +467,8 @@ class RequestsTestCase(unittest.TestCase):
files={'file1': ('test_requests.py', open(__file__, 'rb')),
'file2': ('test_requests', open(__file__, 'rb'),
'text/py-content-type')})
self.assertEqual(r.status_code, 200)
self.assertTrue(b"text/py-content-type" in r.request.body)
assert r.status_code == 200
assert b"text/py-content-type" in r.request.body
def test_hook_receives_request_arguments(self):
def hook(resp, **kwargs):
@@ -455,6 +477,25 @@ class RequestsTestCase(unittest.TestCase):
requests.Request('GET', HTTPBIN, hooks={'response': hook})
def test_session_hooks_are_used_with_no_request_hooks(self):
hook = lambda x, *args, **kwargs: x
s = requests.Session()
s.hooks['response'].append(hook)
r = requests.Request('GET', HTTPBIN)
prep = s.prepare_request(r)
assert prep.hooks['response'] != []
assert prep.hooks['response'] == [hook]
def test_session_hooks_are_overriden_by_request_hooks(self):
hook1 = lambda x, *args, **kwargs: x
hook2 = lambda x, *args, **kwargs: x
assert hook1 is not hook2
s = requests.Session()
s.hooks['response'].append(hook2)
r = requests.Request('GET', HTTPBIN, hooks={'response': [hook1]})
prep = s.prepare_request(r)
assert prep.hooks['response'] == [hook1]
def test_prepared_request_hook(self):
def hook(resp, **kwargs):
resp.hook_working = True
@@ -467,7 +508,7 @@ class RequestsTestCase(unittest.TestCase):
s.proxies = getproxies()
resp = s.send(prep)
self.assertTrue(hasattr(resp, 'hook_working'))
assert hasattr(resp, 'hook_working')
def test_prepared_from_session(self):
class DummyAuth(requests.auth.AuthBase):
@@ -476,7 +517,7 @@ class RequestsTestCase(unittest.TestCase):
return r
req = requests.Request('GET', httpbin('headers'))
self.assertEqual(req.auth, None)
assert not req.auth
s = requests.Session()
s.auth = DummyAuth()
@@ -484,7 +525,7 @@ class RequestsTestCase(unittest.TestCase):
prep = s.prepare_request(req)
resp = s.send(prep)
self.assertTrue(resp.json()['headers']['Dummy-Auth-Test'], 'dummy-auth-test-ok')
assert resp.json()['headers']['Dummy-Auth-Test'] == 'dummy-auth-test-ok'
def test_links(self):
r = requests.Response()
@@ -508,7 +549,7 @@ class RequestsTestCase(unittest.TestCase):
'x-ratelimit-limit': '60',
'x-ratelimit-remaining': '57'
}
self.assertEqual(r.links['next']['rel'], 'next')
assert r.links['next']['rel'] == 'next'
def test_cookie_parameters(self):
key = 'some_cookie'
@@ -520,20 +561,20 @@ class RequestsTestCase(unittest.TestCase):
jar = requests.cookies.RequestsCookieJar()
jar.set(key, value, secure=secure, domain=domain, rest=rest)
self.assertEqual(len(jar), 1)
self.assertTrue('some_cookie' in jar)
assert len(jar) == 1
assert 'some_cookie' in jar
cookie = list(jar)[0]
self.assertEqual(cookie.secure, secure)
self.assertEqual(cookie.domain, domain)
self.assertEqual(cookie._rest['HttpOnly'], rest['HttpOnly'])
assert cookie.secure == secure
assert cookie.domain == domain
assert cookie._rest['HttpOnly'] == rest['HttpOnly']
def test_time_elapsed_blank(self):
r = requests.get(httpbin('get'))
td = r.elapsed
total_seconds = ((td.microseconds + (td.seconds + td.days * 24 * 3600)
* 10**6) / 10**6)
self.assertTrue(total_seconds > 0.0)
assert total_seconds > 0.0
def test_response_is_iterable(self):
r = requests.Response()
@@ -544,27 +585,51 @@ class RequestsTestCase(unittest.TestCase):
return read_(amt)
setattr(io, 'read', read_mock)
r.raw = io
self.assertTrue(next(iter(r)))
assert next(iter(r))
io.close()
def test_request_and_response_are_pickleable(self):
r = requests.get(httpbin('get'))
# verify we can pickle the original request
assert pickle.loads(pickle.dumps(r.request))
# verify we can pickle the response and that we have access to
# the original request.
pr = pickle.loads(pickle.dumps(r))
assert r.request.url == pr.request.url
assert r.request.headers == pr.request.headers
def test_get_auth_from_url(self):
url = 'http://user:pass@complex.url.com/path?query=yes'
self.assertEqual(('user', 'pass'),
requests.utils.get_auth_from_url(url))
assert ('user', 'pass') == requests.utils.get_auth_from_url(url)
def test_get_auth_from_url_encoded_spaces(self):
url = 'http://user:pass%20pass@complex.url.com/path?query=yes'
assert ('user', 'pass pass') == requests.utils.get_auth_from_url(url)
def test_get_auth_from_url_not_encoded_spaces(self):
url = 'http://user:pass pass@complex.url.com/path?query=yes'
assert ('user', 'pass pass') == requests.utils.get_auth_from_url(url)
def test_get_auth_from_url_percent_chars(self):
url = 'http://user%user:pass@complex.url.com/path?query=yes'
assert ('user%user', 'pass') == requests.utils.get_auth_from_url(url)
def test_cannot_send_unprepared_requests(self):
r = requests.Request(url=HTTPBIN)
self.assertRaises(ValueError, requests.Session().send, r)
with pytest.raises(ValueError):
requests.Session().send(r)
def test_http_error(self):
error = requests.exceptions.HTTPError()
self.assertEqual(error.response, None)
assert not error.response
response = requests.Response()
error = requests.exceptions.HTTPError(response=response)
self.assertEqual(error.response, response)
assert error.response == response
error = requests.exceptions.HTTPError('message', response=response)
self.assertEqual(str(error), 'message')
self.assertEqual(error.response, response)
assert str(error) == 'message'
assert error.response == response
def test_session_pickling(self):
r = requests.Request('GET', httpbin('get'))
@@ -574,7 +639,7 @@ class RequestsTestCase(unittest.TestCase):
s.proxies = getproxies()
r = s.send(r.prepare())
self.assertEqual(r.status_code, 200)
assert r.status_code == 200
def test_fixes_1329(self):
"""
@@ -585,30 +650,21 @@ class RequestsTestCase(unittest.TestCase):
s.headers.update({'accept': 'application/json'})
r = s.get(httpbin('get'))
headers = r.request.headers
self.assertEqual(
headers['accept'],
'application/json'
)
self.assertEqual(
headers['Accept'],
'application/json'
)
self.assertEqual(
headers['ACCEPT'],
'application/json'
)
assert headers['accept'] == 'application/json'
assert headers['Accept'] == 'application/json'
assert headers['ACCEPT'] == 'application/json'
def test_uppercase_scheme_redirect(self):
parts = urlparse(httpbin('html'))
url = "HTTP://" + parts.netloc + parts.path
r = requests.get(httpbin('redirect-to'), params={'url': url})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.url.lower(), url.lower())
assert r.status_code == 200
assert r.url.lower() == url.lower()
def test_transport_adapter_ordering(self):
s = requests.Session()
order = ['https://', 'http://']
self.assertEqual(order, list(s.adapters))
assert order == list(s.adapters)
s.mount('http://git', HTTPAdapter())
s.mount('http://github', HTTPAdapter())
s.mount('http://github.com', HTTPAdapter())
@@ -621,7 +677,7 @@ class RequestsTestCase(unittest.TestCase):
'https://',
'http://',
]
self.assertEqual(order, list(s.adapters))
assert order == list(s.adapters)
s.mount('http://gittip', HTTPAdapter())
s.mount('http://gittip.com', HTTPAdapter())
s.mount('http://gittip.com/about/', HTTPAdapter())
@@ -636,12 +692,12 @@ class RequestsTestCase(unittest.TestCase):
'https://',
'http://',
]
self.assertEqual(order, list(s.adapters))
assert order == list(s.adapters)
s2 = requests.Session()
s2.adapters = {'http://': HTTPAdapter()}
s2.mount('https://', HTTPAdapter())
self.assertTrue('http://' in s2.adapters)
self.assertTrue('https://' in s2.adapters)
assert 'http://' in s2.adapters
assert 'https://' in s2.adapters
def test_header_remove_is_case_insensitive(self):
# From issue #1321
@@ -664,7 +720,7 @@ class RequestsTestCase(unittest.TestCase):
'exactly-------------sixty-----------three------------characters',
)
r = requests.Request('GET', url).prepare()
self.assertEqual(r.url, url)
assert r.url == url
def test_header_keys_are_native(self):
headers = {u'unicode': 'blah', 'byte'.encode('ascii'): 'blah'}
@@ -673,8 +729,8 @@ class RequestsTestCase(unittest.TestCase):
# This is testing that they are builtin strings. A bit weird, but there
# we go.
self.assertTrue('unicode' in p.headers.keys())
self.assertTrue('byte' in p.headers.keys())
assert 'unicode' in p.headers.keys()
assert 'byte' in p.headers.keys()
def test_can_send_nonstring_objects_with_files(self):
data = {'a': 0.0}
@@ -682,42 +738,61 @@ class RequestsTestCase(unittest.TestCase):
r = requests.Request('POST', httpbin('post'), data=data, files=files)
p = r.prepare()
self.assertTrue('multipart/form-data' in p.headers['Content-Type'])
assert 'multipart/form-data' in p.headers['Content-Type']
def test_autoset_header_values_are_native(self):
data = 'this is a string'
length = '16'
req = requests.Request('POST', httpbin('post'), data=data)
p = req.prepare()
assert p.headers['Content-Length'] == length
def test_oddball_schemes_dont_check_URLs(self):
test_urls = (
'data:image/gif;base64,R0lGODlhAQABAHAAACH5BAUAAAAALAAAAAABAAEAAAICRAEAOw==',
'file:///etc/passwd',
'magnet:?xt=urn:btih:be08f00302bc2d1d3cfa3af02024fa647a271431',
)
for test_url in test_urls:
req = requests.Request('GET', test_url)
preq = req.prepare()
assert test_url == preq.url
class TestContentEncodingDetection(unittest.TestCase):
def test_none(self):
encodings = requests.utils.get_encodings_from_content('')
self.assertEqual(len(encodings), 0)
assert not len(encodings)
def test_html_charset(self):
"""HTML5 meta charset attribute"""
content = '<meta charset="UTF-8">'
encodings = requests.utils.get_encodings_from_content(content)
self.assertEqual(len(encodings), 1)
self.assertEqual(encodings[0], 'UTF-8')
assert len(encodings) == 1
assert encodings[0] == 'UTF-8'
def test_html4_pragma(self):
"""HTML4 pragma directive"""
content = '<meta http-equiv="Content-type" content="text/html;charset=UTF-8">'
encodings = requests.utils.get_encodings_from_content(content)
self.assertEqual(len(encodings), 1)
self.assertEqual(encodings[0], 'UTF-8')
assert len(encodings) == 1
assert encodings[0] == 'UTF-8'
def test_xhtml_pragma(self):
"""XHTML 1.x served with text/html MIME type"""
content = '<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />'
encodings = requests.utils.get_encodings_from_content(content)
self.assertEqual(len(encodings), 1)
self.assertEqual(encodings[0], 'UTF-8')
assert len(encodings) == 1
assert encodings[0] == 'UTF-8'
def test_xml(self):
"""XHTML 1.x served as XML"""
content = '<?xml version="1.0" encoding="UTF-8"?>'
encodings = requests.utils.get_encodings_from_content(content)
self.assertEqual(len(encodings), 1)
self.assertEqual(encodings[0], 'UTF-8')
assert len(encodings) == 1
assert encodings[0] == 'UTF-8'
def test_precedence(self):
content = '''
@@ -726,44 +801,44 @@ class TestContentEncodingDetection(unittest.TestCase):
<meta http-equiv="Content-type" content="text/html;charset=HTML4" />
'''.strip()
encodings = requests.utils.get_encodings_from_content(content)
self.assertEqual(encodings, ['HTML5', 'HTML4', 'XML'])
assert encodings == ['HTML5', 'HTML4', 'XML']
class TestCaseInsensitiveDict(unittest.TestCase):
def test_mapping_init(self):
cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
self.assertEqual(len(cid), 2)
self.assertTrue('foo' in cid)
self.assertTrue('bar' in cid)
assert len(cid) == 2
assert 'foo' in cid
assert 'bar' in cid
def test_iterable_init(self):
cid = CaseInsensitiveDict([('Foo', 'foo'), ('BAr', 'bar')])
self.assertEqual(len(cid), 2)
self.assertTrue('foo' in cid)
self.assertTrue('bar' in cid)
assert len(cid) == 2
assert 'foo' in cid
assert 'bar' in cid
def test_kwargs_init(self):
cid = CaseInsensitiveDict(FOO='foo', BAr='bar')
self.assertEqual(len(cid), 2)
self.assertTrue('foo' in cid)
self.assertTrue('bar' in cid)
assert len(cid) == 2
assert 'foo' in cid
assert 'bar' in cid
def test_docstring_example(self):
cid = CaseInsensitiveDict()
cid['Accept'] = 'application/json'
self.assertEqual(cid['aCCEPT'], 'application/json')
self.assertEqual(list(cid), ['Accept'])
assert cid['aCCEPT'] == 'application/json'
assert list(cid) == ['Accept']
def test_len(self):
cid = CaseInsensitiveDict({'a': 'a', 'b': 'b'})
cid['A'] = 'a'
self.assertEqual(len(cid), 2)
assert len(cid) == 2
def test_getitem(self):
cid = CaseInsensitiveDict({'Spam': 'blueval'})
self.assertEqual(cid['spam'], 'blueval')
self.assertEqual(cid['SPAM'], 'blueval')
assert cid['spam'] == 'blueval'
assert cid['SPAM'] == 'blueval'
def test_fixes_649(self):
"""__setitem__ should behave case-insensitively."""
@@ -772,74 +847,68 @@ class TestCaseInsensitiveDict(unittest.TestCase):
cid['Spam'] = 'twoval'
cid['sPAM'] = 'redval'
cid['SPAM'] = 'blueval'
self.assertEqual(cid['spam'], 'blueval')
self.assertEqual(cid['SPAM'], 'blueval')
self.assertEqual(list(cid.keys()), ['SPAM'])
assert cid['spam'] == 'blueval'
assert cid['SPAM'] == 'blueval'
assert list(cid.keys()) == ['SPAM']
def test_delitem(self):
cid = CaseInsensitiveDict()
cid['Spam'] = 'someval'
del cid['sPam']
self.assertFalse('spam' in cid)
self.assertEqual(len(cid), 0)
assert 'spam' not in cid
assert len(cid) == 0
def test_contains(self):
cid = CaseInsensitiveDict()
cid['Spam'] = 'someval'
self.assertTrue('Spam' in cid)
self.assertTrue('spam' in cid)
self.assertTrue('SPAM' in cid)
self.assertTrue('sPam' in cid)
self.assertFalse('notspam' in cid)
assert 'Spam' in cid
assert 'spam' in cid
assert 'SPAM' in cid
assert 'sPam' in cid
assert 'notspam' not in cid
def test_get(self):
cid = CaseInsensitiveDict()
cid['spam'] = 'oneval'
cid['SPAM'] = 'blueval'
self.assertEqual(cid.get('spam'), 'blueval')
self.assertEqual(cid.get('SPAM'), 'blueval')
self.assertEqual(cid.get('sPam'), 'blueval')
self.assertEqual(cid.get('notspam', 'default'), 'default')
assert cid.get('spam') == 'blueval'
assert cid.get('SPAM') == 'blueval'
assert cid.get('sPam') == 'blueval'
assert cid.get('notspam', 'default') == 'default'
def test_update(self):
cid = CaseInsensitiveDict()
cid['spam'] = 'blueval'
cid.update({'sPam': 'notblueval'})
self.assertEqual(cid['spam'], 'notblueval')
assert cid['spam'] == 'notblueval'
cid = CaseInsensitiveDict({'Foo': 'foo','BAr': 'bar'})
cid.update({'fOO': 'anotherfoo', 'bAR': 'anotherbar'})
self.assertEqual(len(cid), 2)
self.assertEqual(cid['foo'], 'anotherfoo')
self.assertEqual(cid['bar'], 'anotherbar')
assert len(cid) == 2
assert cid['foo'] == 'anotherfoo'
assert cid['bar'] == 'anotherbar'
def test_update_retains_unchanged(self):
cid = CaseInsensitiveDict({'foo': 'foo', 'bar': 'bar'})
cid.update({'foo': 'newfoo'})
self.assertEquals(cid['bar'], 'bar')
assert cid['bar'] == 'bar'
def test_iter(self):
cid = CaseInsensitiveDict({'Spam': 'spam', 'Eggs': 'eggs'})
keys = frozenset(['Spam', 'Eggs'])
self.assertEqual(frozenset(iter(cid)), keys)
assert frozenset(iter(cid)) == keys
def test_equality(self):
cid = CaseInsensitiveDict({'SPAM': 'blueval', 'Eggs': 'redval'})
othercid = CaseInsensitiveDict({'spam': 'blueval', 'eggs': 'redval'})
self.assertEqual(cid, othercid)
assert cid == othercid
del othercid['spam']
self.assertNotEqual(cid, othercid)
self.assertEqual(cid, {'spam': 'blueval', 'eggs': 'redval'})
assert cid != othercid
assert cid == {'spam': 'blueval', 'eggs': 'redval'}
def test_setdefault(self):
cid = CaseInsensitiveDict({'Spam': 'blueval'})
self.assertEqual(
cid.setdefault('spam', 'notblueval'),
'blueval'
)
self.assertEqual(
cid.setdefault('notspam', 'notblueval'),
'notblueval'
)
assert cid.setdefault('spam', 'notblueval') == 'blueval'
assert cid.setdefault('notspam', 'notblueval') == 'notblueval'
def test_lower_items(self):
cid = CaseInsensitiveDict({
@@ -848,7 +917,7 @@ class TestCaseInsensitiveDict(unittest.TestCase):
})
keyset = frozenset(lowerkey for lowerkey, v in cid.lower_items())
lowerkeyset = frozenset(['accept', 'user-agent'])
self.assertEqual(keyset, lowerkeyset)
assert keyset == lowerkeyset
def test_preserve_key_case(self):
cid = CaseInsensitiveDict({
@@ -856,9 +925,9 @@ class TestCaseInsensitiveDict(unittest.TestCase):
'user-Agent': 'requests',
})
keyset = frozenset(['Accept', 'user-Agent'])
self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
self.assertEqual(frozenset(cid.keys()), keyset)
self.assertEqual(frozenset(cid), keyset)
assert frozenset(i[0] for i in cid.items()) == keyset
assert frozenset(cid.keys()) == keyset
assert frozenset(cid) == keyset
def test_preserve_last_key_case(self):
cid = CaseInsensitiveDict({
@@ -868,9 +937,9 @@ class TestCaseInsensitiveDict(unittest.TestCase):
cid.update({'ACCEPT': 'application/json'})
cid['USER-AGENT'] = 'requests'
keyset = frozenset(['ACCEPT', 'USER-AGENT'])
self.assertEqual(frozenset(i[0] for i in cid.items()), keyset)
self.assertEqual(frozenset(cid.keys()), keyset)
self.assertEqual(frozenset(cid), keyset)
assert frozenset(i[0] for i in cid.items()) == keyset
assert frozenset(cid.keys()) == keyset
assert frozenset(cid) == keyset
class UtilsTestCase(unittest.TestCase):
@@ -881,18 +950,59 @@ class UtilsTestCase(unittest.TestCase):
from io import BytesIO
from requests.utils import super_len
self.assertEqual(super_len(StringIO.StringIO()), 0)
self.assertEqual(super_len(StringIO.StringIO('with so much drama in the LBC')), 29)
assert super_len(StringIO.StringIO()) == 0
assert super_len(StringIO.StringIO('with so much drama in the LBC')) == 29
self.assertEqual(super_len(BytesIO()), 0)
self.assertEqual(super_len(BytesIO(b"it's kinda hard bein' snoop d-o-double-g")), 40)
assert super_len(BytesIO()) == 0
assert super_len(BytesIO(b"it's kinda hard bein' snoop d-o-double-g")) == 40
try:
import cStringIO
except ImportError:
pass
else:
self.assertEqual(super_len(cStringIO.StringIO('but some how, some way...')), 25)
assert super_len(cStringIO.StringIO('but some how, some way...')) == 25
def test_get_environ_proxies_ip_ranges(self):
""" Ensures that IP addresses are correctly matches with ranges in no_proxy variable """
from requests.utils import get_environ_proxies
os.environ['no_proxy'] = "192.168.0.0/24,127.0.0.1,localhost.localdomain,172.16.1.1"
assert get_environ_proxies('http://192.168.0.1:5000/') == {}
assert get_environ_proxies('http://192.168.0.1/') == {}
assert get_environ_proxies('http://172.16.1.1/') == {}
assert get_environ_proxies('http://172.16.1.1:5000/') == {}
assert get_environ_proxies('http://192.168.1.1:5000/') != {}
assert get_environ_proxies('http://192.168.1.1/') != {}
def test_get_environ_proxies(self):
""" Ensures that IP addresses are correctly matches with ranges in no_proxy variable """
from requests.utils import get_environ_proxies
os.environ['no_proxy'] = "127.0.0.1,localhost.localdomain,192.168.0.0/24,172.16.1.1"
assert get_environ_proxies('http://localhost.localdomain:5000/v1.0/') == {}
assert get_environ_proxies('http://www.requests.com/') != {}
def test_is_ipv4_address(self):
from requests.utils import is_ipv4_address
assert is_ipv4_address('8.8.8.8')
assert not is_ipv4_address('8.8.8.8.8')
assert not is_ipv4_address('localhost.localdomain')
def test_is_valid_cidr(self):
from requests.utils import is_valid_cidr
assert not is_valid_cidr('8.8.8.8')
assert is_valid_cidr('192.168.1.0/24')
def test_dotted_netmask(self):
from requests.utils import dotted_netmask
assert dotted_netmask(8) == '255.0.0.0'
assert dotted_netmask(24) == '255.255.255.0'
assert dotted_netmask(25) == '255.255.255.128'
def test_address_in_network(self):
from requests.utils import address_in_network
assert address_in_network('192.168.1.1', '192.168.1.0/24')
assert not address_in_network('172.16.0.1', '192.168.1.0/24')
if __name__ == '__main__':
unittest.main()