15
15
"Alex Yu" ,
16
16
]
17
17
__license__ = "MIT"
18
- __version__ = "0.19.1 "
18
+ __version__ = "0.20.4 "
19
19
20
20
import base64
21
21
import calendar
@@ -78,7 +78,7 @@ def has_timeout(timeout):
78
78
debuglevel = 0
79
79
80
80
# A request will be tried 'RETRIES' times if it fails at the socket/connection level.
81
- RETRIES = 2
81
+ RETRIES = 1
82
82
83
83
84
84
# Open Items:
@@ -151,15 +151,18 @@ def _build_ssl_context(
151
151
# source: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.maximum_version
152
152
if maximum_version is not None :
153
153
if hasattr (context , "maximum_version" ):
154
- context .maximum_version = getattr (ssl .TLSVersion , maximum_version )
154
+ if isinstance (maximum_version , str ):
155
+ maximum_version = getattr (ssl .TLSVersion , maximum_version )
156
+ context .maximum_version = maximum_version
155
157
else :
156
158
raise RuntimeError ("setting tls_maximum_version requires Python 3.7 and OpenSSL 1.1 or newer" )
157
159
if minimum_version is not None :
158
160
if hasattr (context , "minimum_version" ):
159
- context .minimum_version = getattr (ssl .TLSVersion , minimum_version )
161
+ if isinstance (minimum_version , str ):
162
+ minimum_version = getattr (ssl .TLSVersion , minimum_version )
163
+ context .minimum_version = minimum_version
160
164
else :
161
165
raise RuntimeError ("setting tls_minimum_version requires Python 3.7 and OpenSSL 1.1 or newer" )
162
-
163
166
# check_hostname requires python 3.4+
164
167
# we will perform the equivalent in HTTPSConnectionWithTimeout.connect() by calling ssl.match_hostname
165
168
# if check_hostname is not supported.
@@ -180,6 +183,20 @@ def _get_end2end_headers(response):
180
183
return [header for header in list (response .keys ()) if header not in hopbyhop ]
181
184
182
185
186
+ def _errno_from_exception (e ):
187
+ # socket.error and common wrap in .args
188
+ if len (e .args ) > 0 :
189
+ return e .args [0 ].errno if isinstance (e .args [0 ], socket .error ) else e .errno
190
+
191
+ # pysocks.ProxyError wraps in .socket_err
192
+ # https://github.com/httplib2/httplib2/pull/202
193
+ if hasattr (e , "socket_err" ):
194
+ e_int = e .socket_err
195
+ return e_int .args [0 ].errno if isinstance (e_int .args [0 ], socket .error ) else e_int .errno
196
+
197
+ return None
198
+
199
+
183
200
URI = re .compile (r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?" )
184
201
185
202
@@ -223,7 +240,7 @@ def safename(filename):
223
240
filename = filename .decode ("utf-8" )
224
241
else :
225
242
filename_bytes = filename .encode ("utf-8" )
226
- filemd5 = _md5 (filename_bytes ).hexdigest ()
243
+ filesha1 = _sha (filename_bytes ).hexdigest ()
227
244
filename = re_url_scheme .sub ("" , filename )
228
245
filename = re_unsafe .sub ("" , filename )
229
246
@@ -234,7 +251,7 @@ def safename(filename):
234
251
# Thus max safe filename x = 93 chars. Let it be 90 to make a round sum:
235
252
filename = filename [:90 ]
236
253
237
- return "," .join ((filename , filemd5 ))
254
+ return "," .join ((filename , filesha1 ))
238
255
239
256
240
257
NORMALIZE_SPACE = re .compile (r"(?:\r\n)?[ \t]+" )
@@ -834,10 +851,10 @@ def __init__(
834
851
proxy_headers: Additional or modified headers for the proxy connect
835
852
request.
836
853
"""
837
- if isinstance (proxy_user , bytes ):
838
- proxy_user = proxy_user .decode ()
839
- if isinstance (proxy_pass , bytes ):
840
- proxy_pass = proxy_pass .decode ()
854
+ if isinstance (proxy_user , str ):
855
+ proxy_user = proxy_user .encode ()
856
+ if isinstance (proxy_pass , str ):
857
+ proxy_pass = proxy_pass .encode ()
841
858
(
842
859
self .proxy_type ,
843
860
self .proxy_host ,
@@ -913,34 +930,14 @@ def proxy_info_from_url(url, method="http", noproxy=None):
913
930
"""Construct a ProxyInfo from a URL (such as http_proxy env var)
914
931
"""
915
932
url = urllib .parse .urlparse (url )
916
- username = None
917
- password = None
918
- port = None
919
- if "@" in url [1 ]:
920
- ident , host_port = url [1 ].split ("@" , 1 )
921
- if ":" in ident :
922
- username , password = ident .split (":" , 1 )
923
- else :
924
- password = ident
925
- else :
926
- host_port = url [1 ]
927
- if ":" in host_port :
928
- host , port = host_port .split (":" , 1 )
929
- else :
930
- host = host_port
931
-
932
- if port :
933
- port = int (port )
934
- else :
935
- port = dict (https = 443 , http = 80 )[method ]
936
933
937
934
proxy_type = 3 # socks.PROXY_TYPE_HTTP
938
935
pi = ProxyInfo (
939
936
proxy_type = proxy_type ,
940
- proxy_host = host ,
941
- proxy_port = port ,
942
- proxy_user = username or None ,
943
- proxy_pass = password or None ,
937
+ proxy_host = url . hostname ,
938
+ proxy_port = url . port or dict ( https = 443 , http = 80 )[ method ] ,
939
+ proxy_user = url . username or None ,
940
+ proxy_pass = url . password or None ,
944
941
proxy_headers = None ,
945
942
)
946
943
@@ -1068,6 +1065,7 @@ def __init__(
1068
1065
tls_maximum_version = None ,
1069
1066
tls_minimum_version = None ,
1070
1067
key_password = None ,
1068
+ context = None
1071
1069
):
1072
1070
1073
1071
self .disable_ssl_certificate_validation = disable_ssl_certificate_validation
@@ -1077,15 +1075,16 @@ def __init__(
1077
1075
if proxy_info and not isinstance (proxy_info , ProxyInfo ):
1078
1076
self .proxy_info = proxy_info ("https" )
1079
1077
1080
- context = _build_ssl_context (
1081
- self .disable_ssl_certificate_validation ,
1082
- self .ca_certs ,
1083
- cert_file ,
1084
- key_file ,
1085
- maximum_version = tls_maximum_version ,
1086
- minimum_version = tls_minimum_version ,
1087
- key_password = key_password ,
1088
- )
1078
+ if context is None :
1079
+ context = _build_ssl_context (
1080
+ self .disable_ssl_certificate_validation ,
1081
+ self .ca_certs ,
1082
+ cert_file ,
1083
+ key_file ,
1084
+ maximum_version = tls_maximum_version ,
1085
+ minimum_version = tls_minimum_version ,
1086
+ key_password = key_password ,
1087
+ )
1089
1088
super (HTTPSConnectionWithTimeout , self ).__init__ (
1090
1089
host , port = port , timeout = timeout , context = context ,
1091
1090
)
@@ -1212,6 +1211,7 @@ def __init__(
1212
1211
disable_ssl_certificate_validation = False ,
1213
1212
tls_maximum_version = None ,
1214
1213
tls_minimum_version = None ,
1214
+ context = None ,
1215
1215
):
1216
1216
"""If 'cache' is a string then it is used as a directory name for
1217
1217
a disk cache. Otherwise it must be an object that supports the
@@ -1238,12 +1238,13 @@ def __init__(
1238
1238
1239
1239
tls_maximum_version / tls_minimum_version require Python 3.7+ /
1240
1240
OpenSSL 1.1.0g+. A value of "TLSv1_3" requires OpenSSL 1.1.1+.
1241
- """
1241
+ """
1242
1242
self .proxy_info = proxy_info
1243
1243
self .ca_certs = ca_certs
1244
1244
self .disable_ssl_certificate_validation = disable_ssl_certificate_validation
1245
1245
self .tls_maximum_version = tls_maximum_version
1246
1246
self .tls_minimum_version = tls_minimum_version
1247
+ self .context = context
1247
1248
# Map domain name to an httplib connection
1248
1249
self .connections = {}
1249
1250
# The location of the cache, for now a directory
@@ -1352,7 +1353,7 @@ def _conn_request(self, conn, request_uri, method, body, headers):
1352
1353
conn .close ()
1353
1354
raise ServerNotFoundError ("Unable to find the server at %s" % conn .host )
1354
1355
except socket .error as e :
1355
- errno_ = e . args [ 0 ]. errno if isinstance ( e . args [ 0 ], socket . error ) else e . errno
1356
+ errno_ = _errno_from_exception ( e )
1356
1357
if errno_ in (errno .ENETUNREACH , errno .EADDRNOTAVAIL ) and i < RETRIES :
1357
1358
continue # retry on potentially transient errors
1358
1359
raise
@@ -1471,6 +1472,8 @@ def _request(
1471
1472
old_response ["content-location" ] = absolute_uri
1472
1473
redirect_method = method
1473
1474
if response .status in [302 , 303 ]:
1475
+ if 'content-length' in headers :
1476
+ del headers ['content-length' ]
1474
1477
redirect_method = "GET"
1475
1478
body = None
1476
1479
(response , content ) = self .request (
@@ -1556,6 +1559,7 @@ def request(
1556
1559
tls_maximum_version = self .tls_maximum_version ,
1557
1560
tls_minimum_version = self .tls_minimum_version ,
1558
1561
key_password = certs [0 ][2 ],
1562
+ context = self .context ,
1559
1563
)
1560
1564
else :
1561
1565
conn = self .connections [conn_key ] = connection_type (
@@ -1566,6 +1570,7 @@ def request(
1566
1570
disable_ssl_certificate_validation = self .disable_ssl_certificate_validation ,
1567
1571
tls_maximum_version = self .tls_maximum_version ,
1568
1572
tls_minimum_version = self .tls_minimum_version ,
1573
+ context = self .context ,
1569
1574
)
1570
1575
else :
1571
1576
conn = self .connections [conn_key ] = connection_type (
@@ -1657,12 +1662,8 @@ def request(
1657
1662
entry_disposition = _entry_disposition (info , headers )
1658
1663
1659
1664
if entry_disposition == "FRESH" :
1660
- if not cached_value :
1661
- info ["status" ] = "504"
1662
- content = b""
1663
1665
response = Response (info )
1664
- if cached_value :
1665
- response .fromcache = True
1666
+ response .fromcache = True
1666
1667
return (response , content )
1667
1668
1668
1669
if entry_disposition == "STALE" :
0 commit comments