Index: third_party/requests/packages/urllib3/util.py |
diff --git a/third_party/requests/packages/urllib3/util.py b/third_party/requests/packages/urllib3/util.py |
index 544f9ed9d62e1a7345fd1fe70daca0d5e099c7c4..266c9ed32b2ae1c1fe7cead6020957e12f8d4010 100644 |
--- a/third_party/requests/packages/urllib3/util.py |
+++ b/third_party/requests/packages/urllib3/util.py |
@@ -6,10 +6,11 @@ |
from base64 import b64encode |
+from binascii import hexlify, unhexlify |
from collections import namedtuple |
-from socket import error as SocketError |
from hashlib import md5, sha1 |
-from binascii import hexlify, unhexlify |
+from socket import error as SocketError, _GLOBAL_DEFAULT_TIMEOUT |
+import time |
try: |
from select import poll, POLLIN |
@@ -31,9 +32,234 @@ try: # Test for SSL features |
except ImportError: |
pass |
- |
from .packages import six |
-from .exceptions import LocationParseError, SSLError |
+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 |
+ <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. |
+ 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 |
+ <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. |
+ None will set an infinite timeout. |
+ |
+ :type read: integer, float, or None |
+ |
+ :param total: |
+ The maximum amount of time to wait for an HTTP request to connect and |
+ return. This combines the connect and read timeouts into one. 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. |
+ As an example, you may want a request to return within 7 seconds or |
+ fail, so you set the ``total`` timeout to 7 seconds. If the server |
+ sends one byte to you every 5 seconds, the request will **not** trigger |
+ time out. This case is admittedly rare. |
+ """ |
+ |
+ #: A sentinel object representing the default timeout value |
+ DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT |
+ |
+ def __init__(self, connect=_Default, read=_Default, total=None): |
+ 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'])): |
@@ -61,6 +287,13 @@ class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', '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): |
""" |
@@ -114,7 +347,7 @@ def parse_url(url): |
# While this code has overlap with stdlib's urlparse, it is much |
# simplified for our needs and less annoying. |
- # Additionally, this imeplementations does silly things to be optimal |
+ # Additionally, this implementations does silly things to be optimal |
# on CPython. |
scheme = None |
@@ -143,7 +376,8 @@ def parse_url(url): |
# IPv6 |
if url and url[0] == '[': |
- host, url = url[1:].split(']', 1) |
+ host, url = url.split(']', 1) |
+ host += ']' |
# Port |
if ':' in url: |
@@ -341,6 +575,20 @@ def assert_fingerprint(cert, fingerprint): |
.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, |