diff --git a/script.module.urllib3/LICENSE.txt b/script.module.urllib3/LICENSE.txt
index 429a1767e..e6183d027 100644
--- a/script.module.urllib3/LICENSE.txt
+++ b/script.module.urllib3/LICENSE.txt
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2008-2020 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+Copyright (c) 2008-2020 Andrey Petrov and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/script.module.urllib3/README.md b/script.module.urllib3/README.md
deleted file mode 100644
index 523e6b62d..000000000
--- a/script.module.urllib3/README.md
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg)
-
-
-
-
-
-
-
-
-
-
-
-
-urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the
-Python ecosystem already uses urllib3 and you should too.
-urllib3 brings many critical features that are missing from the Python
-standard libraries:
-
-- Thread safety.
-- Connection pooling.
-- Client-side SSL/TLS verification.
-- File uploads with multipart encoding.
-- Helpers for retrying requests and dealing with HTTP redirects.
-- Support for gzip, deflate, and brotli encoding.
-- Proxy support for HTTP and SOCKS.
-- 100% test coverage.
-
-urllib3 is powerful and easy to use:
-
-```python3
->>> import urllib3
->>> http = urllib3.PoolManager()
->>> resp = http.request("GET", "http://httpbin.org/robots.txt")
->>> resp.status
-200
->>> resp.data
-b"User-agent: *\nDisallow: /deny\n"
-```
-
-## Installing
-
-urllib3 can be installed with [pip](https://pip.pypa.io):
-
-```bash
-$ python -m pip install urllib3
-```
-
-Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3):
-
-```bash
-$ git clone https://github.com/urllib3/urllib3.git
-$ cd urllib3
-$ pip install .
-```
-
-
-## Documentation
-
-urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io).
-
-
-## Community
-
-urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and
-collaborating with other contributors. Drop by and say hello 👋
-
-
-## Contributing
-
-urllib3 happily accepts contributions. Please see our
-[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html)
-for some tips on getting started.
-
-
-## Security Disclosures
-
-To report a security vulnerability, please use the
-[Tidelift security contact](https://tidelift.com/security).
-Tidelift will coordinate the fix and disclosure with maintainers.
-
-
-## Maintainers
-
-- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson)
-- [@pquentin](https://github.com/pquentin) (Quentin Pradet)
-- [@theacodes](https://github.com/theacodes) (Thea Flowers)
-- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro)
-- [@lukasa](https://github.com/lukasa) (Cory Benfield)
-- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco)
-- [@shazow](https://github.com/shazow) (Andrey Petrov)
-
-👋
-
-
-## Sponsorship
-
-If your company benefits from this library, please consider [sponsoring its
-development](https://urllib3.readthedocs.io/en/latest/sponsors.html).
-
-
-## For Enterprise
-
-Professional support for urllib3 is available as part of the [Tidelift
-Subscription][1]. Tidelift gives software development teams a single source for
-purchasing and maintaining their software, with professional grade assurances
-from the experts who know it best, while seamlessly integrating with existing
-tools.
-
-[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme
diff --git a/script.module.urllib3/addon.xml b/script.module.urllib3/addon.xml
index 3a16ead2d..41a910ad8 100644
--- a/script.module.urllib3/addon.xml
+++ b/script.module.urllib3/addon.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/script.module.urllib3/lib/urllib3/_version.py b/script.module.urllib3/lib/urllib3/_version.py
index d905b6975..d69ca3145 100644
--- a/script.module.urllib3/lib/urllib3/_version.py
+++ b/script.module.urllib3/lib/urllib3/_version.py
@@ -1,2 +1,2 @@
# This file is protected via CODEOWNERS
-__version__ = "1.26.9"
+__version__ = "1.26.16"
diff --git a/script.module.urllib3/lib/urllib3/connection.py b/script.module.urllib3/lib/urllib3/connection.py
index 7bf395bda..54b96b191 100644
--- a/script.module.urllib3/lib/urllib3/connection.py
+++ b/script.module.urllib3/lib/urllib3/connection.py
@@ -68,7 +68,7 @@ class BrokenPipeError(Exception):
# When it comes time to update this value as a part of regular maintenance
# (ie test_recent_date is failing) update it to ~6 months before the current date.
-RECENT_DATE = datetime.date(2020, 7, 1)
+RECENT_DATE = datetime.date(2022, 1, 1)
_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
@@ -229,6 +229,11 @@ def putheader(self, header, *values):
)
def request(self, method, url, body=None, headers=None):
+ # Update the inner socket's timeout value to send the request.
+ # This only triggers if the connection is re-used.
+ if getattr(self, "sock", None) is not None:
+ self.sock.settimeout(self.timeout)
+
if headers is None:
headers = {}
else:
diff --git a/script.module.urllib3/lib/urllib3/connectionpool.py b/script.module.urllib3/lib/urllib3/connectionpool.py
index 15bffcb23..96844d933 100644
--- a/script.module.urllib3/lib/urllib3/connectionpool.py
+++ b/script.module.urllib3/lib/urllib3/connectionpool.py
@@ -50,6 +50,13 @@
from .util.url import _normalize_host as normalize_host
from .util.url import get_host, parse_url
+try: # Platform-specific: Python 3
+ import weakref
+
+ weakref_finalize = weakref.finalize
+except AttributeError: # Platform-specific: Python 2
+ from .packages.backports.weakref_finalize import weakref_finalize
+
xrange = six.moves.xrange
log = logging.getLogger(__name__)
@@ -220,6 +227,16 @@ def __init__(
self.conn_kw["proxy"] = self.proxy
self.conn_kw["proxy_config"] = self.proxy_config
+ # Do not pass 'self' as callback to 'finalize'.
+ # Then the 'finalize' would keep an endless living (leak) to self.
+ # By just passing a reference to the pool allows the garbage collector
+ # to free self if nobody else has a reference to it.
+ pool = self.pool
+
+ # Close all the HTTPConnections in the pool before the
+ # HTTPConnectionPool object is garbage collected.
+ weakref_finalize(self, _close_pool_connections, pool)
+
def _new_conn(self):
"""
Return a fresh :class:`HTTPConnection`.
@@ -379,7 +396,7 @@ def _make_request(
timeout_obj = self._get_timeout(timeout)
timeout_obj.start_connect()
- conn.timeout = timeout_obj.connect_timeout
+ conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout)
# Trigger any extra validation we need to do.
try:
@@ -489,14 +506,8 @@ def close(self):
# Disable access to the pool
old_pool, self.pool = self.pool, None
- try:
- while True:
- conn = old_pool.get(block=False)
- if conn:
- conn.close()
-
- except queue.Empty:
- pass # Done.
+ # Close all the HTTPConnections in the pool.
+ _close_pool_connections(old_pool)
def is_same_host(self, url):
"""
@@ -767,6 +778,8 @@ def _is_ssl_error_message_from_http_proxy(ssl_error):
isinstance(e, BaseSSLError)
and self.proxy
and _is_ssl_error_message_from_http_proxy(e)
+ and conn.proxy
+ and conn.proxy.scheme == "https"
):
e = ProxyError(
"Your proxy appears to only use HTTP and not HTTPS, "
@@ -860,7 +873,7 @@ def _is_ssl_error_message_from_http_proxy(ssl_error):
)
# Check if we should retry the HTTP response.
- has_retry_after = bool(response.getheader("Retry-After"))
+ has_retry_after = bool(response.headers.get("Retry-After"))
if retries.is_retry(method, response.status, has_retry_after):
try:
retries = retries.increment(method, url, response=response, _pool=self)
@@ -1106,3 +1119,14 @@ def _normalize_host(host, scheme):
if host.startswith("[") and host.endswith("]"):
host = host[1:-1]
return host
+
+
+def _close_pool_connections(pool):
+ """Drains a queue of connections and closes each one."""
+ try:
+ while True:
+ conn = pool.get(block=False)
+ if conn:
+ conn.close()
+ except queue.Empty:
+ pass # Done.
diff --git a/script.module.urllib3/lib/urllib3/contrib/appengine.py b/script.module.urllib3/lib/urllib3/contrib/appengine.py
index f91bdd6e7..a5a6d9103 100644
--- a/script.module.urllib3/lib/urllib3/contrib/appengine.py
+++ b/script.module.urllib3/lib/urllib3/contrib/appengine.py
@@ -224,7 +224,7 @@ def urlopen(
)
# Check if we should retry the HTTP response.
- has_retry_after = bool(http_response.getheader("Retry-After"))
+ has_retry_after = bool(http_response.headers.get("Retry-After"))
if retries.is_retry(method, http_response.status, has_retry_after):
retries = retries.increment(method, url, response=http_response, _pool=self)
log.debug("Retry: %s", url)
diff --git a/script.module.urllib3/lib/urllib3/contrib/ntlmpool.py b/script.module.urllib3/lib/urllib3/contrib/ntlmpool.py
index 41a8fd174..471665754 100644
--- a/script.module.urllib3/lib/urllib3/contrib/ntlmpool.py
+++ b/script.module.urllib3/lib/urllib3/contrib/ntlmpool.py
@@ -69,7 +69,7 @@ def _new_conn(self):
log.debug("Request headers: %s", headers)
conn.request("GET", self.authurl, None, headers)
res = conn.getresponse()
- reshdr = dict(res.getheaders())
+ reshdr = dict(res.headers)
log.debug("Response status: %s %s", res.status, res.reason)
log.debug("Response headers: %s", reshdr)
log.debug("Response data: %s [...]", res.read(100))
@@ -101,7 +101,7 @@ def _new_conn(self):
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 headers: %s", dict(res.headers))
log.debug("Response data: %s [...]", res.read()[:100])
if res.status != 200:
if res.status == 401:
diff --git a/script.module.urllib3/lib/urllib3/contrib/pyopenssl.py b/script.module.urllib3/lib/urllib3/contrib/pyopenssl.py
index def83afdb..1ed214b1d 100644
--- a/script.module.urllib3/lib/urllib3/contrib/pyopenssl.py
+++ b/script.module.urllib3/lib/urllib3/contrib/pyopenssl.py
@@ -47,10 +47,10 @@
"""
from __future__ import absolute_import
+import OpenSSL.crypto
import OpenSSL.SSL
from cryptography import x509
from cryptography.hazmat.backends.openssl import backend as openssl_backend
-from cryptography.hazmat.backends.openssl.x509 import _Certificate
try:
from cryptography.x509 import UnsupportedExtension
@@ -73,11 +73,20 @@ class UnsupportedExtension(Exception):
import logging
import ssl
import sys
+import warnings
from .. import util
from ..packages import six
from ..util.ssl_ import PROTOCOL_TLS_CLIENT
+warnings.warn(
+ "'urllib3.contrib.pyopenssl' module is deprecated and will be removed "
+ "in a future release of urllib3 2.x. Read more in this issue: "
+ "https://github.com/urllib3/urllib3/issues/2680",
+ category=DeprecationWarning,
+ stacklevel=2,
+)
+
__all__ = ["inject_into_urllib3", "extract_from_urllib3"]
# SNI always works.
@@ -219,9 +228,8 @@ def get_subj_alt_name(peer_cert):
if hasattr(peer_cert, "to_cryptography"):
cert = peer_cert.to_cryptography()
else:
- # This is technically using private APIs, but should work across all
- # relevant versions before PyOpenSSL got a proper API for this.
- cert = _Certificate(openssl_backend, peer_cert._x509)
+ der = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, peer_cert)
+ cert = x509.load_der_x509_certificate(der, openssl_backend)
# We want to find the SAN extension. Ask Cryptography to locate it (it's
# faster than looping in Python)
@@ -406,7 +414,6 @@ def makefile(self, mode, bufsize=-1):
self._makefile_refs += 1
return _fileobject(self, mode, bufsize, close=True)
-
else: # Platform-specific: Python 3
makefile = backport_makefile
diff --git a/script.module.urllib3/lib/urllib3/contrib/securetransport.py b/script.module.urllib3/lib/urllib3/contrib/securetransport.py
index 554c015fe..6c46a3b9f 100644
--- a/script.module.urllib3/lib/urllib3/contrib/securetransport.py
+++ b/script.module.urllib3/lib/urllib3/contrib/securetransport.py
@@ -770,7 +770,6 @@ def makefile(self, mode, bufsize=-1):
self._makefile_refs += 1
return _fileobject(self, mode, bufsize, close=True)
-
else: # Platform-specific: Python 3
def makefile(self, mode="r", buffering=None, *args, **kwargs):
diff --git a/script.module.urllib3/lib/urllib3/packages/backports/weakref_finalize.py b/script.module.urllib3/lib/urllib3/packages/backports/weakref_finalize.py
new file mode 100644
index 000000000..a2f2966e5
--- /dev/null
+++ b/script.module.urllib3/lib/urllib3/packages/backports/weakref_finalize.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+"""
+backports.weakref_finalize
+~~~~~~~~~~~~~~~~~~
+
+Backports the Python 3 ``weakref.finalize`` method.
+"""
+from __future__ import absolute_import
+
+import itertools
+import sys
+from weakref import ref
+
+__all__ = ["weakref_finalize"]
+
+
+class weakref_finalize(object):
+ """Class for finalization of weakrefable objects
+ finalize(obj, func, *args, **kwargs) returns a callable finalizer
+ object which will be called when obj is garbage collected. The
+ first time the finalizer is called it evaluates func(*arg, **kwargs)
+ and returns the result. After this the finalizer is dead, and
+ calling it just returns None.
+ When the program exits any remaining finalizers for which the
+ atexit attribute is true will be run in reverse order of creation.
+ By default atexit is true.
+ """
+
+ # Finalizer objects don't have any state of their own. They are
+ # just used as keys to lookup _Info objects in the registry. This
+ # ensures that they cannot be part of a ref-cycle.
+
+ __slots__ = ()
+ _registry = {}
+ _shutdown = False
+ _index_iter = itertools.count()
+ _dirty = False
+ _registered_with_atexit = False
+
+ class _Info(object):
+ __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
+
+ def __init__(self, obj, func, *args, **kwargs):
+ if not self._registered_with_atexit:
+ # We may register the exit function more than once because
+ # of a thread race, but that is harmless
+ import atexit
+
+ atexit.register(self._exitfunc)
+ weakref_finalize._registered_with_atexit = True
+ info = self._Info()
+ info.weakref = ref(obj, self)
+ info.func = func
+ info.args = args
+ info.kwargs = kwargs or None
+ info.atexit = True
+ info.index = next(self._index_iter)
+ self._registry[self] = info
+ weakref_finalize._dirty = True
+
+ def __call__(self, _=None):
+ """If alive then mark as dead and return func(*args, **kwargs);
+ otherwise return None"""
+ info = self._registry.pop(self, None)
+ if info and not self._shutdown:
+ return info.func(*info.args, **(info.kwargs or {}))
+
+ def detach(self):
+ """If alive then mark as dead and return (obj, func, args, kwargs);
+ otherwise return None"""
+ info = self._registry.get(self)
+ obj = info and info.weakref()
+ if obj is not None and self._registry.pop(self, None):
+ return (obj, info.func, info.args, info.kwargs or {})
+
+ def peek(self):
+ """If alive then return (obj, func, args, kwargs);
+ otherwise return None"""
+ info = self._registry.get(self)
+ obj = info and info.weakref()
+ if obj is not None:
+ return (obj, info.func, info.args, info.kwargs or {})
+
+ @property
+ def alive(self):
+ """Whether finalizer is alive"""
+ return self in self._registry
+
+ @property
+ def atexit(self):
+ """Whether finalizer should be called at exit"""
+ info = self._registry.get(self)
+ return bool(info) and info.atexit
+
+ @atexit.setter
+ def atexit(self, value):
+ info = self._registry.get(self)
+ if info:
+ info.atexit = bool(value)
+
+ def __repr__(self):
+ info = self._registry.get(self)
+ obj = info and info.weakref()
+ if obj is None:
+ return "<%s object at %#x; dead>" % (type(self).__name__, id(self))
+ else:
+ return "<%s object at %#x; for %r at %#x>" % (
+ type(self).__name__,
+ id(self),
+ type(obj).__name__,
+ id(obj),
+ )
+
+ @classmethod
+ def _select_for_exit(cls):
+ # Return live finalizers marked for exit, oldest first
+ L = [(f, i) for (f, i) in cls._registry.items() if i.atexit]
+ L.sort(key=lambda item: item[1].index)
+ return [f for (f, i) in L]
+
+ @classmethod
+ def _exitfunc(cls):
+ # At shutdown invoke finalizers for which atexit is true.
+ # This is called once all other non-daemonic threads have been
+ # joined.
+ reenable_gc = False
+ try:
+ if cls._registry:
+ import gc
+
+ if gc.isenabled():
+ reenable_gc = True
+ gc.disable()
+ pending = None
+ while True:
+ if pending is None or weakref_finalize._dirty:
+ pending = cls._select_for_exit()
+ weakref_finalize._dirty = False
+ if not pending:
+ break
+ f = pending.pop()
+ try:
+ # gc is disabled, so (assuming no daemonic
+ # threads) the following is the only line in
+ # this function which might trigger creation
+ # of a new finalizer
+ f()
+ except Exception:
+ sys.excepthook(*sys.exc_info())
+ assert f not in cls._registry
+ finally:
+ # prevent any more finalizers from executing during shutdown
+ weakref_finalize._shutdown = True
+ if reenable_gc:
+ gc.enable()
diff --git a/script.module.urllib3/lib/urllib3/packages/six.py b/script.module.urllib3/lib/urllib3/packages/six.py
index ba50acb06..f099a3dcd 100644
--- a/script.module.urllib3/lib/urllib3/packages/six.py
+++ b/script.module.urllib3/lib/urllib3/packages/six.py
@@ -772,7 +772,6 @@ def reraise(tp, value, tb=None):
value = None
tb = None
-
else:
def exec_(_code_, _globs_=None, _locs_=None):
diff --git a/script.module.urllib3/lib/urllib3/poolmanager.py b/script.module.urllib3/lib/urllib3/poolmanager.py
index ca4ec3411..14b10daf3 100644
--- a/script.module.urllib3/lib/urllib3/poolmanager.py
+++ b/script.module.urllib3/lib/urllib3/poolmanager.py
@@ -171,7 +171,7 @@ class PoolManager(RequestMethods):
def __init__(self, num_pools=10, headers=None, **connection_pool_kw):
RequestMethods.__init__(self, headers)
self.connection_pool_kw = connection_pool_kw
- self.pools = RecentlyUsedContainer(num_pools, dispose_func=lambda p: p.close())
+ self.pools = RecentlyUsedContainer(num_pools)
# Locally set the pool classes and keys so other PoolManagers can
# override them.
diff --git a/script.module.urllib3/lib/urllib3/response.py b/script.module.urllib3/lib/urllib3/response.py
index fdb50ddb2..0bd13d40b 100644
--- a/script.module.urllib3/lib/urllib3/response.py
+++ b/script.module.urllib3/lib/urllib3/response.py
@@ -2,6 +2,8 @@
import io
import logging
+import sys
+import warnings
import zlib
from contextlib import contextmanager
from socket import error as SocketError
@@ -15,6 +17,7 @@
except ImportError:
brotli = None
+from . import util
from ._collections import HTTPHeaderDict
from .connection import BaseSSLError, HTTPException
from .exceptions import (
@@ -481,6 +484,54 @@ def _error_catcher(self):
if self._original_response and self._original_response.isclosed():
self.release_conn()
+ def _fp_read(self, amt):
+ """
+ Read a response with the thought that reading the number of bytes
+ larger than can fit in a 32-bit int at a time via SSL in some
+ known cases leads to an overflow error that has to be prevented
+ if `amt` or `self.length_remaining` indicate that a problem may
+ happen.
+
+ The known cases:
+ * 3.8 <= CPython < 3.9.7 because of a bug
+ https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900.
+ * urllib3 injected with pyOpenSSL-backed SSL-support.
+ * CPython < 3.10 only when `amt` does not fit 32-bit int.
+ """
+ assert self._fp
+ c_int_max = 2 ** 31 - 1
+ if (
+ (
+ (amt and amt > c_int_max)
+ or (self.length_remaining and self.length_remaining > c_int_max)
+ )
+ and not util.IS_SECURETRANSPORT
+ and (util.IS_PYOPENSSL or sys.version_info < (3, 10))
+ ):
+ buffer = io.BytesIO()
+ # Besides `max_chunk_amt` being a maximum chunk size, it
+ # affects memory overhead of reading a response by this
+ # method in CPython.
+ # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum
+ # chunk size that does not lead to an overflow error, but
+ # 256 MiB is a compromise.
+ max_chunk_amt = 2 ** 28
+ while amt is None or amt != 0:
+ if amt is not None:
+ chunk_amt = min(amt, max_chunk_amt)
+ amt -= chunk_amt
+ else:
+ chunk_amt = max_chunk_amt
+ data = self._fp.read(chunk_amt)
+ if not data:
+ break
+ buffer.write(data)
+ del data # to reduce peak memory usage by `max_chunk_amt`.
+ return buffer.getvalue()
+ else:
+ # StringIO doesn't like amt=None
+ return self._fp.read(amt) if amt is not None else self._fp.read()
+
def read(self, amt=None, decode_content=None, cache_content=False):
"""
Similar to :meth:`http.client.HTTPResponse.read`, but with two additional
@@ -513,13 +564,11 @@ def read(self, amt=None, decode_content=None, cache_content=False):
fp_closed = getattr(self._fp, "closed", False)
with self._error_catcher():
+ data = self._fp_read(amt) if not fp_closed else b""
if amt is None:
- # cStringIO doesn't like amt=None
- data = self._fp.read() if not fp_closed else b""
flush_decoder = True
else:
cache_content = False
- data = self._fp.read(amt) if not fp_closed else b""
if (
amt != 0 and not data
): # Platform-specific: Buggy versions of Python.
@@ -615,9 +664,21 @@ def from_httplib(ResponseCls, r, **response_kw):
# Backwards-compatibility methods for http.client.HTTPResponse
def getheaders(self):
+ warnings.warn(
+ "HTTPResponse.getheaders() is deprecated and will be removed "
+ "in urllib3 v2.1.0. Instead access HTTPResponse.headers directly.",
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
return self.headers
def getheader(self, name, default=None):
+ warnings.warn(
+ "HTTPResponse.getheader() is deprecated and will be removed "
+ "in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).",
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
return self.headers.get(name, default)
# Backwards compatibility for http.cookiejar
diff --git a/script.module.urllib3/lib/urllib3/util/retry.py b/script.module.urllib3/lib/urllib3/util/retry.py
index 3398323fd..2490d5e5b 100644
--- a/script.module.urllib3/lib/urllib3/util/retry.py
+++ b/script.module.urllib3/lib/urllib3/util/retry.py
@@ -394,7 +394,7 @@ def parse_retry_after(self, retry_after):
def get_retry_after(self, response):
"""Get the value of Retry-After in seconds."""
- retry_after = response.getheader("Retry-After")
+ retry_after = response.headers.get("Retry-After")
if retry_after is None:
return None
diff --git a/script.module.urllib3/lib/urllib3/util/timeout.py b/script.module.urllib3/lib/urllib3/util/timeout.py
index ff69593b0..78e18a627 100644
--- a/script.module.urllib3/lib/urllib3/util/timeout.py
+++ b/script.module.urllib3/lib/urllib3/util/timeout.py
@@ -2,9 +2,8 @@
import time
-# The default socket timeout, used by httplib to indicate that no timeout was
-# specified by the user
-from socket import _GLOBAL_DEFAULT_TIMEOUT
+# The default socket timeout, used by httplib to indicate that no timeout was; specified by the user
+from socket import _GLOBAL_DEFAULT_TIMEOUT, getdefaulttimeout
from ..exceptions import TimeoutStateError
@@ -116,6 +115,10 @@ def __repr__(self):
# __str__ provided for backwards compatibility
__str__ = __repr__
+ @classmethod
+ def resolve_default_timeout(cls, timeout):
+ return getdefaulttimeout() if timeout is cls.DEFAULT_TIMEOUT else timeout
+
@classmethod
def _validate_timeout(cls, value, name):
"""Check that a timeout attribute is valid.
diff --git a/script.module.urllib3/lib/urllib3/util/url.py b/script.module.urllib3/lib/urllib3/util/url.py
index 81a03da9e..e5682d3be 100644
--- a/script.module.urllib3/lib/urllib3/util/url.py
+++ b/script.module.urllib3/lib/urllib3/util/url.py
@@ -50,7 +50,7 @@
"(?:(?:%(hex)s:){0,6}%(hex)s)?::",
]
-UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._!\-~"
+UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._\-~"
IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")"
ZONE_ID_PAT = "(?:%25|%)(?:[" + UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+"
IPV6_ADDRZ_PAT = r"\[" + IPV6_PAT + r"(?:" + ZONE_ID_PAT + r")?\]"
@@ -63,7 +63,7 @@
BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$")
ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$")
-_HOST_PORT_PAT = ("^(%s|%s|%s)(?::([0-9]{0,5}))?$") % (
+_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % (
REG_NAME_PAT,
IPV4_PAT,
IPV6_ADDRZ_PAT,
@@ -279,6 +279,9 @@ def _normalize_host(host, scheme):
if scheme in NORMALIZABLE_SCHEMES:
is_ipv6 = IPV6_ADDRZ_RE.match(host)
if is_ipv6:
+ # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as
+ # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID
+ # separator as necessary to return a valid RFC 4007 scoped IP.
match = ZONE_ID_RE.search(host)
if match:
start, end = match.span(1)
@@ -300,7 +303,7 @@ def _normalize_host(host, scheme):
def _idna_encode(name):
- if name and any([ord(x) > 128 for x in name]):
+ if name and any(ord(x) >= 128 for x in name):
try:
import idna
except ImportError:
@@ -331,7 +334,7 @@ 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.
- This parser is RFC 3986 compliant.
+ This parser is RFC 3986 and RFC 6874 compliant.
The parser logic and helper functions are based heavily on
work done in the ``rfc3986`` module.
diff --git a/script.module.urllib3/lib/urllib3/util/wait.py b/script.module.urllib3/lib/urllib3/util/wait.py
index c280646c7..21b4590b3 100644
--- a/script.module.urllib3/lib/urllib3/util/wait.py
+++ b/script.module.urllib3/lib/urllib3/util/wait.py
@@ -42,7 +42,6 @@ class NoWayToWaitForSocketError(Exception):
def _retry_on_intr(fn, timeout):
return fn(timeout)
-
else:
# Old and broken Pythons.
def _retry_on_intr(fn, timeout):