Skip to content

Commit 2175515

Browse files
Upgrade httplib2 to 0.22.0 (#305)
1 parent 7907787 commit 2175515

12 files changed

Lines changed: 3211 additions & 122 deletions

File tree

shotgun_api3/lib/httplib2/auth.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import base64
2+
import re
3+
4+
from ... import pyparsing as pp
5+
6+
from .error import *
7+
8+
9+
try: # pyparsing>=3.0.0
10+
downcaseTokens = pp.common.downcaseTokens
11+
except AttributeError:
12+
downcaseTokens = pp.downcaseTokens
13+
14+
UNQUOTE_PAIRS = re.compile(r"\\(.)")
15+
unquote = lambda s, l, t: UNQUOTE_PAIRS.sub(r"\1", t[0][1:-1])
16+
17+
# https://tools.ietf.org/html/rfc7235#section-1.2
18+
# https://tools.ietf.org/html/rfc7235#appendix-B
19+
tchar = "!#$%&'*+-.^_`|~" + pp.nums + pp.alphas
20+
token = pp.Word(tchar).setName("token")
21+
token68 = pp.Combine(pp.Word("-._~+/" + pp.nums + pp.alphas) + pp.Optional(pp.Word("=").leaveWhitespace())).setName(
22+
"token68"
23+
)
24+
25+
quoted_string = pp.dblQuotedString.copy().setName("quoted-string").setParseAction(unquote)
26+
auth_param_name = token.copy().setName("auth-param-name").addParseAction(downcaseTokens)
27+
auth_param = auth_param_name + pp.Suppress("=") + (quoted_string | token)
28+
params = pp.Dict(pp.delimitedList(pp.Group(auth_param)))
29+
30+
scheme = token("scheme")
31+
challenge = scheme + (params("params") | token68("token"))
32+
33+
authentication_info = params.copy()
34+
www_authenticate = pp.delimitedList(pp.Group(challenge))
35+
36+
37+
def _parse_authentication_info(headers, headername="authentication-info"):
38+
"""https://tools.ietf.org/html/rfc7615
39+
"""
40+
header = headers.get(headername, "").strip()
41+
if not header:
42+
return {}
43+
try:
44+
parsed = authentication_info.parseString(header)
45+
except pp.ParseException as ex:
46+
# print(ex.explain(ex))
47+
raise MalformedHeader(headername)
48+
49+
return parsed.asDict()
50+
51+
52+
def _parse_www_authenticate(headers, headername="www-authenticate"):
53+
"""Returns a dictionary of dictionaries, one dict per auth_scheme."""
54+
header = headers.get(headername, "").strip()
55+
if not header:
56+
return {}
57+
try:
58+
parsed = www_authenticate.parseString(header)
59+
except pp.ParseException as ex:
60+
# print(ex.explain(ex))
61+
raise MalformedHeader(headername)
62+
63+
retval = {
64+
challenge["scheme"].lower(): challenge["params"].asDict()
65+
if "params" in challenge
66+
else {"token": challenge.get("token")}
67+
for challenge in parsed
68+
}
69+
return retval

shotgun_api3/lib/httplib2/cacerts.txt

Lines changed: 2225 additions & 0 deletions
Large diffs are not rendered by default.

shotgun_api3/lib/httplib2/certs.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Utilities for certificate management."""
2+
3+
import os
4+
5+
certifi_available = False
6+
certifi_where = None
7+
try:
8+
from certifi import where as certifi_where
9+
certifi_available = True
10+
except ImportError:
11+
pass
12+
13+
custom_ca_locater_available = False
14+
custom_ca_locater_where = None
15+
try:
16+
from ca_certs_locater import get as custom_ca_locater_where
17+
custom_ca_locater_available = True
18+
except ImportError:
19+
pass
20+
21+
22+
BUILTIN_CA_CERTS = os.path.join(
23+
os.path.dirname(os.path.abspath(__file__)), "cacerts.txt"
24+
)
25+
26+
27+
def where():
28+
env = os.environ.get("HTTPLIB2_CA_CERTS")
29+
if env is not None:
30+
if os.path.isfile(env):
31+
return env
32+
else:
33+
raise RuntimeError("Environment variable HTTPLIB2_CA_CERTS not a valid file")
34+
if custom_ca_locater_available:
35+
return custom_ca_locater_where()
36+
if certifi_available:
37+
return certifi_where()
38+
return BUILTIN_CA_CERTS
39+
40+
41+
if __name__ == "__main__":
42+
print(where())

shotgun_api3/lib/httplib2/error.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# All exceptions raised here derive from HttpLib2Error
2+
class HttpLib2Error(Exception):
3+
pass
4+
5+
6+
# Some exceptions can be caught and optionally
7+
# be turned back into responses.
8+
class HttpLib2ErrorWithResponse(HttpLib2Error):
9+
def __init__(self, desc, response, content):
10+
self.response = response
11+
self.content = content
12+
HttpLib2Error.__init__(self, desc)
13+
14+
15+
class RedirectMissingLocation(HttpLib2ErrorWithResponse):
16+
pass
17+
18+
19+
class RedirectLimit(HttpLib2ErrorWithResponse):
20+
pass
21+
22+
23+
class FailedToDecompressContent(HttpLib2ErrorWithResponse):
24+
pass
25+
26+
27+
class UnimplementedDigestAuthOptionError(HttpLib2ErrorWithResponse):
28+
pass
29+
30+
31+
class UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse):
32+
pass
33+
34+
35+
class MalformedHeader(HttpLib2Error):
36+
pass
37+
38+
39+
class RelativeURIError(HttpLib2Error):
40+
pass
41+
42+
43+
class ServerNotFoundError(HttpLib2Error):
44+
pass
45+
46+
47+
class ProxiesUnavailableError(HttpLib2Error):
48+
pass
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# -*- coding: utf-8 -*-
2+
"""Converts an IRI to a URI."""
3+
4+
__author__ = "Joe Gregorio (joe@bitworking.org)"
5+
__copyright__ = "Copyright 2006, Joe Gregorio"
6+
__contributors__ = []
7+
__version__ = "1.0.0"
8+
__license__ = "MIT"
9+
10+
import urllib.parse
11+
12+
# Convert an IRI to a URI following the rules in RFC 3987
13+
#
14+
# The characters we need to enocde and escape are defined in the spec:
15+
#
16+
# iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
17+
# ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
18+
# / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
19+
# / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
20+
# / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
21+
# / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
22+
# / %xD0000-DFFFD / %xE1000-EFFFD
23+
24+
escape_range = [
25+
(0xA0, 0xD7FF),
26+
(0xE000, 0xF8FF),
27+
(0xF900, 0xFDCF),
28+
(0xFDF0, 0xFFEF),
29+
(0x10000, 0x1FFFD),
30+
(0x20000, 0x2FFFD),
31+
(0x30000, 0x3FFFD),
32+
(0x40000, 0x4FFFD),
33+
(0x50000, 0x5FFFD),
34+
(0x60000, 0x6FFFD),
35+
(0x70000, 0x7FFFD),
36+
(0x80000, 0x8FFFD),
37+
(0x90000, 0x9FFFD),
38+
(0xA0000, 0xAFFFD),
39+
(0xB0000, 0xBFFFD),
40+
(0xC0000, 0xCFFFD),
41+
(0xD0000, 0xDFFFD),
42+
(0xE1000, 0xEFFFD),
43+
(0xF0000, 0xFFFFD),
44+
(0x100000, 0x10FFFD),
45+
]
46+
47+
48+
def encode(c):
49+
retval = c
50+
i = ord(c)
51+
for low, high in escape_range:
52+
if i < low:
53+
break
54+
if i >= low and i <= high:
55+
retval = "".join(["%%%2X" % o for o in c.encode("utf-8")])
56+
break
57+
return retval
58+
59+
60+
def iri2uri(uri):
61+
"""Convert an IRI to a URI. Note that IRIs must be
62+
passed in a unicode strings. That is, do not utf-8 encode
63+
the IRI before passing it into the function."""
64+
if isinstance(uri, str):
65+
(scheme, authority, path, query, fragment) = urllib.parse.urlsplit(uri)
66+
authority = authority.encode("idna").decode("utf-8")
67+
# For each character in 'ucschar' or 'iprivate'
68+
# 1. encode as utf-8
69+
# 2. then %-encode each octet of that utf-8
70+
uri = urllib.parse.urlunsplit((scheme, authority, path, query, fragment))
71+
uri = "".join([encode(c) for c in uri])
72+
return uri
73+
74+
75+
if __name__ == "__main__":
76+
import unittest
77+
78+
class Test(unittest.TestCase):
79+
def test_uris(self):
80+
"""Test that URIs are invariant under the transformation."""
81+
invariant = [
82+
"ftp://ftp.is.co.za/rfc/rfc1808.txt",
83+
"http://www.ietf.org/rfc/rfc2396.txt",
84+
"ldap://[2001:db8::7]/c=GB?objectClass?one",
85+
"mailto:John.Doe@example.com",
86+
"news:comp.infosystems.www.servers.unix",
87+
"tel:+1-816-555-1212",
88+
"telnet://192.0.2.16:80/",
89+
"urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
90+
]
91+
for uri in invariant:
92+
self.assertEqual(uri, iri2uri(uri))
93+
94+
def test_iri(self):
95+
"""Test that the right type of escaping is done for each part of the URI."""
96+
self.assertEqual(
97+
"http://xn--o3h.com/%E2%98%84",
98+
iri2uri("http://\N{COMET}.com/\N{COMET}"),
99+
)
100+
self.assertEqual(
101+
"http://bitworking.org/?fred=%E2%98%84",
102+
iri2uri("http://bitworking.org/?fred=\N{COMET}"),
103+
)
104+
self.assertEqual(
105+
"http://bitworking.org/#%E2%98%84",
106+
iri2uri("http://bitworking.org/#\N{COMET}"),
107+
)
108+
self.assertEqual("#%E2%98%84", iri2uri("#\N{COMET}"))
109+
self.assertEqual(
110+
"/fred?bar=%E2%98%9A#%E2%98%84",
111+
iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"),
112+
)
113+
self.assertEqual(
114+
"/fred?bar=%E2%98%9A#%E2%98%84",
115+
iri2uri(iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")),
116+
)
117+
self.assertNotEqual(
118+
"/fred?bar=%E2%98%9A#%E2%98%84",
119+
iri2uri(
120+
"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode("utf-8")
121+
),
122+
)
123+
124+
unittest.main()

shotgun_api3/lib/httplib2/python2/__init__.py

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
"Sam Ruby",
1818
"Louis Nyffenegger",
1919
"Alex Yu",
20+
"Lai Han",
2021
]
2122
__license__ = "MIT"
22-
__version__ = "0.19.1"
23+
__version__ = "0.22.0"
2324

2425
import base64
2526
import calendar
@@ -467,7 +468,10 @@ def _decompressContent(response, new_content):
467468
if encoding == "gzip":
468469
content = gzip.GzipFile(fileobj=StringIO.StringIO(new_content)).read()
469470
if encoding == "deflate":
470-
content = zlib.decompress(content, -zlib.MAX_WBITS)
471+
try:
472+
content = zlib.decompress(content, zlib.MAX_WBITS)
473+
except (IOError, zlib.error):
474+
content = zlib.decompress(content, -zlib.MAX_WBITS)
471475
response["content-length"] = str(len(content))
472476
# Record the historical presence of the encoding in a way the won't interfere.
473477
response["-content-encoding"] = response["content-encoding"]
@@ -961,34 +965,14 @@ def proxy_info_from_url(url, method="http", noproxy=None):
961965
"""Construct a ProxyInfo from a URL (such as http_proxy env var)
962966
"""
963967
url = urlparse.urlparse(url)
964-
username = None
965-
password = None
966-
port = None
967-
if "@" in url[1]:
968-
ident, host_port = url[1].split("@", 1)
969-
if ":" in ident:
970-
username, password = ident.split(":", 1)
971-
else:
972-
password = ident
973-
else:
974-
host_port = url[1]
975-
if ":" in host_port:
976-
host, port = host_port.split(":", 1)
977-
else:
978-
host = host_port
979-
980-
if port:
981-
port = int(port)
982-
else:
983-
port = dict(https=443, http=80)[method]
984968

985969
proxy_type = 3 # socks.PROXY_TYPE_HTTP
986970
pi = ProxyInfo(
987971
proxy_type=proxy_type,
988-
proxy_host=host,
989-
proxy_port=port,
990-
proxy_user=username or None,
991-
proxy_pass=password or None,
972+
proxy_host=url.hostname,
973+
proxy_port=url.port or dict(https=443, http=80)[method],
974+
proxy_user=url.username or None,
975+
proxy_pass=url.password or None,
992976
proxy_headers=None,
993977
)
994978

0 commit comments

Comments
 (0)