httplib2 0.11.3

This commit is contained in:
Jay Lee
2018-07-04 20:24:51 -04:00
parent 044686b564
commit 5966e39406
5 changed files with 179 additions and 95 deletions

View File

@@ -1,4 +1,4 @@
from __future__ import generators
from __future__ import print_function
"""
httplib2
@@ -23,7 +23,7 @@ __contributors__ = ["Thomas Broyer (t.broyer@ltgt.net)",
"Louis Nyffenegger",
"Alex Yu"]
__license__ = "MIT"
__version__ = "0.10.3"
__version__ = '0.11.3'
import re
import sys
@@ -146,6 +146,7 @@ if sys.version_info < (2,4):
seq.sort()
return seq
# Python 2.3 support
def HTTPResponse__getheaders(self):
"""Return list of (header, value) tuples."""
@@ -223,6 +224,7 @@ except ImportError:
# Which headers are hop-by-hop headers by default
HOP_BY_HOP = ['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade']
def _get_end2end_headers(response):
hopbyhop = list(HOP_BY_HOP)
hopbyhop.extend([x.strip() for x in response.get('connection', '').split(',')])
@@ -230,6 +232,7 @@ def _get_end2end_headers(response):
URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?")
def parse_uri(uri):
"""Parses a URI using the regex given in Appendix B of RFC 3986.
@@ -238,6 +241,7 @@ def parse_uri(uri):
groups = URI.match(uri).groups()
return (groups[1], groups[3], groups[4], groups[6], groups[8])
def urlnorm(uri):
(scheme, authority, path, query, fragment) = parse_uri(uri)
if not scheme or not authority:
@@ -258,6 +262,7 @@ def urlnorm(uri):
re_url_scheme = re.compile(r'^\w+://')
re_slash = re.compile(r'[?/:|]+')
def safename(filename):
"""Return a filename suitable for the cache.
@@ -286,12 +291,15 @@ def safename(filename):
return ",".join((filename, filemd5))
NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
def _normalize_headers(headers):
return dict([ (key.lower(), NORMALIZE_SPACE.sub(value, ' ').strip()) for (key, value) in headers.iteritems()])
def _parse_cache_control(headers):
retval = {}
if headers.has_key('cache-control'):
if 'cache-control' in headers:
parts = headers['cache-control'].split(',')
parts_with_args = [tuple([x.strip().lower() for x in part.split("=", 1)]) for part in parts if -1 != part.find("=")]
parts_wo_args = [(name.strip().lower(), 1) for name in parts if -1 == name.find("=")]
@@ -316,7 +324,7 @@ def _parse_www_authenticate(headers, headername='www-authenticate'):
"""Returns a dictionary of dictionaries, one dict
per auth_scheme."""
retval = {}
if headers.has_key(headername):
if headername in headers:
try:
authenticate = headers[headername].strip()
@@ -344,6 +352,7 @@ def _parse_www_authenticate(headers, headername='www-authenticate'):
return retval
# TODO: add current time as _entry_disposition argument to avoid sleep in tests
def _entry_disposition(response_headers, request_headers):
"""Determine freshness from the Date, Expires and Cache-Control headers.
@@ -376,26 +385,26 @@ def _entry_disposition(response_headers, request_headers):
cc = _parse_cache_control(request_headers)
cc_response = _parse_cache_control(response_headers)
if request_headers.has_key('pragma') and request_headers['pragma'].lower().find('no-cache') != -1:
if 'pragma' in request_headers and request_headers['pragma'].lower().find('no-cache') != -1:
retval = "TRANSPARENT"
if 'cache-control' not in request_headers:
request_headers['cache-control'] = 'no-cache'
elif cc.has_key('no-cache'):
elif 'no-cache' in cc:
retval = "TRANSPARENT"
elif cc_response.has_key('no-cache'):
elif 'no-cache' in cc_response:
retval = "STALE"
elif cc.has_key('only-if-cached'):
elif 'only-if-cached' in cc:
retval = "FRESH"
elif response_headers.has_key('date'):
elif 'date' in response_headers:
date = calendar.timegm(email.Utils.parsedate_tz(response_headers['date']))
now = time.time()
current_age = max(0, now - date)
if cc_response.has_key('max-age'):
if 'max-age' in cc_response:
try:
freshness_lifetime = int(cc_response['max-age'])
except ValueError:
freshness_lifetime = 0
elif response_headers.has_key('expires'):
elif 'expires' in response_headers:
expires = email.Utils.parsedate_tz(response_headers['expires'])
if None == expires:
freshness_lifetime = 0
@@ -403,12 +412,12 @@ def _entry_disposition(response_headers, request_headers):
freshness_lifetime = max(0, calendar.timegm(expires) - date)
else:
freshness_lifetime = 0
if cc.has_key('max-age'):
if 'max-age' in cc:
try:
freshness_lifetime = int(cc['max-age'])
except ValueError:
freshness_lifetime = 0
if cc.has_key('min-fresh'):
if 'min-fresh' in cc:
try:
min_fresh = int(cc['min-fresh'])
except ValueError:
@@ -418,6 +427,7 @@ def _entry_disposition(response_headers, request_headers):
retval = "FRESH"
return retval
def _decompressContent(response, new_content):
content = new_content
try:
@@ -426,21 +436,22 @@ def _decompressContent(response, new_content):
if encoding == 'gzip':
content = gzip.GzipFile(fileobj=StringIO.StringIO(new_content)).read()
if encoding == 'deflate':
content = zlib.decompress(content)
content = zlib.decompress(content, -zlib.MAX_WBITS)
response['content-length'] = str(len(content))
# Record the historical presence of the encoding in a way the won't interfere.
response['-content-encoding'] = response['content-encoding']
del response['content-encoding']
except IOError:
except (IOError, zlib.error):
content = ""
raise FailedToDecompressContent(_("Content purported to be compressed with %s but failed to decompress.") % response.get('content-encoding'), response, content)
return content
def _updateCache(request_headers, response_headers, content, cache, cachekey):
if cachekey:
cc = _parse_cache_control(request_headers)
cc_response = _parse_cache_control(response_headers)
if cc.has_key('no-store') or cc_response.has_key('no-store'):
if 'no-store' in cc or 'no-store' in cc_response:
cache.delete(cachekey)
else:
info = email.Message.Message()
@@ -522,7 +533,6 @@ class Authentication(object):
return False
class BasicAuthentication(Authentication):
def __init__(self, credentials, host, request_uri, headers, response, content, http):
Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
@@ -576,7 +586,7 @@ class DigestAuthentication(Authentication):
self.challenge['nc'] += 1
def response(self, response, content):
if not response.has_key('authentication-info'):
if 'authentication-info' not in response:
challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest', {})
if 'true' == challenge.get('stale'):
self.challenge['nonce'] = challenge['nonce']
@@ -585,7 +595,7 @@ class DigestAuthentication(Authentication):
else:
updated_challenge = _parse_www_authenticate(response, 'authentication-info').get('digest', {})
if updated_challenge.has_key('nextnonce'):
if 'nextnonce' in updated_challenge:
self.challenge['nonce'] = updated_challenge['nextnonce']
self.challenge['nc'] = 1
return False
@@ -675,6 +685,7 @@ class WsseAuthentication(Authentication):
cnonce,
iso_now)
class GoogleLoginAuthentication(Authentication):
def __init__(self, credentials, host, request_uri, headers, response, content, http):
from urllib import urlencode
@@ -714,12 +725,13 @@ AUTH_SCHEME_CLASSES = {
AUTH_SCHEME_ORDER = ["hmacdigest", "googlelogin", "digest", "wsse", "basic"]
class FileCache(object):
"""Uses a local directory as a store for cached files.
Not really safe to use if multiple threads or processes are going to
be running on the same cache.
"""
def __init__(self, cache, safe=safename): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior
def __init__(self, cache, safe=safename): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior
self.cache = cache
self.safe = safe
if not os.path.exists(cache):
@@ -747,6 +759,7 @@ class FileCache(object):
if os.path.exists(cacheFullPath):
os.remove(cacheFullPath)
class Credentials(object):
def __init__(self):
self.credentials = []
@@ -762,14 +775,17 @@ class Credentials(object):
if cdomain == "" or domain == cdomain:
yield (name, password)
class KeyCerts(Credentials):
"""Identical to Credentials except that
name/password are mapped to key/cert."""
pass
class AllHosts(object):
pass
class ProxyInfo(object):
"""Collect information required to use a proxy."""
bypass_hosts = ()
@@ -822,12 +838,20 @@ class ProxyInfo(object):
if self.bypass_hosts is AllHosts:
return True
bypass = False
for domain in self.bypass_hosts:
if hostname.endswith(domain):
bypass = True
hostname = '.' + hostname.lstrip('.')
for skip_name in self.bypass_hosts:
# *.suffix
if skip_name.startswith('.') and hostname.endswith(skip_name):
return True
# exact match
if hostname == '.' + skip_name:
return True
return False
return bypass
def __repr__(self):
return (
'<ProxyInfo type={p.proxy_type} host:port={p.proxy_host}:{p.proxy_port} rdns={p.proxy_rdns}' +
' user={p.proxy_user} headers={p.proxy_headers}>').format(p=self)
def proxy_info_from_environment(method='http'):
@@ -841,20 +865,10 @@ def proxy_info_from_environment(method='http'):
url = os.environ.get(env_var, os.environ.get(env_var.upper()))
if not url:
return
pi = proxy_info_from_url(url, method)
return proxy_info_from_url(url, method, None)
no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY', ''))
bypass_hosts = []
if no_proxy:
bypass_hosts = no_proxy.split(',')
# special case, no_proxy=* means all hosts bypassed
if no_proxy == '*':
bypass_hosts = AllHosts
pi.bypass_hosts = bypass_hosts
return pi
def proxy_info_from_url(url, method='http'):
def proxy_info_from_url(url, method='http', noproxy=None):
"""
Construct a ProxyInfo from a URL (such as http_proxy env var)
"""
@@ -880,8 +894,8 @@ def proxy_info_from_url(url, method='http'):
else:
port = dict(https=443, http=80)[method]
proxy_type = 3 # socks.PROXY_TYPE_HTTP
return ProxyInfo(
proxy_type = 3 # socks.PROXY_TYPE_HTTP
pi = ProxyInfo(
proxy_type = proxy_type,
proxy_host = host,
proxy_port = port,
@@ -890,6 +904,20 @@ def proxy_info_from_url(url, method='http'):
proxy_headers = None,
)
bypass_hosts = []
# If not given an explicit noproxy value, respect values in env vars.
if noproxy is None:
noproxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY', ''))
# Special case: A single '*' character means all hosts should be bypassed.
if noproxy == '*':
bypass_hosts = AllHosts
elif noproxy.strip():
bypass_hosts = noproxy.split(',')
bypass_hosts = filter(bool, bypass_hosts) # To exclude empty string.
pi.bypass_hosts = bypass_hosts
return pi
class HTTPConnectionWithTimeout(httplib.HTTPConnection):
"""
@@ -939,16 +967,18 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection):
self.sock.settimeout(self.timeout)
# End of difference from httplib.
if self.debuglevel > 0:
print "connect: (%s, %s) ************" % (self.host, self.port)
print("connect: (%s, %s) ************" % (self.host, self.port))
if use_proxy:
print "proxy: %s ************" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers))
self.sock.connect((self.host, self.port) + sa[2:])
except socket.error, msg:
print("proxy: %s ************" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers)))
if use_proxy:
self.sock.connect((self.host, self.port) + sa[2:])
else:
self.sock.connect(sa)
except socket.error as msg:
if self.debuglevel > 0:
print "connect fail: (%s, %s)" % (self.host, self.port)
print("connect fail: (%s, %s)" % (self.host, self.port))
if use_proxy:
print "proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers))
print("proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers)))
if self.sock:
self.sock.close()
self.sock = None
@@ -957,6 +987,7 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection):
if not self.sock:
raise socket.error, msg
class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
"""
This class allows communication via SSL.
@@ -1062,15 +1093,19 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
if has_timeout(self.timeout):
sock.settimeout(self.timeout)
sock.connect((self.host, self.port))
if use_proxy:
sock.connect((self.host, self.port) + sockaddr[:2])
else:
sock.connect(sockaddr)
self.sock =_ssl_wrap_socket(
sock, self.key_file, self.cert_file,
self.disable_ssl_certificate_validation, self.ca_certs,
self.ssl_version, self.host)
if self.debuglevel > 0:
print "connect: (%s, %s)" % (self.host, self.port)
print("connect: (%s, %s)" % (self.host, self.port))
if use_proxy:
print "proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers))
print("proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers)))
if not self.disable_ssl_certificate_validation:
cert = self.sock.getpeercert()
hostname = self.host.split(':', 0)[0]
@@ -1078,7 +1113,7 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
raise CertificateHostnameMismatch(
'Server presented certificate that does not match '
'host %s: %s' % (hostname, cert), hostname, cert)
except (ssl_SSLError, ssl_CertificateError, CertificateHostnameMismatch), e:
except (ssl_SSLError, ssl_CertificateError, CertificateHostnameMismatch) as e:
if sock:
sock.close()
if self.sock:
@@ -1094,11 +1129,11 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
raise
except (socket.timeout, socket.gaierror):
raise
except socket.error, msg:
except socket.error as msg:
if self.debuglevel > 0:
print "connect fail: (%s, %s)" % (self.host, self.port)
print("connect fail: (%s, %s)" % (self.host, self.port))
if use_proxy:
print "proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers))
print("proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers)))
if self.sock:
self.sock.close()
self.sock = None
@@ -1290,7 +1325,7 @@ class Http(object):
challenges = _parse_www_authenticate(response, 'www-authenticate')
for cred in self.credentials.iter(host):
for scheme in AUTH_SCHEME_ORDER:
if challenges.has_key(scheme):
if scheme in challenges:
yield AUTH_SCHEME_CLASSES[scheme](cred, host, request_uri, headers, response, content, self)
def add_credentials(self, name, password, domain=""):
@@ -1326,13 +1361,13 @@ class Http(object):
except ssl_SSLError:
conn.close()
raise
except socket.error, e:
except socket.error as e:
err = 0
if hasattr(e, 'args'):
err = getattr(e, 'args')[0]
else:
err = e.errno
if err == errno.ECONNREFUSED: # Connection refused
if err == errno.ECONNREFUSED: # Connection refused
raise
if err in (errno.ENETUNREACH, errno.EADDRNOTAVAIL) and i < RETRIES:
continue # retry on potentially transient socket errors
@@ -1418,29 +1453,29 @@ class Http(object):
# Pick out the location header and basically start from the beginning
# remembering first to strip the ETag header and decrement our 'depth'
if redirections:
if not response.has_key('location') and response.status != 300:
if 'location' not in response and response.status != 300:
raise RedirectMissingLocation( _("Redirected but the response is missing a Location: header."), response, content)
# Fix-up relative redirects (which violate an RFC 2616 MUST)
if response.has_key('location'):
if 'location' in response:
location = response['location']
(scheme, authority, path, query, fragment) = parse_uri(location)
if authority == None:
response['location'] = urlparse.urljoin(absolute_uri, location)
if response.status == 301 and method in ["GET", "HEAD"]:
response['-x-permanent-redirect-url'] = response['location']
if not response.has_key('content-location'):
if 'content-location' not in response:
response['content-location'] = absolute_uri
_updateCache(headers, response, content, self.cache, cachekey)
if headers.has_key('if-none-match'):
if 'if-none-match' in headers:
del headers['if-none-match']
if headers.has_key('if-modified-since'):
if 'if-modified-since' in headers:
del headers['if-modified-since']
if 'authorization' in headers and not self.forward_authorization_headers:
del headers['authorization']
if response.has_key('location'):
if 'location' in response:
location = response['location']
old_response = copy.deepcopy(response)
if not old_response.has_key('content-location'):
if 'content-location' not in old_response:
old_response['content-location'] = absolute_uri
redirect_method = method
if response.status in [302, 303]:
@@ -1455,7 +1490,7 @@ class Http(object):
raise RedirectLimit("Redirected more times than rediection_limit allows.", response, content)
elif response.status in [200, 203] and method in ["GET", "HEAD"]:
# Don't cache 206's since we aren't going to handle byte range requests
if not response.has_key('content-location'):
if 'content-location' not in response:
response['content-location'] = absolute_uri
_updateCache(headers, response, content, self.cache, cachekey)
@@ -1497,7 +1532,7 @@ class Http(object):
else:
headers = self._normalize_headers(headers)
if not headers.has_key('user-agent'):
if 'user-agent' not in headers:
headers['user-agent'] = "Python-httplib2/%s (gzip)" % __version__
uri = iri2uri(uri)
@@ -1568,7 +1603,7 @@ class Http(object):
else:
cachekey = None
if method in self.optimistic_concurrency_methods and self.cache and info.has_key('etag') and not self.ignore_etag and 'if-match' not in headers:
if method in self.optimistic_concurrency_methods and self.cache and 'etag' in info and not self.ignore_etag and 'if-match' not in headers:
# http://www.w3.org/1999/04/Editing/
headers['if-match'] = info['etag']
@@ -1589,7 +1624,7 @@ class Http(object):
break
if cached_value and method in ["GET", "HEAD"] and self.cache and 'range' not in headers:
if info.has_key('-x-permanent-redirect-url'):
if '-x-permanent-redirect-url' in info:
# Should cached permanent redirects be counted in our redirection count? For now, yes.
if redirections <= 0:
raise RedirectLimit("Redirected more times than rediection_limit allows.", {}, "")
@@ -1619,9 +1654,9 @@ class Http(object):
return (response, content)
if entry_disposition == "STALE":
if info.has_key('etag') and not self.ignore_etag and not 'if-none-match' in headers:
if 'etag' in info and not self.ignore_etag and not 'if-none-match' in headers:
headers['if-none-match'] = info['etag']
if info.has_key('last-modified') and not 'last-modified' in headers:
if 'last-modified' in info and not 'last-modified' in headers:
headers['if-modified-since'] = info['last-modified']
elif entry_disposition == "TRANSPARENT":
pass
@@ -1651,13 +1686,13 @@ class Http(object):
content = new_content
else:
cc = _parse_cache_control(headers)
if cc.has_key('only-if-cached'):
if 'only-if-cached' in cc:
info['status'] = '504'
response = Response(info)
content = ""
else:
(response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
except Exception, e:
except Exception as e:
if self.force_exception_to_status_code:
if isinstance(e, HttpLib2ErrorWithResponse):
response = e.response
@@ -1738,9 +1773,8 @@ class Response(dict):
self.status = int(self.get('status', self.status))
self.reason = self.get('reason', self.reason)
def __getattr__(self, name):
if name == 'dict':
return self
else:
raise AttributeError, name
raise AttributeError(name)

View File

@@ -2165,3 +2165,32 @@ R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
# Issuer: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
# Subject: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
# Serial: 33af1e6a711a9a0bb2864b11d09fae5
# MD5 Fingerprint: E4:A6:8A:C8:54:AC:52:42:46:0A:FD:72:48:1B:2A:44
# SHA1 Fingerprint: DF:3C:24:F9:BF:D6:66:76:1B:26:80:73:FE:06:D1:CC:8D:4F:82:A4
# SHA256 Fingerprint: CB:3C:CB:B7:60:31:E5:E0:13:8F:8D:D3:9A:23:F9:DE:47:FF:C3:5E:43:C1:14:4C:EA:27:D4:6A:5A:B1:CB:5F
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----

View File

@@ -254,7 +254,7 @@ class socksocket(socket.socket):
if self.__proxy[3]:
# Resolve remotely
ipaddr = None
req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr.encode()
else:
# Resolve locally
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))

View File

@@ -1,3 +1,4 @@
from __future__ import print_function
import unittest
import errno
import os
@@ -52,7 +53,7 @@ class FunctionalProxyHttpTest(unittest.TestCase):
# TODO use subprocess.check_call when 2.4 is dropped
ret = subprocess.call(['tinyproxy', '-c', self.conffile])
self.assertEqual(0, ret)
except OSError, e:
except OSError as e:
if e.errno == errno.ENOENT:
raise nose.SkipTest('tinyproxy not available')
raise
@@ -62,11 +63,11 @@ class FunctionalProxyHttpTest(unittest.TestCase):
try:
pid = int(open(self.pidfile).read())
os.kill(pid, signal.SIGTERM)
except OSError, e:
except OSError as e:
if e.errno == errno.ESRCH:
print '\n\n\nTinyProxy Failed to start, log follows:'
print open(self.logfile).read()
print 'end tinyproxy log\n\n\n'
print('\n\n\nTinyProxy Failed to start, log follows:')
print(open(self.logfile).read())
print('end tinyproxy log\n\n\n')
raise
map(os.unlink, (self.pidfile,
self.logfile,

View File

@@ -1,16 +1,19 @@
#!/usr/bin/python2
#!/usr/bin/env python2
from __future__ import print_function
import BaseHTTPServer
import logging
import os.path
import unittest
import ssl
import sys
import unittest
import httplib2
from httplib2.test import miniserver
logger = logging.getLogger(__name__)
class KeepAliveHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""
Request handler that keeps the HTTP connection open, so that the test can
@@ -28,27 +31,23 @@ class KeepAliveHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# output via logging so nose can catch it
logger.info(s, *args)
class HttpsContextTest(unittest.TestCase):
def setUp(self):
if sys.version_info < (2, 7, 9):
return
if hasattr(self, "skipTest"):
self.skipTest("SSLContext requires Python 2.7.9")
else:
return
self.httpd, self.port = miniserver.start_server(
KeepAliveHandler, True)
self.ca_certs_path = os.path.join(os.path.dirname(__file__), 'server.pem')
self.httpd, self.port = miniserver.start_server(KeepAliveHandler, True)
def tearDown(self):
self.httpd.shutdown()
def testHttpsContext(self):
if sys.version_info < (2, 7, 9):
if hasattr(unittest, "skipTest"):
self.skipTest("SSLContext requires Python 2.7.9")# Python 2.7.0
else:
return
import ssl
client = httplib2.Http(
ca_certs=os.path.join(os.path.dirname(__file__), 'server.pem'))
client = httplib2.Http(ca_certs=self.ca_certs_path)
# Establish connection to local server
client.request('https://localhost:%d/' % (self.port))
@@ -61,6 +60,27 @@ class HttpsContextTest(unittest.TestCase):
self.assertIsInstance(conn.sock.context, ssl.SSLContext)
self.assertTrue(conn.sock.context.check_hostname)
self.assertEqual(conn.sock.server_hostname, 'localhost')
self.assertEqual(conn.sock.context.check_hostname, True)
self.assertEqual(conn.sock.context.verify_mode, ssl.CERT_REQUIRED)
self.assertEqual(conn.sock.context.protocol, ssl.PROTOCOL_SSLv23)
def test_ssl_hostname_mismatch_repeat(self):
# https://github.com/httplib2/httplib2/issues/5
# FIXME(temoto): as of 2017-01-05 this is only a reference code, not useful test.
# Because it doesn't provoke described error on my machine.
# Instead `SSLContext.wrap_socket` raises `ssl.CertificateError`
# which was also added to original patch.
# url host is intentionally different, we provoke ssl hostname mismatch error
url = 'https://127.0.0.1:%d/' % (self.port,)
http = httplib2.Http(ca_certs=self.ca_certs_path, proxy_info=None)
def once():
try:
http.request(url)
assert False, 'expected certificate hostname mismatch error'
except Exception as e:
print('%s errno=%s' % (repr(e), getattr(e, 'errno', None)))
once()
once()