Hello community,
here is the log from the commit of package python3-urllib3 for openSUSE:Factory checked in at 2016-01-04 09:21:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python3-urllib3 (Old)
and /work/SRC/openSUSE:Factory/.python3-urllib3.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-urllib3"
Changes:
--------
--- /work/SRC/openSUSE:Factory/python3-urllib3/python3-urllib3.changes 2015-12-23 09:57:08.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python3-urllib3.new/python3-urllib3.changes 2016-01-04 09:21:59.000000000 +0100
@@ -1,0 +2,8 @@
+Thu Dec 31 19:57:42 UTC 2015 - arun@gmx.de
+
+- update to version 1.14:
+ * contrib: SOCKS proxy support! (Issue #762)
+ * Fixed AppEngine handling of transfer-encoding header and bug
+ in Timeout defaults checking. (Issue #763)
+
+-------------------------------------------------------------------
Old:
----
urllib3-1.13.1.tar.gz
New:
----
urllib3-1.14.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python3-urllib3.spec ++++++
--- /var/tmp/diff_new_pack.rvrL2r/_old 2016-01-04 09:22:00.000000000 +0100
+++ /var/tmp/diff_new_pack.rvrL2r/_new 2016-01-04 09:22:00.000000000 +0100
@@ -17,7 +17,7 @@
Name: python3-urllib3
-Version: 1.13.1
+Version: 1.14
Release: 0
Summary: HTTP library with thread-safe connection pooling, file post, and more
License: MIT
++++++ urllib3-1.13.1.tar.gz -> urllib3-1.14.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/CHANGES.rst new/urllib3-1.14/CHANGES.rst
--- old/urllib3-1.13.1/CHANGES.rst 2015-12-18 23:47:24.000000000 +0100
+++ new/urllib3-1.14/CHANGES.rst 2015-12-29 21:28:18.000000000 +0100
@@ -1,6 +1,15 @@
Changes
=======
+1.14 (2015-12-29)
++++++++++++++++++
+
+* contrib: SOCKS proxy support! (Issue #762)
+
+* Fixed AppEngine handling of transfer-encoding header and bug
+ in Timeout defaults checking. (Issue #763)
+
+
1.13.1 (2015-12-18)
+++++++++++++++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/CONTRIBUTORS.txt new/urllib3-1.14/CONTRIBUTORS.txt
--- old/urllib3-1.13.1/CONTRIBUTORS.txt 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/CONTRIBUTORS.txt 2015-12-29 21:28:18.000000000 +0100
@@ -184,5 +184,8 @@
* Andy Caldwell
* Bugfix related to reusing connections in indeterminate states.
+* Ville Skyttä
+ * Logging efficiency improvements, spelling fixes, Travis config.
+
* [Your name or handle] <[email or website]>
* [Brief summary of your changes]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/PKG-INFO new/urllib3-1.14/PKG-INFO
--- old/urllib3-1.13.1/PKG-INFO 2015-12-18 23:47:28.000000000 +0100
+++ new/urllib3-1.14/PKG-INFO 2015-12-29 21:28:24.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: urllib3
-Version: 1.13.1
+Version: 1.14
Summary: HTTP library with thread-safe connection pooling, file post, and more.
Home-page: http://urllib3.readthedocs.org/
Author: Andrey Petrov
@@ -156,6 +156,15 @@
Changes
=======
+ 1.14 (2015-12-29)
+ +++++++++++++++++
+
+ * contrib: SOCKS proxy support! (Issue #762)
+
+ * Fixed AppEngine handling of transfer-encoding header and bug
+ in Timeout defaults checking. (Issue #763)
+
+
1.13.1 (2015-12-18)
+++++++++++++++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/docs/contrib.rst new/urllib3-1.14/docs/contrib.rst
--- old/urllib3-1.13.1/docs/contrib.rst 2015-09-04 02:16:43.000000000 +0200
+++ new/urllib3-1.14/docs/contrib.rst 2015-12-29 21:28:18.000000000 +0100
@@ -4,7 +4,7 @@
===============
These modules implement various extra features, that may not be ready for
-prime time.
+prime time or that require optional third-party dependencies.
.. _contrib-pyopenssl:
@@ -16,7 +16,7 @@
.. _gae:
-Google App Engine
+Google App Engine
-----------------
The :mod:`urllib3.contrib.appengine` module provides a pool manager that
@@ -45,8 +45,62 @@
1. You can use :class:`AppEngineManager` with URLFetch. URLFetch is cost-effective in many circumstances as long as your usage is within the limitations.
2. You can use a normal :class:`PoolManager` by enabling sockets. Sockets also have `limitations and restrictions https://cloud.google.com/appengine/docs/python/sockets/#limitations-and-rest...`_ and have a lower free quota than URLFetch. To use sockets, be sure to specify the following in your ``app.yaml``::
-
+
env_variables:
GAE_USE_SOCKETS_HTTPLIB : 'true'
3. If you are using `Managed VMs https://cloud.google.com/appengine/docs/managed-vms/`_, you can use the standard :class:`PoolManager` without any configuration or special environment variables.
+
+
+SOCKS Proxies
+-------------
+
+.. versionadded:: 1.14.0
+
+The :mod:`urllib3.contrib.socks` module enables urllib3 to work with proxies
+that use either the SOCKS4 or SOCKS5 protocols. These proxies are common in
+environments that want to allow generic TCP/UDP traffic through their borders,
+but don't want unrestricted traffic flows.
+
+To use it, either install ``PySocks`` or install urllib3 with the ``socks``
+extra, like so:
+
+.. code-block:: bash
+
+ $ pip install urllib3[socks]
+
+If you have already got urllib3 1.14.0 or later installed, run:
+
+.. code-block:: bash
+
+ $ pip install -U urllib3[socks]
+
+The SOCKS module provides a
+:class:`SOCKSProxyManager ` that can
+be used when SOCKS support is required. This class behaves very much like a
+standard :class:`ProxyManager `, but allows
+the use of a SOCKS proxy instead.
+
+Using it is simple. For example, with a SOCKS5 proxy running on the local
+machine, listening on port 8889:
+
+.. code-block:: python
+
+ from urllib3.contrib.socks import SOCKSProxyManager
+
+ http = SOCKSProxyManager('socks5://localhost:8889/')
+ r = http.request('GET', 'https://www.google.com/')
+
+The SOCKS implementation supports the full range of urllib3 features. It also
+supports the following SOCKS features:
+
+- SOCKS4
+- SOCKS4a
+- SOCKS5
+- Usernames and passwords for the SOCKS proxy
+
+The SOCKS module does have the following limitations:
+
+- No support for contacting a SOCKS proxy via IPv6.
+- No support for reaching websites via a literal IPv6 address: domain names
+ must be used.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/docs/index.rst new/urllib3-1.14/docs/index.rst
--- old/urllib3-1.13.1/docs/index.rst 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/docs/index.rst 2015-12-29 21:28:18.000000000 +0100
@@ -327,7 +327,7 @@
---------------
These modules implement various extra features, that may not be ready for
-prime time.
+prime time or that require optional third-party dependencies.
* :ref:`contrib-modules`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/docs/security.rst new/urllib3-1.14/docs/security.rst
--- old/urllib3-1.13.1/docs/security.rst 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/docs/security.rst 2015-12-29 21:28:18.000000000 +0100
@@ -199,9 +199,11 @@
succeed on more featureful platforms to fail, and can cause certain security
features to be unavailable.
-If you encounter this warning, it is strongly recommended you upgrade to a
-newer Python version, or that you use pyOpenSSL as described in the
-:ref:`pyopenssl` section.
+If you encounter this warning, it is strongly recommended you:
+
+- upgrade to a newer Python version
+- upgrade ``ndg-httpsclient`` with ``pip install --upgrade ndg-httpsclient``
+- use pyOpenSSL as described in the :ref:`pyopenssl` section
For info about disabling warnings, see `Disabling Warnings`_.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/dummyserver/server.py new/urllib3-1.14/dummyserver/server.py
--- old/urllib3-1.13.1/dummyserver/server.py 2015-12-18 23:47:24.000000000 +0100
+++ new/urllib3-1.14/dummyserver/server.py 2015-12-29 21:28:18.000000000 +0100
@@ -90,6 +90,8 @@
:param ready_event: Event which gets set when the socket handler is
ready to receive requests.
"""
+ USE_IPV6 = HAS_IPV6_AND_DNS
+
def __init__(self, socket_handler, host='localhost', port=8081,
ready_event=None):
threading.Thread.__init__(self)
@@ -100,7 +102,7 @@
self.ready_event = ready_event
def _start_server(self):
- if HAS_IPV6_AND_DNS:
+ if self.USE_IPV6:
sock = socket.socket(socket.AF_INET6)
else:
warnings.warn("No IPv6 support. Falling back to IPv4.",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/dummyserver/testcase.py new/urllib3-1.14/dummyserver/testcase.py
--- old/urllib3-1.13.1/dummyserver/testcase.py 2015-12-18 23:47:24.000000000 +0100
+++ new/urllib3-1.14/dummyserver/testcase.py 2015-12-29 21:28:18.000000000 +0100
@@ -71,6 +71,21 @@
cls.server_thread.join(0.1)
+class IPV4SocketDummyServerTestCase(SocketDummyServerTestCase):
+ @classmethod
+ def _start_server(cls, socket_handler):
+ ready_event = threading.Event()
+ cls.server_thread = SocketServerThread(socket_handler=socket_handler,
+ ready_event=ready_event,
+ host=cls.host)
+ cls.server_thread.USE_IPV6 = False
+ cls.server_thread.start()
+ ready_event.wait(5)
+ if not ready_event.is_set():
+ raise Exception("most likely failed to start server")
+ cls.port = cls.server_thread.port
+
+
class HTTPDummyServerTestCase(unittest.TestCase):
""" A simple HTTP server that runs when your test class runs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/setup.py new/urllib3-1.14/setup.py
--- old/urllib3-1.13.1/setup.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/setup.py 2015-12-29 21:28:18.000000000 +0100
@@ -61,5 +61,8 @@
'pyasn1',
'certifi',
],
+ 'socks': [
+ 'PySocks>=1.5.6,<2.0',
+ ]
},
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/test/__init__.py new/urllib3-1.14/test/__init__.py
--- old/urllib3-1.13.1/test/__init__.py 2015-09-06 20:40:19.000000000 +0200
+++ new/urllib3-1.14/test/__init__.py 2015-12-29 21:28:18.000000000 +0100
@@ -57,7 +57,7 @@
return wrapper
def onlyPy279OrNewer(test):
- """Skips this test unless you are onl Python 2.7.9 or later."""
+ """Skips this test unless you are on Python 2.7.9 or later."""
@functools.wraps(test)
def wrapper(*args, **kwargs):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/test/contrib/test_socks.py new/urllib3-1.14/test/contrib/test_socks.py
--- old/urllib3-1.13.1/test/contrib/test_socks.py 1970-01-01 01:00:00.000000000 +0100
+++ new/urllib3-1.14/test/contrib/test_socks.py 2015-12-29 21:28:18.000000000 +0100
@@ -0,0 +1,599 @@
+import threading
+import socket
+
+from urllib3.contrib import socks
+from urllib3.exceptions import ConnectTimeoutError, NewConnectionError
+
+from dummyserver.server import DEFAULT_CERTS
+from dummyserver.testcase import IPV4SocketDummyServerTestCase
+
+from nose.plugins.skip import SkipTest
+
+try:
+ import ssl
+ from urllib3.util import ssl_ as better_ssl
+ HAS_SSL = True
+except ImportError:
+ ssl = None
+ better_ssl = None
+ HAS_SSL = False
+
+
+SOCKS_NEGOTIATION_NONE = b'\x00'
+SOCKS_NEGOTIATION_PASSWORD = b'\x02'
+
+SOCKS_VERSION_SOCKS4 = b'\x04'
+SOCKS_VERSION_SOCKS5 = b'\x05'
+
+
+def _get_free_port(host):
+ """
+ Gets a free port by opening a socket, binding it, checking the assigned
+ port, and then closing it.
+ """
+ s = socket.socket()
+ s.bind((host, 0))
+ port = s.getsockname()[1]
+ s.close()
+ return port
+
+
+def _read_exactly(sock, amt):
+ """
+ Read *exactly* ``amt`` bytes from the socket ``sock``.
+ """
+ data = b''
+
+ while amt > 0:
+ chunk = sock.recv(amt)
+ data += chunk
+ amt -= len(chunk)
+
+ return data
+
+
+def _read_until(sock, char):
+ """
+ Read from the socket until the character is received.
+ """
+ chunks = []
+ while True:
+ chunk = sock.recv(1)
+ chunks.append(chunk)
+ if chunk == char:
+ break
+
+ return b''.join(chunks)
+
+
+def _address_from_socket(sock):
+ """
+ Returns the address from the SOCKS socket
+ """
+ addr_type = sock.recv(1)
+
+ if addr_type == b'\x01':
+ ipv4_addr = _read_exactly(sock, 4)
+ return socket.inet_ntoa(ipv4_addr)
+ elif addr_type == b'\x04':
+ ipv6_addr = _read_exactly(sock, 16)
+ return socket.inet_ntop(socket.AF_INET6, ipv6_addr)
+ elif addr_type == b'\x03':
+ addr_len = ord(sock.recv(1))
+ return _read_exactly(sock, addr_len)
+ else:
+ raise RuntimeError("Unexpected addr type: %r" % addr_type)
+
+
+def handle_socks5_negotiation(sock, negotiate, username=None,
+ password=None):
+ """
+ Handle the SOCKS5 handshake.
+
+ Returns a generator object that allows us to break the handshake into
+ steps so that the test code can intervene at certain useful points.
+ """
+ received_version = sock.recv(1)
+ assert received_version == SOCKS_VERSION_SOCKS5
+ nmethods = ord(sock.recv(1))
+ methods = _read_exactly(sock, nmethods)
+
+ if negotiate:
+ assert SOCKS_NEGOTIATION_PASSWORD in methods
+ send_data = SOCKS_VERSION_SOCKS5 + SOCKS_NEGOTIATION_PASSWORD
+ sock.sendall(send_data)
+
+ # This is the password negotiation.
+ negotiation_version = sock.recv(1)
+ assert negotiation_version == b'\x01'
+ ulen = ord(sock.recv(1))
+ provided_username = _read_exactly(sock, ulen)
+ plen = ord(sock.recv(1))
+ provided_password = _read_exactly(sock, plen)
+
+ if username == provided_username and password == provided_password:
+ sock.sendall(b'\x01\x00')
+ else:
+ sock.sendall(b'\x01\x01')
+ sock.close()
+ yield False
+ return
+ else:
+ assert SOCKS_NEGOTIATION_NONE in methods
+ send_data = SOCKS_VERSION_SOCKS5 + SOCKS_NEGOTIATION_NONE
+ sock.sendall(send_data)
+
+ # Client sends where they want to go.
+ received_version = sock.recv(1)
+ command = sock.recv(1)
+ reserved = sock.recv(1)
+ addr = _address_from_socket(sock)
+ port = _read_exactly(sock, 2)
+ port = (ord(port[0:1]) << 8) + (ord(port[1:2]))
+
+ # Check some basic stuff.
+ assert received_version == SOCKS_VERSION_SOCKS5
+ assert command == b'\x01' # Only support connect, not bind.
+ assert reserved == b'\x00'
+
+ # Yield the address port tuple.
+ succeed = yield addr, port
+
+ if succeed:
+ # Hard-coded response for now.
+ response = (
+ SOCKS_VERSION_SOCKS5 + b'\x00\x00\x01\x7f\x00\x00\x01\xea\x60'
+ )
+ else:
+ # Hard-coded response for now.
+ response = SOCKS_VERSION_SOCKS5 + b'\x01\00'
+
+ sock.sendall(response)
+ yield True # Avoid StopIteration exceptions getting fired.
+
+
+def handle_socks4_negotiation(sock, username=None):
+ """
+ Handle the SOCKS4 handshake.
+
+ Returns a generator object that allows us to break the handshake into
+ steps so that the test code can intervene at certain useful points.
+ """
+ received_version = sock.recv(1)
+ command = sock.recv(1)
+ port = _read_exactly(sock, 2)
+ port = (ord(port[0:1]) << 8) + (ord(port[1:2]))
+ addr = _read_exactly(sock, 4)
+ provided_username = _read_until(sock, b'\x00')[:-1] # Strip trailing null.
+
+ if addr == b'\x00\x00\x00\x01':
+ # Magic string: means DNS name.
+ addr = _read_until(sock, b'\x00')[:-1] # Strip trailing null.
+ else:
+ addr = socket.inet_ntoa(addr)
+
+ # Check some basic stuff.
+ assert received_version == SOCKS_VERSION_SOCKS4
+ assert command == b'\x01' # Only support connect, not bind.
+
+ if username is not None and username != provided_username:
+ sock.sendall(b'\x00\x5d\x00\x00\x00\x00\x00\x00')
+ sock.close()
+ yield False
+ return
+
+ # Yield the address port tuple.
+ succeed = yield addr, port
+
+ if succeed:
+ response = b'\x00\x5a\xea\x60\x7f\x00\x00\x01'
+ else:
+ response = b'\x00\x5b\x00\x00\x00\x00\x00\x00'
+
+ sock.sendall(response)
+ yield True # Avoid StopIteration exceptions getting fired.
+
+
+class TestSocks5Proxy(IPV4SocketDummyServerTestCase):
+ """
+ Test the SOCKS proxy in SOCKS5 mode.
+ """
+ def test_basic_request(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks5_negotiation(sock, negotiate=False)
+ addr, port = next(handler)
+
+ self.assertEqual(addr, '16.17.18.19')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ while True:
+ buf = sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+ response = pm.request('GET', 'http://16.17.18.19')
+
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.data, b'')
+ self.assertEqual(response.headers['Server'], 'SocksTestServer')
+
+ def test_correct_header_line(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks5_negotiation(sock, negotiate=False)
+ addr, port = next(handler)
+
+ self.assertEqual(addr, b'example.com')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ buf = b''
+ while True:
+ buf += sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ self.assertTrue(buf.startswith(b'GET / HTTP/1.1'))
+ self.assertTrue(b'Host: example.com' in buf)
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+ response = pm.request('GET', 'http://example.com')
+ self.assertEqual(response.status, 200)
+
+ def test_connection_timeouts(self):
+ event = threading.Event()
+
+ def request_handler(listener):
+ event.wait()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+
+ self.assertRaises(
+ ConnectTimeoutError, pm.request, 'GET', 'http://example.com',
+ timeout=0.001, retries=False
+ )
+ event.set()
+
+ def test_connection_failure(self):
+ event = threading.Event()
+
+ def request_handler(listener):
+ listener.close()
+ event.set()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+
+ event.wait()
+ self.assertRaises(
+ NewConnectionError, pm.request, 'GET', 'http://example.com',
+ retries=False
+ )
+
+ def test_proxy_rejection(self):
+ evt = threading.Event()
+
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks5_negotiation(sock, negotiate=False)
+ addr, port = next(handler)
+ handler.send(False)
+
+ evt.wait()
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+
+ self.assertRaises(
+ NewConnectionError, pm.request, 'GET', 'http://example.com',
+ retries=False
+ )
+ evt.set()
+
+ def test_socks_with_password(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks5_negotiation(
+ sock, negotiate=True, username=b'user', password=b'pass'
+ )
+ addr, port = next(handler)
+
+ self.assertEqual(addr, '16.17.18.19')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ while True:
+ buf = sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url, username='user',
+ password='pass')
+ response = pm.request('GET', 'http://16.17.18.19')
+
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.data, b'')
+ self.assertEqual(response.headers['Server'], 'SocksTestServer')
+
+ def test_socks_with_invalid_password(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks5_negotiation(
+ sock, negotiate=True, username=b'user', password=b'pass'
+ )
+ next(handler)
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url, username='user',
+ password='badpass')
+
+ try:
+ pm.request('GET', 'http://example.com', retries=False)
+ except NewConnectionError as e:
+ self.assertTrue("SOCKS5 authentication failed" in str(e))
+ else:
+ self.fail("Did not raise")
+
+ def test_source_address_works(self):
+ expected_port = _get_free_port(self.host)
+
+ def request_handler(listener):
+ sock = listener.accept()[0]
+ self.assertEqual(sock.getpeername()[0], '127.0.0.1')
+ self.assertEqual(sock.getpeername()[1], expected_port)
+
+ handler = handle_socks5_negotiation(sock, negotiate=False)
+ addr, port = next(handler)
+
+ self.assertEqual(addr, '16.17.18.19')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ while True:
+ buf = sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(
+ proxy_url, source_address=('127.0.0.1', expected_port)
+ )
+ response = pm.request('GET', 'http://16.17.18.19')
+ self.assertEqual(response.status, 200)
+
+
+class TestSOCKS4Proxy(IPV4SocketDummyServerTestCase):
+ """
+ Test the SOCKS proxy in SOCKS4 mode.
+
+ Has relatively fewer tests than the SOCKS5 case, mostly because once the
+ negotiation is done the two cases behave identically.
+ """
+ def test_basic_request(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks4_negotiation(sock)
+ addr, port = next(handler)
+
+ self.assertEqual(addr, '16.17.18.19')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ while True:
+ buf = sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks4://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+ response = pm.request('GET', 'http://16.17.18.19')
+
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.headers['Server'], 'SocksTestServer')
+ self.assertEqual(response.data, b'')
+
+ def test_correct_header_line(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks4_negotiation(sock)
+ addr, port = next(handler)
+
+ self.assertEqual(addr, b'example.com')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ buf = b''
+ while True:
+ buf += sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ self.assertTrue(buf.startswith(b'GET / HTTP/1.1'))
+ self.assertTrue(b'Host: example.com' in buf)
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks4://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+ response = pm.request('GET', 'http://example.com')
+ self.assertEqual(response.status, 200)
+
+ def test_proxy_rejection(self):
+ evt = threading.Event()
+
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks4_negotiation(sock)
+ addr, port = next(handler)
+ handler.send(False)
+
+ evt.wait()
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks4://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+
+ self.assertRaises(
+ NewConnectionError, pm.request, 'GET', 'http://example.com',
+ retries=False
+ )
+ evt.set()
+
+ def test_socks4_with_username(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks4_negotiation(sock, username=b'user')
+ addr, port = next(handler)
+
+ self.assertEqual(addr, '16.17.18.19')
+ self.assertTrue(port, 80)
+ handler.send(True)
+
+ while True:
+ buf = sock.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ sock.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ sock.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks4://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url, username='user')
+ response = pm.request('GET', 'http://16.17.18.19')
+
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.data, b'')
+ self.assertEqual(response.headers['Server'], 'SocksTestServer')
+
+ def test_socks_with_invalid_username(self):
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks4_negotiation(sock, username=b'user')
+ next(handler)
+
+ self._start_server(request_handler)
+ proxy_url = "socks4://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url, username='baduser')
+
+ try:
+ pm.request('GET', 'http://example.com', retries=False)
+ except NewConnectionError as e:
+ self.assertTrue("different user-ids" in str(e))
+ else:
+ self.fail("Did not raise")
+
+
+class TestSOCKSWithTLS(IPV4SocketDummyServerTestCase):
+ """
+ Test that TLS behaves properly for SOCKS proxies.
+ """
+ def test_basic_request(self):
+ if not HAS_SSL:
+ raise SkipTest("No TLS available")
+
+ def request_handler(listener):
+ sock = listener.accept()[0]
+
+ handler = handle_socks5_negotiation(sock, negotiate=False)
+ addr, port = next(handler)
+
+ self.assertEqual(addr, b'localhost')
+ self.assertTrue(port, 443)
+ handler.send(True)
+
+ # Wrap in TLS
+ context = better_ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ context.load_cert_chain(
+ DEFAULT_CERTS['certfile'], DEFAULT_CERTS['keyfile']
+ )
+ tls = context.wrap_socket(sock, server_side=True)
+ buf = b''
+
+ while True:
+ buf += tls.recv(65535)
+ if buf.endswith(b'\r\n\r\n'):
+ break
+
+ self.assertTrue(buf.startswith(b'GET / HTTP/1.1\r\n'))
+
+ tls.sendall(b'HTTP/1.1 200 OK\r\n'
+ b'Server: SocksTestServer\r\n'
+ b'Content-Length: 0\r\n'
+ b'\r\n')
+ tls.close()
+
+ self._start_server(request_handler)
+ proxy_url = "socks5://%s:%s" % (self.host, self.port)
+ pm = socks.SOCKSProxyManager(proxy_url)
+ response = pm.request('GET', 'https://localhost')
+
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.data, b'')
+ self.assertEqual(response.headers['Server'], 'SocksTestServer')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/__init__.py new/urllib3-1.14/urllib3/__init__.py
--- old/urllib3-1.13.1/urllib3/__init__.py 2015-12-18 23:47:24.000000000 +0100
+++ new/urllib3-1.14/urllib3/__init__.py 2015-12-29 21:28:18.000000000 +0100
@@ -32,7 +32,7 @@
__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
__license__ = 'MIT'
-__version__ = '1.13.1'
+__version__ = '1.14'
__all__ = (
'HTTPConnectionPool',
@@ -68,7 +68,7 @@
handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
logger.addHandler(handler)
logger.setLevel(level)
- logger.debug('Added a stderr logging handler to logger: %s' % __name__)
+ logger.debug('Added a stderr logging handler to logger: %s', __name__)
return handler
# ... Clean up.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/connectionpool.py new/urllib3-1.14/urllib3/connectionpool.py
--- old/urllib3-1.13.1/urllib3/connectionpool.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/connectionpool.py 2015-12-29 21:28:18.000000000 +0100
@@ -203,8 +203,8 @@
Return a fresh :class:`HTTPConnection`.
"""
self.num_connections += 1
- log.info("Starting new HTTP connection (%d): %s" %
- (self.num_connections, self.host))
+ log.info("Starting new HTTP connection (%d): %s",
+ self.num_connections, self.host)
conn = self.ConnectionCls(host=self.host, port=self.port,
timeout=self.timeout.connect_timeout,
@@ -239,7 +239,7 @@
# If this is a persistent connection, check if it got disconnected
if conn and is_connection_dropped(conn):
- log.info("Resetting dropped connection: %s" % self.host)
+ log.info("Resetting dropped connection: %s", self.host)
conn.close()
if getattr(conn, 'auto_open', 1) == 0:
# This is a proxied connection that has been mutated by
@@ -272,7 +272,7 @@
except Full:
# This should never happen if self.block == True
log.warning(
- "Connection pool is full, discarding connection: %s" %
+ "Connection pool is full, discarding connection: %s",
self.host)
# Connection never got put back into the pool, close it.
@@ -382,9 +382,8 @@
# AppEngine doesn't have a version attr.
http_version = getattr(conn, '_http_vsn_str', 'HTTP/?')
- log.debug("\"%s %s %s\" %s %s" % (method, url, http_version,
- httplib_response.status,
- httplib_response.length))
+ log.debug("\"%s %s %s\" %s %s", method, url, http_version,
+ httplib_response.status, httplib_response.length)
try:
assert_header_parsing(httplib_response.msg)
@@ -622,7 +621,7 @@
if not conn:
# Try again
log.warning("Retrying (%r) after connection "
- "broken by '%r': %s" % (retries, err, url))
+ "broken by '%r': %s", retries, err, url)
return self.urlopen(method, url, body, headers, retries,
redirect, assert_same_host,
timeout=timeout, pool_timeout=pool_timeout,
@@ -644,7 +643,7 @@
raise
return response
- log.info("Redirecting %s -> %s" % (url, redirect_location))
+ log.info("Redirecting %s -> %s", url, redirect_location)
return self.urlopen(
method, redirect_location, body, headers,
retries=retries, redirect=redirect,
@@ -656,7 +655,7 @@
if retries.is_forced_retry(method, status_code=response.status):
retries = retries.increment(method, url, response=response, _pool=self)
retries.sleep()
- log.info("Forced retry: %s" % url)
+ log.info("Forced retry: %s", url)
return self.urlopen(
method, url, body, headers,
retries=retries, redirect=redirect,
@@ -754,8 +753,8 @@
Return a fresh :class:`httplib.HTTPSConnection`.
"""
self.num_connections += 1
- log.info("Starting new HTTPS connection (%d): %s"
- % (self.num_connections, self.host))
+ log.info("Starting new HTTPS connection (%d): %s",
+ self.num_connections, self.host)
if not self.ConnectionCls or self.ConnectionCls is DummyConnection:
raise SSLError("Can't connect to HTTPS URL because the SSL "
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/contrib/appengine.py new/urllib3-1.14/urllib3/contrib/appengine.py
--- old/urllib3-1.13.1/urllib3/contrib/appengine.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/contrib/appengine.py 2015-12-29 21:28:18.000000000 +0100
@@ -144,7 +144,7 @@
if retries.is_forced_retry(method, status_code=http_response.status):
retries = retries.increment(
method, url, response=http_response, _pool=self)
- log.info("Forced retry: %s" % url)
+ log.info("Forced retry: %s", url)
retries.sleep()
return self.urlopen(
method, url,
@@ -164,6 +164,14 @@
if content_encoding == 'deflate':
del urlfetch_resp.headers['content-encoding']
+ transfer_encoding = urlfetch_resp.headers.get('transfer-encoding')
+ # We have a full response's content,
+ # so let's make sure we don't report ourselves as chunked data.
+ if transfer_encoding == 'chunked':
+ encodings = transfer_encoding.split(",")
+ encodings.remove('chunked')
+ urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings)
+
return HTTPResponse(
# In order for decoding to work, we must present the content as
# a file-like object.
@@ -177,7 +185,7 @@
if timeout is Timeout.DEFAULT_TIMEOUT:
return 5 # 5s is the default timeout for URLFetch.
if isinstance(timeout, Timeout):
- if timeout.read is not timeout.connect:
+ if timeout._read is not timeout._connect:
warnings.warn(
"URLFetch does not support granular timeout settings, "
"reverting to total timeout.", AppEnginePlatformWarning)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/contrib/ntlmpool.py new/urllib3-1.14/urllib3/contrib/ntlmpool.py
--- old/urllib3-1.13.1/urllib3/contrib/ntlmpool.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/contrib/ntlmpool.py 2015-12-29 21:28:18.000000000 +0100
@@ -43,8 +43,8 @@
# Performs the NTLM handshake that secures the connection. The socket
# must be kept open while requests are performed.
self.num_connections += 1
- log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s' %
- (self.num_connections, self.host, self.authurl))
+ log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s',
+ self.num_connections, self.host, self.authurl)
headers = {}
headers['Connection'] = 'Keep-Alive'
@@ -56,13 +56,13 @@
# Send negotiation message
headers[req_header] = (
'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser))
- log.debug('Request headers: %s' % headers)
+ log.debug('Request headers: %s', headers)
conn.request('GET', self.authurl, None, headers)
res = conn.getresponse()
reshdr = dict(res.getheaders())
- log.debug('Response status: %s %s' % (res.status, res.reason))
- log.debug('Response headers: %s' % reshdr)
- log.debug('Response data: %s [...]' % res.read(100))
+ log.debug('Response status: %s %s', res.status, res.reason)
+ log.debug('Response headers: %s', reshdr)
+ log.debug('Response data: %s [...]', res.read(100))
# Remove the reference to the socket, so that it can not be closed by
# the response object (we want to keep the socket open)
@@ -87,12 +87,12 @@
self.pw,
NegotiateFlags)
headers[req_header] = 'NTLM %s' % auth_msg
- log.debug('Request headers: %s' % headers)
+ log.debug('Request headers: %s', headers)
conn.request('GET', self.authurl, None, headers)
res = conn.getresponse()
- log.debug('Response status: %s %s' % (res.status, res.reason))
- log.debug('Response headers: %s' % dict(res.getheaders()))
- log.debug('Response data: %s [...]' % res.read()[:100])
+ log.debug('Response status: %s %s', res.status, res.reason)
+ log.debug('Response headers: %s', dict(res.getheaders()))
+ log.debug('Response data: %s [...]', res.read()[:100])
if res.status != 200:
if res.status == 401:
raise Exception('Server rejected request: wrong '
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/contrib/socks.py new/urllib3-1.14/urllib3/contrib/socks.py
--- old/urllib3-1.13.1/urllib3/contrib/socks.py 1970-01-01 01:00:00.000000000 +0100
+++ new/urllib3-1.14/urllib3/contrib/socks.py 2015-12-29 21:28:18.000000000 +0100
@@ -0,0 +1,172 @@
+# -*- coding: utf-8 -*-
+"""
+SOCKS support for urllib3
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This contrib module contains provisional support for SOCKS proxies from within
+urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and
+SOCKS5. To enable its functionality, either install PySocks or install this
+module with the ``socks`` extra.
+
+Known Limitations:
+
+- Currently PySocks does not support contacting remote websites via literal
+ IPv6 addresses. Any such connection attempt will fail.
+- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any
+ such connection attempt will fail.
+"""
+from __future__ import absolute_import
+
+try:
+ import socks
+except ImportError:
+ import warnings
+ from urllib3.exceptions import DependencyWarning
+
+ warnings.warn((
+ 'SOCKS support in urllib3 requires the installation of optional '
+ 'dependencies: specifically, PySocks. For more information, see '
+ 'https://urllib3.readthedocs.org/en/latest/contrib.html#socks-proxies'
+ ),
+ DependencyWarning
+ )
+ raise
+
+from socket import error as SocketError, timeout as SocketTimeout
+
+from urllib3.connection import (
+ HTTPConnection, HTTPSConnection
+)
+from urllib3.connectionpool import (
+ HTTPConnectionPool, HTTPSConnectionPool
+)
+from urllib3.exceptions import ConnectTimeoutError, NewConnectionError
+from urllib3.poolmanager import PoolManager
+from urllib3.util.url import parse_url
+
+try:
+ import ssl
+except ImportError:
+ ssl = None
+
+
+class SOCKSConnection(HTTPConnection):
+ """
+ A plain-text HTTP connection that connects via a SOCKS proxy.
+ """
+ def __init__(self, *args, **kwargs):
+ self._socks_options = kwargs.pop('_socks_options')
+ super(SOCKSConnection, self).__init__(*args, **kwargs)
+
+ def _new_conn(self):
+ """
+ Establish a new connection via the SOCKS proxy.
+ """
+ extra_kw = {}
+ if self.source_address:
+ extra_kw['source_address'] = self.source_address
+
+ if self.socket_options:
+ extra_kw['socket_options'] = self.socket_options
+
+ try:
+ conn = socks.create_connection(
+ (self.host, self.port),
+ proxy_type=self._socks_options['socks_version'],
+ proxy_addr=self._socks_options['proxy_host'],
+ proxy_port=self._socks_options['proxy_port'],
+ proxy_username=self._socks_options['username'],
+ proxy_password=self._socks_options['password'],
+ timeout=self.timeout,
+ **extra_kw
+ )
+
+ except SocketTimeout as e:
+ raise ConnectTimeoutError(
+ self, "Connection to %s timed out. (connect timeout=%s)" %
+ (self.host, self.timeout))
+
+ except socks.ProxyError as e:
+ # This is fragile as hell, but it seems to be the only way to raise
+ # useful errors here.
+ if e.socket_err:
+ error = e.socket_err
+ if isinstance(error, SocketTimeout):
+ raise ConnectTimeoutError(
+ self,
+ "Connection to %s timed out. (connect timeout=%s)" %
+ (self.host, self.timeout)
+ )
+ else:
+ raise NewConnectionError(
+ self,
+ "Failed to establish a new connection: %s" % error
+ )
+ else:
+ raise NewConnectionError(
+ self,
+ "Failed to establish a new connection: %s" % e
+ )
+
+ except SocketError as e: # Defensive: PySocks should catch all these.
+ raise NewConnectionError(
+ self, "Failed to establish a new connection: %s" % e)
+
+ return conn
+
+
+# We don't need to duplicate the Verified/Unverified distinction from
+# urllib3/connection.py here because the HTTPSConnection will already have been
+# correctly set to either the Verified or Unverified form by that module. This
+# means the SOCKSHTTPSConnection will automatically be the correct type.
+class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection):
+ pass
+
+
+class SOCKSHTTPConnectionPool(HTTPConnectionPool):
+ ConnectionCls = SOCKSConnection
+
+
+class SOCKSHTTPSConnectionPool(HTTPSConnectionPool):
+ ConnectionCls = SOCKSHTTPSConnection
+
+
+class SOCKSProxyManager(PoolManager):
+ """
+ A version of the urllib3 ProxyManager that routes connections via the
+ defined SOCKS proxy.
+ """
+ pool_classes_by_scheme = {
+ 'http': SOCKSHTTPConnectionPool,
+ 'https': SOCKSHTTPSConnectionPool,
+ }
+
+ def __init__(self, proxy_url, username=None, password=None,
+ num_pools=10, headers=None, **connection_pool_kw):
+ parsed = parse_url(proxy_url)
+
+ if parsed.scheme == 'socks5':
+ socks_version = socks.PROXY_TYPE_SOCKS5
+ elif parsed.scheme == 'socks4':
+ socks_version = socks.PROXY_TYPE_SOCKS4
+ else:
+ raise ValueError(
+ "Unable to determine SOCKS version from %s" % proxy_url
+ )
+
+ self.proxy_url = proxy_url
+
+ socks_options = {
+ 'socks_version': socks_version,
+ 'proxy_host': parsed.host,
+ 'proxy_port': parsed.port,
+ 'username': username,
+ 'password': password,
+ }
+ connection_pool_kw['_socks_options'] = socks_options
+
+ super(SOCKSProxyManager, self).__init__(
+ num_pools, headers, **connection_pool_kw
+ )
+
+ self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/exceptions.py new/urllib3-1.14/urllib3/exceptions.py
--- old/urllib3-1.13.1/urllib3/exceptions.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/exceptions.py 2015-12-29 21:28:18.000000000 +0100
@@ -180,6 +180,14 @@
pass
+class DependencyWarning(HTTPWarning):
+ """
+ Warned when an attempt is made to import a module with missing optional
+ dependencies.
+ """
+ pass
+
+
class ResponseNotChunked(ProtocolError, ValueError):
"Response needs to be chunked in order to read it as chunks."
pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/poolmanager.py new/urllib3-1.14/urllib3/poolmanager.py
--- old/urllib3-1.13.1/urllib3/poolmanager.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/poolmanager.py 2015-12-29 21:28:18.000000000 +0100
@@ -18,16 +18,16 @@
__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url']
-pool_classes_by_scheme = {
- 'http': HTTPConnectionPool,
- 'https': HTTPSConnectionPool,
-}
-
log = logging.getLogger(__name__)
SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs',
'ssl_version', 'ca_cert_dir')
+pool_classes_by_scheme = {
+ 'http': HTTPConnectionPool,
+ 'https': HTTPSConnectionPool,
+}
+
class PoolManager(RequestMethods):
"""
@@ -65,6 +65,9 @@
self.pools = RecentlyUsedContainer(num_pools,
dispose_func=lambda p: p.close())
+ # Locally set the pool classes so other PoolManagers can override them.
+ self.pool_classes_by_scheme = pool_classes_by_scheme
+
def __enter__(self):
return self
@@ -81,7 +84,7 @@
by :meth:`connection_from_url` and companion methods. It is intended
to be overridden for customization.
"""
- pool_cls = pool_classes_by_scheme[scheme]
+ pool_cls = self.pool_classes_by_scheme[scheme]
kwargs = self.connection_pool_kw
if scheme == 'http':
kwargs = self.connection_pool_kw.copy()
@@ -186,7 +189,7 @@
kw['retries'] = retries
kw['redirect'] = redirect
- log.info("Redirecting %s -> %s" % (url, redirect_location))
+ log.info("Redirecting %s -> %s", url, redirect_location)
return self.urlopen(method, redirect_location, **kw)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/util/response.py new/urllib3-1.14/urllib3/util/response.py
--- old/urllib3-1.13.1/urllib3/util/response.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/util/response.py 2015-12-29 21:28:18.000000000 +0100
@@ -61,7 +61,7 @@
def is_response_to_head(response):
"""
- Checks, wether a the request of a response has been a HEAD-request.
+ Checks whether the request of a response has been a HEAD-request.
Handles the quirks of AppEngine.
:param conn:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/util/retry.py new/urllib3-1.14/urllib3/util/retry.py
--- old/urllib3-1.13.1/urllib3/util/retry.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/util/retry.py 2015-12-29 21:28:18.000000000 +0100
@@ -153,7 +153,7 @@
redirect = bool(redirect) and None
new_retries = cls(retries, redirect=redirect)
- log.debug("Converted retries value: %r -> %r" % (retries, new_retries))
+ log.debug("Converted retries value: %r -> %r", retries, new_retries)
return new_retries
def get_backoff_time(self):
@@ -272,7 +272,7 @@
if new_retry.is_exhausted():
raise MaxRetryError(_pool, url, error or ResponseError(cause))
- log.debug("Incremented Retry for (url='%s'): %r" % (url, new_retry))
+ log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)
return new_retry
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3/util/ssl_.py new/urllib3-1.14/urllib3/util/ssl_.py
--- old/urllib3-1.13.1/urllib3/util/ssl_.py 2015-12-14 22:06:26.000000000 +0100
+++ new/urllib3-1.14/urllib3/util/ssl_.py 2015-12-29 21:28:18.000000000 +0100
@@ -110,7 +110,7 @@
)
self.ciphers = cipher_suite
- def wrap_socket(self, socket, server_hostname=None):
+ def wrap_socket(self, socket, server_hostname=None, server_side=False):
warnings.warn(
'A true SSLContext object is not available. This prevents '
'urllib3 from configuring SSL appropriately and may cause '
@@ -125,6 +125,7 @@
'ca_certs': self.ca_certs,
'cert_reqs': self.verify_mode,
'ssl_version': self.protocol,
+ 'server_side': server_side,
}
if self.supports_set_ciphers: # Platform-specific: Python 2.7+
return wrap_socket(socket, ciphers=self.ciphers, **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3.egg-info/PKG-INFO new/urllib3-1.14/urllib3.egg-info/PKG-INFO
--- old/urllib3-1.13.1/urllib3.egg-info/PKG-INFO 2015-12-18 23:47:28.000000000 +0100
+++ new/urllib3-1.14/urllib3.egg-info/PKG-INFO 2015-12-29 21:28:24.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: urllib3
-Version: 1.13.1
+Version: 1.14
Summary: HTTP library with thread-safe connection pooling, file post, and more.
Home-page: http://urllib3.readthedocs.org/
Author: Andrey Petrov
@@ -156,6 +156,15 @@
Changes
=======
+ 1.14 (2015-12-29)
+ +++++++++++++++++
+
+ * contrib: SOCKS proxy support! (Issue #762)
+
+ * Fixed AppEngine handling of transfer-encoding header and bug
+ in Timeout defaults checking. (Issue #763)
+
+
1.13.1 (2015-12-18)
+++++++++++++++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3.egg-info/SOURCES.txt new/urllib3-1.14/urllib3.egg-info/SOURCES.txt
--- old/urllib3-1.13.1/urllib3.egg-info/SOURCES.txt 2015-12-18 23:47:28.000000000 +0100
+++ new/urllib3-1.14/urllib3.egg-info/SOURCES.txt 2015-12-29 21:28:24.000000000 +0100
@@ -67,6 +67,7 @@
test/contrib/__init__.py
test/contrib/test_gae_manager.py
test/contrib/test_pyopenssl.py
+test/contrib/test_socks.py
test/with_dummyserver/__init__.py
test/with_dummyserver/test_connectionpool.py
test/with_dummyserver/test_https.py
@@ -94,6 +95,7 @@
urllib3/contrib/appengine.py
urllib3/contrib/ntlmpool.py
urllib3/contrib/pyopenssl.py
+urllib3/contrib/socks.py
urllib3/packages/__init__.py
urllib3/packages/ordered_dict.py
urllib3/packages/six.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3.egg-info/pbr.json new/urllib3-1.14/urllib3.egg-info/pbr.json
--- old/urllib3-1.13.1/urllib3.egg-info/pbr.json 2015-12-18 23:47:28.000000000 +0100
+++ new/urllib3-1.14/urllib3.egg-info/pbr.json 2015-12-29 21:28:24.000000000 +0100
@@ -1 +1 @@
-{"is_release": false, "git_version": "12d04b7"}
\ No newline at end of file
+{"is_release": false, "git_version": "27df29b"}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.13.1/urllib3.egg-info/requires.txt new/urllib3-1.14/urllib3.egg-info/requires.txt
--- old/urllib3-1.13.1/urllib3.egg-info/requires.txt 2015-12-18 23:47:28.000000000 +0100
+++ new/urllib3-1.14/urllib3.egg-info/requires.txt 2015-12-29 21:28:24.000000000 +0100
@@ -4,3 +4,6 @@
ndg-httpsclient
pyasn1
certifi
+
+[socks]
+PySocks>=1.5.6,<2.0