From c0b4f11174df5009b8c0cc9268ed2b2bc7796e1f Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 09:39:37 +0000 Subject: New upstream release --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index a515f72..650f914 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium + + * New upstream release + + -- Daniele Tricoli Sat, 24 May 2014 11:07:10 +0200 + python-urllib3 (1.8-2) unstable; urgency=medium * Team upload. -- cgit v1.2.3 From 2dbb15409aa65fdd002d9ea105b0065882bc9309 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 14:25:34 +0000 Subject: Refreshed 01_do-not-use-embedded-python-six.patch --- debian/changelog | 4 +++- .../01_do-not-use-embedded-python-six.patch | 27 +++++++++++----------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/debian/changelog b/debian/changelog index 650f914..0cee92f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * New upstream release + * debian/patches/01_do-not-use-embedded-python-six.patch + - Refreshed - -- Daniele Tricoli Sat, 24 May 2014 11:07:10 +0200 + -- Daniele Tricoli Sat, 24 May 2014 16:24:14 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/01_do-not-use-embedded-python-six.patch b/debian/patches/01_do-not-use-embedded-python-six.patch index 95d6de1..f95a1e1 100644 --- a/debian/patches/01_do-not-use-embedded-python-six.patch +++ b/debian/patches/01_do-not-use-embedded-python-six.patch @@ -1,7 +1,7 @@ Description: Do not use embedded copy of python-six. Author: Daniele Tricoli Forwarded: not-needed -Last-Update: 2014-03-31 +Last-Update: 2014-05-24 --- a/test/test_collections.py +++ b/test/test_collections.py @@ -16,7 +16,7 @@ Last-Update: 2014-03-31 --- a/urllib3/connectionpool.py +++ b/urllib3/connectionpool.py -@@ -30,7 +30,7 @@ +@@ -32,7 +32,7 @@ ProxyError, ) from .packages.ssl_match_hostname import CertificateError @@ -49,17 +49,6 @@ Last-Update: 2014-03-31 from .util import is_fp_closed ---- a/urllib3/util.py -+++ b/urllib3/util.py -@@ -32,7 +32,7 @@ - except ImportError: - pass - --from .packages import six -+import six - from .exceptions import LocationParseError, SSLError, TimeoutStateError - - --- a/test/test_filepost.py +++ b/test/test_filepost.py @@ -2,7 +2,7 @@ @@ -73,7 +62,7 @@ Last-Update: 2014-03-31 BOUNDARY = '!! test boundary !!' --- a/dummyserver/handlers.py +++ b/dummyserver/handlers.py -@@ -186,7 +186,7 @@ +@@ -190,7 +190,7 @@ """ import tornado.httputil import email.utils @@ -126,3 +115,13 @@ Last-Update: 2014-03-31 from .util import ( assert_fingerprint, resolve_cert_reqs, +--- a/urllib3/util/request.py ++++ b/urllib3/util/request.py +@@ -1,6 +1,6 @@ + from base64 import b64encode + +-from ..packages import six ++import six + + + ACCEPT_ENCODING = 'gzip,deflate' -- cgit v1.2.3 From 8ef7b13ab10cdc5d29eb8c3489ecf47aa62edf02 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 17:17:39 +0000 Subject: Refreshed 02_require-cert-verification.patch --- debian/changelog | 4 +++- debian/patches/02_require-cert-verification.patch | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/debian/changelog b/debian/changelog index 0cee92f..495ab62 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,8 +3,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * New upstream release * debian/patches/01_do-not-use-embedded-python-six.patch - Refreshed + * debian/patches/02_require-cert-verification.patch + - Refreshed - -- Daniele Tricoli Sat, 24 May 2014 16:24:14 +0200 + -- Daniele Tricoli Sat, 24 May 2014 19:12:41 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/02_require-cert-verification.patch b/debian/patches/02_require-cert-verification.patch index 14b6a73..1c09012 100644 --- a/debian/patches/02_require-cert-verification.patch +++ b/debian/patches/02_require-cert-verification.patch @@ -3,11 +3,11 @@ Description: require SSL certificate validation by default by using CERT_REQUIRED and using the system /etc/ssl/certs/ca-certificates.crt Bug-Ubuntu: https://launchpad.net/bugs/1047054 Bug-Debian: http://bugs.debian.org/686872 -Last-Update: 2014-03-31 +Last-Update: 2014-05-24 --- a/urllib3/connectionpool.py +++ b/urllib3/connectionpool.py -@@ -583,6 +583,8 @@ +@@ -591,6 +591,8 @@ ``ssl_version`` are only used if :mod:`ssl` is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket into an SSL socket. @@ -16,7 +16,7 @@ Last-Update: 2014-03-31 """ scheme = 'https' -@@ -592,8 +594,8 @@ +@@ -600,8 +602,8 @@ strict=False, timeout=None, maxsize=1, block=False, headers=None, _proxy=None, _proxy_headers=None, @@ -24,6 +24,6 @@ Last-Update: 2014-03-31 - ca_certs=None, ssl_version=None, + key_file=None, cert_file=None, cert_reqs='CERT_REQUIRED', + ca_certs='/etc/ssl/certs/ca-certificates.crt', ssl_version=None, - assert_hostname=None, assert_fingerprint=None): + assert_hostname=None, assert_fingerprint=None, + **conn_kw): - HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, -- cgit v1.2.3 From dd3cf2867dde8f22db6b86d64757e042640b77c5 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 17:56:07 +0000 Subject: Removed debian/patches/03_no-setuptools.patch; superseded by debian/patches/setuptools.patch --- debian/changelog | 4 +++- debian/patches/03_no-setuptools.patch | 26 -------------------------- debian/patches/series | 1 - 3 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 debian/patches/03_no-setuptools.patch diff --git a/debian/changelog b/debian/changelog index 495ab62..b8d3ce6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,8 +5,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Refreshed * debian/patches/02_require-cert-verification.patch - Refreshed + * debian/patches/03_no-setuptools.patch + - Superseded by debian/patches/setuptools.patch - -- Daniele Tricoli Sat, 24 May 2014 19:12:41 +0200 + -- Daniele Tricoli Sat, 24 May 2014 19:52:20 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/03_no-setuptools.patch b/debian/patches/03_no-setuptools.patch deleted file mode 100644 index ee2e913..0000000 --- a/debian/patches/03_no-setuptools.patch +++ /dev/null @@ -1,26 +0,0 @@ -Description: Do not use setuptools. -Author: Daniele Tricoli -Forwarded: not-needed -Last-Update: 2013-05-10 - ---- a/setup.py -+++ b/setup.py -@@ -5,11 +5,6 @@ - import os - import re - --try: -- import setuptools --except ImportError: -- pass # No 'develop' command, oh well. -- - base_path = os.path.dirname(__file__) - - # Get the version (borrowed from SQLAlchemy) -@@ -49,6 +44,4 @@ - 'urllib3.contrib', - ], - requires=requirements, -- tests_require=tests_requirements, -- test_suite='test', - ) diff --git a/debian/patches/series b/debian/patches/series index 52e03c4..a8d7977 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,5 +1,4 @@ 01_do-not-use-embedded-python-six.patch 02_require-cert-verification.patch -03_no-setuptools.patch 04_relax_nosetests_options.patch setuptools.patch -- cgit v1.2.3 From 9a75888c602026604defb891768e21a4c21f7565 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 18:11:00 +0000 Subject: Renamed setuptools.patch to 03_force_setuptools.patch --- debian/changelog | 4 +++- debian/patches/03_force_setuptools.patch | 10 ++++++++++ debian/patches/series | 2 +- debian/patches/setuptools.patch | 10 ---------- 4 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 debian/patches/03_force_setuptools.patch delete mode 100644 debian/patches/setuptools.patch diff --git a/debian/changelog b/debian/changelog index b8d3ce6..df098f8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,8 +7,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Refreshed * debian/patches/03_no-setuptools.patch - Superseded by debian/patches/setuptools.patch + * debian/patches/03_force-setuptools.patch + - Renamed from setuptools.patch - -- Daniele Tricoli Sat, 24 May 2014 19:52:20 +0200 + -- Daniele Tricoli Sat, 24 May 2014 20:02:56 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/03_force_setuptools.patch b/debian/patches/03_force_setuptools.patch new file mode 100644 index 0000000..ff8f7e9 --- /dev/null +++ b/debian/patches/03_force_setuptools.patch @@ -0,0 +1,10 @@ +--- a/setup.py ++++ b/setup.py +@@ -1,6 +1,6 @@ + #!/usr/bin/env python + +-from distutils.core import setup ++from setuptools import setup + + import os + import re diff --git a/debian/patches/series b/debian/patches/series index a8d7977..8570821 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,4 +1,4 @@ 01_do-not-use-embedded-python-six.patch 02_require-cert-verification.patch +03_force_setuptools.patch 04_relax_nosetests_options.patch -setuptools.patch diff --git a/debian/patches/setuptools.patch b/debian/patches/setuptools.patch deleted file mode 100644 index ff8f7e9..0000000 --- a/debian/patches/setuptools.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/setup.py -+++ b/setup.py -@@ -1,6 +1,6 @@ - #!/usr/bin/env python - --from distutils.core import setup -+from setuptools import setup - - import os - import re -- cgit v1.2.3 From 12283202d3dda201ae753c6e671f7b492bc24d63 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 18:19:28 +0000 Subject: Added description --- debian/changelog | 3 ++- debian/patches/03_force_setuptools.patch | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index df098f8..0164f98 100644 --- a/debian/changelog +++ b/debian/changelog @@ -9,8 +9,9 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Superseded by debian/patches/setuptools.patch * debian/patches/03_force-setuptools.patch - Renamed from setuptools.patch + - Added description - -- Daniele Tricoli Sat, 24 May 2014 20:02:56 +0200 + -- Daniele Tricoli Sat, 24 May 2014 20:18:12 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/03_force_setuptools.patch b/debian/patches/03_force_setuptools.patch index ff8f7e9..e829b50 100644 --- a/debian/patches/03_force_setuptools.patch +++ b/debian/patches/03_force_setuptools.patch @@ -1,3 +1,8 @@ +Author: Barry Warsaw +Description: Use setuptools.setup() so that the bdist_wheel + command will work. +Last-Update: 2014-05-15 + --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ -- cgit v1.2.3 From b6564b803240a6f543f55153b47a50475b7ff3b2 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 22:26:42 +0000 Subject: Do not use embedded copy of ssl.match_hostname --- debian/changelog | 4 +- ...05_do-not-use-embedded-ssl-match-hostname.patch | 45 ++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch diff --git a/debian/changelog b/debian/changelog index 0164f98..d425682 100644 --- a/debian/changelog +++ b/debian/changelog @@ -10,8 +10,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * debian/patches/03_force-setuptools.patch - Renamed from setuptools.patch - Added description + * debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch + - Do not use embedded copy of ssl.match_hostname - -- Daniele Tricoli Sat, 24 May 2014 20:18:12 +0200 + -- Daniele Tricoli Sun, 25 May 2014 00:22:12 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch b/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch new file mode 100644 index 0000000..e536e69 --- /dev/null +++ b/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch @@ -0,0 +1,45 @@ +Description: Do not use embedded copy of ssl.match_hostname. +Author: Daniele Tricoli +Forwarded: not-needed +Last-Update: 2014-05-25 + +--- a/test/test_connectionpool.py ++++ b/test/test_connectionpool.py +@@ -6,7 +6,7 @@ + HTTPConnectionPool, + ) + from urllib3.util import Timeout +-from urllib3.packages.ssl_match_hostname import CertificateError ++from ssl import CertificateError + from urllib3.exceptions import ( + ClosedPoolError, + EmptyPoolError, +--- a/urllib3/connection.py ++++ b/urllib3/connection.py +@@ -38,7 +38,7 @@ + from .exceptions import ( + ConnectTimeoutError, + ) +-from .packages.ssl_match_hostname import match_hostname ++from ssl import match_hostname + import six + from .util import ( + assert_fingerprint, +--- a/urllib3/connectionpool.py ++++ b/urllib3/connectionpool.py +@@ -31,7 +31,7 @@ + ReadTimeoutError, + ProxyError, + ) +-from .packages.ssl_match_hostname import CertificateError ++from ssl import CertificateError + import six + from .connection import ( + port_by_scheme, +--- a/urllib3/packages/__init__.py ++++ b/urllib3/packages/__init__.py +@@ -1,4 +1,3 @@ + from __future__ import absolute_import + +-from . import ssl_match_hostname + diff --git a/debian/patches/series b/debian/patches/series index 8570821..cddf757 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -2,3 +2,4 @@ 02_require-cert-verification.patch 03_force_setuptools.patch 04_relax_nosetests_options.patch +05_do-not-use-embedded-ssl-match-hostname.patch -- cgit v1.2.3 From 6021f0bea8a1e2b9b3da2020691be801ac957b27 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sat, 24 May 2014 23:00:14 +0000 Subject: Removed ssl_match_hostname package from setup.py --- .../patches/05_do-not-use-embedded-ssl-match-hostname.patch | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch b/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch index e536e69..9ee4cb9 100644 --- a/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch +++ b/debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch @@ -43,3 +43,14 @@ Last-Update: 2014-05-25 -from . import ssl_match_hostname +--- a/setup.py ++++ b/setup.py +@@ -45,7 +45,7 @@ + url='http://urllib3.readthedocs.org/', + license='MIT', + packages=['urllib3', +- 'urllib3.packages', 'urllib3.packages.ssl_match_hostname', ++ 'urllib3.packages', + 'urllib3.contrib', 'urllib3.util', + ], + requires=requirements, -- cgit v1.2.3 From a1948a084cb9354239c288f19ed725f0b7925774 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sun, 25 May 2014 00:46:03 +0000 Subject: Relax version of packages needed for testing --- debian/changelog | 4 +++- debian/patches/06_relax-test-requirements.patch | 16 ++++++++++++++++ debian/patches/series | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 debian/patches/06_relax-test-requirements.patch diff --git a/debian/changelog b/debian/changelog index d425682..b960c49 100644 --- a/debian/changelog +++ b/debian/changelog @@ -12,8 +12,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Added description * debian/patches/05_do-not-use-embedded-ssl-match-hostname.patch - Do not use embedded copy of ssl.match_hostname + * debian/patches/06_relax-test-requirements.patch + - Relax version of packages needed for testing - -- Daniele Tricoli Sun, 25 May 2014 00:22:12 +0200 + -- Daniele Tricoli Sun, 25 May 2014 02:42:20 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/patches/06_relax-test-requirements.patch b/debian/patches/06_relax-test-requirements.patch new file mode 100644 index 0000000..e0fe46c --- /dev/null +++ b/debian/patches/06_relax-test-requirements.patch @@ -0,0 +1,16 @@ +Description: Relax version of packages needed for testing. +Author: Daniele Tricoli +Forwarded: not-needed +Last-Update: 2014-05-25 + +--- a/test-requirements.txt ++++ b/test-requirements.txt +@@ -1,4 +1,4 @@ +-nose==1.3 +-mock==1.0.1 +-tornado==3.1.1 +-coverage==3.6 ++nose>=1.3 ++mock>=1.0.1 ++tornado>=3.1.1 ++coverage>=3.6 diff --git a/debian/patches/series b/debian/patches/series index cddf757..6bb1581 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,3 +3,4 @@ 03_force_setuptools.patch 04_relax_nosetests_options.patch 05_do-not-use-embedded-ssl-match-hostname.patch +06_relax-test-requirements.patch -- cgit v1.2.3 From d37f1c3d270af99f57fe78c4a74aec9192791e78 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sun, 25 May 2014 01:00:13 +0000 Subject: * debian/control - Added python3-coverage, python3-mock, python3-nose, python3-tornado to Build-Depends - Bumped python() --- debian/changelog | 6 +++++- debian/control | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index b960c49..83c3f0c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * New upstream release + * debian/control + - Added python3-coverage, python3-mock, python3-nose, + python3-tornado to Build-Depends + - Bumped python(3)-coverage to (>=3.6) * debian/patches/01_do-not-use-embedded-python-six.patch - Refreshed * debian/patches/02_require-cert-verification.patch @@ -15,7 +19,7 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * debian/patches/06_relax-test-requirements.patch - Relax version of packages needed for testing - -- Daniele Tricoli Sun, 25 May 2014 02:42:20 +0200 + -- Daniele Tricoli Sun, 25 May 2014 02:52:55 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/control b/debian/control index 1211fa5..90c2b7d 100644 --- a/debian/control +++ b/debian/control @@ -7,15 +7,19 @@ Build-Depends: debhelper (>= 9), dh-python, python-all (>= 2.6.6-3), - python-coverage (>= 3.4), + python-coverage (>= 3.6), python-mock, python-nose (>=1.1.2), python-setuptools, python-six, python-tornado, python3-all, + python3-coverage (>= 3.6), + python3-mock, + python3-nose (>=1.1.2), python3-setuptools, python3-six, + python3-tornado, python3-wheel, Standards-Version: 3.9.5 X-Python-Version: >= 2.6 -- cgit v1.2.3 From a83049459ec68bb7eb3d4cb093a1921085c44f45 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Sun, 25 May 2014 01:05:44 +0000 Subject: Enabled tests at build time for Python 3 --- debian/changelog | 4 +++- debian/rules | 16 +++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/debian/changelog b/debian/changelog index 83c3f0c..36eb8f5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -18,8 +18,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Do not use embedded copy of ssl.match_hostname * debian/patches/06_relax-test-requirements.patch - Relax version of packages needed for testing + * debian/rules + - Enabled tests at build time for Python 3 - -- Daniele Tricoli Sun, 25 May 2014 02:52:55 +0200 + -- Daniele Tricoli Sun, 25 May 2014 03:04:16 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/rules b/debian/rules index f3dea6c..364062f 100755 --- a/debian/rules +++ b/debian/rules @@ -3,6 +3,8 @@ export PYBUILD_NAME=urllib3 export PYTHONWARNINGS=d +PYVERS := $(shell pyversions -r) +PY3VERS := $(shell py3versions -r) %: dh $@ --with python2,python3 --buildsystem=pybuild @@ -20,11 +22,15 @@ override_dh_auto_install: override_dh_auto_test: ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) - # Python3 testing is not possible at the moment because missing - # dependencies: python3-coverage. - # Upstream is using a python2.7 features: assertRaises() as a context - # manager - set -ex; python2.7 /usr/bin/nosetests + set -ex; \ + for python in $(PYVERS); do \ + $$python setup.py nosetests; \ + done + + set -ex; \ + for python in $(PY3VERS); do \ + $$python setup.py nosetests; \ + done endif override_dh_installchangelogs: -- cgit v1.2.3 From 4aa7d7f3a9cfc3b67279f3ed4853db4f60de3d00 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Tue, 27 May 2014 14:42:20 +0000 Subject: Enabled tests at build time also for Python 3 using the custom build plugin for tests --- debian/changelog | 5 +++-- debian/rules | 13 ++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/debian/changelog b/debian/changelog index 36eb8f5..8faf2d7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -19,9 +19,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * debian/patches/06_relax-test-requirements.patch - Relax version of packages needed for testing * debian/rules - - Enabled tests at build time for Python 3 + - Enabled tests at build time also for Python 3 using the custom build + plugin for tests - -- Daniele Tricoli Sun, 25 May 2014 03:04:16 +0200 + -- Daniele Tricoli Tue, 27 May 2014 16:40:30 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/rules b/debian/rules index 364062f..7e92dea 100755 --- a/debian/rules +++ b/debian/rules @@ -21,17 +21,8 @@ override_dh_auto_install: -d $(CURDIR)/debian/tmp/usr/share/python-wheels override_dh_auto_test: -ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) - set -ex; \ - for python in $(PYVERS); do \ - $$python setup.py nosetests; \ - done - - set -ex; \ - for python in $(PY3VERS); do \ - $$python setup.py nosetests; \ - done -endif + PYBUILD_SYSTEM=custom \ + PYBUILD_TEST_ARGS="{interpreter} -m nose" dh_auto_test override_dh_installchangelogs: dh_installchangelogs CHANGES.rst -- cgit v1.2.3 From f02102f3ef7ed76f1604f663347374d75f6d16e1 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Tue, 27 May 2014 14:44:17 +0000 Subject: s/using the custom build plugin for tests/using the custom build plugin of pybuild --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 8faf2d7..4082bcf 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,7 +20,7 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Relax version of packages needed for testing * debian/rules - Enabled tests at build time also for Python 3 using the custom build - plugin for tests + plugin of pybuild -- Daniele Tricoli Tue, 27 May 2014 16:40:30 +0200 -- cgit v1.2.3 From 144d87bf8411de394d8eb933781a8f8c5f069aa7 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Tue, 27 May 2014 21:55:13 +0000 Subject: Cleanup --- debian/rules | 3 --- 1 file changed, 3 deletions(-) diff --git a/debian/rules b/debian/rules index 7e92dea..8e9492a 100755 --- a/debian/rules +++ b/debian/rules @@ -3,9 +3,6 @@ export PYBUILD_NAME=urllib3 export PYTHONWARNINGS=d -PYVERS := $(shell pyversions -r) -PY3VERS := $(shell py3versions -r) - %: dh $@ --with python2,python3 --buildsystem=pybuild -- cgit v1.2.3 From 7143d4d66d4e8a16296fb65e792540b3887369c9 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Tue, 27 May 2014 22:03:43 +0000 Subject: No need to remove dummyserver since it is not installed anymore --- debian/changelog | 3 ++- debian/rules | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 4082bcf..8d125ee 100644 --- a/debian/changelog +++ b/debian/changelog @@ -21,8 +21,9 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * debian/rules - Enabled tests at build time also for Python 3 using the custom build plugin of pybuild + - No need to remove dummyserver since it is not installed anymore - -- Daniele Tricoli Tue, 27 May 2014 16:40:30 +0200 + -- Daniele Tricoli Wed, 28 May 2014 00:02:36 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/rules b/debian/rules index 8e9492a..090bf6a 100755 --- a/debian/rules +++ b/debian/rules @@ -11,8 +11,6 @@ override_dh_auto_configure: override_dh_auto_install: dh_auto_install - # Remove dummyserver/ tests to not pollute namespace. - rm -rf debian/python*-urllib3/usr/lib/python*/dist-packages/dummyserver python3 setup.py bdist_wheel \ --universal \ -d $(CURDIR)/debian/tmp/usr/share/python-wheels -- cgit v1.2.3 From 255aa594a309828870419d49c7d985bd66208e18 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Wed, 28 May 2014 15:38:15 +0000 Subject: Run tests from build directory --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 090bf6a..c0abdb2 100755 --- a/debian/rules +++ b/debian/rules @@ -17,7 +17,7 @@ override_dh_auto_install: override_dh_auto_test: PYBUILD_SYSTEM=custom \ - PYBUILD_TEST_ARGS="{interpreter} -m nose" dh_auto_test + PYBUILD_TEST_ARGS="cd {build_dir}; {interpreter} -m nose {dir}/test --with-coverage" dh_auto_test override_dh_installchangelogs: dh_installchangelogs CHANGES.rst -- cgit v1.2.3 From a92fa7cc08692dd007d316b9564feb5e072a7e20 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Wed, 28 May 2014 15:49:49 +0000 Subject: Removed python-tornado from Build-Depends since it was used only for dummyserver --- debian/changelog | 7 ++++--- debian/control | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8d125ee..52d5f54 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,9 +2,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * New upstream release * debian/control - - Added python3-coverage, python3-mock, python3-nose, - python3-tornado to Build-Depends + - Added python3-coverage, python3-mock, python3-nose to Build-Depends - Bumped python(3)-coverage to (>=3.6) + - Removed python-tornado from Build-Depends since it was used only for + dummyserver * debian/patches/01_do-not-use-embedded-python-six.patch - Refreshed * debian/patches/02_require-cert-verification.patch @@ -23,7 +24,7 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium plugin of pybuild - No need to remove dummyserver since it is not installed anymore - -- Daniele Tricoli Wed, 28 May 2014 00:02:36 +0200 + -- Daniele Tricoli Wed, 28 May 2014 17:39:35 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/control b/debian/control index 90c2b7d..87415bb 100644 --- a/debian/control +++ b/debian/control @@ -12,14 +12,12 @@ Build-Depends: python-nose (>=1.1.2), python-setuptools, python-six, - python-tornado, python3-all, python3-coverage (>= 3.6), python3-mock, python3-nose (>=1.1.2), python3-setuptools, python3-six, - python3-tornado, python3-wheel, Standards-Version: 3.9.5 X-Python-Version: >= 2.6 -- cgit v1.2.3 From bdbf0b134eec09ceb5099207453a85a915e2628f Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Wed, 28 May 2014 15:52:50 +0000 Subject: Updated copyright years --- debian/changelog | 4 +++- debian/copyright | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 52d5f54..b2878da 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,6 +6,8 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium - Bumped python(3)-coverage to (>=3.6) - Removed python-tornado from Build-Depends since it was used only for dummyserver + * debian/copyright + - Updated copyright years * debian/patches/01_do-not-use-embedded-python-six.patch - Refreshed * debian/patches/02_require-cert-verification.patch @@ -24,7 +26,7 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium plugin of pybuild - No need to remove dummyserver since it is not installed anymore - -- Daniele Tricoli Wed, 28 May 2014 17:39:35 +0200 + -- Daniele Tricoli Wed, 28 May 2014 17:51:59 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/copyright b/debian/copyright index d28dac4..1662f05 100644 --- a/debian/copyright +++ b/debian/copyright @@ -20,7 +20,7 @@ Copyright: 2011, Python Software Foundation License: PSF-2 Files: debian/* -Copyright: 2012-2013, Daniele Tricoli +Copyright: 2012-2014, Daniele Tricoli License: Expat License: Expat -- cgit v1.2.3 From 27d663a17dfe2fe5e07b9e26a33b6227cae8ae90 Mon Sep 17 00:00:00 2001 From: Daniele Tricoli Date: Wed, 28 May 2014 17:44:10 +0000 Subject: Cleaned .coverage file generated by nose using coverage plugin --- debian/changelog | 5 ++++- debian/clean | 1 - debian/rules | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index b2878da..99ea330 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * New upstream release + * debian/clean + - Removed .coverage entry * debian/control - Added python3-coverage, python3-mock, python3-nose to Build-Depends - Bumped python(3)-coverage to (>=3.6) @@ -24,9 +26,10 @@ python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium * debian/rules - Enabled tests at build time also for Python 3 using the custom build plugin of pybuild + - Cleaned .coverage file generated by nose using coverage plugin - No need to remove dummyserver since it is not installed anymore - -- Daniele Tricoli Wed, 28 May 2014 17:51:59 +0200 + -- Daniele Tricoli Wed, 28 May 2014 19:41:18 +0200 python-urllib3 (1.8-2) unstable; urgency=medium diff --git a/debian/clean b/debian/clean index 5202946..80574db 100644 --- a/debian/clean +++ b/debian/clean @@ -1,2 +1 @@ urllib3.egg-info/* -.coverage diff --git a/debian/rules b/debian/rules index c0abdb2..e66f170 100755 --- a/debian/rules +++ b/debian/rules @@ -18,6 +18,9 @@ override_dh_auto_install: override_dh_auto_test: PYBUILD_SYSTEM=custom \ PYBUILD_TEST_ARGS="cd {build_dir}; {interpreter} -m nose {dir}/test --with-coverage" dh_auto_test + # Clean here .coverage because it is created by nose using the coverage + # plugin + find . -name .coverage -delete override_dh_installchangelogs: dh_installchangelogs CHANGES.rst -- cgit v1.2.3 From bf803d191d5f87a08137ed939ab99e477b652def Mon Sep 17 00:00:00 2001 From: Piotr Ożarowski Date: Wed, 28 May 2014 19:50:44 +0000 Subject: s/UNRELEASED/unstable/ --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 99ea330..827b7bb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -python-urllib3 (1.8.2-1) UNRELEASED; urgency=medium +python-urllib3 (1.8.2-1) unstable; urgency=medium * New upstream release * debian/clean -- cgit v1.2.3 From 35fb123b995cbbe27d3edd5ed14abc6e56b7ad13 Mon Sep 17 00:00:00 2001 From: SVN-Git Migration Date: Thu, 8 Oct 2015 13:19:37 -0700 Subject: Imported Upstream version 1.8.2 --- CHANGES.rst | 17 + CONTRIBUTORS.txt | 6 + MANIFEST.in | 1 + PKG-INFO | 19 +- dummyserver/__init__.pyc | Bin 0 -> 141 bytes dummyserver/__pycache__/__init__.cpython-33.pyc | Bin 0 -> 149 bytes dummyserver/__pycache__/handlers.cpython-33.pyc | Bin 0 -> 10341 bytes dummyserver/__pycache__/proxy.cpython-33.pyc | Bin 0 -> 5809 bytes dummyserver/__pycache__/server.cpython-33.pyc | Bin 0 -> 6928 bytes dummyserver/__pycache__/testcase.cpython-33.pyc | Bin 0 -> 7203 bytes dummyserver/certs/cacert.key | 15 + dummyserver/certs/cacert.pem | 23 + dummyserver/certs/client.csr | 23 + dummyserver/certs/client.key | 15 + dummyserver/certs/client.pem | 22 + dummyserver/certs/client_bad.pem | 17 + dummyserver/certs/server.crt | 22 + dummyserver/certs/server.csr | 22 + dummyserver/certs/server.key | 15 + dummyserver/certs/server.key.org | 12 + dummyserver/handlers.py | 4 + dummyserver/handlers.pyc | Bin 0 -> 8967 bytes dummyserver/proxy.pyc | Bin 0 -> 4740 bytes dummyserver/server.pyc | Bin 0 -> 5992 bytes dummyserver/testcase.pyc | Bin 0 -> 5090 bytes setup.py | 4 +- test/test_poolmanager.py | 8 +- test/test_util.py | 6 +- urllib3.egg-info/PKG-INFO | 19 +- urllib3.egg-info/SOURCES.txt | 30 +- urllib3.egg-info/top_level.txt | 1 - urllib3/__init__.py | 2 +- urllib3/connection.py | 42 +- urllib3/connectionpool.py | 25 +- urllib3/contrib/pyopenssl.py | 17 +- urllib3/util.py | 648 ------------------------ urllib3/util/__init__.py | 27 + urllib3/util/connection.py | 45 ++ urllib3/util/request.py | 68 +++ urllib3/util/response.py | 13 + urllib3/util/ssl_.py | 133 +++++ urllib3/util/timeout.py | 234 +++++++++ urllib3/util/url.py | 162 ++++++ 43 files changed, 1029 insertions(+), 688 deletions(-) create mode 100644 dummyserver/__init__.pyc create mode 100644 dummyserver/__pycache__/__init__.cpython-33.pyc create mode 100644 dummyserver/__pycache__/handlers.cpython-33.pyc create mode 100644 dummyserver/__pycache__/proxy.cpython-33.pyc create mode 100644 dummyserver/__pycache__/server.cpython-33.pyc create mode 100644 dummyserver/__pycache__/testcase.cpython-33.pyc create mode 100644 dummyserver/certs/cacert.key create mode 100644 dummyserver/certs/cacert.pem create mode 100644 dummyserver/certs/client.csr create mode 100644 dummyserver/certs/client.key create mode 100644 dummyserver/certs/client.pem create mode 100644 dummyserver/certs/client_bad.pem create mode 100644 dummyserver/certs/server.crt create mode 100644 dummyserver/certs/server.csr create mode 100644 dummyserver/certs/server.key create mode 100644 dummyserver/certs/server.key.org create mode 100644 dummyserver/handlers.pyc create mode 100644 dummyserver/proxy.pyc create mode 100644 dummyserver/server.pyc create mode 100644 dummyserver/testcase.pyc delete mode 100644 urllib3/util.py create mode 100644 urllib3/util/__init__.py create mode 100644 urllib3/util/connection.py create mode 100644 urllib3/util/request.py create mode 100644 urllib3/util/response.py create mode 100644 urllib3/util/ssl_.py create mode 100644 urllib3/util/timeout.py create mode 100644 urllib3/util/url.py diff --git a/CHANGES.rst b/CHANGES.rst index ae63682..3f836e9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,23 @@ Changes ======= +1.8.2 (2014-04-17) +++++++++++++++++++ + +* Fix ``urllib3.util`` not being included in the package. + + +1.8.1 (2014-04-17) +++++++++++++++++++ + +* Fix AppEngine bug of HTTPS requests going out as HTTP. (Issue #356) + +* Don't install ``dummyserver`` into ``site-packages`` as it's only needed + for the test suite. (Issue #362) + +* Added support for specifying ``source_address``. (Issue #352) + + 1.8 (2014-03-04) ++++++++++++++++ diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e2dba35..e6178f1 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -111,5 +111,11 @@ In chronological order: * HTTPHeaderDict and associated tests and docs * Bugfixes, docs, test coverage +* Tahia Khan + * Added Timeout examples in docs + +* Arthur Grunseid + * source_address support and tests (with https://github.com/bui) + * [Your name or handle] <[email or website]> * [Brief summary of your changes] diff --git a/MANIFEST.in b/MANIFEST.in index d1abae2..3f344d1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ include README.rst CHANGES.rst LICENSE.txt CONTRIBUTORS.txt test-requirements.txt +recursive-include dummyserver *.* diff --git a/PKG-INFO b/PKG-INFO index 6a4f31a..0021e34 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: urllib3 -Version: 1.8 +Version: 1.8.2 Summary: HTTP library with thread-safe connection pooling, file post, and more. Home-page: http://urllib3.readthedocs.org/ Author: Andrey Petrov @@ -121,6 +121,23 @@ Description: ======= Changes ======= + 1.8.2 (2014-04-17) + ++++++++++++++++++ + + * Fix ``urllib3.util`` not being included in the package. + + + 1.8.1 (2014-04-17) + ++++++++++++++++++ + + * Fix AppEngine bug of HTTPS requests going out as HTTP. (Issue #356) + + * Don't install ``dummyserver`` into ``site-packages`` as it's only needed + for the test suite. (Issue #362) + + * Added support for specifying ``source_address``. (Issue #352) + + 1.8 (2014-03-04) ++++++++++++++++ diff --git a/dummyserver/__init__.pyc b/dummyserver/__init__.pyc new file mode 100644 index 0000000..b017ac5 Binary files /dev/null and b/dummyserver/__init__.pyc differ diff --git a/dummyserver/__pycache__/__init__.cpython-33.pyc b/dummyserver/__pycache__/__init__.cpython-33.pyc new file mode 100644 index 0000000..d1e84e3 Binary files /dev/null and b/dummyserver/__pycache__/__init__.cpython-33.pyc differ diff --git a/dummyserver/__pycache__/handlers.cpython-33.pyc b/dummyserver/__pycache__/handlers.cpython-33.pyc new file mode 100644 index 0000000..e3bab97 Binary files /dev/null and b/dummyserver/__pycache__/handlers.cpython-33.pyc differ diff --git a/dummyserver/__pycache__/proxy.cpython-33.pyc b/dummyserver/__pycache__/proxy.cpython-33.pyc new file mode 100644 index 0000000..4cca456 Binary files /dev/null and b/dummyserver/__pycache__/proxy.cpython-33.pyc differ diff --git a/dummyserver/__pycache__/server.cpython-33.pyc b/dummyserver/__pycache__/server.cpython-33.pyc new file mode 100644 index 0000000..49504c9 Binary files /dev/null and b/dummyserver/__pycache__/server.cpython-33.pyc differ diff --git a/dummyserver/__pycache__/testcase.cpython-33.pyc b/dummyserver/__pycache__/testcase.cpython-33.pyc new file mode 100644 index 0000000..21e3d10 Binary files /dev/null and b/dummyserver/__pycache__/testcase.cpython-33.pyc differ diff --git a/dummyserver/certs/cacert.key b/dummyserver/certs/cacert.key new file mode 100644 index 0000000..fc8be6e --- /dev/null +++ b/dummyserver/certs/cacert.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDKz8a9X2SfNms9TffyNaFO/K42fAjUI1dAM1G8TVoj0a81ay7W +z4R7V1zfjXFT/WoRW04Y6xek0bff0OtsW+AriooUy7+pPYnrchpAW0p7hPjH1DIB +Vab01CJMhQ24er92Q1dF4WBv4yKqEaV1IYz1cvqvCCJgAbsWn1I8Cna1lwIDAQAB +AoGAPpkK+oBrCkk9qFpcYUH0W/DZxK9b+j4+O+6bF8e4Pr4FmjNO7bZ3aap5W/bI +N+hLyLepzz8guRqR6l8NixCAi+JiVW/agh5o4Jrek8UJWQamwSL4nJ36U3Iw/l7w +vcN1txfkpsA2SB9QFPGfDKcP3+IZMOZ7uFLzk/gzgLYiCEECQQD+M5Lj+e/sNBkb +XeIBxWIrPfEeIkk4SDkqImzDjq1FcfxZkvfskqyJgUvcLe5hb+ibY8jqWvtpvFTI +5v/tzHvPAkEAzD8fNrGz8KiAVTo7+0vrb4AebAdSLZUvbp0AGs5pXUAuQx6VEgz8 +opNKpZjBwAFsZKlwhgDqaChiAt9aKUkzuQJBALlai9I2Dg7SkjgVRdX6wjE7slRB +tdgXOa+SeHJD1+5aRiJeeu8CqFJ/d/wtdbOQsTCVGwxfmREpZT00ywrvXpsCQQCU +gs1Kcrn5Ijx2PCrDFbfyUkFMoaIiXNipYGVkGHRKhtFcoo8YGfNUry7W7BTtbNuI +8h9MgLvw0nQ5zHf9jymZAkEA7o4uA6XSS1zUqEQ55bZRFHcz/99pLH35G906iwVb +d5rd1Z4Cf5s/91o5gwL6ZP2Ig34CCn+NSL4avgz6K0VUaA== +-----END RSA PRIVATE KEY----- diff --git a/dummyserver/certs/cacert.pem b/dummyserver/certs/cacert.pem new file mode 100644 index 0000000..38d32dc --- /dev/null +++ b/dummyserver/certs/cacert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDzDCCAzWgAwIBAgIJALPrscov4b/jMA0GCSqGSIb3DQEBBQUAMIGBMQswCQYD +VQQGEwJGSTEOMAwGA1UECBMFZHVtbXkxDjAMBgNVBAcTBWR1bW15MQ4wDAYDVQQK +EwVkdW1teTEOMAwGA1UECxMFZHVtbXkxETAPBgNVBAMTCFNuYWtlT2lsMR8wHQYJ +KoZIhvcNAQkBFhBkdW1teUB0ZXN0LmxvY2FsMB4XDTExMTIyMjA3NTYxNVoXDTIx +MTIxOTA3NTYxNVowgYExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwG +A1UEBxMFZHVtbXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTERMA8G +A1UEAxMIU25ha2VPaWwxHzAdBgkqhkiG9w0BCQEWEGR1bW15QHRlc3QubG9jYWww +gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrPxr1fZJ82az1N9/I1oU78rjZ8 +CNQjV0AzUbxNWiPRrzVrLtbPhHtXXN+NcVP9ahFbThjrF6TRt9/Q62xb4CuKihTL +v6k9ietyGkBbSnuE+MfUMgFVpvTUIkyFDbh6v3ZDV0XhYG/jIqoRpXUhjPVy+q8I +ImABuxafUjwKdrWXAgMBAAGjggFIMIIBRDAdBgNVHQ4EFgQUGXd/I2JiQllF+3Wd +x3NyBLszCi0wgbYGA1UdIwSBrjCBq4AUGXd/I2JiQllF+3Wdx3NyBLszCi2hgYek +gYQwgYExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwGA1UEBxMFZHVt +bXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTERMA8GA1UEAxMIU25h +a2VPaWwxHzAdBgkqhkiG9w0BCQEWEGR1bW15QHRlc3QubG9jYWyCCQCz67HKL+G/ +4zAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIEAjAA +MCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRlMA4G +A1UdDwEB/wQEAwICBDANBgkqhkiG9w0BAQUFAAOBgQBnnwtO8onsyhGOvS6cS8af +IRZyAXgouuPeP3Zrf5W80iZcV23u94969sPEIsD8Ujv5u0hUSrToGl4ahOMEOFNL +R5ndQOkh3VsepJnoE+RklZzbHWxU8onWlVzsNBFbclxidzaU3UHmdgXJAJL5nVSd +Zpn44QSS0UXsaC0mBimVNw== +-----END CERTIFICATE----- diff --git a/dummyserver/certs/client.csr b/dummyserver/certs/client.csr new file mode 100644 index 0000000..703d351 --- /dev/null +++ b/dummyserver/certs/client.csr @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1TCCAz6gAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMCRkkx +DjAMBgNVBAgTBWR1bW15MQ4wDAYDVQQHEwVkdW1teTEOMAwGA1UEChMFZHVtbXkx +DjAMBgNVBAsTBWR1bW15MREwDwYDVQQDEwhTbmFrZU9pbDEfMB0GCSqGSIb3DQEJ +ARYQZHVtbXlAdGVzdC5sb2NhbDAeFw0xMTEyMjIwNzU5NTlaFw0yMTEyMTgwNzU5 +NTlaMH8xCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwGA1UEBxMFZHVt +bXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTEPMA0GA1UEAxMGY2xp +ZW50MR8wHQYJKoZIhvcNAQkBFhBjbGllbnRAbG9jYWxob3N0MIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDaITA/XCzviqjex+lJJP+pgmQQ+ncUf+PDaFw86kWh +cWuI2eSBVaIaP6SsxYgIODQTjqYGjRogsd1Nvx3gRdIMEagTfVQyVwfDfNp8aT8v +SY/wDYFjsD07asmjGvwiu0sLp4t/tMz+x5ELlU4+hGnmPInH6hLK150DqgbNmJus +3wIDAQABo4IBXDCCAVgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBLAwKwYJ +YIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O +BBYEFG71FCU2yisH1GyrcqYaPKVeTWxBMIG2BgNVHSMEga4wgauAFBl3fyNiYkJZ +Rft1ncdzcgS7MwotoYGHpIGEMIGBMQswCQYDVQQGEwJGSTEOMAwGA1UECBMFZHVt +bXkxDjAMBgNVBAcTBWR1bW15MQ4wDAYDVQQKEwVkdW1teTEOMAwGA1UECxMFZHVt +bXkxETAPBgNVBAMTCFNuYWtlT2lsMR8wHQYJKoZIhvcNAQkBFhBkdW1teUB0ZXN0 +LmxvY2FsggkAs+uxyi/hv+MwCQYDVR0SBAIwADAbBgNVHREEFDASgRBjbGllbnRA +bG9jYWxob3N0MAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQUFAAOBgQDEwZmp3yE8 +R4U9Ob/IeEo6O3p0T4o7GNvufGksM/mELmzyC+Qh/Ul6fNn+IhdKWpo61sMZou+n +eOufXVouc8dGhQ1Qi5s0i51d/ouhfYNs+AGRcpwEieVjZhgE1XfrNwvvjIx3yPtK +m9LSmCtVKcTWqOHQywKn+G83a+7bsh835Q== +-----END CERTIFICATE----- diff --git a/dummyserver/certs/client.key b/dummyserver/certs/client.key new file mode 100644 index 0000000..0d1c343 --- /dev/null +++ b/dummyserver/certs/client.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDaITA/XCzviqjex+lJJP+pgmQQ+ncUf+PDaFw86kWhcWuI2eSB +VaIaP6SsxYgIODQTjqYGjRogsd1Nvx3gRdIMEagTfVQyVwfDfNp8aT8vSY/wDYFj +sD07asmjGvwiu0sLp4t/tMz+x5ELlU4+hGnmPInH6hLK150DqgbNmJus3wIDAQAB +AoGAKMMg+AYqo4z+57rl/nQ6jpu+RWn4zMzlbEPZUMzavEOsu8M0L3MoOs1/4YV8 +WUTffnQe1ISTyF5Uo82+MIX7rUtfJITFSQrIWe7AGdm6Nir8TQQ7fD97modXyAUx +69I9SQjQlseg5PCRCp/DfcBncvHeYuf8gAJK5FfC1VW1cQECQQDvzFNoGrwnsrtm +4gj1Kt0c20jkIYFN6iQ6Sjs/1fk1cXDeWzjPaa92zF+i+02Ma/eWJ0ZVrhisw6sv +zxGp+ByBAkEA6N4SpuGWytJqCRfwenQZ4Oa8mNcVo5ulGf/eUHVXvHewWxQ7xWRi +iWUj/z1byR9+yno8Yfd04kaNCPYN/ICZXwJAAf5//xCh2e6pkkx06J0Ho7LLI2KH +8b7tuDJf1cMQxHoCB0dY7JijZeiDLxbJ6U4IjA4djp7ZA67I4KfnLLOsgQJARLZS +dp+WKR7RXwGLWfasNCqhd8/veKlSnEtdxAv76Ya/qQBdaq9mS/hmGMh4Lu52MTTE +YHvuJ159+yjvk5Q2rQJABjlU1+GZqwv/7QM7GxfJO+GPI4PHv5Yji5s7LLu2c6dL +XY2XiTHQL9PnPrKp3+qDDzxjyej30lfz4he6E5pI+g== +-----END RSA PRIVATE KEY----- diff --git a/dummyserver/certs/client.pem b/dummyserver/certs/client.pem new file mode 100644 index 0000000..29aea38 --- /dev/null +++ b/dummyserver/certs/client.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCAxGgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMCRkkx +DjAMBgNVBAgTBWR1bW15MQ4wDAYDVQQHEwVkdW1teTEOMAwGA1UEChMFZHVtbXkx +DjAMBgNVBAsTBWR1bW15MREwDwYDVQQDEwhTbmFrZU9pbDEfMB0GCSqGSIb3DQEJ +ARYQZHVtbXlAdGVzdC5sb2NhbDAeFw0xMTEyMjIwNzU4NDBaFw0yMTEyMTgwNzU4 +NDBaMGExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwGA1UEBxMFZHVt +bXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTESMBAGA1UEAxMJbG9j +YWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXe3FqmCWvP8XPxqtT ++0bfL1Tvzvebi46k0WIcUV8bP3vyYiSRXG9ALmyzZH4GHY9UVs4OEDkCMDOBSezB +0y9ai/9doTNcaictdEBu8nfdXKoTtzrn+VX4UPrkH5hm7NQ1fTQuj1MR7yBCmYqN +3Q2Q+Efuujyx0FwBzAuy1aKYuwIDAQABo4IBTTCCAUkwCQYDVR0TBAIwADARBglg +hkgBhvhCAQEEBAMCBkAwKwYJYIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQg +Q2VydGlmaWNhdGUwHQYDVR0OBBYEFBvnSuVKLNPEFMAFqHw292vGHGJSMIG2BgNV +HSMEga4wgauAFBl3fyNiYkJZRft1ncdzcgS7MwotoYGHpIGEMIGBMQswCQYDVQQG +EwJGSTEOMAwGA1UECBMFZHVtbXkxDjAMBgNVBAcTBWR1bW15MQ4wDAYDVQQKEwVk +dW1teTEOMAwGA1UECxMFZHVtbXkxETAPBgNVBAMTCFNuYWtlT2lsMR8wHQYJKoZI +hvcNAQkBFhBkdW1teUB0ZXN0LmxvY2FsggkAs+uxyi/hv+MwCQYDVR0SBAIwADAZ +BgNVHREEEjAQgQ5yb290QGxvY2FsaG9zdDANBgkqhkiG9w0BAQUFAAOBgQBXdedG +XHLPmOVBeKWjTmaekcaQi44snhYqE1uXRoIQXQsyw+Ya5+n/uRxPKZO/C78EESL0 +8rnLTdZXm4GBYyHYmMy0AdWR7y030viOzAkWWRRRbuecsaUzFCI+F9jTV5LHuRzz +V8fUKwiEE9swzkWgMpfVTPFuPgzxwG9gMbrBfg== +-----END CERTIFICATE----- diff --git a/dummyserver/certs/client_bad.pem b/dummyserver/certs/client_bad.pem new file mode 100644 index 0000000..e9402fb --- /dev/null +++ b/dummyserver/certs/client_bad.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICsDCCAhmgAwIBAgIJAL63Nc6KY94BMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTExMDExMjMxMjAzWhcNMjExMDA4MjMxMjAzWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQC8HGxvblJ4Z0i/lIlG8jrNsFrCqYRAXtj3xdnnjfUpd/kNhU/KahMsG6urAe/4 +Yj+Zqf1sVnt0Cye8FZE3cN9RAcwJrlTCRiicJiXEbA7cPfMphqNGqjVHtmxQ1OsU +NHK7cxKa9OX3xmg4h55vxSZYgibAEPO2g3ueGk7RWIAQ8wIDAQABo4GnMIGkMB0G +A1UdDgQWBBSeeo/YRpdn5DK6bUI7ZDJ57pzGdDB1BgNVHSMEbjBsgBSeeo/YRpdn +5DK6bUI7ZDJ57pzGdKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt +U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAL63Nc6K +Y94BMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAOntoloMGt1325UR0 +GGEKQJbiRhLXY4otdgFjEvCG2RPZVLxWYhLMu0LkB6HBYULEuoy12ushtRWlhS1k +6PNRkaZ+LQTSREj6Do4c4zzLxCDmxYmejOz63cIWX2x5IY6qEx2BNOfmM4xEdF8W +LSGGbQfuAghiEh0giAi4AQloDlY= +-----END CERTIFICATE----- diff --git a/dummyserver/certs/server.crt b/dummyserver/certs/server.crt new file mode 100644 index 0000000..29aea38 --- /dev/null +++ b/dummyserver/certs/server.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCAxGgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMCRkkx +DjAMBgNVBAgTBWR1bW15MQ4wDAYDVQQHEwVkdW1teTEOMAwGA1UEChMFZHVtbXkx +DjAMBgNVBAsTBWR1bW15MREwDwYDVQQDEwhTbmFrZU9pbDEfMB0GCSqGSIb3DQEJ +ARYQZHVtbXlAdGVzdC5sb2NhbDAeFw0xMTEyMjIwNzU4NDBaFw0yMTEyMTgwNzU4 +NDBaMGExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwGA1UEBxMFZHVt +bXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTESMBAGA1UEAxMJbG9j +YWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXe3FqmCWvP8XPxqtT ++0bfL1Tvzvebi46k0WIcUV8bP3vyYiSRXG9ALmyzZH4GHY9UVs4OEDkCMDOBSezB +0y9ai/9doTNcaictdEBu8nfdXKoTtzrn+VX4UPrkH5hm7NQ1fTQuj1MR7yBCmYqN +3Q2Q+Efuujyx0FwBzAuy1aKYuwIDAQABo4IBTTCCAUkwCQYDVR0TBAIwADARBglg +hkgBhvhCAQEEBAMCBkAwKwYJYIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQg +Q2VydGlmaWNhdGUwHQYDVR0OBBYEFBvnSuVKLNPEFMAFqHw292vGHGJSMIG2BgNV +HSMEga4wgauAFBl3fyNiYkJZRft1ncdzcgS7MwotoYGHpIGEMIGBMQswCQYDVQQG +EwJGSTEOMAwGA1UECBMFZHVtbXkxDjAMBgNVBAcTBWR1bW15MQ4wDAYDVQQKEwVk +dW1teTEOMAwGA1UECxMFZHVtbXkxETAPBgNVBAMTCFNuYWtlT2lsMR8wHQYJKoZI +hvcNAQkBFhBkdW1teUB0ZXN0LmxvY2FsggkAs+uxyi/hv+MwCQYDVR0SBAIwADAZ +BgNVHREEEjAQgQ5yb290QGxvY2FsaG9zdDANBgkqhkiG9w0BAQUFAAOBgQBXdedG +XHLPmOVBeKWjTmaekcaQi44snhYqE1uXRoIQXQsyw+Ya5+n/uRxPKZO/C78EESL0 +8rnLTdZXm4GBYyHYmMy0AdWR7y030viOzAkWWRRRbuecsaUzFCI+F9jTV5LHuRzz +V8fUKwiEE9swzkWgMpfVTPFuPgzxwG9gMbrBfg== +-----END CERTIFICATE----- diff --git a/dummyserver/certs/server.csr b/dummyserver/certs/server.csr new file mode 100644 index 0000000..29aea38 --- /dev/null +++ b/dummyserver/certs/server.csr @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCAxGgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMCRkkx +DjAMBgNVBAgTBWR1bW15MQ4wDAYDVQQHEwVkdW1teTEOMAwGA1UEChMFZHVtbXkx +DjAMBgNVBAsTBWR1bW15MREwDwYDVQQDEwhTbmFrZU9pbDEfMB0GCSqGSIb3DQEJ +ARYQZHVtbXlAdGVzdC5sb2NhbDAeFw0xMTEyMjIwNzU4NDBaFw0yMTEyMTgwNzU4 +NDBaMGExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwGA1UEBxMFZHVt +bXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTESMBAGA1UEAxMJbG9j +YWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXe3FqmCWvP8XPxqtT ++0bfL1Tvzvebi46k0WIcUV8bP3vyYiSRXG9ALmyzZH4GHY9UVs4OEDkCMDOBSezB +0y9ai/9doTNcaictdEBu8nfdXKoTtzrn+VX4UPrkH5hm7NQ1fTQuj1MR7yBCmYqN +3Q2Q+Efuujyx0FwBzAuy1aKYuwIDAQABo4IBTTCCAUkwCQYDVR0TBAIwADARBglg +hkgBhvhCAQEEBAMCBkAwKwYJYIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQg +Q2VydGlmaWNhdGUwHQYDVR0OBBYEFBvnSuVKLNPEFMAFqHw292vGHGJSMIG2BgNV +HSMEga4wgauAFBl3fyNiYkJZRft1ncdzcgS7MwotoYGHpIGEMIGBMQswCQYDVQQG +EwJGSTEOMAwGA1UECBMFZHVtbXkxDjAMBgNVBAcTBWR1bW15MQ4wDAYDVQQKEwVk +dW1teTEOMAwGA1UECxMFZHVtbXkxETAPBgNVBAMTCFNuYWtlT2lsMR8wHQYJKoZI +hvcNAQkBFhBkdW1teUB0ZXN0LmxvY2FsggkAs+uxyi/hv+MwCQYDVR0SBAIwADAZ +BgNVHREEEjAQgQ5yb290QGxvY2FsaG9zdDANBgkqhkiG9w0BAQUFAAOBgQBXdedG +XHLPmOVBeKWjTmaekcaQi44snhYqE1uXRoIQXQsyw+Ya5+n/uRxPKZO/C78EESL0 +8rnLTdZXm4GBYyHYmMy0AdWR7y030viOzAkWWRRRbuecsaUzFCI+F9jTV5LHuRzz +V8fUKwiEE9swzkWgMpfVTPFuPgzxwG9gMbrBfg== +-----END CERTIFICATE----- diff --git a/dummyserver/certs/server.key b/dummyserver/certs/server.key new file mode 100644 index 0000000..89ab057 --- /dev/null +++ b/dummyserver/certs/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDXe3FqmCWvP8XPxqtT+0bfL1Tvzvebi46k0WIcUV8bP3vyYiSR +XG9ALmyzZH4GHY9UVs4OEDkCMDOBSezB0y9ai/9doTNcaictdEBu8nfdXKoTtzrn ++VX4UPrkH5hm7NQ1fTQuj1MR7yBCmYqN3Q2Q+Efuujyx0FwBzAuy1aKYuwIDAQAB +AoGBANOGBM6bbhq7ImYU4qf8+RQrdVg2tc9Fzo+yTnn30sF/rx8/AiCDOV4qdGAh +HKjKKaGj2H/rotqoEFcxBy05LrgJXxydBP72e9PYhNgKOcSmCQu4yALIPEXfKuIM +zgAErHVJ2l79fif3D4hzNyz+u5E1A9n3FG9cgaJSiYP8IG2RAkEA82GZ8rBkSGQQ +ZQ3oFuzPAAL21lbj8D0p76fsCpvS7427DtZDOjhOIKZmaeykpv+qSzRraqEqjDRi +S4kjQvwh6QJBAOKniZ+NDo2lSpbOFk+XlmABK1DormVpj8KebHEZYok1lRI+WiX9 +Nnoe9YLgix7++6H5SBBCcTB4HvM+5A4BuwMCQQChcX/eZbXP81iQwB3Rfzp8xnqY +icDf7qKvz9Ma4myU7Y5E9EpaB1mD/P14jDpYcMW050vNyqTfpiwB8TFL0NZpAkEA +02jkFH9UyMgZV6qo4tqI98l/ZrtyF8OrxSNSEPhVkZf6EQc5vN9/lc8Uv1vESEgb +3AwRrKDcxRH2BHtv6qSwkwJAGjqnkIcEkA75r1e55/EF2chcZW1+tpwKupE8CtAH +VXGd5DVwt4cYWkLUj2gF2fJbV97uu2MAg5CFDb+vQ6p5eA== +-----END RSA PRIVATE KEY----- diff --git a/dummyserver/certs/server.key.org b/dummyserver/certs/server.key.org new file mode 100644 index 0000000..709082e --- /dev/null +++ b/dummyserver/certs/server.key.org @@ -0,0 +1,12 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,8B3708EAD53963D4 + +uyLo4sFmSo7+K1uVgSENI+85JsG5o1JmovvxD/ucUl9CDhDj4KgFzs95r7gjjlhS +kA/hIY8Ec9i6T3zMXpAswWI5Mv2LE+UdYR5h60dYtIinLC7KF0QIztSecNWy20Bi +/NkobZhN7VZUuCEoSRWj4Ia3EuATF8Y9ZRGFPNsqMbSAhsGZ1P5xbDMEpE+5PbJP +LvdF9yWDT77rHeI4CKV4aP/yxtm1heEhKw5o6hdpPBQajPpjSQbh7/V6Qd0QsKcV +n27kPnSabsTbbc2IR40il4mZfHvXAlp4KoHL3RUgaons7q0hAUpUi+vJXbEukGGt +3dlyWwKwEFS7xBQ1pQvzcePI4/fRQxhZNxeFZW6n12Y3X61vg1IsG7usPhRe3iDP +3g1MXQMAhxaECnDN9b006IeoYdaktd4wrs/fn8x6Yz4= +-----END RSA PRIVATE KEY----- diff --git a/dummyserver/handlers.py b/dummyserver/handlers.py index bc51f31..5d6e2e6 100644 --- a/dummyserver/handlers.py +++ b/dummyserver/handlers.py @@ -70,6 +70,10 @@ class TestingApp(WSGIHandler): "Render simple message" return Response("Dummy server!") + def source_address(self, request): + """Return the requester's IP address.""" + return Response(request.remote_ip) + def set_up(self, request): test_type = request.params.get('test_type') test_id = request.params.get('test_id') diff --git a/dummyserver/handlers.pyc b/dummyserver/handlers.pyc new file mode 100644 index 0000000..ddf66d2 Binary files /dev/null and b/dummyserver/handlers.pyc differ diff --git a/dummyserver/proxy.pyc b/dummyserver/proxy.pyc new file mode 100644 index 0000000..a23689e Binary files /dev/null and b/dummyserver/proxy.pyc differ diff --git a/dummyserver/server.pyc b/dummyserver/server.pyc new file mode 100644 index 0000000..c0df815 Binary files /dev/null and b/dummyserver/server.pyc differ diff --git a/dummyserver/testcase.pyc b/dummyserver/testcase.pyc new file mode 100644 index 0000000..a1f9bdf Binary files /dev/null and b/dummyserver/testcase.pyc differ diff --git a/setup.py b/setup.py index 392b885..92fad33 100644 --- a/setup.py +++ b/setup.py @@ -44,9 +44,9 @@ setup(name='urllib3', author_email='andrey.petrov@shazow.net', url='http://urllib3.readthedocs.org/', license='MIT', - packages=['urllib3', 'dummyserver', + packages=['urllib3', 'urllib3.packages', 'urllib3.packages.ssl_match_hostname', - 'urllib3.contrib', + 'urllib3.contrib', 'urllib3.util', ], requires=requirements, tests_require=tests_requirements, diff --git a/test/test_poolmanager.py b/test/test_poolmanager.py index 2faab94..759b5e3 100644 --- a/test/test_poolmanager.py +++ b/test/test_poolmanager.py @@ -2,7 +2,10 @@ import unittest from urllib3.poolmanager import PoolManager from urllib3 import connection_from_url -from urllib3.exceptions import ClosedPoolError +from urllib3.exceptions import ( + ClosedPoolError, + LocationParseError, +) class TestPoolManager(unittest.TestCase): @@ -63,6 +66,9 @@ class TestPoolManager(unittest.TestCase): self.assertEqual(len(p.pools), 0) + def test_nohost(self): + p = PoolManager(5) + self.assertRaises(LocationParseError, p.connection_from_url, 'http://@') if __name__ == '__main__': diff --git a/test/test_util.py b/test/test_util.py index ebd3b5f..5dcaeab 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -77,6 +77,7 @@ class TestUtil(unittest.TestCase): for location in invalid_host: self.assertRaises(LocationParseError, get_host, location) + def test_parse_url(self): url_host_map = { 'http://google.com/mail': Url('http', host='google.com', path='/mail'), @@ -107,6 +108,7 @@ class TestUtil(unittest.TestCase): 'http://foo:bar@localhost/': Url('http', auth='foo:bar', host='localhost', path='/'), 'http://foo@localhost/': Url('http', auth='foo', host='localhost', path='/'), 'http://foo:bar@baz@localhost/': Url('http', auth='foo:bar@baz', host='localhost', path='/'), + 'http://@': Url('http', host=None, auth='') } for url, expected_url in url_host_map.items(): returned_url = parse_url(url) @@ -231,7 +233,7 @@ class TestUtil(unittest.TestCase): self.assertTrue('int or float' in str(e)) - @patch('urllib3.util.current_time') + @patch('urllib3.util.timeout.current_time') def test_timeout(self, current_time): timeout = Timeout(total=3) @@ -278,7 +280,7 @@ class TestUtil(unittest.TestCase): self.assertEqual(str(timeout), "Timeout(connect=1, read=None, total=3)") - @patch('urllib3.util.current_time') + @patch('urllib3.util.timeout.current_time') def test_timeout_elapsed(self, current_time): current_time.return_value = TIMEOUT_EPOCH timeout = Timeout(total=3) diff --git a/urllib3.egg-info/PKG-INFO b/urllib3.egg-info/PKG-INFO index 6a4f31a..0021e34 100644 --- a/urllib3.egg-info/PKG-INFO +++ b/urllib3.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: urllib3 -Version: 1.8 +Version: 1.8.2 Summary: HTTP library with thread-safe connection pooling, file post, and more. Home-page: http://urllib3.readthedocs.org/ Author: Andrey Petrov @@ -121,6 +121,23 @@ Description: ======= Changes ======= + 1.8.2 (2014-04-17) + ++++++++++++++++++ + + * Fix ``urllib3.util`` not being included in the package. + + + 1.8.1 (2014-04-17) + ++++++++++++++++++ + + * Fix AppEngine bug of HTTPS requests going out as HTTP. (Issue #356) + + * Don't install ``dummyserver`` into ``site-packages`` as it's only needed + for the test suite. (Issue #362) + + * Added support for specifying ``source_address``. (Issue #352) + + 1.8 (2014-03-04) ++++++++++++++++ diff --git a/urllib3.egg-info/SOURCES.txt b/urllib3.egg-info/SOURCES.txt index a5170fb..72e3351 100644 --- a/urllib3.egg-info/SOURCES.txt +++ b/urllib3.egg-info/SOURCES.txt @@ -7,10 +7,30 @@ setup.cfg setup.py test-requirements.txt dummyserver/__init__.py +dummyserver/__init__.pyc dummyserver/handlers.py +dummyserver/handlers.pyc dummyserver/proxy.py +dummyserver/proxy.pyc dummyserver/server.py +dummyserver/server.pyc dummyserver/testcase.py +dummyserver/testcase.pyc +dummyserver/__pycache__/__init__.cpython-33.pyc +dummyserver/__pycache__/handlers.cpython-33.pyc +dummyserver/__pycache__/proxy.cpython-33.pyc +dummyserver/__pycache__/server.cpython-33.pyc +dummyserver/__pycache__/testcase.cpython-33.pyc +dummyserver/certs/cacert.key +dummyserver/certs/cacert.pem +dummyserver/certs/client.csr +dummyserver/certs/client.key +dummyserver/certs/client.pem +dummyserver/certs/client_bad.pem +dummyserver/certs/server.crt +dummyserver/certs/server.csr +dummyserver/certs/server.key +dummyserver/certs/server.key.org test/test_collections.py test/test_compatibility.py test/test_connectionpool.py @@ -31,7 +51,6 @@ urllib3/filepost.py urllib3/poolmanager.py urllib3/request.py urllib3/response.py -urllib3/util.py urllib3.egg-info/PKG-INFO urllib3.egg-info/SOURCES.txt urllib3.egg-info/dependency_links.txt @@ -43,4 +62,11 @@ urllib3/packages/__init__.py urllib3/packages/ordered_dict.py urllib3/packages/six.py urllib3/packages/ssl_match_hostname/__init__.py -urllib3/packages/ssl_match_hostname/_implementation.py \ No newline at end of file +urllib3/packages/ssl_match_hostname/_implementation.py +urllib3/util/__init__.py +urllib3/util/connection.py +urllib3/util/request.py +urllib3/util/response.py +urllib3/util/ssl_.py +urllib3/util/timeout.py +urllib3/util/url.py \ No newline at end of file diff --git a/urllib3.egg-info/top_level.txt b/urllib3.egg-info/top_level.txt index 93675d9..a42590b 100644 --- a/urllib3.egg-info/top_level.txt +++ b/urllib3.egg-info/top_level.txt @@ -1,2 +1 @@ urllib3 -dummyserver diff --git a/urllib3/__init__.py b/urllib3/__init__.py index 086387f..bd237a6 100644 --- a/urllib3/__init__.py +++ b/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.8' +__version__ = '1.8.2' from .connectionpool import ( diff --git a/urllib3/connection.py b/urllib3/connection.py index 662bd2e..de7b925 100644 --- a/urllib3/connection.py +++ b/urllib3/connection.py @@ -68,15 +68,17 @@ class HTTPConnection(_HTTPConnection, object): def __init__(self, *args, **kw): if six.PY3: # Python 3 kw.pop('strict', None) - - if sys.version_info < (2, 7): # Python 2.6 and earlier + if sys.version_info < (2, 7): # Python 2.6 and older kw.pop('source_address', None) - self.source_address = None - _HTTPConnection.__init__(self, *args, **kw) + # Pre-set source_address in case we have an older Python like 2.6. + self.source_address = kw.get('source_address') + + # Superclass also sets self.source_address in Python 2.7+. + _HTTPConnection.__init__(self, *args, **kw) def _new_conn(self): - """ Establish a socket connection and set nodelay settings on it + """ Establish a socket connection and set nodelay settings on it. :return: a new socket connection """ @@ -85,12 +87,10 @@ class HTTPConnection(_HTTPConnection, object): extra_args.append(self.source_address) conn = socket.create_connection( - (self.host, self.port), - self.timeout, - *extra_args - ) - conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, - self.tcp_nodelay) + (self.host, self.port), self.timeout, *extra_args) + conn.setsockopt( + socket.IPPROTO_TCP, socket.TCP_NODELAY, self.tcp_nodelay) + return conn def _prepare_conn(self, conn): @@ -108,17 +108,18 @@ class HTTPSConnection(HTTPConnection): default_port = port_by_scheme['https'] def __init__(self, host, port=None, key_file=None, cert_file=None, - strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None): + strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **kw): - HTTPConnection.__init__(self, host, port, - strict=strict, - timeout=timeout, - source_address=source_address) + HTTPConnection.__init__(self, host, port, strict=strict, + timeout=timeout, **kw) self.key_file = key_file self.cert_file = cert_file + # Required property for Google AppEngine 1.9.0 which otherwise causes + # HTTPS requests to go out as HTTP. (See Issue #356) + self._protocol = 'https' + def connect(self): conn = self._new_conn() self._prepare_conn(conn) @@ -133,6 +134,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): cert_reqs = None ca_certs = None ssl_version = None + conn_kw = {} def set_cert(self, key_file=None, cert_file=None, cert_reqs=None, ca_certs=None, @@ -147,11 +149,11 @@ class VerifiedHTTPSConnection(HTTPSConnection): def connect(self): # Add certificate verification + try: sock = socket.create_connection( - address=(self.host, self.port), - timeout=self.timeout, - ) + address=(self.host, self.port), timeout=self.timeout, + **self.conn_kw) except SocketTimeout: raise ConnectTimeoutError( self, "Connection to %s timed out. (connect timeout=%s)" % diff --git a/urllib3/connectionpool.py b/urllib3/connectionpool.py index 6d0dbb1..95a53a7 100644 --- a/urllib3/connectionpool.py +++ b/urllib3/connectionpool.py @@ -4,6 +4,7 @@ # This module is part of urllib3 and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php +import sys import errno import logging @@ -23,6 +24,7 @@ from .exceptions import ( ConnectTimeoutError, EmptyPoolError, HostChangedError, + LocationParseError, MaxRetryError, SSLError, TimeoutError, @@ -40,7 +42,6 @@ from .connection import ( from .request import RequestMethods from .response import HTTPResponse from .util import ( - assert_fingerprint, get_host, is_connection_dropped, Timeout, @@ -65,6 +66,9 @@ class ConnectionPool(object): QueueCls = LifoQueue def __init__(self, host, port=None): + if host is None: + raise LocationParseError(host) + # httplib doesn't like it when we include brackets in ipv6 addresses host = host.strip('[]') @@ -136,7 +140,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): def __init__(self, host, port=None, strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, - headers=None, _proxy=None, _proxy_headers=None): + headers=None, _proxy=None, _proxy_headers=None, **conn_kw): ConnectionPool.__init__(self, host, port) RequestMethods.__init__(self, headers) @@ -163,6 +167,10 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): self.num_connections = 0 self.num_requests = 0 + if sys.version_info < (2, 7): # Python 2.6 and older + conn_kw.pop('source_address', None) + self.conn_kw = conn_kw + def _new_conn(self): """ Return a fresh :class:`HTTPConnection`. @@ -173,7 +181,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): conn = self.ConnectionCls(host=self.host, port=self.port, timeout=self.timeout.connect_timeout, - strict=self.strict) + strict=self.strict, **self.conn_kw) if self.proxy is not None: # Enable Nagle's algorithm for proxies, to avoid packet # fragmentation. @@ -594,10 +602,14 @@ class HTTPSConnectionPool(HTTPConnectionPool): _proxy=None, _proxy_headers=None, key_file=None, cert_file=None, cert_reqs=None, ca_certs=None, ssl_version=None, - assert_hostname=None, assert_fingerprint=None): + assert_hostname=None, assert_fingerprint=None, + **conn_kw): + + if sys.version_info < (2, 7): # Python 2.6 or older + conn_kw.pop('source_address', None) HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, - block, headers, _proxy, _proxy_headers) + block, headers, _proxy, _proxy_headers, **conn_kw) self.key_file = key_file self.cert_file = cert_file self.cert_reqs = cert_reqs @@ -605,6 +617,7 @@ class HTTPSConnectionPool(HTTPConnectionPool): self.ssl_version = ssl_version self.assert_hostname = assert_hostname self.assert_fingerprint = assert_fingerprint + self.conn_kw = conn_kw def _prepare_conn(self, conn): """ @@ -620,6 +633,7 @@ class HTTPSConnectionPool(HTTPConnectionPool): assert_hostname=self.assert_hostname, assert_fingerprint=self.assert_fingerprint) conn.ssl_version = self.ssl_version + conn.conn_kw = self.conn_kw if self.proxy is not None: # Python 2.7+ @@ -656,6 +670,7 @@ class HTTPSConnectionPool(HTTPConnectionPool): extra_params = {} if not six.PY3: # Python 2 extra_params['strict'] = self.strict + extra_params.update(self.conn_kw) conn = self.ConnectionCls(host=actual_host, port=actual_port, timeout=self.timeout.connect_timeout, diff --git a/urllib3/contrib/pyopenssl.py b/urllib3/contrib/pyopenssl.py index 7c513f3..21a12c6 100644 --- a/urllib3/contrib/pyopenssl.py +++ b/urllib3/contrib/pyopenssl.py @@ -1,4 +1,7 @@ -'''SSL with SNI_-support for Python 2. +'''SSL with SNI_-support for Python 2. Follow these instructions if you would +like to verify SSL certificates in Python 2. Note, the default libraries do +*not* do certificate checking; you need to do additional work to validate +certificates yourself. This needs the following packages installed: @@ -6,9 +9,15 @@ This needs the following packages installed: * ndg-httpsclient (tested with 0.3.2) * pyasn1 (tested with 0.1.6) -To activate it call :func:`~urllib3.contrib.pyopenssl.inject_into_urllib3`. -This can be done in a ``sitecustomize`` module, or at any other time before -your application begins using ``urllib3``, like this:: +You can install them with the following command: + + pip install pyopenssl ndg-httpsclient pyasn1 + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this:: try: import urllib3.contrib.pyopenssl diff --git a/urllib3/util.py b/urllib3/util.py deleted file mode 100644 index bd26631..0000000 --- a/urllib3/util.py +++ /dev/null @@ -1,648 +0,0 @@ -# urllib3/util.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - - -from base64 import b64encode -from binascii import hexlify, unhexlify -from collections import namedtuple -from hashlib import md5, sha1 -from socket import error as SocketError, _GLOBAL_DEFAULT_TIMEOUT -import time - -try: - from select import poll, POLLIN -except ImportError: # `poll` doesn't exist on OSX and other platforms - poll = False - try: - from select import select - except ImportError: # `select` doesn't exist on AppEngine. - select = False - -try: # Test for SSL features - SSLContext = None - HAS_SNI = False - - import ssl - from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 - from ssl import SSLContext # Modern SSL? - from ssl import HAS_SNI # Has SNI? -except ImportError: - pass - -from .packages import six -from .exceptions import LocationParseError, SSLError, TimeoutStateError - - -_Default = object() -# The default timeout to use for socket connections. This is the attribute used -# by httplib to define the default timeout - - -def current_time(): - """ - Retrieve the current time, this function is mocked out in unit testing. - """ - return time.time() - - -class Timeout(object): - """ - Utility object for storing timeout values. - - Example usage: - - .. code-block:: python - - timeout = urllib3.util.Timeout(connect=2.0, read=7.0) - pool = HTTPConnectionPool('www.google.com', 80, timeout=timeout) - pool.request(...) # Etc, etc - - :param connect: - The maximum amount of time to wait for a connection attempt to a server - to succeed. Omitting the parameter will default the connect timeout to - the system default, probably `the global default timeout in socket.py - `_. - None will set an infinite timeout for connection attempts. - - :type connect: integer, float, or None - - :param read: - The maximum amount of time to wait between consecutive - read operations for a response from the server. Omitting - the parameter will default the read timeout to the system - default, probably `the global default timeout in socket.py - `_. - None will set an infinite timeout. - - :type read: integer, float, or None - - :param total: - This combines the connect and read timeouts into one; the read timeout - will be set to the time leftover from the connect attempt. In the - event that both a connect timeout and a total are specified, or a read - timeout and a total are specified, the shorter timeout will be applied. - - Defaults to None. - - :type total: integer, float, or None - - .. note:: - - Many factors can affect the total amount of time for urllib3 to return - an HTTP response. Specifically, Python's DNS resolver does not obey the - timeout specified on the socket. Other factors that can affect total - request time include high CPU load, high swap, the program running at a - low priority level, or other behaviors. The observed running time for - urllib3 to return a response may be greater than the value passed to - `total`. - - In addition, the read and total timeouts only measure the time between - read operations on the socket connecting the client and the server, - not the total amount of time for the request to return a complete - response. For most requests, the timeout is raised because the server - has not sent the first byte in the specified time. This is not always - the case; if a server streams one byte every fifteen seconds, a timeout - of 20 seconds will not ever trigger, even though the request will - take several minutes to complete. - - If your goal is to cut off any request after a set amount of wall clock - time, consider having a second "watcher" thread to cut off a slow - request. - """ - - #: A sentinel object representing the default timeout value - DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT - - def __init__(self, total=None, connect=_Default, read=_Default): - self._connect = self._validate_timeout(connect, 'connect') - self._read = self._validate_timeout(read, 'read') - self.total = self._validate_timeout(total, 'total') - self._start_connect = None - - def __str__(self): - return '%s(connect=%r, read=%r, total=%r)' % ( - type(self).__name__, self._connect, self._read, self.total) - - - @classmethod - def _validate_timeout(cls, value, name): - """ Check that a timeout attribute is valid - - :param value: The timeout value to validate - :param name: The name of the timeout attribute to validate. This is used - for clear error messages - :return: the value - :raises ValueError: if the type is not an integer or a float, or if it - is a numeric value less than zero - """ - if value is _Default: - return cls.DEFAULT_TIMEOUT - - if value is None or value is cls.DEFAULT_TIMEOUT: - return value - - try: - float(value) - except (TypeError, ValueError): - raise ValueError("Timeout value %s was %s, but it must be an " - "int or float." % (name, value)) - - try: - if value < 0: - raise ValueError("Attempted to set %s timeout to %s, but the " - "timeout cannot be set to a value less " - "than 0." % (name, value)) - except TypeError: # Python 3 - raise ValueError("Timeout value %s was %s, but it must be an " - "int or float." % (name, value)) - - return value - - @classmethod - def from_float(cls, timeout): - """ Create a new Timeout from a legacy timeout value. - - The timeout value used by httplib.py sets the same timeout on the - connect(), and recv() socket requests. This creates a :class:`Timeout` - object that sets the individual timeouts to the ``timeout`` value passed - to this function. - - :param timeout: The legacy timeout value - :type timeout: integer, float, sentinel default object, or None - :return: a Timeout object - :rtype: :class:`Timeout` - """ - return Timeout(read=timeout, connect=timeout) - - def clone(self): - """ Create a copy of the timeout object - - Timeout properties are stored per-pool but each request needs a fresh - Timeout object to ensure each one has its own start/stop configured. - - :return: a copy of the timeout object - :rtype: :class:`Timeout` - """ - # We can't use copy.deepcopy because that will also create a new object - # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to - # detect the user default. - return Timeout(connect=self._connect, read=self._read, - total=self.total) - - def start_connect(self): - """ Start the timeout clock, used during a connect() attempt - - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to start a timer that has been started already. - """ - if self._start_connect is not None: - raise TimeoutStateError("Timeout timer has already been started.") - self._start_connect = current_time() - return self._start_connect - - def get_connect_duration(self): - """ Gets the time elapsed since the call to :meth:`start_connect`. - - :return: the elapsed time - :rtype: float - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to get duration for a timer that hasn't been started. - """ - if self._start_connect is None: - raise TimeoutStateError("Can't get connect duration for timer " - "that has not started.") - return current_time() - self._start_connect - - @property - def connect_timeout(self): - """ Get the value to use when setting a connection timeout. - - This will be a positive float or integer, the value None - (never timeout), or the default system timeout. - - :return: the connect timeout - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - """ - if self.total is None: - return self._connect - - if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: - return self.total - - return min(self._connect, self.total) - - @property - def read_timeout(self): - """ Get the value for the read timeout. - - This assumes some time has elapsed in the connection timeout and - computes the read timeout appropriately. - - If self.total is set, the read timeout is dependent on the amount of - time taken by the connect timeout. If the connection time has not been - established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be - raised. - - :return: the value to use for the read timeout - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` - has not yet been called on this object. - """ - if (self.total is not None and - self.total is not self.DEFAULT_TIMEOUT and - self._read is not None and - self._read is not self.DEFAULT_TIMEOUT): - # in case the connect timeout has not yet been established. - if self._start_connect is None: - return self._read - return max(0, min(self.total - self.get_connect_duration(), - self._read)) - elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: - return max(0, self.total - self.get_connect_duration()) - else: - return self._read - - -class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'])): - """ - Datastructure for representing an HTTP URL. Used as a return value for - :func:`parse_url`. - """ - slots = () - - def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, query=None, fragment=None): - return super(Url, cls).__new__(cls, scheme, auth, host, port, path, query, fragment) - - @property - def hostname(self): - """For backwards-compatibility with urlparse. We're nice like that.""" - return self.host - - @property - def request_uri(self): - """Absolute path including the query string.""" - uri = self.path or '/' - - if self.query is not None: - uri += '?' + self.query - - return uri - - @property - def netloc(self): - """Network location including host and port""" - if self.port: - return '%s:%d' % (self.host, self.port) - return self.host - - -def split_first(s, delims): - """ - Given a string and an iterable of delimiters, split on the first found - delimiter. Return two split parts and the matched delimiter. - - If not found, then the first part is the full input string. - - Example: :: - - >>> split_first('foo/bar?baz', '?/=') - ('foo', 'bar?baz', '/') - >>> split_first('foo/bar?baz', '123') - ('foo/bar?baz', '', None) - - Scales linearly with number of delims. Not ideal for large number of delims. - """ - min_idx = None - min_delim = None - for d in delims: - idx = s.find(d) - if idx < 0: - continue - - if min_idx is None or idx < min_idx: - min_idx = idx - min_delim = d - - if min_idx is None or min_idx < 0: - return s, '', None - - return s[:min_idx], s[min_idx+1:], min_delim - - -def parse_url(url): - """ - Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is - performed to parse incomplete urls. Fields not provided will be None. - - Partly backwards-compatible with :mod:`urlparse`. - - Example: :: - - >>> parse_url('http://google.com/mail/') - Url(scheme='http', host='google.com', port=None, path='/', ...) - >>> parse_url('google.com:80') - Url(scheme=None, host='google.com', port=80, path=None, ...) - >>> parse_url('/foo?bar') - Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) - """ - - # While this code has overlap with stdlib's urlparse, it is much - # simplified for our needs and less annoying. - # Additionally, this implementations does silly things to be optimal - # on CPython. - - scheme = None - auth = None - host = None - port = None - path = None - fragment = None - query = None - - # Scheme - if '://' in url: - scheme, url = url.split('://', 1) - - # Find the earliest Authority Terminator - # (http://tools.ietf.org/html/rfc3986#section-3.2) - url, path_, delim = split_first(url, ['/', '?', '#']) - - if delim: - # Reassemble the path - path = delim + path_ - - # Auth - if '@' in url: - # Last '@' denotes end of auth part - auth, url = url.rsplit('@', 1) - - # IPv6 - if url and url[0] == '[': - host, url = url.split(']', 1) - host += ']' - - # Port - if ':' in url: - _host, port = url.split(':', 1) - - if not host: - host = _host - - if port: - # If given, ports must be integers. - if not port.isdigit(): - raise LocationParseError("Failed to parse: %s" % url) - port = int(port) - else: - # Blank ports are cool, too. (rfc3986#section-3.2.3) - port = None - - elif not host and url: - host = url - - if not path: - return Url(scheme, auth, host, port, path, query, fragment) - - # Fragment - if '#' in path: - path, fragment = path.split('#', 1) - - # Query - if '?' in path: - path, query = path.split('?', 1) - - return Url(scheme, auth, host, port, path, query, fragment) - - -def get_host(url): - """ - Deprecated. Use :func:`.parse_url` instead. - """ - p = parse_url(url) - return p.scheme or 'http', p.hostname, p.port - - -def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, - basic_auth=None, proxy_basic_auth=None): - """ - Shortcuts for generating request headers. - - :param keep_alive: - If ``True``, adds 'connection: keep-alive' header. - - :param accept_encoding: - Can be a boolean, list, or string. - ``True`` translates to 'gzip,deflate'. - List will get joined by comma. - String will be used as provided. - - :param user_agent: - String representing the user-agent you want, such as - "python-urllib3/0.6" - - :param basic_auth: - 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") - {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} - >>> make_headers(accept_encoding=True) - {'accept-encoding': 'gzip,deflate'} - """ - headers = {} - if accept_encoding: - if isinstance(accept_encoding, str): - pass - elif isinstance(accept_encoding, list): - accept_encoding = ','.join(accept_encoding) - else: - accept_encoding = 'gzip,deflate' - headers['accept-encoding'] = accept_encoding - - if user_agent: - headers['user-agent'] = user_agent - - if keep_alive: - headers['connection'] = 'keep-alive' - - if basic_auth: - 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 - - -def is_connection_dropped(conn): # Platform-specific - """ - Returns True if the connection is dropped and should be closed. - - :param conn: - :class:`httplib.HTTPConnection` object. - - Note: For platforms like AppEngine, this will always return ``False`` to - let the platform handle connection recycling transparently for us. - """ - sock = getattr(conn, 'sock', False) - if not sock: # Platform-specific: AppEngine - return False - - if not poll: - if not select: # Platform-specific: AppEngine - return False - - try: - return select([sock], [], [], 0.0)[0] - except SocketError: - return True - - # This version is better on platforms that support it. - p = poll() - p.register(sock, POLLIN) - for (fno, ev) in p.poll(0.0): - if fno == sock.fileno(): - # Either data is buffered (bad), or the connection is dropped. - return True - - -def resolve_cert_reqs(candidate): - """ - Resolves the argument to a numeric constant, which can be passed to - the wrap_socket function/method from the ssl module. - Defaults to :data:`ssl.CERT_NONE`. - If given a string it is assumed to be the name of the constant in the - :mod:`ssl` module or its abbrevation. - (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. - If it's neither `None` nor a string we assume it is already the numeric - constant which can directly be passed to wrap_socket. - """ - if candidate is None: - return CERT_NONE - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, 'CERT_' + candidate) - return res - - return candidate - - -def resolve_ssl_version(candidate): - """ - like resolve_cert_reqs - """ - if candidate is None: - return PROTOCOL_SSLv23 - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, 'PROTOCOL_' + candidate) - return res - - return candidate - - -def assert_fingerprint(cert, fingerprint): - """ - Checks if given fingerprint matches the supplied certificate. - - :param cert: - Certificate as bytes object. - :param fingerprint: - Fingerprint as string of hexdigits, can be interspersed by colons. - """ - - # Maps the length of a digest to a possible hash function producing - # this digest. - hashfunc_map = { - 16: md5, - 20: sha1 - } - - fingerprint = fingerprint.replace(':', '').lower() - - digest_length, rest = divmod(len(fingerprint), 2) - - if rest or digest_length not in hashfunc_map: - raise SSLError('Fingerprint is of invalid length.') - - # We need encode() here for py32; works on py2 and p33. - fingerprint_bytes = unhexlify(fingerprint.encode()) - - hashfunc = hashfunc_map[digest_length] - - cert_digest = hashfunc(cert).digest() - - if not cert_digest == fingerprint_bytes: - raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' - .format(hexlify(fingerprint_bytes), - hexlify(cert_digest))) - -def is_fp_closed(obj): - """ - Checks whether a given file-like object is closed. - - :param obj: - The file-like object to check. - """ - if hasattr(obj, 'fp'): - # Object is a container for another file-like object that gets released - # on exhaustion (e.g. HTTPResponse) - return obj.fp is None - - return obj.closed - - -if SSLContext is not None: # Python 3.2+ - def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, - ca_certs=None, server_hostname=None, - ssl_version=None): - """ - All arguments except `server_hostname` have the same meaning as for - :func:`ssl.wrap_socket` - - :param server_hostname: - Hostname of the expected certificate - """ - context = SSLContext(ssl_version) - context.verify_mode = cert_reqs - - # Disable TLS compression to migitate CRIME attack (issue #309) - OP_NO_COMPRESSION = 0x20000 - context.options |= OP_NO_COMPRESSION - - if ca_certs: - try: - context.load_verify_locations(ca_certs) - # Py32 raises IOError - # Py33 raises FileNotFoundError - except Exception as e: # Reraise as SSLError - raise SSLError(e) - if certfile: - # FIXME: This block needs a test. - context.load_cert_chain(certfile, keyfile) - if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI - return context.wrap_socket(sock, server_hostname=server_hostname) - return context.wrap_socket(sock) - -else: # Python 3.1 and earlier - def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, - ca_certs=None, server_hostname=None, - ssl_version=None): - return wrap_socket(sock, keyfile=keyfile, certfile=certfile, - ca_certs=ca_certs, cert_reqs=cert_reqs, - ssl_version=ssl_version) diff --git a/urllib3/util/__init__.py b/urllib3/util/__init__.py new file mode 100644 index 0000000..a40185e --- /dev/null +++ b/urllib3/util/__init__.py @@ -0,0 +1,27 @@ +# urllib3/util/__init__.py +# Copyright 2008-2014 Andrey Petrov and contributors (see CONTRIBUTORS.txt) +# +# This module is part of urllib3 and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from .connection import is_connection_dropped +from .request import make_headers +from .response import is_fp_closed +from .ssl_ import ( + SSLContext, + HAS_SNI, + assert_fingerprint, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .timeout import ( + current_time, + Timeout, +) +from .url import ( + get_host, + parse_url, + split_first, + Url, +) diff --git a/urllib3/util/connection.py b/urllib3/util/connection.py new file mode 100644 index 0000000..8deeab5 --- /dev/null +++ b/urllib3/util/connection.py @@ -0,0 +1,45 @@ +from socket import error as SocketError +try: + from select import poll, POLLIN +except ImportError: # `poll` doesn't exist on OSX and other platforms + poll = False + try: + from select import select + except ImportError: # `select` doesn't exist on AppEngine. + select = False + +def is_connection_dropped(conn): # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + + :param conn: + :class:`httplib.HTTPConnection` object. + + Note: For platforms like AppEngine, this will always return ``False`` to + let the platform handle connection recycling transparently for us. + """ + sock = getattr(conn, 'sock', False) + if sock is False: # Platform-specific: AppEngine + return False + if sock is None: # Connection already closed (such as by httplib). + return False + + if not poll: + if not select: # Platform-specific: AppEngine + return False + + try: + return select([sock], [], [], 0.0)[0] + except SocketError: + return True + + # This version is better on platforms that support it. + p = poll() + p.register(sock, POLLIN) + for (fno, ev) in p.poll(0.0): + if fno == sock.fileno(): + # Either data is buffered (bad), or the connection is dropped. + return True + + + diff --git a/urllib3/util/request.py b/urllib3/util/request.py new file mode 100644 index 0000000..d48d651 --- /dev/null +++ b/urllib3/util/request.py @@ -0,0 +1,68 @@ +from base64 import b64encode + +from ..packages import six + + +ACCEPT_ENCODING = 'gzip,deflate' + + +def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, + basic_auth=None, proxy_basic_auth=None): + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + 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") + {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + >>> make_headers(accept_encoding=True) + {'accept-encoding': 'gzip,deflate'} + """ + headers = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ','.join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers['accept-encoding'] = accept_encoding + + if user_agent: + headers['user-agent'] = user_agent + + if keep_alive: + headers['connection'] = 'keep-alive' + + if basic_auth: + 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 + + diff --git a/urllib3/util/response.py b/urllib3/util/response.py new file mode 100644 index 0000000..d0325bc --- /dev/null +++ b/urllib3/util/response.py @@ -0,0 +1,13 @@ +def is_fp_closed(obj): + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + if hasattr(obj, 'fp'): + # Object is a container for another file-like object that gets released + # on exhaustion (e.g. HTTPResponse) + return obj.fp is None + + return obj.closed diff --git a/urllib3/util/ssl_.py b/urllib3/util/ssl_.py new file mode 100644 index 0000000..dee4b87 --- /dev/null +++ b/urllib3/util/ssl_.py @@ -0,0 +1,133 @@ +from binascii import hexlify, unhexlify +from hashlib import md5, sha1 + +from ..exceptions import SSLError + + +try: # Test for SSL features + SSLContext = None + HAS_SNI = False + + import ssl + from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 + from ssl import SSLContext # Modern SSL? + from ssl import HAS_SNI # Has SNI? +except ImportError: + pass + + +def assert_fingerprint(cert, fingerprint): + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + # Maps the length of a digest to a possible hash function producing + # this digest. + hashfunc_map = { + 16: md5, + 20: sha1 + } + + fingerprint = fingerprint.replace(':', '').lower() + + digest_length, rest = divmod(len(fingerprint), 2) + + if rest or digest_length not in hashfunc_map: + raise SSLError('Fingerprint is of invalid length.') + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + hashfunc = hashfunc_map[digest_length] + + cert_digest = hashfunc(cert).digest() + + if not cert_digest == fingerprint_bytes: + raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' + .format(hexlify(fingerprint_bytes), + hexlify(cert_digest))) + + +def resolve_cert_reqs(candidate): + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_NONE`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbrevation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_NONE + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'CERT_' + candidate) + return res + + return candidate + + +def resolve_ssl_version(candidate): + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_SSLv23 + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'PROTOCOL_' + candidate) + return res + + return candidate + + +if SSLContext is not None: # Python 3.2+ + def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None): + """ + All arguments except `server_hostname` have the same meaning as for + :func:`ssl.wrap_socket` + + :param server_hostname: + Hostname of the expected certificate + """ + context = SSLContext(ssl_version) + context.verify_mode = cert_reqs + + # Disable TLS compression to migitate CRIME attack (issue #309) + OP_NO_COMPRESSION = 0x20000 + context.options |= OP_NO_COMPRESSION + + if ca_certs: + try: + context.load_verify_locations(ca_certs) + # Py32 raises IOError + # Py33 raises FileNotFoundError + except Exception as e: # Reraise as SSLError + raise SSLError(e) + if certfile: + # FIXME: This block needs a test. + context.load_cert_chain(certfile, keyfile) + if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI + return context.wrap_socket(sock, server_hostname=server_hostname) + return context.wrap_socket(sock) + +else: # Python 3.1 and earlier + def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None): + return wrap_socket(sock, keyfile=keyfile, certfile=certfile, + ca_certs=ca_certs, cert_reqs=cert_reqs, + ssl_version=ssl_version) diff --git a/urllib3/util/timeout.py b/urllib3/util/timeout.py new file mode 100644 index 0000000..4f947cb --- /dev/null +++ b/urllib3/util/timeout.py @@ -0,0 +1,234 @@ +from socket import _GLOBAL_DEFAULT_TIMEOUT +import time + +from ..exceptions import TimeoutStateError + + +def current_time(): + """ + Retrieve the current time, this function is mocked out in unit testing. + """ + return time.time() + + +_Default = object() +# The default timeout to use for socket connections. This is the attribute used +# by httplib to define the default timeout + + +class Timeout(object): + """ + Utility object for storing timeout values. + + Example usage: + + .. code-block:: python + + timeout = urllib3.util.Timeout(connect=2.0, read=7.0) + pool = HTTPConnectionPool('www.google.com', 80, timeout=timeout) + pool.request(...) # Etc, etc + + :param connect: + The maximum amount of time to wait for a connection attempt to a server + to succeed. Omitting the parameter will default the connect timeout to + the system default, probably `the global default timeout in socket.py + `_. + None will set an infinite timeout for connection attempts. + + :type connect: integer, float, or None + + :param read: + The maximum amount of time to wait between consecutive + read operations for a response from the server. Omitting + the parameter will default the read timeout to the system + default, probably `the global default timeout in socket.py + `_. + None will set an infinite timeout. + + :type read: integer, float, or None + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: integer, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. Specifically, Python's DNS resolver does not obey the + timeout specified on the socket. Other factors that can affect total + request time include high CPU load, high swap, the program running at a + low priority level, or other behaviors. The observed running time for + urllib3 to return a response may be greater than the value passed to + `total`. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not ever trigger, even though the request will + take several minutes to complete. + + If your goal is to cut off any request after a set amount of wall clock + time, consider having a second "watcher" thread to cut off a slow + request. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT + + def __init__(self, total=None, connect=_Default, read=_Default): + self._connect = self._validate_timeout(connect, 'connect') + self._read = self._validate_timeout(read, 'read') + self.total = self._validate_timeout(total, 'total') + self._start_connect = None + + def __str__(self): + return '%s(connect=%r, read=%r, total=%r)' % ( + type(self).__name__, self._connect, self._read, self.total) + + + @classmethod + def _validate_timeout(cls, value, name): + """ Check that a timeout attribute is valid + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is used + for clear error messages + :return: the value + :raises ValueError: if the type is not an integer or a float, or if it + is a numeric value less than zero + """ + if value is _Default: + return cls.DEFAULT_TIMEOUT + + if value is None or value is cls.DEFAULT_TIMEOUT: + return value + + try: + float(value) + except (TypeError, ValueError): + raise ValueError("Timeout value %s was %s, but it must be an " + "int or float." % (name, value)) + + try: + if value < 0: + raise ValueError("Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than 0." % (name, value)) + except TypeError: # Python 3 + raise ValueError("Timeout value %s was %s, but it must be an " + "int or float." % (name, value)) + + return value + + @classmethod + def from_float(cls, timeout): + """ Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value passed + to this function. + + :param timeout: The legacy timeout value + :type timeout: integer, float, sentinel default object, or None + :return: a Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self): + """ Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, + total=self.total) + + def start_connect(self): + """ Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = current_time() + return self._start_connect + + def get_connect_duration(self): + """ Gets the time elapsed since the call to :meth:`start_connect`. + + :return: the elapsed time + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError("Can't get connect duration for timer " + "that has not started.") + return current_time() - self._start_connect + + @property + def connect_timeout(self): + """ Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: the connect timeout + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) + + @property + def read_timeout(self): + """ Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: the value to use for the read timeout + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if (self.total is not None and + self.total is not self.DEFAULT_TIMEOUT and + self._read is not None and + self._read is not self.DEFAULT_TIMEOUT): + # in case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), + self._read)) + elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self._read diff --git a/urllib3/util/url.py b/urllib3/util/url.py new file mode 100644 index 0000000..362d216 --- /dev/null +++ b/urllib3/util/url.py @@ -0,0 +1,162 @@ +from collections import namedtuple + +from ..exceptions import LocationParseError + + +class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'])): + """ + Datastructure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. + """ + slots = () + + def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, query=None, fragment=None): + return super(Url, cls).__new__(cls, scheme, auth, host, port, path, query, fragment) + + @property + def hostname(self): + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self): + """Absolute path including the query string.""" + uri = self.path or '/' + + if self.query is not None: + uri += '?' + self.query + + return uri + + @property + def netloc(self): + """Network location including host and port""" + if self.port: + return '%s:%d' % (self.host, self.port) + return self.host + + +def split_first(s, delims): + """ + Given a string and an iterable of delimiters, split on the first found + delimiter. Return two split parts and the matched delimiter. + + If not found, then the first part is the full input string. + + Example: :: + + >>> split_first('foo/bar?baz', '?/=') + ('foo', 'bar?baz', '/') + >>> split_first('foo/bar?baz', '123') + ('foo/bar?baz', '', None) + + Scales linearly with number of delims. Not ideal for large number of delims. + """ + min_idx = None + min_delim = None + for d in delims: + idx = s.find(d) + if idx < 0: + continue + + if min_idx is None or idx < min_idx: + min_idx = idx + min_delim = d + + if min_idx is None or min_idx < 0: + return s, '', None + + return s[:min_idx], s[min_idx+1:], min_delim + + +def parse_url(url): + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + + Partly backwards-compatible with :mod:`urlparse`. + + Example: :: + + >>> parse_url('http://google.com/mail/') + Url(scheme='http', host='google.com', port=None, path='/', ...) + >>> parse_url('google.com:80') + Url(scheme=None, host='google.com', port=80, path=None, ...) + >>> parse_url('/foo?bar') + Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + + # While this code has overlap with stdlib's urlparse, it is much + # simplified for our needs and less annoying. + # Additionally, this implementations does silly things to be optimal + # on CPython. + + scheme = None + auth = None + host = None + port = None + path = None + fragment = None + query = None + + # Scheme + if '://' in url: + scheme, url = url.split('://', 1) + + # Find the earliest Authority Terminator + # (http://tools.ietf.org/html/rfc3986#section-3.2) + url, path_, delim = split_first(url, ['/', '?', '#']) + + if delim: + # Reassemble the path + path = delim + path_ + + # Auth + if '@' in url: + # Last '@' denotes end of auth part + auth, url = url.rsplit('@', 1) + + # IPv6 + if url and url[0] == '[': + host, url = url.split(']', 1) + host += ']' + + # Port + if ':' in url: + _host, port = url.split(':', 1) + + if not host: + host = _host + + if port: + # If given, ports must be integers. + if not port.isdigit(): + raise LocationParseError(url) + port = int(port) + else: + # Blank ports are cool, too. (rfc3986#section-3.2.3) + port = None + + elif not host and url: + host = url + + if not path: + return Url(scheme, auth, host, port, path, query, fragment) + + # Fragment + if '#' in path: + path, fragment = path.split('#', 1) + + # Query + if '?' in path: + path, query = path.split('?', 1) + + return Url(scheme, auth, host, port, path, query, fragment) + + +def get_host(url): + """ + Deprecated. Use :func:`.parse_url` instead. + """ + p = parse_url(url) + return p.scheme or 'http', p.hostname, p.port -- cgit v1.2.3