Compare commits

..

55 Commits

Author SHA1 Message Date
Cory Benfield
1285f576ae v2.12.4 2016-12-14 11:37:52 +00:00
Cory Benfield
9726f5314e Merge pull request #3766 from nateprewitt/1979_test_401
testing HTTPDigestAuth hooks
2016-12-14 09:41:11 +00:00
Nate Prewitt
1da1213561 add socket tests for 401 redirect and 401 failure 2016-12-13 14:43:16 -07:00
Cory Benfield
4683f16990 Merge pull request #3760 from sigmavirus24/deprecation-warning
Add deprecation warnings for 3.0
2016-12-09 14:48:42 +00:00
Ian Cordasco
fae6701478 Add deprecation warnings for 3.0
Add extra test parameter for basic auth encoding
2016-12-09 08:43:54 -06:00
Cory Benfield
449d74842d Merge pull request #3758 from nateprewitt/3753_allow_non_string_params_basic_auth
adding string casting for non-bytes values
2016-12-09 08:45:14 +00:00
Nate Prewitt
576b447a37 adding string casting for non-bytes values 2016-12-08 21:13:14 -07:00
Cory Benfield
11a86a5651 Merge pull request #3754 from nateprewitt/3066_auth_and_data
test auth doesn't generate incorrect http headers
2016-12-08 09:05:12 +00:00
Nate Prewitt
13716728e7 minor doc string cleanup 2016-12-04 12:11:09 -07:00
Casey Davidson
14d71fd41a Test chunked upload doesn't set content length 2016-12-04 12:01:07 -07:00
Casey Davidson
2d2c831d07 Add tests to verify that correct 'Content-Length' or 'Transfer-Encoding' headers are being set in PreparedRequest. 2016-12-04 12:00:57 -07:00
Cory Benfield
c398ab0e7d v2.12.3 2016-12-01 10:48:35 +00:00
Cory Benfield
7d2dfa8684 Merge pull request #3738 from Lukasa/revert-exclusion-patch
Revert "Restrict URL preparation to HTTP/HTTPS"
2016-12-01 10:45:25 +00:00
Cory Benfield
6f659a4179 Tests for our URL handling. 2016-11-30 21:45:09 +00:00
Cory Benfield
402a55b647 Revert "Restrict URL preparation to HTTP/HTTPS"
This reverts commit 34af72c87d.
2016-11-30 21:19:31 +00:00
Cory Benfield
ca15d48087 v2.12.2 2016-11-30 12:38:26 +00:00
Cory Benfield
5c4549493b Merge pull request #3695 from nateprewitt/idna_bypass
idna bypass
2016-11-25 13:18:01 +00:00
Cory Benfield
36c9c8c3de Merge pull request #3718 from nsoranzo/master
Make Response.content() return None for an empty response
2016-11-24 13:35:37 +00:00
Nicola Soranzo
f897be58bf Make Response.content return None if raw is None
Add test.
2016-11-24 12:54:26 +00:00
Kenneth Reitz
dfad00a6e8 Update README.rst 2016-11-23 20:10:44 -05:00
Kenneth Reitz
d5e050da85 Update README.rst 2016-11-23 20:10:04 -05:00
Kenneth Reitz
1ec08bedd4 links 2016-11-23 20:00:33 -05:00
Kenneth Reitz
724ae1274e new logo :D
https://kennethreitz.org/tattoos
2016-11-23 19:58:18 -05:00
Nicola Soranzo
4f428228d7 Make Response.content() return None if status_code is None
Fix #3698.
2016-11-23 21:15:07 +00:00
Cory Benfield
ccabcf1fca Merge pull request #3717 from nateprewitt/streaming_docs
streaming doc clarification
2016-11-23 10:17:31 +00:00
Nate Prewitt
4f9d0e0455 streaming doc clarification 2016-11-21 14:17:28 -07:00
Cory Benfield
b9f1c448f7 Merge pull request #3673 from klimenko-forks/auth-unicode
Enable unicode in basic http auth
2016-11-21 19:27:39 +00:00
Cory Benfield
22ec73d71a Merge pull request #3713 from tiran/strict_http_protocol_check
Restrict URL preparation to HTTP/HTTPS
2016-11-21 19:13:56 +00:00
Dmitry Klimenko
005f93fd0f added new test, original test restored 2016-11-21 21:42:44 +03:00
Christian Heimes
34af72c87d Restrict URL preparation to HTTP/HTTPS
Requests treats all URLs starting with the string 'http' as HTTP URLs.
Preparation with IDNA breaks non-standard URIs like http+unix. Requests
now prepares only URLs with prefix http:// and https://.

Signed-off-by: Christian Heimes <christian@python.org>
2016-11-21 18:46:57 +01:00
Nate Prewitt
a83685ce00 modifying IDNA encoding check to allow fallback 2016-11-21 08:22:42 -07:00
Nate Prewitt
264f5bd5e1 adding unicode_is_ascii utility function 2016-11-21 08:22:41 -07:00
Nate Prewitt
d52e9b8c80 updated tests with IDNA encoded and IPv6 urls 2016-11-21 08:22:37 -07:00
Kenneth Reitz
268672ab33 Update sidebarlogo.html 2016-11-20 22:49:17 -05:00
Kenneth Reitz
3b956495c1 Update sidebarintro.html 2016-11-20 22:48:43 -05:00
Dmitry Klimenko
3b84be3c99 changed behavior of basic-http-auth test 2016-11-18 20:39:55 +03:00
Dmitry Klimenko
e15280e49d fixed: httpbin with unicode auth 2016-11-18 19:04:51 +03:00
Dmitry Klimenko
55dea233ce fixed some error 2016-11-18 15:19:43 +03:00
Dmitry Klimenko
4f0efdb77e unicode test for _basic_auth_str 2016-11-18 13:31:31 +03:00
Dmitry Klimenko
7c4821231f Fixed: httpbin doesn't support bytes 2016-11-18 13:14:08 +03:00
Dmitry Klimenko
bf3e960e15 fixed usage unicode string 2016-11-17 23:13:03 +03:00
Cory Benfield
53d4a1feee Merge pull request #3704 from afeld/patch-1
clarify that the `chunk_size` is optional when streaming to a file
2016-11-17 17:27:47 +00:00
Aidan Feldman
ed3ba3dbb8 correct backtick formatting 2016-11-17 12:26:19 -05:00
Dmitry Klimenko
37bde4b638 added unicode auth test 2016-11-17 20:22:32 +03:00
Aidan Feldman
5562c0f097 clarify that the chunk_size is optional when streaming to a file 2016-11-17 12:21:58 -05:00
Dmitry Klimenko
2185fac626 remove extra import 2016-11-17 19:52:13 +03:00
Dmitry Klimenko
329a5cfdd4 Order of type check 2016-11-17 19:50:53 +03:00
Kenneth Reitz
5b66b364a7 Merge pull request #3703 from kennethreitz/revert-3700-master
Revert "Adding 'import requests' to readme code snippet."
2016-11-17 10:53:38 -05:00
Kenneth Reitz
5f4d0d08de Revert "Adding 'import requests' to readme code snippet." 2016-11-17 10:53:33 -05:00
Rakib Hasan
7e138f945e Adding 'import requests' to readme code snippet. (#3700)
* added import requests to small demo
2016-11-17 08:23:57 +00:00
Cory Benfield
1e198f9115 Merge pull request #3697 from hussaintamboli/master
Fixed issue #3696. Added a test for it as well.
2016-11-16 18:32:59 +00:00
Hussain
be8b13e89b Added myself to AUTHORS 2016-11-16 23:33:42 +05:30
Hussain
0d7de50dbd Just lstrip instead of strip because trailing whitespace already works. 2016-11-16 23:26:48 +05:30
Hussain
248d3e9f0f Fixed issue #3696. Added a test for it as well. 2016-11-16 22:48:45 +05:30
Dmitry Klimenko
e514920e19 Unable unicode in basic http auth
Fixed the issue with unicode characters in basic http auth
2016-11-12 01:39:37 +03:00
16 changed files with 8565 additions and 2150 deletions

View File

@@ -172,3 +172,5 @@ Patches and Suggestions
- Brian Bamsch <bbamsch32@gmail.com> (`@bbamsch <https://github.com/bbamsch>`_)
- Om Prakash Kumar <omprakash070@gmail.com> (`@iamprakashom <https://github.com/iamprakashom>`_)
- Philipp Konrad <gardiac2002@gmail.com> (`@gardiac2002 <https://github.com/gardiac2002>`_)
- Hussain Tamboli <hussaintamboli18@gmail.com> (`@hussaintamboli <https://github.com/hussaintamboli>`_)
- Casey Davidson (`@davidsoncasey <https://github.com/davidsoncasey>`_)

View File

@@ -3,6 +3,47 @@
Release History
---------------
2.12.4 (2016-12-14)
+++++++++++++++++++
**Bugfixes**
- Fixed regression from 2.12.2 where non-string types were rejected in the
basic auth parameters. While support for this behaviour has been readded,
the behaviour is deprecated and will be removed in the future.
2.12.3 (2016-12-01)
+++++++++++++++++++
**Bugfixes**
- Fixed regression from v2.12.1 for URLs with schemes that begin with "http".
These URLs have historically been processed as though they were HTTP-schemed
URLs, and so have had parameters added. This was removed in v2.12.2 in an
overzealous attempt to resolve problems with IDNA-encoding those URLs. This
change was reverted: the other fixes for IDNA-encoding have been judged to
be sufficient to return to the behaviour Requests had before v2.12.0.
2.12.2 (2016-11-30)
+++++++++++++++++++
**Bugfixes**
- Fixed several issues with IDNA-encoding URLs that are technically invalid but
which are widely accepted. Requests will now attempt to IDNA-encode a URL if
it can but, if it fails, and the host contains only ASCII characters, it will
be passed through optimistically. This will allow users to opt-in to using
IDNA2003 themselves if they want to, and will also allow technically invalid
but still common hostnames.
- Fixed an issue where URLs with leading whitespace would raise
``InvalidSchema`` errors.
- Fixed an issue where some URLs without the HTTP or HTTPS schemes would still
have HTTP URL preparation applied to them.
- Fixed an issue where Unicode strings could not be used in basic auth.
- Fixed an issue encountered by some Requests plugins where constructing a
Response object would cause ``Response.content`` to raise an
``AttributeError``.
2.12.1 (2016-11-16)
+++++++++++++++++++

View File

@@ -3,7 +3,7 @@ Requests: HTTP for Humans
.. image:: https://img.shields.io/pypi/v/requests.svg
:target: https://pypi.python.org/pypi/requests
Requests is the only *Non-GMO* HTTP library for Python, safe for human
consumption.
@@ -29,6 +29,10 @@ Behold, the power of Requests:
See `the similar code, sans Requests <https://gist.github.com/973705>`_.
.. image:: http://docs.python-requests.org/en/master/_static/requests-sidebar.png
:target: http://docs.python-requests.org/
Requests allows you to send *organic, grass-fed* HTTP/1.1 requests, without the
need for manual labor. There's no need to manually add query strings to your
URLs, or to form-encode your POST data. Keep-alive and HTTP connection pooling

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 299 KiB

View File

@@ -1,6 +1,6 @@
<p class="logo">
<a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" title="Rezzy the Requests Sea Turtle"/>
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" title="https://kennethreitz.org/tattoos"/>
</a>
</p>
@@ -21,7 +21,7 @@
allowtransparency="true" frameborder="0" scrolling="0" width="200" height="20"></iframe></p>
<p><a href="https://twitter.com/kennethreitz" class="twitter-follow-button" data-show-count="false">Follow @kennethreitz</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script></p>
<p><a href="https://saythanks.io/to/kennethreitz">Say Thanks!</a></p>
<p><a href="http://tinyletter.com/kennethreitz">Join Mailing List</a>.</p>
<h3>Other Projects</h3>

View File

@@ -1,6 +1,6 @@
<p class="logo">
<a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" title="Rezzy the Requests Sea Turtle"/>
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" title="https://kennethreitz.org/tattoos"/>
</a>
</p>
<p>
@@ -19,6 +19,10 @@
<p><a href="http://tinyletter.com/kennethreitz">Join Mailing List</a>.</p>
<hr/>
<p>If you enjoy using this project, <a href="https://saythanks.io/to/kennethreitz">Say Thanks!</a></p>
<p><iframe src="http://ghbtns.com/github-btn.html?user=kennethreitz&type=follow&count=false"
allowtransparency="true" frameborder="0" scrolling="0" width="200" height="20"></iframe></p>

View File

@@ -178,13 +178,14 @@ In general, however, you should use a pattern like this to save what is being
streamed to a file::
with open(filename, 'wb') as fd:
for chunk in r.iter_content(chunk_size):
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
Using ``Response.iter_content`` will handle a lot of what you would otherwise
have to handle when using ``Response.raw`` directly. When streaming a
download, the above is the preferred and recommended way to retrieve the
content.
content. Note that ``chunk_size`` can be freely adjusted to a number that
may better fit your use cases.
Custom Headers

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 863 KiB

View File

@@ -41,8 +41,8 @@ is at <http://python-requests.org>.
"""
__title__ = 'requests'
__version__ = '2.12.1'
__build__ = 0x021201
__version__ = '2.12.4'
__build__ = 0x021204
__author__ = 'Kenneth Reitz'
__license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2016 Kenneth Reitz'

View File

@@ -8,7 +8,7 @@ Provides utility functions that are consumed internally by Requests
which depend on extremely few external helpers (such as compat)
"""
from .compat import is_py2, builtin_str
from .compat import is_py2, builtin_str, str
def to_native_string(string, encoding='ascii'):
@@ -25,3 +25,18 @@ def to_native_string(string, encoding='ascii'):
out = string.decode(encoding)
return out
def unicode_is_ascii(u_string):
"""Determine if unicode string only contains ASCII characters.
:param str u_string: unicode string to check. Must be unicode
and not Python 2 `str`.
:rtype: bool
"""
assert isinstance(u_string, str)
try:
u_string.encode('ascii')
return True
except UnicodeEncodeError:
return False

View File

@@ -12,10 +12,11 @@ import re
import time
import hashlib
import threading
import warnings
from base64 import b64encode
from .compat import urlparse, str
from .compat import urlparse, str, basestring
from .cookies import extract_cookies_to_jar
from ._internal_utils import to_native_string
from .utils import parse_dict_header
@@ -28,8 +29,42 @@ CONTENT_TYPE_MULTI_PART = 'multipart/form-data'
def _basic_auth_str(username, password):
"""Returns a Basic Auth string."""
# "I want us to put a big-ol' comment on top of it that
# says that this behaviour is dumb but we need to preserve
# it because people are relying on it."
# - Lukasa
#
# These are here solely to maintain backwards compatibility
# for things like ints. This will be removed in 3.0.0.
if not isinstance(username, basestring):
warnings.warn(
"Non-string usernames will no longer be supported in Requests "
"3.0.0. Please convert the object you've passed in ({!r}) to "
"a string or bytes object in the near future to avoid "
"problems.".format(username),
category=DeprecationWarning,
)
username = str(username)
if not isinstance(password, basestring):
warnings.warn(
"Non-string passwords will no longer be supported in Requests "
"3.0.0. Please convert the object you've passed in ({!r}) to "
"a string or bytes object in the near future to avoid "
"problems.".format(password),
category=DeprecationWarning,
)
password = str(password)
# -- End Removal --
if isinstance(username, str):
username = username.encode('latin1')
if isinstance(password, str):
password = password.encode('latin1')
authstr = 'Basic ' + to_native_string(
b64encode(('%s:%s' % (username, password)).encode('latin1')).strip()
b64encode(b':'.join((username, password))).strip()
)
return authstr

View File

@@ -30,7 +30,7 @@ from .packages.urllib3.exceptions import (
from .exceptions import (
HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError,
ContentDecodingError, ConnectionError, StreamConsumedError)
from ._internal_utils import to_native_string
from ._internal_utils import to_native_string, unicode_is_ascii
from .utils import (
guess_filename, get_auth_from_url, requote_uri,
stream_decode_response_unicode, to_key_val_list, parse_header_links,
@@ -343,6 +343,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
else:
url = unicode(url) if is_py2 else str(url)
# Remove leading whitespaces from url
url = url.lstrip()
# Don't do any URL preparation for non-HTTP schemes like `mailto`,
# `data` etc to work around exceptions from `url_parse`, which
# handles RFC 3986 only.
@@ -365,11 +368,17 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
if not host:
raise InvalidURL("Invalid URL %r: No host supplied" % url)
# Only want to apply IDNA to the hostname
# In general, we want to try IDNA encoding every hostname, as that
# allows users to automatically get the correct behaviour. However,
# were quite strict about IDNA encoding, so certain valid hostnames
# may fail to encode. On failure, we verify the hostname meets a
# minimum standard of only containing ASCII characters, and not starting
# with a wildcard (*), before allowing the unencoded hostname through.
try:
host = idna.encode(host, uts46=True).decode('utf-8')
except (UnicodeError, idna.IDNAError):
raise InvalidURL('URL has an invalid label.')
if not unicode_is_ascii(host) or host.startswith(u'*'):
raise InvalidURL('URL has an invalid label.')
# Carefully reconstruct the network location
netloc = auth or ''
@@ -766,7 +775,7 @@ class Response(object):
raise RuntimeError(
'The content for this response was already consumed')
if self.status_code == 0:
if self.status_code == 0 or self.raw is None:
self._content = None
else:
self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()

View File

@@ -5,7 +5,7 @@ import pytest
import threading
import requests
from tests.testserver.server import Server
from tests.testserver.server import Server, consume_socket_content
from .utils import override_environ
@@ -25,6 +25,117 @@ def test_chunked_upload():
assert r.request.headers['Transfer-Encoding'] == 'chunked'
def test_digestauth_401_count_reset_on_redirect():
"""Ensure we correctly reset num_401_calls after a successful digest auth,
followed by a 302 redirect to another digest auth prompt.
See https://github.com/kennethreitz/requests/issues/1979.
"""
text_401 = (b'HTTP/1.1 401 UNAUTHORIZED\r\n'
b'Content-Length: 0\r\n'
b'WWW-Authenticate: Digest nonce="6bf5d6e4da1ce66918800195d6b9130d"'
b', opaque="372825293d1c26955496c80ed6426e9e", '
b'realm="me@kennethreitz.com", qop=auth\r\n\r\n')
text_302 = (b'HTTP/1.1 302 FOUND\r\n'
b'Content-Length: 0\r\n'
b'Location: /\r\n\r\n')
text_200 = (b'HTTP/1.1 200 OK\r\n'
b'Content-Length: 0\r\n\r\n')
expected_digest = (b'Authorization: Digest username="user", '
b'realm="me@kennethreitz.com", '
b'nonce="6bf5d6e4da1ce66918800195d6b9130d", uri="/"')
auth = requests.auth.HTTPDigestAuth('user', 'pass')
def digest_response_handler(sock):
# Respond to initial GET with a challenge.
request_content = consume_socket_content(sock, timeout=0.5)
assert request_content.startswith(b"GET / HTTP/1.1")
sock.send(text_401)
# Verify we receive an Authorization header in response, then redirect.
request_content = consume_socket_content(sock, timeout=0.5)
assert expected_digest in request_content
sock.send(text_302)
# Verify Authorization isn't sent to the redirected host,
# then send another challenge.
request_content = consume_socket_content(sock, timeout=0.5)
assert b'Authorization:' not in request_content
sock.send(text_401)
# Verify Authorization is sent correctly again, and return 200 OK.
request_content = consume_socket_content(sock, timeout=0.5)
assert expected_digest in request_content
sock.send(text_200)
return request_content
close_server = threading.Event()
server = Server(digest_response_handler, wait_to_close_event=close_server)
with server as (host, port):
url = 'http://{0}:{1}/'.format(host, port)
r = requests.get(url, auth=auth)
# Verify server succeeded in authenticating.
assert r.status_code == 200
# Verify Authorization was sent in final request.
assert 'Authorization' in r.request.headers
assert r.request.headers['Authorization'].startswith('Digest ')
# Verify redirect happened as we expected.
assert r.history[0].status_code == 302
close_server.set()
def test_digestauth_401_only_sent_once():
"""Ensure we correctly respond to a 401 challenge once, and then
stop responding if challenged again.
"""
text_401 = (b'HTTP/1.1 401 UNAUTHORIZED\r\n'
b'Content-Length: 0\r\n'
b'WWW-Authenticate: Digest nonce="6bf5d6e4da1ce66918800195d6b9130d"'
b', opaque="372825293d1c26955496c80ed6426e9e", '
b'realm="me@kennethreitz.com", qop=auth\r\n\r\n')
expected_digest = (b'Authorization: Digest username="user", '
b'realm="me@kennethreitz.com", '
b'nonce="6bf5d6e4da1ce66918800195d6b9130d", uri="/"')
auth = requests.auth.HTTPDigestAuth('user', 'pass')
def digest_failed_response_handler(sock):
# Respond to initial GET with a challenge.
request_content = consume_socket_content(sock, timeout=0.5)
assert request_content.startswith(b"GET / HTTP/1.1")
sock.send(text_401)
# Verify we receive an Authorization header in response, then
# challenge again.
request_content = consume_socket_content(sock, timeout=0.5)
assert expected_digest in request_content
sock.send(text_401)
# Verify the client didn't respond to second challenge.
request_content = consume_socket_content(sock, timeout=0.5)
assert request_content == b''
return request_content
close_server = threading.Event()
server = Server(digest_failed_response_handler, wait_to_close_event=close_server)
with server as (host, port):
url = 'http://{0}:{1}/'.format(host, port)
r = requests.get(url, auth=auth)
# Verify server didn't authenticate us.
assert r.status_code == 401
assert r.history[0].status_code == 401
close_server.set()
_schemes_by_var_prefix = [
('http', ['http']),
('https', ['https']),

View File

@@ -140,6 +140,11 @@ class TestRequests:
data=u"ööö".encode("utf-8")).prepare()
assert isinstance(request.body, bytes)
def test_whitespaces_are_removed_from_url(self):
# Test for issue #3696
request = requests.Request('GET', ' http://example.com').prepare()
assert request.url == 'http://example.com/'
@pytest.mark.parametrize('scheme', ('http://', 'HTTP://', 'hTTp://', 'HttP://'))
def test_mixed_case_scheme_acceptable(self, httpbin, scheme):
s = requests.Session()
@@ -475,6 +480,32 @@ class TestRequests:
r = s.get(url)
assert r.status_code == 200
@pytest.mark.parametrize(
'username, password', (
('user', 'pass'),
(u'имя'.encode('utf-8'), u'пароль'.encode('utf-8')),
(42, 42),
(None, None),
))
def test_set_basicauth(self, httpbin, username, password):
auth = (username, password)
url = httpbin('get')
r = requests.Request('GET', url, auth=auth)
p = r.prepare()
assert p.headers['Authorization'] == _basic_auth_str(username, password)
def test_basicauth_encodes_byte_strings(self):
"""Ensure b'test' formats as the byte string "test" rather
than the unicode string "b'test'" in Python 3.
"""
auth = (b'\xc5\xafsername', b'test\xc6\xb6')
r = requests.Request('GET', 'http://localhost', auth=auth)
p = r.prepare()
assert p.headers['Authorization'] == 'Basic xa9zZXJuYW1lOnRlc3TGtg=='
@pytest.mark.parametrize(
'url, exception', (
# Connecting to an unknown domain should raise a ConnectionError
@@ -1075,6 +1106,10 @@ class TestRequests:
total_seconds = ((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6)
assert total_seconds > 0.0
def test_empty_response_has_content_none(self):
r = requests.Response()
assert r.content is None
def test_response_is_iterable(self):
r = requests.Response()
io = StringIO.StringIO('abc')
@@ -1568,10 +1603,15 @@ class TestRequests:
self._patch_adapter_gzipped_redirect(s, url)
s.get(url)
def test_basic_auth_str_is_always_native(self):
s = _basic_auth_str("test", "test")
@pytest.mark.parametrize(
'username, password, auth_str', (
('test', 'test', 'Basic dGVzdDp0ZXN0'),
(u'имя'.encode('utf-8'), u'пароль'.encode('utf-8'), 'Basic 0LjQvNGPOtC/0LDRgNC+0LvRjA=='),
))
def test_basic_auth_str_is_always_native(self, username, password, auth_str):
s = _basic_auth_str(username, password)
assert isinstance(s, builtin_str)
assert s == "Basic dGVzdDp0ZXN0"
assert s == auth_str
def test_requests_history_is_saved(self, httpbin):
r = requests.get(httpbin('redirect/5'))
@@ -1665,6 +1705,42 @@ class TestRequests:
resp.close()
assert resp.raw.closed
def test_empty_stream_with_auth_does_not_set_content_length_header(self, httpbin):
"""Ensure that a byte stream with size 0 will not set both a Content-Length
and Transfer-Encoding header.
"""
auth = ('user', 'pass')
url = httpbin('post')
file_obj = io.BytesIO(b'')
r = requests.Request('POST', url, auth=auth, data=file_obj)
prepared_request = r.prepare()
assert 'Transfer-Encoding' in prepared_request.headers
assert 'Content-Length' not in prepared_request.headers
def test_stream_with_auth_does_not_set_transfer_encoding_header(self, httpbin):
"""Ensure that a byte stream with size > 0 will not set both a Content-Length
and Transfer-Encoding header.
"""
auth = ('user', 'pass')
url = httpbin('post')
file_obj = io.BytesIO(b'test data')
r = requests.Request('POST', url, auth=auth, data=file_obj)
prepared_request = r.prepare()
assert 'Transfer-Encoding' not in prepared_request.headers
assert 'Content-Length' in prepared_request.headers
def test_chunked_upload_does_not_set_content_length_header(self, httpbin):
"""Ensure that requests with a generator body stream using
Transfer-Encoding: chunked, not a Content-Length header.
"""
data = (i for i in [b'a', b'b', b'c'])
url = httpbin('post')
r = requests.Request('POST', url, data=data)
prepared_request = r.prepare()
assert 'Transfer-Encoding' in prepared_request.headers
assert 'Content-Length' not in prepared_request.headers
class TestCaseInsensitiveDict:
@pytest.mark.parametrize(
@@ -2093,6 +2169,7 @@ class TestPreparingURLs(object):
(
('http://google.com', 'http://google.com/'),
(u'http://ジェーピーニック.jp', u'http://xn--hckqz9bzb1cyrb.jp/'),
(u'http://xn--n3h.net/', u'http://xn--n3h.net/'),
(
u'http://ジェーピーニック.jp'.encode('utf-8'),
u'http://xn--hckqz9bzb1cyrb.jp/'
@@ -2113,6 +2190,18 @@ class TestPreparingURLs(object):
u'http://Königsgäßchen.de/straße'.encode('utf-8'),
u'http://xn--knigsgchen-b4a3dun.de/stra%C3%9Fe'
),
(
b'http://xn--n3h.net/',
u'http://xn--n3h.net/'
),
(
b'http://[1200:0000:ab00:1234:0000:2552:7777:1313]:12345/',
u'http://[1200:0000:ab00:1234:0000:2552:7777:1313]:12345/'
),
(
u'http://[1200:0000:ab00:1234:0000:2552:7777:1313]:12345/',
u'http://[1200:0000:ab00:1234:0000:2552:7777:1313]:12345/'
)
)
)
def test_preparing_url(self, url, expected):
@@ -2127,9 +2216,81 @@ class TestPreparingURLs(object):
b"http://*",
u"http://*.google.com",
u"http://*",
u"http://☃.net/"
)
)
def test_preparing_bad_url(self, url):
r = requests.Request('GET', url=url)
with pytest.raises(requests.exceptions.InvalidURL):
r.prepare()
@pytest.mark.parametrize(
'input, expected',
(
(
b"http+unix://%2Fvar%2Frun%2Fsocket/path",
u"http+unix://%2fvar%2frun%2fsocket/path",
),
(
u"http+unix://%2Fvar%2Frun%2Fsocket/path",
u"http+unix://%2fvar%2frun%2fsocket/path",
),
(
b"mailto:user@example.org",
u"mailto:user@example.org",
),
(
u"mailto:user@example.org",
u"mailto:user@example.org",
),
(
b"data:SSDimaUgUHl0aG9uIQ==",
u"data:SSDimaUgUHl0aG9uIQ==",
)
)
)
def test_url_mutation(self, input, expected):
"""
This test validates that we correctly exclude some URLs from
preparation, and that we handle others. Specifically, it tests that
any URL whose scheme doesn't begin with "http" is left alone, and
those whose scheme *does* begin with "http" are mutated.
"""
r = requests.Request('GET', url=input)
p = r.prepare()
assert p.url == expected
@pytest.mark.parametrize(
'input, params, expected',
(
(
b"http+unix://%2Fvar%2Frun%2Fsocket/path",
{"key": "value"},
u"http+unix://%2fvar%2frun%2fsocket/path?key=value",
),
(
u"http+unix://%2Fvar%2Frun%2Fsocket/path",
{"key": "value"},
u"http+unix://%2fvar%2frun%2fsocket/path?key=value",
),
(
b"mailto:user@example.org",
{"key": "value"},
u"mailto:user@example.org",
),
(
u"mailto:user@example.org",
{"key": "value"},
u"mailto:user@example.org",
),
)
)
def test_parameters_for_nonstandard_schemes(self, input, params, expected):
"""
Setting paramters for nonstandard schemes is allowed if those schemes
begin with "http", and is forbidden otherwise.
"""
r = requests.Request('GET', url=input, params=params)
p = r.prepare()
assert p.url == expected

View File

@@ -17,6 +17,7 @@ from requests.utils import (
to_key_val_list, to_native_string,
unquote_header_value, unquote_unreserved,
urldefragauth, add_dict_to_cookiejar)
from requests._internal_utils import unicode_is_ascii
from .compat import StringIO, cStringIO
@@ -515,6 +516,7 @@ def test_should_bypass_proxies(url, expected, monkeypatch):
monkeypatch.setenv('NO_PROXY', '192.168.0.0/24,127.0.0.1,localhost.localdomain,172.16.1.1')
assert should_bypass_proxies(url) == expected
@pytest.mark.parametrize(
'cookiejar', (
compat.cookielib.CookieJar(),
@@ -529,3 +531,14 @@ def test_add_dict_to_cookiejar(cookiejar):
cj = add_dict_to_cookiejar(cookiejar, cookiedict)
cookies = dict((cookie.name, cookie.value) for cookie in cj)
assert cookiedict == cookies
@pytest.mark.parametrize(
'value, expected', (
(u'test', True),
(u'æíöû', False),
(u'ジェーピーニック', False),
)
)
def test_unicode_is_ascii(value, expected):
assert unicode_is_ascii(value) is expected