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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..544f9ed9d62e1a7345fd1fe70daca0d5e099c7c4 |
--- /dev/null |
+++ b/third_party/requests/packages/urllib3/util.py |
@@ -0,0 +1,378 @@ |
+# urllib3/util.py |
+# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) |
+# |
+# This module is part of urllib3 and is released under |
+# the MIT License: http://www.opensource.org/licenses/mit-license.php |
+ |
+ |
+from base64 import b64encode |
+from collections import namedtuple |
+from socket import error as SocketError |
+from hashlib import md5, sha1 |
+from binascii import hexlify, unhexlify |
+ |
+try: |
+ from select import poll, POLLIN |
+except ImportError: # `poll` doesn't exist on OSX and other platforms |
+ poll = False |
+ try: |
+ from select import select |
+ except ImportError: # `select` doesn't exist on AppEngine. |
+ select = False |
+ |
+try: # Test for SSL features |
+ SSLContext = None |
+ HAS_SNI = False |
+ |
+ import ssl |
+ from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 |
+ from ssl import SSLContext # Modern SSL? |
+ from ssl import HAS_SNI # Has SNI? |
+except ImportError: |
+ pass |
+ |
+ |
+from .packages import six |
+from .exceptions import LocationParseError, SSLError |
+ |
+ |
+class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'])): |
+ """ |
+ Datastructure for representing an HTTP URL. Used as a return value for |
+ :func:`parse_url`. |
+ """ |
+ slots = () |
+ |
+ def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, query=None, fragment=None): |
+ return super(Url, cls).__new__(cls, scheme, auth, host, port, path, query, fragment) |
+ |
+ @property |
+ def hostname(self): |
+ """For backwards-compatibility with urlparse. We're nice like that.""" |
+ return self.host |
+ |
+ @property |
+ def request_uri(self): |
+ """Absolute path including the query string.""" |
+ uri = self.path or '/' |
+ |
+ if self.query is not None: |
+ uri += '?' + self.query |
+ |
+ return uri |
+ |
+ |
+def split_first(s, delims): |
+ """ |
+ Given a string and an iterable of delimiters, split on the first found |
+ delimiter. Return two split parts and the matched delimiter. |
+ |
+ If not found, then the first part is the full input string. |
+ |
+ Example: :: |
+ |
+ >>> split_first('foo/bar?baz', '?/=') |
+ ('foo', 'bar?baz', '/') |
+ >>> split_first('foo/bar?baz', '123') |
+ ('foo/bar?baz', '', None) |
+ |
+ Scales linearly with number of delims. Not ideal for large number of delims. |
+ """ |
+ min_idx = None |
+ min_delim = None |
+ for d in delims: |
+ idx = s.find(d) |
+ if idx < 0: |
+ continue |
+ |
+ if min_idx is None or idx < min_idx: |
+ min_idx = idx |
+ min_delim = d |
+ |
+ if min_idx is None or min_idx < 0: |
+ return s, '', None |
+ |
+ return s[:min_idx], s[min_idx+1:], min_delim |
+ |
+ |
+def parse_url(url): |
+ """ |
+ Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is |
+ performed to parse incomplete urls. Fields not provided will be None. |
+ |
+ Partly backwards-compatible with :mod:`urlparse`. |
+ |
+ Example: :: |
+ |
+ >>> parse_url('http://google.com/mail/') |
+ Url(scheme='http', host='google.com', port=None, path='/', ...) |
+ >>> parse_url('google.com:80') |
+ Url(scheme=None, host='google.com', port=80, path=None, ...) |
+ >>> parse_url('/foo?bar') |
+ Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) |
+ """ |
+ |
+ # While this code has overlap with stdlib's urlparse, it is much |
+ # simplified for our needs and less annoying. |
+ # Additionally, this imeplementations does silly things to be optimal |
+ # on CPython. |
+ |
+ scheme = None |
+ auth = None |
+ host = None |
+ port = None |
+ path = None |
+ fragment = None |
+ query = None |
+ |
+ # Scheme |
+ if '://' in url: |
+ scheme, url = url.split('://', 1) |
+ |
+ # Find the earliest Authority Terminator |
+ # (http://tools.ietf.org/html/rfc3986#section-3.2) |
+ url, path_, delim = split_first(url, ['/', '?', '#']) |
+ |
+ if delim: |
+ # Reassemble the path |
+ path = delim + path_ |
+ |
+ # Auth |
+ if '@' in url: |
+ auth, url = url.split('@', 1) |
+ |
+ # IPv6 |
+ if url and url[0] == '[': |
+ host, url = url[1:].split(']', 1) |
+ |
+ # Port |
+ if ':' in url: |
+ _host, port = url.split(':', 1) |
+ |
+ if not host: |
+ host = _host |
+ |
+ if not port.isdigit(): |
+ raise LocationParseError("Failed to parse: %s" % url) |
+ |
+ port = int(port) |
+ |
+ elif not host and url: |
+ host = url |
+ |
+ if not path: |
+ return Url(scheme, auth, host, port, path, query, fragment) |
+ |
+ # Fragment |
+ if '#' in path: |
+ path, fragment = path.split('#', 1) |
+ |
+ # Query |
+ if '?' in path: |
+ path, query = path.split('?', 1) |
+ |
+ return Url(scheme, auth, host, port, path, query, fragment) |
+ |
+ |
+def get_host(url): |
+ """ |
+ Deprecated. Use :func:`.parse_url` instead. |
+ """ |
+ p = parse_url(url) |
+ return p.scheme or 'http', p.hostname, p.port |
+ |
+ |
+def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, |
+ basic_auth=None): |
+ """ |
+ Shortcuts for generating request headers. |
+ |
+ :param keep_alive: |
+ If ``True``, adds 'connection: keep-alive' header. |
+ |
+ :param accept_encoding: |
+ Can be a boolean, list, or string. |
+ ``True`` translates to 'gzip,deflate'. |
+ List will get joined by comma. |
+ String will be used as provided. |
+ |
+ :param user_agent: |
+ String representing the user-agent you want, such as |
+ "python-urllib3/0.6" |
+ |
+ :param basic_auth: |
+ Colon-separated username:password string for 'authorization: basic ...' |
+ auth header. |
+ |
+ Example: :: |
+ |
+ >>> make_headers(keep_alive=True, user_agent="Batman/1.0") |
+ {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} |
+ >>> make_headers(accept_encoding=True) |
+ {'accept-encoding': 'gzip,deflate'} |
+ """ |
+ headers = {} |
+ if accept_encoding: |
+ if isinstance(accept_encoding, str): |
+ pass |
+ elif isinstance(accept_encoding, list): |
+ accept_encoding = ','.join(accept_encoding) |
+ else: |
+ accept_encoding = 'gzip,deflate' |
+ headers['accept-encoding'] = accept_encoding |
+ |
+ if user_agent: |
+ headers['user-agent'] = user_agent |
+ |
+ if keep_alive: |
+ headers['connection'] = 'keep-alive' |
+ |
+ if basic_auth: |
+ headers['authorization'] = 'Basic ' + \ |
+ b64encode(six.b(basic_auth)).decode('utf-8') |
+ |
+ return headers |
+ |
+ |
+def is_connection_dropped(conn): # Platform-specific |
+ """ |
+ Returns True if the connection is dropped and should be closed. |
+ |
+ :param conn: |
+ :class:`httplib.HTTPConnection` object. |
+ |
+ Note: For platforms like AppEngine, this will always return ``False`` to |
+ let the platform handle connection recycling transparently for us. |
+ """ |
+ sock = getattr(conn, 'sock', False) |
+ if not sock: # Platform-specific: AppEngine |
+ return False |
+ |
+ if not poll: |
+ if not select: # Platform-specific: AppEngine |
+ return False |
+ |
+ try: |
+ return select([sock], [], [], 0.0)[0] |
+ except SocketError: |
+ return True |
+ |
+ # This version is better on platforms that support it. |
+ p = poll() |
+ p.register(sock, POLLIN) |
+ for (fno, ev) in p.poll(0.0): |
+ if fno == sock.fileno(): |
+ # Either data is buffered (bad), or the connection is dropped. |
+ return True |
+ |
+ |
+def resolve_cert_reqs(candidate): |
+ """ |
+ Resolves the argument to a numeric constant, which can be passed to |
+ the wrap_socket function/method from the ssl module. |
+ Defaults to :data:`ssl.CERT_NONE`. |
+ If given a string it is assumed to be the name of the constant in the |
+ :mod:`ssl` module or its abbrevation. |
+ (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. |
+ If it's neither `None` nor a string we assume it is already the numeric |
+ constant which can directly be passed to wrap_socket. |
+ """ |
+ if candidate is None: |
+ return CERT_NONE |
+ |
+ if isinstance(candidate, str): |
+ res = getattr(ssl, candidate, None) |
+ if res is None: |
+ res = getattr(ssl, 'CERT_' + candidate) |
+ return res |
+ |
+ return candidate |
+ |
+ |
+def resolve_ssl_version(candidate): |
+ """ |
+ like resolve_cert_reqs |
+ """ |
+ if candidate is None: |
+ return PROTOCOL_SSLv23 |
+ |
+ if isinstance(candidate, str): |
+ res = getattr(ssl, candidate, None) |
+ if res is None: |
+ res = getattr(ssl, 'PROTOCOL_' + candidate) |
+ return res |
+ |
+ return candidate |
+ |
+ |
+def assert_fingerprint(cert, fingerprint): |
+ """ |
+ Checks if given fingerprint matches the supplied certificate. |
+ |
+ :param cert: |
+ Certificate as bytes object. |
+ :param fingerprint: |
+ Fingerprint as string of hexdigits, can be interspersed by colons. |
+ """ |
+ |
+ # Maps the length of a digest to a possible hash function producing |
+ # this digest. |
+ hashfunc_map = { |
+ 16: md5, |
+ 20: sha1 |
+ } |
+ |
+ fingerprint = fingerprint.replace(':', '').lower() |
+ |
+ digest_length, rest = divmod(len(fingerprint), 2) |
+ |
+ if rest or digest_length not in hashfunc_map: |
+ raise SSLError('Fingerprint is of invalid length.') |
+ |
+ # We need encode() here for py32; works on py2 and p33. |
+ fingerprint_bytes = unhexlify(fingerprint.encode()) |
+ |
+ hashfunc = hashfunc_map[digest_length] |
+ |
+ cert_digest = hashfunc(cert).digest() |
+ |
+ if not cert_digest == fingerprint_bytes: |
+ raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' |
+ .format(hexlify(fingerprint_bytes), |
+ hexlify(cert_digest))) |
+ |
+ |
+if SSLContext is not None: # Python 3.2+ |
+ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, |
+ ca_certs=None, server_hostname=None, |
+ ssl_version=None): |
+ """ |
+ All arguments except `server_hostname` have the same meaning as for |
+ :func:`ssl.wrap_socket` |
+ |
+ :param server_hostname: |
+ Hostname of the expected certificate |
+ """ |
+ context = SSLContext(ssl_version) |
+ context.verify_mode = cert_reqs |
+ if ca_certs: |
+ try: |
+ context.load_verify_locations(ca_certs) |
+ # Py32 raises IOError |
+ # Py33 raises FileNotFoundError |
+ except Exception as e: # Reraise as SSLError |
+ raise SSLError(e) |
+ if certfile: |
+ # FIXME: This block needs a test. |
+ context.load_cert_chain(certfile, keyfile) |
+ if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI |
+ return context.wrap_socket(sock, server_hostname=server_hostname) |
+ return context.wrap_socket(sock) |
+ |
+else: # Python 3.1 and earlier |
+ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, |
+ ca_certs=None, server_hostname=None, |
+ ssl_version=None): |
+ return wrap_socket(sock, keyfile=keyfile, certfile=certfile, |
+ ca_certs=ca_certs, cert_reqs=cert_reqs, |
+ ssl_version=ssl_version) |