comparison lib/python3.8/site-packages/pip/_vendor/urllib3/connection.py @ 0:9e54283cc701 draft

"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author guerler
date Mon, 27 Jul 2020 03:47:31 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:9e54283cc701
1 from __future__ import absolute_import
2 import datetime
3 import logging
4 import os
5 import socket
6 from socket import error as SocketError, timeout as SocketTimeout
7 import warnings
8 from .packages import six
9 from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection
10 from .packages.six.moves.http_client import HTTPException # noqa: F401
11
12 try: # Compiled with SSL?
13 import ssl
14
15 BaseSSLError = ssl.SSLError
16 except (ImportError, AttributeError): # Platform-specific: No SSL.
17 ssl = None
18
19 class BaseSSLError(BaseException):
20 pass
21
22
23 try:
24 # Python 3: not a no-op, we're adding this to the namespace so it can be imported.
25 ConnectionError = ConnectionError
26 except NameError:
27 # Python 2
28 class ConnectionError(Exception):
29 pass
30
31
32 from .exceptions import (
33 NewConnectionError,
34 ConnectTimeoutError,
35 SubjectAltNameWarning,
36 SystemTimeWarning,
37 )
38 from .packages.ssl_match_hostname import match_hostname, CertificateError
39
40 from .util.ssl_ import (
41 resolve_cert_reqs,
42 resolve_ssl_version,
43 assert_fingerprint,
44 create_urllib3_context,
45 ssl_wrap_socket,
46 )
47
48
49 from .util import connection
50
51 from ._collections import HTTPHeaderDict
52
53 log = logging.getLogger(__name__)
54
55 port_by_scheme = {"http": 80, "https": 443}
56
57 # When it comes time to update this value as a part of regular maintenance
58 # (ie test_recent_date is failing) update it to ~6 months before the current date.
59 RECENT_DATE = datetime.date(2019, 1, 1)
60
61
62 class DummyConnection(object):
63 """Used to detect a failed ConnectionCls import."""
64
65 pass
66
67
68 class HTTPConnection(_HTTPConnection, object):
69 """
70 Based on httplib.HTTPConnection but provides an extra constructor
71 backwards-compatibility layer between older and newer Pythons.
72
73 Additional keyword parameters are used to configure attributes of the connection.
74 Accepted parameters include:
75
76 - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool`
77 - ``source_address``: Set the source address for the current connection.
78 - ``socket_options``: Set specific options on the underlying socket. If not specified, then
79 defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling
80 Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy.
81
82 For example, if you wish to enable TCP Keep Alive in addition to the defaults,
83 you might pass::
84
85 HTTPConnection.default_socket_options + [
86 (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
87 ]
88
89 Or you may want to disable the defaults by passing an empty list (e.g., ``[]``).
90 """
91
92 default_port = port_by_scheme["http"]
93
94 #: Disable Nagle's algorithm by default.
95 #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]``
96 default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
97
98 #: Whether this connection verifies the host's certificate.
99 is_verified = False
100
101 def __init__(self, *args, **kw):
102 if not six.PY2:
103 kw.pop("strict", None)
104
105 # Pre-set source_address.
106 self.source_address = kw.get("source_address")
107
108 #: The socket options provided by the user. If no options are
109 #: provided, we use the default options.
110 self.socket_options = kw.pop("socket_options", self.default_socket_options)
111
112 _HTTPConnection.__init__(self, *args, **kw)
113
114 @property
115 def host(self):
116 """
117 Getter method to remove any trailing dots that indicate the hostname is an FQDN.
118
119 In general, SSL certificates don't include the trailing dot indicating a
120 fully-qualified domain name, and thus, they don't validate properly when
121 checked against a domain name that includes the dot. In addition, some
122 servers may not expect to receive the trailing dot when provided.
123
124 However, the hostname with trailing dot is critical to DNS resolution; doing a
125 lookup with the trailing dot will properly only resolve the appropriate FQDN,
126 whereas a lookup without a trailing dot will search the system's search domain
127 list. Thus, it's important to keep the original host around for use only in
128 those cases where it's appropriate (i.e., when doing DNS lookup to establish the
129 actual TCP connection across which we're going to send HTTP requests).
130 """
131 return self._dns_host.rstrip(".")
132
133 @host.setter
134 def host(self, value):
135 """
136 Setter for the `host` property.
137
138 We assume that only urllib3 uses the _dns_host attribute; httplib itself
139 only uses `host`, and it seems reasonable that other libraries follow suit.
140 """
141 self._dns_host = value
142
143 def _new_conn(self):
144 """ Establish a socket connection and set nodelay settings on it.
145
146 :return: New socket connection.
147 """
148 extra_kw = {}
149 if self.source_address:
150 extra_kw["source_address"] = self.source_address
151
152 if self.socket_options:
153 extra_kw["socket_options"] = self.socket_options
154
155 try:
156 conn = connection.create_connection(
157 (self._dns_host, self.port), self.timeout, **extra_kw
158 )
159
160 except SocketTimeout:
161 raise ConnectTimeoutError(
162 self,
163 "Connection to %s timed out. (connect timeout=%s)"
164 % (self.host, self.timeout),
165 )
166
167 except SocketError as e:
168 raise NewConnectionError(
169 self, "Failed to establish a new connection: %s" % e
170 )
171
172 return conn
173
174 def _prepare_conn(self, conn):
175 self.sock = conn
176 # Google App Engine's httplib does not define _tunnel_host
177 if getattr(self, "_tunnel_host", None):
178 # TODO: Fix tunnel so it doesn't depend on self.sock state.
179 self._tunnel()
180 # Mark this connection as not reusable
181 self.auto_open = 0
182
183 def connect(self):
184 conn = self._new_conn()
185 self._prepare_conn(conn)
186
187 def request_chunked(self, method, url, body=None, headers=None):
188 """
189 Alternative to the common request method, which sends the
190 body with chunked encoding and not as one block
191 """
192 headers = HTTPHeaderDict(headers if headers is not None else {})
193 skip_accept_encoding = "accept-encoding" in headers
194 skip_host = "host" in headers
195 self.putrequest(
196 method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host
197 )
198 for header, value in headers.items():
199 self.putheader(header, value)
200 if "transfer-encoding" not in headers:
201 self.putheader("Transfer-Encoding", "chunked")
202 self.endheaders()
203
204 if body is not None:
205 stringish_types = six.string_types + (bytes,)
206 if isinstance(body, stringish_types):
207 body = (body,)
208 for chunk in body:
209 if not chunk:
210 continue
211 if not isinstance(chunk, bytes):
212 chunk = chunk.encode("utf8")
213 len_str = hex(len(chunk))[2:]
214 self.send(len_str.encode("utf-8"))
215 self.send(b"\r\n")
216 self.send(chunk)
217 self.send(b"\r\n")
218
219 # After the if clause, to always have a closed body
220 self.send(b"0\r\n\r\n")
221
222
223 class HTTPSConnection(HTTPConnection):
224 default_port = port_by_scheme["https"]
225
226 ssl_version = None
227
228 def __init__(
229 self,
230 host,
231 port=None,
232 key_file=None,
233 cert_file=None,
234 key_password=None,
235 strict=None,
236 timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
237 ssl_context=None,
238 server_hostname=None,
239 **kw
240 ):
241
242 HTTPConnection.__init__(self, host, port, strict=strict, timeout=timeout, **kw)
243
244 self.key_file = key_file
245 self.cert_file = cert_file
246 self.key_password = key_password
247 self.ssl_context = ssl_context
248 self.server_hostname = server_hostname
249
250 # Required property for Google AppEngine 1.9.0 which otherwise causes
251 # HTTPS requests to go out as HTTP. (See Issue #356)
252 self._protocol = "https"
253
254 def connect(self):
255 conn = self._new_conn()
256 self._prepare_conn(conn)
257
258 # Wrap socket using verification with the root certs in
259 # trusted_root_certs
260 default_ssl_context = False
261 if self.ssl_context is None:
262 default_ssl_context = True
263 self.ssl_context = create_urllib3_context(
264 ssl_version=resolve_ssl_version(self.ssl_version),
265 cert_reqs=resolve_cert_reqs(self.cert_reqs),
266 )
267
268 # Try to load OS default certs if none are given.
269 # Works well on Windows (requires Python3.4+)
270 context = self.ssl_context
271 if (
272 not self.ca_certs
273 and not self.ca_cert_dir
274 and default_ssl_context
275 and hasattr(context, "load_default_certs")
276 ):
277 context.load_default_certs()
278
279 self.sock = ssl_wrap_socket(
280 sock=conn,
281 keyfile=self.key_file,
282 certfile=self.cert_file,
283 key_password=self.key_password,
284 ssl_context=self.ssl_context,
285 server_hostname=self.server_hostname,
286 )
287
288
289 class VerifiedHTTPSConnection(HTTPSConnection):
290 """
291 Based on httplib.HTTPSConnection but wraps the socket with
292 SSL certification.
293 """
294
295 cert_reqs = None
296 ca_certs = None
297 ca_cert_dir = None
298 ssl_version = None
299 assert_fingerprint = None
300
301 def set_cert(
302 self,
303 key_file=None,
304 cert_file=None,
305 cert_reqs=None,
306 key_password=None,
307 ca_certs=None,
308 assert_hostname=None,
309 assert_fingerprint=None,
310 ca_cert_dir=None,
311 ):
312 """
313 This method should only be called once, before the connection is used.
314 """
315 # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also
316 # have an SSLContext object in which case we'll use its verify_mode.
317 if cert_reqs is None:
318 if self.ssl_context is not None:
319 cert_reqs = self.ssl_context.verify_mode
320 else:
321 cert_reqs = resolve_cert_reqs(None)
322
323 self.key_file = key_file
324 self.cert_file = cert_file
325 self.cert_reqs = cert_reqs
326 self.key_password = key_password
327 self.assert_hostname = assert_hostname
328 self.assert_fingerprint = assert_fingerprint
329 self.ca_certs = ca_certs and os.path.expanduser(ca_certs)
330 self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir)
331
332 def connect(self):
333 # Add certificate verification
334 conn = self._new_conn()
335 hostname = self.host
336
337 # Google App Engine's httplib does not define _tunnel_host
338 if getattr(self, "_tunnel_host", None):
339 self.sock = conn
340 # Calls self._set_hostport(), so self.host is
341 # self._tunnel_host below.
342 self._tunnel()
343 # Mark this connection as not reusable
344 self.auto_open = 0
345
346 # Override the host with the one we're requesting data from.
347 hostname = self._tunnel_host
348
349 server_hostname = hostname
350 if self.server_hostname is not None:
351 server_hostname = self.server_hostname
352
353 is_time_off = datetime.date.today() < RECENT_DATE
354 if is_time_off:
355 warnings.warn(
356 (
357 "System time is way off (before {0}). This will probably "
358 "lead to SSL verification errors"
359 ).format(RECENT_DATE),
360 SystemTimeWarning,
361 )
362
363 # Wrap socket using verification with the root certs in
364 # trusted_root_certs
365 default_ssl_context = False
366 if self.ssl_context is None:
367 default_ssl_context = True
368 self.ssl_context = create_urllib3_context(
369 ssl_version=resolve_ssl_version(self.ssl_version),
370 cert_reqs=resolve_cert_reqs(self.cert_reqs),
371 )
372
373 context = self.ssl_context
374 context.verify_mode = resolve_cert_reqs(self.cert_reqs)
375
376 # Try to load OS default certs if none are given.
377 # Works well on Windows (requires Python3.4+)
378 if (
379 not self.ca_certs
380 and not self.ca_cert_dir
381 and default_ssl_context
382 and hasattr(context, "load_default_certs")
383 ):
384 context.load_default_certs()
385
386 self.sock = ssl_wrap_socket(
387 sock=conn,
388 keyfile=self.key_file,
389 certfile=self.cert_file,
390 key_password=self.key_password,
391 ca_certs=self.ca_certs,
392 ca_cert_dir=self.ca_cert_dir,
393 server_hostname=server_hostname,
394 ssl_context=context,
395 )
396
397 if self.assert_fingerprint:
398 assert_fingerprint(
399 self.sock.getpeercert(binary_form=True), self.assert_fingerprint
400 )
401 elif (
402 context.verify_mode != ssl.CERT_NONE
403 and not getattr(context, "check_hostname", False)
404 and self.assert_hostname is not False
405 ):
406 # While urllib3 attempts to always turn off hostname matching from
407 # the TLS library, this cannot always be done. So we check whether
408 # the TLS Library still thinks it's matching hostnames.
409 cert = self.sock.getpeercert()
410 if not cert.get("subjectAltName", ()):
411 warnings.warn(
412 (
413 "Certificate for {0} has no `subjectAltName`, falling back to check for a "
414 "`commonName` for now. This feature is being removed by major browsers and "
415 "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 "
416 "for details.)".format(hostname)
417 ),
418 SubjectAltNameWarning,
419 )
420 _match_hostname(cert, self.assert_hostname or server_hostname)
421
422 self.is_verified = (
423 context.verify_mode == ssl.CERT_REQUIRED
424 or self.assert_fingerprint is not None
425 )
426
427
428 def _match_hostname(cert, asserted_hostname):
429 try:
430 match_hostname(cert, asserted_hostname)
431 except CertificateError as e:
432 log.warning(
433 "Certificate did not match expected hostname: %s. Certificate: %s",
434 asserted_hostname,
435 cert,
436 )
437 # Add cert to exception and reraise so client code can inspect
438 # the cert when catching the exception, if they want to
439 e._peer_cert = cert
440 raise
441
442
443 if ssl:
444 # Make a copy for testing.
445 UnverifiedHTTPSConnection = HTTPSConnection
446 HTTPSConnection = VerifiedHTTPSConnection
447 else:
448 HTTPSConnection = DummyConnection