| Index: third_party/requests/packages/urllib3/connectionpool.py
|
| diff --git a/third_party/requests/packages/urllib3/connectionpool.py b/third_party/requests/packages/urllib3/connectionpool.py
|
| index f3e926089f91e39b159e50679a5852de8fbde4d5..691d4e238b370a23e1900cc951979162d0b1530a 100644
|
| --- a/third_party/requests/packages/urllib3/connectionpool.py
|
| +++ b/third_party/requests/packages/urllib3/connectionpool.py
|
| @@ -4,12 +4,11 @@
|
| # This module is part of urllib3 and is released under
|
| # the MIT License: http://www.opensource.org/licenses/mit-license.php
|
|
|
| -import logging
|
| -import socket
|
| import errno
|
| +import logging
|
|
|
| from socket import error as SocketError, timeout as SocketTimeout
|
| -from .util import resolve_cert_reqs, resolve_ssl_version, assert_fingerprint
|
| +import socket
|
|
|
| try: # Python 3
|
| from http.client import HTTPConnection, HTTPException
|
| @@ -22,11 +21,15 @@ try: # Python 3
|
| from queue import LifoQueue, Empty, Full
|
| except ImportError:
|
| from Queue import LifoQueue, Empty, Full
|
| + import Queue as _ # Platform-specific: Windows
|
|
|
|
|
| try: # Compiled with SSL?
|
| HTTPSConnection = object
|
| - BaseSSLError = None
|
| +
|
| + class BaseSSLError(BaseException):
|
| + pass
|
| +
|
| ssl = None
|
|
|
| try: # Python 3
|
| @@ -41,21 +44,29 @@ except (ImportError, AttributeError): # Platform-specific: No SSL.
|
| pass
|
|
|
|
|
| -from .request import RequestMethods
|
| -from .response import HTTPResponse
|
| -from .util import get_host, is_connection_dropped, ssl_wrap_socket
|
| from .exceptions import (
|
| ClosedPoolError,
|
| + ConnectTimeoutError,
|
| EmptyPoolError,
|
| HostChangedError,
|
| MaxRetryError,
|
| SSLError,
|
| - TimeoutError,
|
| + ReadTimeoutError,
|
| + ProxyError,
|
| )
|
| -
|
| -from .packages.ssl_match_hostname import match_hostname, CertificateError
|
| +from .packages.ssl_match_hostname import CertificateError, match_hostname
|
| from .packages import six
|
| -
|
| +from .request import RequestMethods
|
| +from .response import HTTPResponse
|
| +from .util import (
|
| + assert_fingerprint,
|
| + get_host,
|
| + is_connection_dropped,
|
| + resolve_cert_reqs,
|
| + resolve_ssl_version,
|
| + ssl_wrap_socket,
|
| + Timeout,
|
| +)
|
|
|
| xrange = six.moves.xrange
|
|
|
| @@ -93,11 +104,24 @@ class VerifiedHTTPSConnection(HTTPSConnection):
|
|
|
| def connect(self):
|
| # Add certificate verification
|
| - sock = socket.create_connection((self.host, self.port), self.timeout)
|
| + try:
|
| + sock = socket.create_connection(
|
| + address=(self.host, self.port),
|
| + timeout=self.timeout)
|
| + except SocketTimeout:
|
| + raise ConnectTimeoutError(
|
| + self, "Connection to %s timed out. (connect timeout=%s)" %
|
| + (self.host, self.timeout))
|
|
|
| resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
|
| resolved_ssl_version = resolve_ssl_version(self.ssl_version)
|
|
|
| + if self._tunnel_host:
|
| + self.sock = sock
|
| + # Calls self._set_hostport(), so self.host is
|
| + # self._tunnel_host below.
|
| + self._tunnel()
|
| +
|
| # Wrap socket using verification with the root certs in
|
| # trusted_root_certs
|
| self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file,
|
| @@ -110,10 +134,11 @@ class VerifiedHTTPSConnection(HTTPSConnection):
|
| if self.assert_fingerprint:
|
| assert_fingerprint(self.sock.getpeercert(binary_form=True),
|
| self.assert_fingerprint)
|
| - else:
|
| + elif self.assert_hostname is not False:
|
| match_hostname(self.sock.getpeercert(),
|
| self.assert_hostname or self.host)
|
|
|
| +
|
| ## Pool objects
|
|
|
| class ConnectionPool(object):
|
| @@ -126,6 +151,9 @@ class ConnectionPool(object):
|
| QueueCls = LifoQueue
|
|
|
| def __init__(self, host, port=None):
|
| + # httplib doesn't like it when we include brackets in ipv6 addresses
|
| + host = host.strip('[]')
|
| +
|
| self.host = host
|
| self.port = port
|
|
|
| @@ -133,6 +161,8 @@ class ConnectionPool(object):
|
| return '%s(host=%r, port=%r)' % (type(self).__name__,
|
| self.host, self.port)
|
|
|
| +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252
|
| +_blocking_errnos = set([errno.EAGAIN, errno.EWOULDBLOCK])
|
|
|
| class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| """
|
| @@ -151,9 +181,15 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| as a valid HTTP/1.0 or 1.1 status line, passed into
|
| :class:`httplib.HTTPConnection`.
|
|
|
| + .. note::
|
| + Only works in Python 2. This parameter is ignored in Python 3.
|
| +
|
| :param timeout:
|
| - Socket timeout for each individual connection, can be a float. None
|
| - disables timeout.
|
| + Socket timeout in seconds for each individual connection. This can
|
| + be a float or integer, which sets the timeout for the HTTP request,
|
| + or an instance of :class:`urllib3.util.Timeout` which gives you more
|
| + fine-grained control over request timeouts. After the constructor has
|
| + been parsed, this is always a `urllib3.util.Timeout` object.
|
|
|
| :param maxsize:
|
| Number of connections to save that can be reused. More than 1 is useful
|
| @@ -171,20 +207,39 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| :param headers:
|
| Headers to include with all requests, unless other headers are given
|
| explicitly.
|
| +
|
| + :param _proxy:
|
| + Parsed proxy URL, should not be used directly, instead, see
|
| + :class:`urllib3.connectionpool.ProxyManager`"
|
| +
|
| + :param _proxy_headers:
|
| + A dictionary with proxy headers, should not be used directly,
|
| + instead, see :class:`urllib3.connectionpool.ProxyManager`"
|
| """
|
|
|
| scheme = 'http'
|
|
|
| - def __init__(self, host, port=None, strict=False, timeout=None, maxsize=1,
|
| - block=False, headers=None):
|
| + def __init__(self, host, port=None, strict=False,
|
| + timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False,
|
| + headers=None, _proxy=None, _proxy_headers=None):
|
| ConnectionPool.__init__(self, host, port)
|
| RequestMethods.__init__(self, headers)
|
|
|
| self.strict = strict
|
| +
|
| + # This is for backwards compatibility and can be removed once a timeout
|
| + # can only be set to a Timeout object
|
| + if not isinstance(timeout, Timeout):
|
| + timeout = Timeout.from_float(timeout)
|
| +
|
| self.timeout = timeout
|
| +
|
| self.pool = self.QueueCls(maxsize)
|
| self.block = block
|
|
|
| + self.proxy = _proxy
|
| + self.proxy_headers = _proxy_headers or {}
|
| +
|
| # Fill the queue up so that doing get() on it will block properly
|
| for _ in xrange(maxsize):
|
| self.pool.put(None)
|
| @@ -200,9 +255,14 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| self.num_connections += 1
|
| log.info("Starting new HTTP connection (%d): %s" %
|
| (self.num_connections, self.host))
|
| - return HTTPConnection(host=self.host,
|
| - port=self.port,
|
| - strict=self.strict)
|
| + extra_params = {}
|
| + if not six.PY3: # Python 2
|
| + extra_params['strict'] = self.strict
|
| +
|
| + return HTTPConnection(host=self.host, port=self.port,
|
| + timeout=self.timeout.connect_timeout,
|
| + **extra_params)
|
| +
|
|
|
| def _get_conn(self, timeout=None):
|
| """
|
| @@ -263,31 +323,89 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| % self.host)
|
|
|
| # Connection never got put back into the pool, close it.
|
| - conn.close()
|
| + if conn:
|
| + conn.close()
|
| +
|
| + def _get_timeout(self, timeout):
|
| + """ Helper that always returns a :class:`urllib3.util.Timeout` """
|
| + if timeout is _Default:
|
| + return self.timeout.clone()
|
| +
|
| + if isinstance(timeout, Timeout):
|
| + return timeout.clone()
|
| + else:
|
| + # User passed us an int/float. This is for backwards compatibility,
|
| + # can be removed later
|
| + return Timeout.from_float(timeout)
|
|
|
| def _make_request(self, conn, method, url, timeout=_Default,
|
| **httplib_request_kw):
|
| """
|
| Perform a request on a given httplib connection object taken from our
|
| pool.
|
| +
|
| + :param conn:
|
| + a connection from one of our connection pools
|
| +
|
| + :param timeout:
|
| + Socket timeout in seconds for the request. This can be a
|
| + float or integer, which will set the same timeout value for
|
| + the socket connect and the socket read, or an instance of
|
| + :class:`urllib3.util.Timeout`, which gives you more fine-grained
|
| + control over your timeouts.
|
| """
|
| self.num_requests += 1
|
|
|
| - if timeout is _Default:
|
| - timeout = self.timeout
|
| -
|
| - conn.timeout = timeout # This only does anything in Py26+
|
| - conn.request(method, url, **httplib_request_kw)
|
| + timeout_obj = self._get_timeout(timeout)
|
|
|
| - # Set timeout
|
| - sock = getattr(conn, 'sock', False) # AppEngine doesn't have sock attr.
|
| - if sock:
|
| - sock.settimeout(timeout)
|
| + try:
|
| + timeout_obj.start_connect()
|
| + conn.timeout = timeout_obj.connect_timeout
|
| + # conn.request() calls httplib.*.request, not the method in
|
| + # request.py. It also calls makefile (recv) on the socket
|
| + conn.request(method, url, **httplib_request_kw)
|
| + except SocketTimeout:
|
| + raise ConnectTimeoutError(
|
| + self, "Connection to %s timed out. (connect timeout=%s)" %
|
| + (self.host, timeout_obj.connect_timeout))
|
| +
|
| + # Reset the timeout for the recv() on the socket
|
| + read_timeout = timeout_obj.read_timeout
|
| + log.debug("Setting read timeout to %s" % read_timeout)
|
| + # App Engine doesn't have a sock attr
|
| + if hasattr(conn, 'sock') and \
|
| + read_timeout is not None and \
|
| + read_timeout is not Timeout.DEFAULT_TIMEOUT:
|
| + # In Python 3 socket.py will catch EAGAIN and return None when you
|
| + # try and read into the file pointer created by http.client, which
|
| + # instead raises a BadStatusLine exception. Instead of catching
|
| + # the exception and assuming all BadStatusLine exceptions are read
|
| + # timeouts, check for a zero timeout before making the request.
|
| + if read_timeout == 0:
|
| + raise ReadTimeoutError(
|
| + self, url,
|
| + "Read timed out. (read timeout=%s)" % read_timeout)
|
| + conn.sock.settimeout(read_timeout)
|
| +
|
| + # Receive the response from the server
|
| + try:
|
| + try: # Python 2.7+, use buffering of HTTP responses
|
| + httplib_response = conn.getresponse(buffering=True)
|
| + except TypeError: # Python 2.6 and older
|
| + httplib_response = conn.getresponse()
|
| + except SocketTimeout:
|
| + raise ReadTimeoutError(
|
| + self, url, "Read timed out. (read timeout=%s)" % read_timeout)
|
| +
|
| + except SocketError as e: # Platform-specific: Python 2
|
| + # See the above comment about EAGAIN in Python 3. In Python 2 we
|
| + # have to specifically catch it and throw the timeout error
|
| + if e.errno in _blocking_errnos:
|
| + raise ReadTimeoutError(
|
| + self, url,
|
| + "Read timed out. (read timeout=%s)" % read_timeout)
|
| + raise
|
|
|
| - try: # Python 2.7+, use buffering of HTTP responses
|
| - httplib_response = conn.getresponse(buffering=True)
|
| - except TypeError: # Python 2.6 and older
|
| - httplib_response = conn.getresponse()
|
|
|
| # AppEngine doesn't have a version attr.
|
| http_version = getattr(conn, '_http_vsn_str', 'HTTP/?')
|
| @@ -367,7 +485,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
|
|
| :param redirect:
|
| If True, automatically handle redirects (status codes 301, 302,
|
| - 303, 307). Each redirect counts as a retry.
|
| + 303, 307, 308). Each redirect counts as a retry.
|
|
|
| :param assert_same_host:
|
| If ``True``, will make sure that the host of the pool requests is
|
| @@ -375,7 +493,9 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| use the pool on an HTTP proxy and request foreign hosts.
|
|
|
| :param timeout:
|
| - If specified, overrides the default timeout for this one request.
|
| + If specified, overrides the default timeout for this one
|
| + request. It may be a float (in seconds) or an instance of
|
| + :class:`urllib3.util.Timeout`.
|
|
|
| :param pool_timeout:
|
| If set and the pool is set to block=True, then this method will
|
| @@ -402,18 +522,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| if retries < 0:
|
| raise MaxRetryError(self, url)
|
|
|
| - if timeout is _Default:
|
| - timeout = self.timeout
|
| -
|
| if release_conn is None:
|
| release_conn = response_kw.get('preload_content', True)
|
|
|
| # Check host
|
| if assert_same_host and not self.is_same_host(url):
|
| - host = "%s://%s" % (self.scheme, self.host)
|
| - if self.port:
|
| - host = "%s:%d" % (host, self.port)
|
| -
|
| raise HostChangedError(self, url, retries - 1)
|
|
|
| conn = None
|
| @@ -444,20 +557,20 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| # ``response.release_conn()`` is called (implicitly by
|
| # ``response.read()``)
|
|
|
| - except Empty as e:
|
| + except Empty:
|
| # Timed out by queue
|
| - raise TimeoutError(self, url,
|
| - "Request timed out. (pool_timeout=%s)" %
|
| - pool_timeout)
|
| + raise ReadTimeoutError(
|
| + self, url, "Read timed out, no pool connections are available.")
|
|
|
| - except SocketTimeout as e:
|
| + except SocketTimeout:
|
| # Timed out by socket
|
| - raise TimeoutError(self, url,
|
| - "Request timed out. (timeout=%s)" %
|
| - timeout)
|
| + raise ReadTimeoutError(self, url, "Read timed out.")
|
|
|
| except BaseSSLError as e:
|
| # SSL certificate error
|
| + if 'timed out' in str(e) or \
|
| + 'did not complete (read)' in str(e): # Platform-specific: Python 2.6
|
| + raise ReadTimeoutError(self, url, "Read timed out.")
|
| raise SSLError(e)
|
|
|
| except CertificateError as e:
|
| @@ -465,6 +578,10 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
| raise SSLError(e)
|
|
|
| except (HTTPException, SocketError) as e:
|
| + if isinstance(e, SocketError) and self.proxy is not None:
|
| + raise ProxyError('Cannot connect to proxy. '
|
| + 'Socket error: %s.' % e)
|
| +
|
| # Connection broken, discard. It will be replaced next _get_conn().
|
| conn = None
|
| # This is necessary so we can access e below
|
| @@ -513,6 +630,7 @@ class HTTPSConnectionPool(HTTPConnectionPool):
|
|
|
| :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``,
|
| ``assert_hostname`` and ``host`` in this order to verify connections.
|
| + If ``assert_hostname`` is False, no verification is done.
|
|
|
| The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs`` and
|
| ``ssl_version`` are only used if :mod:`ssl` is available and are fed into
|
| @@ -525,13 +643,13 @@ class HTTPSConnectionPool(HTTPConnectionPool):
|
| def __init__(self, host, port=None,
|
| strict=False, timeout=None, maxsize=1,
|
| block=False, headers=None,
|
| + _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):
|
|
|
| - HTTPConnectionPool.__init__(self, host, port,
|
| - strict, timeout, maxsize,
|
| - block, headers)
|
| + HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize,
|
| + block, headers, _proxy, _proxy_headers)
|
| self.key_file = key_file
|
| self.cert_file = cert_file
|
| self.cert_reqs = cert_reqs
|
| @@ -540,6 +658,34 @@ class HTTPSConnectionPool(HTTPConnectionPool):
|
| self.assert_hostname = assert_hostname
|
| self.assert_fingerprint = assert_fingerprint
|
|
|
| + def _prepare_conn(self, connection):
|
| + """
|
| + Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket`
|
| + and establish the tunnel if proxy is used.
|
| + """
|
| +
|
| + if isinstance(connection, VerifiedHTTPSConnection):
|
| + connection.set_cert(key_file=self.key_file,
|
| + cert_file=self.cert_file,
|
| + cert_reqs=self.cert_reqs,
|
| + ca_certs=self.ca_certs,
|
| + assert_hostname=self.assert_hostname,
|
| + assert_fingerprint=self.assert_fingerprint)
|
| + connection.ssl_version = self.ssl_version
|
| +
|
| + if self.proxy is not None:
|
| + # Python 2.7+
|
| + try:
|
| + set_tunnel = connection.set_tunnel
|
| + except AttributeError: # Platform-specific: Python 2.6
|
| + set_tunnel = connection._set_tunnel
|
| + set_tunnel(self.host, self.port, self.proxy_headers)
|
| + # Establish tunnel connection early, because otherwise httplib
|
| + # would improperly set Host: header to proxy's IP:port.
|
| + connection.connect()
|
| +
|
| + return connection
|
| +
|
| def _new_conn(self):
|
| """
|
| Return a fresh :class:`httplib.HTTPSConnection`.
|
| @@ -548,26 +694,28 @@ class HTTPSConnectionPool(HTTPConnectionPool):
|
| log.info("Starting new HTTPS connection (%d): %s"
|
| % (self.num_connections, self.host))
|
|
|
| + actual_host = self.host
|
| + actual_port = self.port
|
| + if self.proxy is not None:
|
| + actual_host = self.proxy.host
|
| + actual_port = self.proxy.port
|
| +
|
| if not ssl: # Platform-specific: Python compiled without +ssl
|
| if not HTTPSConnection or HTTPSConnection is object:
|
| raise SSLError("Can't connect to HTTPS URL because the SSL "
|
| "module is not available.")
|
| -
|
| - return HTTPSConnection(host=self.host,
|
| - port=self.port,
|
| - strict=self.strict)
|
| -
|
| - connection = VerifiedHTTPSConnection(host=self.host,
|
| - port=self.port,
|
| - strict=self.strict)
|
| - connection.set_cert(key_file=self.key_file, cert_file=self.cert_file,
|
| - cert_reqs=self.cert_reqs, ca_certs=self.ca_certs,
|
| - assert_hostname=self.assert_hostname,
|
| - assert_fingerprint=self.assert_fingerprint)
|
| -
|
| - connection.ssl_version = self.ssl_version
|
| -
|
| - return connection
|
| + connection_class = HTTPSConnection
|
| + else:
|
| + connection_class = VerifiedHTTPSConnection
|
| +
|
| + extra_params = {}
|
| + if not six.PY3: # Python 2
|
| + extra_params['strict'] = self.strict
|
| + connection = connection_class(host=actual_host, port=actual_port,
|
| + timeout=self.timeout.connect_timeout,
|
| + **extra_params)
|
| +
|
| + return self._prepare_conn(connection)
|
|
|
|
|
| def connection_from_url(url, **kw):
|
|
|