From 3a4ef8165fb2951781a7bcc4189e90faf26caf2d Mon Sep 17 00:00:00 2001 From: SVN-Git Migration Date: Thu, 8 Oct 2015 13:41:22 -0700 Subject: Imported Upstream version 0.11.2 --- HISTORY.rst | 11 + MANIFEST.in | 2 +- PKG-INFO | 31 +- README.rst | 4 +- requests.egg-info/PKG-INFO | 31 +- requests.egg-info/SOURCES.txt | 6 +- requests/__init__.py | 4 +- requests/auth.py | 20 +- requests/exceptions.py | 5 +- requests/models.py | 101 ++-- requests/packages/urllib3/__init__.py | 2 +- requests/packages/urllib3/connectionpool.py | 9 +- requests/sessions.py | 3 +- requests/utils.py | 73 ++- setup.py | 4 +- tests/test_requests.py | 846 ++++++++++++++++++++++++++++ tests/test_requests_async.py | 68 +++ tests/test_requests_ext.py | 112 ++++ tests/test_requests_https.py | 31 + 19 files changed, 1274 insertions(+), 89 deletions(-) create mode 100755 tests/test_requests.py create mode 100755 tests/test_requests_async.py create mode 100644 tests/test_requests_ext.py create mode 100755 tests/test_requests_https.py diff --git a/HISTORY.rst b/HISTORY.rst index e73903d..153cbb5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,17 @@ +.. :changelog: + History ------- +0.11.2 (2012-04-22) ++++++++++++++++++++ + +- Attempt to use the OS's certificate bundle if ``certifi`` isn't available. +- Infinite digest auth redirect fix +- Multi-part file upload improvements +- Fix decoding of invalid %encodings in URLs +- If there is no content in a response don't throw an error the second time that content is attempted to be read. +- Upload data on redirects. 0.11.1 (2012-03-30) +++++++++++++++++++ diff --git a/MANIFEST.in b/MANIFEST.in index ef350d0..0417d6f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include README.rst LICENSE NOTICE HISTORY.rst test_requests.py +include README.rst LICENSE NOTICE HISTORY.rst tests/*.py diff --git a/PKG-INFO b/PKG-INFO index 68c9ccc..dc8b700 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,14 +1,27 @@ Metadata-Version: 1.0 Name: requests -Version: 0.11.1 +Version: 0.11.2 Summary: Python HTTP for Humans. Home-page: http://python-requests.org Author: Kenneth Reitz Author-email: me@kennethreitz.com -License: ISC +License: Copyright (c) 2012 Kenneth Reitz. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Description: Requests: HTTP for Humans ========================= + .. image:: https://secure.travis-ci.org/kennethreitz/requests.png?branch=develop Requests is an ISC Licensed HTTP library, written in Python, for human @@ -34,8 +47,7 @@ Description: Requests: HTTP for Humans See `the same code, without Requests `_. - Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**, - **PATCH**, and **DELETE** HTTP requests. You can add headers, form data, + Requests allow you to send HTTP/1.1 requests. You can add headers, form data, multipart files, and parameters with simple Python dictionaries, and access the response data in the same way. It's powered by httplib and `urllib3 `_, but it does all the hard work and crazy @@ -84,9 +96,20 @@ Description: Requests: HTTP for Humans .. _AUTHORS: https://github.com/kennethreitz/requests/blob/develop/AUTHORS.rst + .. :changelog: + History ------- + 0.11.2 (2012-04-22) + +++++++++++++++++++ + + - Attempt to use the OS's certificate bundle if ``certifi`` isn't available. + - Infinite digest auth redirect fix + - Multi-part file upload improvements + - Fix decoding of invalid %encodings in URLs + - If there is no content in a response don't throw an error the second time that content is attempted to be read. + - Upload data on redirects. 0.11.1 (2012-03-30) +++++++++++++++++++ diff --git a/README.rst b/README.rst index 7ea6ab6..46f1862 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,7 @@ Requests: HTTP for Humans ========================= + .. image:: https://secure.travis-ci.org/kennethreitz/requests.png?branch=develop Requests is an ISC Licensed HTTP library, written in Python, for human @@ -26,8 +27,7 @@ Things shouldn't be this way. Not in Python. See `the same code, without Requests `_. -Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**, -**PATCH**, and **DELETE** HTTP requests. You can add headers, form data, +Requests allow you to send HTTP/1.1 requests. You can add headers, form data, multipart files, and parameters with simple Python dictionaries, and access the response data in the same way. It's powered by httplib and `urllib3 `_, but it does all the hard work and crazy diff --git a/requests.egg-info/PKG-INFO b/requests.egg-info/PKG-INFO index 68c9ccc..dc8b700 100644 --- a/requests.egg-info/PKG-INFO +++ b/requests.egg-info/PKG-INFO @@ -1,14 +1,27 @@ Metadata-Version: 1.0 Name: requests -Version: 0.11.1 +Version: 0.11.2 Summary: Python HTTP for Humans. Home-page: http://python-requests.org Author: Kenneth Reitz Author-email: me@kennethreitz.com -License: ISC +License: Copyright (c) 2012 Kenneth Reitz. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Description: Requests: HTTP for Humans ========================= + .. image:: https://secure.travis-ci.org/kennethreitz/requests.png?branch=develop Requests is an ISC Licensed HTTP library, written in Python, for human @@ -34,8 +47,7 @@ Description: Requests: HTTP for Humans See `the same code, without Requests `_. - Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**, - **PATCH**, and **DELETE** HTTP requests. You can add headers, form data, + Requests allow you to send HTTP/1.1 requests. You can add headers, form data, multipart files, and parameters with simple Python dictionaries, and access the response data in the same way. It's powered by httplib and `urllib3 `_, but it does all the hard work and crazy @@ -84,9 +96,20 @@ Description: Requests: HTTP for Humans .. _AUTHORS: https://github.com/kennethreitz/requests/blob/develop/AUTHORS.rst + .. :changelog: + History ------- + 0.11.2 (2012-04-22) + +++++++++++++++++++ + + - Attempt to use the OS's certificate bundle if ``certifi`` isn't available. + - Infinite digest auth redirect fix + - Multi-part file upload improvements + - Fix decoding of invalid %encodings in URLs + - If there is no content in a response don't throw an error the second time that content is attempted to be read. + - Upload data on redirects. 0.11.1 (2012-03-30) +++++++++++++++++++ diff --git a/requests.egg-info/SOURCES.txt b/requests.egg-info/SOURCES.txt index 71152be..f228744 100644 --- a/requests.egg-info/SOURCES.txt +++ b/requests.egg-info/SOURCES.txt @@ -39,4 +39,8 @@ requests/packages/urllib3/util.py requests/packages/urllib3/packages/__init__.py requests/packages/urllib3/packages/six.py requests/packages/urllib3/packages/mimetools_choose_boundary/__init__.py -requests/packages/urllib3/packages/ssl_match_hostname/__init__.py \ No newline at end of file +requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +tests/test_requests.py +tests/test_requests_async.py +tests/test_requests_ext.py +tests/test_requests_https.py \ No newline at end of file diff --git a/requests/__init__.py b/requests/__init__.py index 0ace12b..96ef774 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -15,8 +15,8 @@ requests """ __title__ = 'requests' -__version__ = '0.11.1' -__build__ = 0x001101 +__version__ = '0.11.2' +__build__ = 0x001102 __author__ = 'Kenneth Reitz' __license__ = 'ISC' __copyright__ = 'Copyright 2012 Kenneth Reitz' diff --git a/requests/auth.py b/requests/auth.py index 385dd27..353180a 100644 --- a/requests/auth.py +++ b/requests/auth.py @@ -56,6 +56,8 @@ class HTTPDigestAuth(AuthBase): def handle_401(self, r): """Takes the given response and tries digest-auth, if needed.""" + r.request.deregister_hook('response', self.handle_401) + s_auth = r.headers.get('www-authenticate', '') if 'digest' in s_auth.lower(): @@ -74,21 +76,21 @@ class HTTPDigestAuth(AuthBase): algorithm = algorithm.upper() # lambdas assume digest modules are imported at the top level if algorithm == 'MD5': - def h(x): + def md5_utf8(x): if isinstance(x, str): x = x.encode('utf-8') return hashlib.md5(x).hexdigest() - H = h + hash_utf8 = md5_utf8 elif algorithm == 'SHA': - def h(x): + def sha_utf8(x): if isinstance(x, str): x = x.encode('utf-8') return hashlib.sha1(x).hexdigest() - H = h + hash_utf8 = sha_utf8 # XXX MD5-sess - KD = lambda s, d: H("%s:%s" % (s, d)) + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) - if H is None: + if hash_utf8 is None: return None # XXX not implemented yet @@ -115,10 +117,10 @@ class HTTPDigestAuth(AuthBase): s += randombytes(8) cnonce = (hashlib.sha1(s).hexdigest()[:16]) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2)) - respdig = KD(H(A1), noncebit) + noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2)) + respdig = KD(hash_utf8(A1), noncebit) elif qop is None: - respdig = KD(H(A1), "%s:%s" % (nonce, H(A2))) + respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2))) else: # XXX handle auth-int. return None diff --git a/requests/exceptions.py b/requests/exceptions.py index 3c262e3..1cffa80 100644 --- a/requests/exceptions.py +++ b/requests/exceptions.py @@ -35,4 +35,7 @@ class MissingSchema(RequestException, ValueError): """The URL schema (e.g. http or https) is missing.""" class InvalidSchema(RequestException, ValueError): - """See defaults.py for valid schemas.""" \ No newline at end of file + """See defaults.py for valid schemas.""" + +class InvalidURL(RequestException, ValueError): + """ The URL provided was somehow invalid. """ \ No newline at end of file diff --git a/requests/models.py b/requests/models.py index 70e3503..60f58d2 100644 --- a/requests/models.py +++ b/requests/models.py @@ -16,7 +16,7 @@ from .status_codes import codes from .auth import HTTPBasicAuth, HTTPProxyAuth from .packages.urllib3.response import HTTPResponse -from .packages.urllib3.exceptions import MaxRetryError +from .packages.urllib3.exceptions import MaxRetryError, LocationParseError from .packages.urllib3.exceptions import SSLError as _SSLError from .packages.urllib3.exceptions import HTTPError as _HTTPError from .packages.urllib3 import connectionpool, poolmanager @@ -24,10 +24,11 @@ from .packages.urllib3.filepost import encode_multipart_formdata from .defaults import SCHEMAS from .exceptions import ( ConnectionError, HTTPError, RequestException, Timeout, TooManyRedirects, - URLRequired, SSLError, MissingSchema, InvalidSchema) + URLRequired, SSLError, MissingSchema, InvalidSchema, InvalidURL) from .utils import ( get_encoding_from_headers, stream_untransfer, guess_filename, requote_uri, - dict_from_string, stream_decode_response_unicode, get_netrc_auth) + dict_from_string, stream_decode_response_unicode, get_netrc_auth, + DEFAULT_CA_BUNDLE_PATH) from .compat import ( urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes, SimpleCookie, is_py2) @@ -61,6 +62,7 @@ class Request(object): proxies=None, hooks=None, config=None, + prefetch=False, _poolmanager=None, verify=None, session=None, @@ -80,7 +82,7 @@ class Request(object): self.headers = dict(headers or []) #: Dictionary of files to multipart upload (``{filename: content}``). - self.files = files + self.files = None #: HTTP Method to use. self.method = method @@ -90,7 +92,8 @@ class Request(object): self.data = None #: Dictionary or byte of querystring data to attach to the - #: :class:`Request `. + #: :class:`Request `. The dictionary values can be lists for representing + #: multivalued query parameters. self.params = None #: True if :class:`Request ` is part of a redirect chain (disables history @@ -113,6 +116,7 @@ class Request(object): self.data, self._enc_data = self._encode_params(data) self.params, self._enc_params = self._encode_params(params) + self.files, self._enc_files = self._encode_files(files) #: :class:`Response ` instance, containing #: content and metadata of HTTP Response, once :attr:`sent `. @@ -147,6 +151,9 @@ class Request(object): #: SSL Certificate self.cert = cert + #: Prefetch response content + self.prefetch = prefetch + if headers: headers = CaseInsensitiveDict(self.headers) else: @@ -328,6 +335,29 @@ class Request(object): else: return data, data + def _encode_files(self,files): + + if (not files) or isinstance(self.data, str): + return None, None + + try: + fields = self.data.copy() + except AttributeError: + fields = dict(self.data) + + for (k, v) in list(files.items()): + # support for explicit filename + if isinstance(v, (tuple, list)): + fn, fp = v + else: + fn = guess_filename(v) or k + fp = v + fields.update({k: (fn, fp.read())}) + + (body, content_type) = encode_multipart_formdata(fields) + + return files, (body, content_type) + @property def full_url(self): """Build the actual URL to use.""" @@ -407,7 +437,18 @@ class Request(object): def register_hook(self, event, hook): """Properly register a hook.""" - return self.hooks[event].append(hook) + self.hooks[event].append(hook) + + def deregister_hook(self,event,hook): + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False def send(self, anyway=False, prefetch=False): """Sends the request. Returns True of successful, False if not. @@ -435,26 +476,7 @@ class Request(object): # Multi-part file uploads. if self.files: - if not isinstance(self.data, str): - - try: - fields = self.data.copy() - except AttributeError: - fields = dict(self.data) - - for (k, v) in list(self.files.items()): - # support for explicit filename - if isinstance(v, (tuple, list)): - fn, fp = v - else: - fn = guess_filename(v) or k - fp = v - fields.update({k: (fn, fp.read())}) - - (body, content_type) = encode_multipart_formdata(fields) - else: - pass - # TODO: Conflict? + (body, content_type) = self._enc_files else: if self.data: @@ -496,10 +518,13 @@ class Request(object): self.__dict__.update(r.__dict__) else: # Check to see if keep_alive is allowed. - if self.config.get('keep_alive'): - conn = self._poolmanager.connection_from_url(url) - else: - conn = connectionpool.connection_from_url(url) + try: + if self.config.get('keep_alive'): + conn = self._poolmanager.connection_from_url(url) + else: + conn = connectionpool.connection_from_url(url) + except LocationParseError as e: + raise InvalidURL(e) if url.startswith('https') and self.verify: @@ -513,13 +538,15 @@ class Request(object): if not cert_loc and self.config.get('trust_env'): cert_loc = os.environ.get('REQUESTS_CA_BUNDLE') - # Curl compatiblity. + # Curl compatibility. if not cert_loc and self.config.get('trust_env'): cert_loc = os.environ.get('CURL_CA_BUNDLE') - # Use the awesome certifi list. if not cert_loc: - cert_loc = __import__('certifi').where() + cert_loc = DEFAULT_CA_BUNDLE_PATH + + if not cert_loc: + raise Exception("Could not find a suitable SSL CA certificate bundle.") conn.cert_reqs = 'CERT_REQUIRED' conn.ca_certs = cert_loc @@ -604,7 +631,7 @@ class Request(object): self.__dict__.update(r.__dict__) # If prefetch is True, mark content as consumed. - if prefetch: + if prefetch or self.prefetch: # Save the response. self.response.content @@ -623,7 +650,7 @@ class Response(object): def __init__(self): - self._content = None + self._content = False self._content_consumed = False #: Integer Code of responded HTTP Status. @@ -679,7 +706,7 @@ class Response(object): return False return True - def iter_content(self, chunk_size=10 * 1024, decode_unicode=False): + def iter_content(self, chunk_size=1, decode_unicode=False): """Iterates over the response data. This avoids reading the content at once into memory for large responses. The chunk size is the number of bytes it should read into memory. This is not necessarily the @@ -736,7 +763,7 @@ class Response(object): def content(self): """Content of the response, in bytes.""" - if self._content is None: + if self._content is False: # Read the contents. try: if self._content_consumed: diff --git a/requests/packages/urllib3/__init__.py b/requests/packages/urllib3/__init__.py index 2d6fece..81e76f5 100644 --- a/requests/packages/urllib3/__init__.py +++ b/requests/packages/urllib3/__init__.py @@ -10,7 +10,7 @@ urllib3 - Thread-safe connection pooling and re-using. __author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' __license__ = 'MIT' -__version__ = '1.3' +__version__ = 'dev' from .connectionpool import ( diff --git a/requests/packages/urllib3/connectionpool.py b/requests/packages/urllib3/connectionpool.py index c3cb3b1..336aa77 100644 --- a/requests/packages/urllib3/connectionpool.py +++ b/requests/packages/urllib3/connectionpool.py @@ -260,10 +260,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): httplib_response = conn.getresponse() - log.debug("\"%s %s %s\" %s %s" % - (method, url, - conn._http_vsn_str, # pylint: disable-msg=W0212 - httplib_response.status, httplib_response.length)) + # AppEngine doesn't have a version attr. + http_version = getattr(conn, '_http_vsn_str', 'HTTP/?'), + log.debug("\"%s %s %s\" %s %s" % (method, url, http_version, + httplib_response.status, + httplib_response.length)) return httplib_response diff --git a/requests/sessions.py b/requests/sessions.py index 94c94bf..0e43030 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -52,7 +52,7 @@ class Session(object): __attrs__ = [ 'headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks', - 'params', 'config', 'verify', 'cert'] + 'params', 'config', 'verify', 'cert', 'prefetch'] def __init__(self, @@ -179,6 +179,7 @@ class Session(object): allow_redirects=allow_redirects, proxies=proxies, config=config, + prefetch=prefetch, verify=verify, cert=cert, _poolmanager=self.poolmanager diff --git a/requests/utils.py b/requests/utils.py index b722d99..925547a 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -21,9 +21,36 @@ from .compat import parse_http_list as _parse_list_header from .compat import quote, cookielib, SimpleCookie, is_py2, urlparse from .compat import basestring, bytes, str +CERTIFI_BUNDLE_PATH = None +try: + # see if requests's own CA certificate bundle is installed + import certifi + CERTIFI_BUNDLE_PATH = certifi.where() +except ImportError: + pass NETRC_FILES = ('.netrc', '_netrc') +# common paths for the OS's CA certificate bundle +POSSIBLE_CA_BUNDLE_PATHS = [ + # Red Hat, CentOS, Fedora and friends (provided by the ca-certificates package): + '/etc/pki/tls/certs/ca-bundle.crt', + # Ubuntu, Debian, and friends (provided by the ca-certificates package): + '/etc/ssl/certs/ca-certificates.crt', + # FreeBSD (provided by the ca_root_nss package): + '/usr/local/share/certs/ca-root-nss.crt', +] + +def get_os_ca_bundle_path(): + """Try to pick an available CA certificate bundle provided by the OS.""" + for path in POSSIBLE_CA_BUNDLE_PATHS: + if os.path.exists(path): + return path + return None + +# if certifi is installed, use its CA bundle; +# otherwise, try and use the OS bundle +DEFAULT_CA_BUNDLE_PATH = CERTIFI_BUNDLE_PATH or get_os_ca_bundle_path() def dict_to_sequence(d): """Returns an internal sequence dictionary update.""" @@ -37,34 +64,40 @@ def dict_to_sequence(d): def get_netrc_auth(url): """Returns the Requests tuple auth for a given url from netrc.""" - locations = (os.path.expanduser('~/{0}'.format(f)) for f in NETRC_FILES) - netrc_path = None + try: + locations = (os.path.expanduser('~/{0}'.format(f)) for f in NETRC_FILES) + netrc_path = None - for loc in locations: - if os.path.exists(loc) and not netrc_path: - netrc_path = loc + for loc in locations: + if os.path.exists(loc) and not netrc_path: + netrc_path = loc - # Abort early if there isn't one. - if netrc_path is None: - return netrc_path + # Abort early if there isn't one. + if netrc_path is None: + return netrc_path - ri = urlparse(url) + ri = urlparse(url) - # Strip port numbers from netloc - host = ri.netloc.split(':')[0] + # Strip port numbers from netloc + host = ri.netloc.split(':')[0] - try: - _netrc = netrc(netrc_path).authenticators(host) - if _netrc: - # Return with login / password - login_i = (0 if _netrc[0] else 1) - return (_netrc[login_i], _netrc[2]) - except (NetrcParseError, IOError, AttributeError): - # If there was a parsing error or a permissions issue reading the file, - # we'll just skip netrc auth + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = (0 if _netrc[0] else 1) + return (_netrc[login_i], _netrc[2]) + except (NetrcParseError, IOError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth + pass + + # AppEngine hackiness. + except AttributeError: pass + def dict_from_string(s): """Returns a MultiDict with Cookies.""" diff --git a/setup.py b/setup.py index 849ab83..3133a97 100755 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ if sys.argv[-1] == 'publish': sys.exit() if sys.argv[-1] == 'test': - os.system('python test_requests.py') + os.system('python tests/test_requests.py') sys.exit() required = ['certifi>=0.0.7',] @@ -51,7 +51,7 @@ setup( package_data={'': ['LICENSE', 'NOTICE']}, include_package_data=True, install_requires=required, - license='ISC', + license=open("LICENSE").read(), classifiers=( 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', diff --git a/tests/test_requests.py b/tests/test_requests.py new file mode 100755 index 0000000..ac8329e --- /dev/null +++ b/tests/test_requests.py @@ -0,0 +1,846 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# from __future__ import unicode_literals + +# Path hack. +import sys +import os +sys.path.insert(0, os.path.abspath('..')) + +import json +import os +import sys +import unittest +import pickle + +import requests +from requests.compat import str, StringIO +# import envoy +from requests import HTTPError +from requests import get, post, head, put +from requests.auth import HTTPBasicAuth, HTTPDigestAuth + +if 'HTTPBIN_URL' not in os.environ: + os.environ['HTTPBIN_URL'] = 'http://httpbin.org/' + +HTTPBIN_URL = os.environ.get('HTTPBIN_URL') + + +def httpbin(*suffix): + """Returns url for HTTPBIN resource.""" + return HTTPBIN_URL + '/'.join(suffix) + + +SERVICES = (httpbin, ) + +_httpbin = False + + +class TestSetup(object): + """Requests test cases.""" + + # It goes to eleven. + _multiprocess_can_split_ = True + + def setUp(self): + + global _httpbin + + if (not 'HTTPBIN_URL' in os.environ) and not _httpbin: + # c = envoy.connect('httpbin %s' % (PORT)) + # time.sleep(1) + _httpbin = True + + +class RequestsTestSuite(TestSetup, unittest.TestCase): + """Requests test cases.""" + + def test_entry_points(self): + + requests.session + requests.session().get + requests.session().head + requests.get + requests.head + requests.put + requests.patch + requests.post + + def test_invalid_url(self): + self.assertRaises(ValueError, get, 'hiwpefhipowhefopw') + + def test_path_is_not_double_encoded(self): + request = requests.Request("http://0.0.0.0/get/test case") + + self.assertEqual(request.path_url, "/get/test%20case") + + def test_HTTP_200_OK_GET(self): + r = get(httpbin('get')) + self.assertEqual(r.status_code, 200) + + def test_response_sent(self): + r = get(httpbin('get')) + + self.assertTrue(r.request.sent) + + def test_HTTP_302_ALLOW_REDIRECT_GET(self): + r = get(httpbin('redirect', '1')) + self.assertEqual(r.status_code, 200) + + def test_HTTP_302_GET(self): + r = get(httpbin('redirect', '1'), allow_redirects=False) + self.assertEqual(r.status_code, 302) + + def test_HTTP_200_OK_GET_WITH_PARAMS(self): + heads = {'User-agent': 'Mozilla/5.0'} + + r = get(httpbin('user-agent'), headers=heads) + + assert heads['User-agent'] in r.text + self.assertEqual(r.status_code, 200) + + def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self): + heads = {'User-agent': 'Mozilla/5.0'} + + r = get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads) + self.assertEqual(r.status_code, 200) + + # def test_unicode_headers(self): + # # Simply calling requests with a unicode instance should simply work + # # when the characters are all representable using latin-1: + # heads = { u'User-Agent': u'Requests Test Suite' } + # requests.get(url=httpbin('get'), headers=heads) + + # # Characters outside latin-1 should raise an exception: + # heads = { u'User-Agent': u'\u30cd\u30c3\u30c8\u30ef\u30fc\u30af' } + # self.assertRaises(UnicodeEncodeError, requests.get, + # url=httpbin('get'), headers=heads) + + # def test_session_with_escaped_url(self): + # # Test a URL that contains percent-escaped characters + # # This URL should not be modified (double-escaped) + # # Tests: + # # - Quoted illegal characters ("%20" (' '), "%3C" ('<'), "%3E" ('>')) + # # - Quoted reserved characters ("%25" ('%'), "%23" ('#'), "%2F" ('/')) + # # - Quoted non-ASCII characters ("%C3%98", "%C3%A5") + # path_fully_escaped = '%3Ca%25b%23c%2Fd%3E/%C3%98%20%C3%A5' + # url = httpbin('get/' + path_fully_escaped) + # response = get(url) + # self.assertEqual(response.url, httpbin('get/' + path_fully_escaped)) + + # # Test that illegal characters in a path get properly percent-escaped + # # Tests: + # # - Bare illegal characters (space, '<') + # # - Bare non-ASCII characters ('\u00d8') + # path = u' 127, + then unicode(url) breaks. + ''' + r = requests.get('http://www.marketwire.com/mw/release.' + + 'do?id=1628202&sourceType=3') + assert r.ok + + def test_unicode_url_outright(self): + '''This url visits in my browser''' + r = requests.get('http://www.marketwire.com/press-release/' + + 'jp-morgan-behauptet-sich-der-spitze-euro' + + 'p%C3%A4ischer-anleihe-analysten-laut-umf' + + 'rageergebnissen-1628202.htm') + assert r.ok + + def test_redirect_encoding(self): + '''This url redirects to + http://www.dealipedia.com/deal_view_investment.php?r=20012''' + + r = requests.get('http://feedproxy.google.com/~r/Dealipedia' + + 'News/~3/BQtUJRJeZlo/deal_view_investment.' + + 'php') + assert r.ok + + + + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/test_requests_https.py b/tests/test_requests_https.py new file mode 100755 index 0000000..c6ea8f3 --- /dev/null +++ b/tests/test_requests_https.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys, os +import json +import unittest + +# Path hack. +sys.path.insert(0, os.path.abspath('..')) +import requests + +class HTTPSTest(unittest.TestCase): + """Smoke test for https functionality.""" + + smoke_url = "https://github.com" + + def perform_smoke_test(self, verify=False): + result = requests.get(self.smoke_url, verify=verify) + self.assertEqual(result.status_code, 200) + + def test_smoke(self): + """Smoke test without verification.""" + self.perform_smoke_test(verify=False) + + def test_smoke_verified(self): + """Smoke test with SSL verification.""" + self.perform_smoke_test(verify=True) + + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3