comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/urllib3/connectionpool.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
comparison
equal deleted inserted replaced
67:0e9998148a16 69:33d812a61356
1 from __future__ import annotations
2
3 import errno
4 import logging
5 import queue
6 import sys
7 import typing
8 import warnings
9 import weakref
10 from socket import timeout as SocketTimeout
11 from types import TracebackType
12
13 from ._base_connection import _TYPE_BODY
14 from ._collections import HTTPHeaderDict
15 from ._request_methods import RequestMethods
16 from .connection import (
17 BaseSSLError,
18 BrokenPipeError,
19 DummyConnection,
20 HTTPConnection,
21 HTTPException,
22 HTTPSConnection,
23 ProxyConfig,
24 _wrap_proxy_error,
25 )
26 from .connection import port_by_scheme as port_by_scheme
27 from .exceptions import (
28 ClosedPoolError,
29 EmptyPoolError,
30 FullPoolError,
31 HostChangedError,
32 InsecureRequestWarning,
33 LocationValueError,
34 MaxRetryError,
35 NewConnectionError,
36 ProtocolError,
37 ProxyError,
38 ReadTimeoutError,
39 SSLError,
40 TimeoutError,
41 )
42 from .response import BaseHTTPResponse
43 from .util.connection import is_connection_dropped
44 from .util.proxy import connection_requires_http_tunnel
45 from .util.request import _TYPE_BODY_POSITION, set_file_position
46 from .util.retry import Retry
47 from .util.ssl_match_hostname import CertificateError
48 from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_DEFAULT, Timeout
49 from .util.url import Url, _encode_target
50 from .util.url import _normalize_host as normalize_host
51 from .util.url import parse_url
52 from .util.util import to_str
53
54 if typing.TYPE_CHECKING:
55 import ssl
56
57 from typing_extensions import Self
58
59 from ._base_connection import BaseHTTPConnection, BaseHTTPSConnection
60
61 log = logging.getLogger(__name__)
62
63 _TYPE_TIMEOUT = typing.Union[Timeout, float, _TYPE_DEFAULT, None]
64
65
66 # Pool objects
67 class ConnectionPool:
68 """
69 Base class for all connection pools, such as
70 :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`.
71
72 .. note::
73 ConnectionPool.urlopen() does not normalize or percent-encode target URIs
74 which is useful if your target server doesn't support percent-encoded
75 target URIs.
76 """
77
78 scheme: str | None = None
79 QueueCls = queue.LifoQueue
80
81 def __init__(self, host: str, port: int | None = None) -> None:
82 if not host:
83 raise LocationValueError("No host specified.")
84
85 self.host = _normalize_host(host, scheme=self.scheme)
86 self.port = port
87
88 # This property uses 'normalize_host()' (not '_normalize_host()')
89 # to avoid removing square braces around IPv6 addresses.
90 # This value is sent to `HTTPConnection.set_tunnel()` if called
91 # because square braces are required for HTTP CONNECT tunneling.
92 self._tunnel_host = normalize_host(host, scheme=self.scheme).lower()
93
94 def __str__(self) -> str:
95 return f"{type(self).__name__}(host={self.host!r}, port={self.port!r})"
96
97 def __enter__(self) -> Self:
98 return self
99
100 def __exit__(
101 self,
102 exc_type: type[BaseException] | None,
103 exc_val: BaseException | None,
104 exc_tb: TracebackType | None,
105 ) -> typing.Literal[False]:
106 self.close()
107 # Return False to re-raise any potential exceptions
108 return False
109
110 def close(self) -> None:
111 """
112 Close all pooled connections and disable the pool.
113 """
114
115
116 # This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252
117 _blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK}
118
119
120 class HTTPConnectionPool(ConnectionPool, RequestMethods):
121 """
122 Thread-safe connection pool for one host.
123
124 :param host:
125 Host used for this HTTP Connection (e.g. "localhost"), passed into
126 :class:`http.client.HTTPConnection`.
127
128 :param port:
129 Port used for this HTTP Connection (None is equivalent to 80), passed
130 into :class:`http.client.HTTPConnection`.
131
132 :param timeout:
133 Socket timeout in seconds for each individual connection. This can
134 be a float or integer, which sets the timeout for the HTTP request,
135 or an instance of :class:`urllib3.util.Timeout` which gives you more
136 fine-grained control over request timeouts. After the constructor has
137 been parsed, this is always a `urllib3.util.Timeout` object.
138
139 :param maxsize:
140 Number of connections to save that can be reused. More than 1 is useful
141 in multithreaded situations. If ``block`` is set to False, more
142 connections will be created but they will not be saved once they've
143 been used.
144
145 :param block:
146 If set to True, no more than ``maxsize`` connections will be used at
147 a time. When no free connections are available, the call will block
148 until a connection has been released. This is a useful side effect for
149 particular multithreaded situations where one does not want to use more
150 than maxsize connections per host to prevent flooding.
151
152 :param headers:
153 Headers to include with all requests, unless other headers are given
154 explicitly.
155
156 :param retries:
157 Retry configuration to use by default with requests in this pool.
158
159 :param _proxy:
160 Parsed proxy URL, should not be used directly, instead, see
161 :class:`urllib3.ProxyManager`
162
163 :param _proxy_headers:
164 A dictionary with proxy headers, should not be used directly,
165 instead, see :class:`urllib3.ProxyManager`
166
167 :param \\**conn_kw:
168 Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`,
169 :class:`urllib3.connection.HTTPSConnection` instances.
170 """
171
172 scheme = "http"
173 ConnectionCls: (
174 type[BaseHTTPConnection] | type[BaseHTTPSConnection]
175 ) = HTTPConnection
176
177 def __init__(
178 self,
179 host: str,
180 port: int | None = None,
181 timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT,
182 maxsize: int = 1,
183 block: bool = False,
184 headers: typing.Mapping[str, str] | None = None,
185 retries: Retry | bool | int | None = None,
186 _proxy: Url | None = None,
187 _proxy_headers: typing.Mapping[str, str] | None = None,
188 _proxy_config: ProxyConfig | None = None,
189 **conn_kw: typing.Any,
190 ):
191 ConnectionPool.__init__(self, host, port)
192 RequestMethods.__init__(self, headers)
193
194 if not isinstance(timeout, Timeout):
195 timeout = Timeout.from_float(timeout)
196
197 if retries is None:
198 retries = Retry.DEFAULT
199
200 self.timeout = timeout
201 self.retries = retries
202
203 self.pool: queue.LifoQueue[typing.Any] | None = self.QueueCls(maxsize)
204 self.block = block
205
206 self.proxy = _proxy
207 self.proxy_headers = _proxy_headers or {}
208 self.proxy_config = _proxy_config
209
210 # Fill the queue up so that doing get() on it will block properly
211 for _ in range(maxsize):
212 self.pool.put(None)
213
214 # These are mostly for testing and debugging purposes.
215 self.num_connections = 0
216 self.num_requests = 0
217 self.conn_kw = conn_kw
218
219 if self.proxy:
220 # Enable Nagle's algorithm for proxies, to avoid packet fragmentation.
221 # We cannot know if the user has added default socket options, so we cannot replace the
222 # list.
223 self.conn_kw.setdefault("socket_options", [])
224
225 self.conn_kw["proxy"] = self.proxy
226 self.conn_kw["proxy_config"] = self.proxy_config
227
228 # Do not pass 'self' as callback to 'finalize'.
229 # Then the 'finalize' would keep an endless living (leak) to self.
230 # By just passing a reference to the pool allows the garbage collector
231 # to free self if nobody else has a reference to it.
232 pool = self.pool
233
234 # Close all the HTTPConnections in the pool before the
235 # HTTPConnectionPool object is garbage collected.
236 weakref.finalize(self, _close_pool_connections, pool)
237
238 def _new_conn(self) -> BaseHTTPConnection:
239 """
240 Return a fresh :class:`HTTPConnection`.
241 """
242 self.num_connections += 1
243 log.debug(
244 "Starting new HTTP connection (%d): %s:%s",
245 self.num_connections,
246 self.host,
247 self.port or "80",
248 )
249
250 conn = self.ConnectionCls(
251 host=self.host,
252 port=self.port,
253 timeout=self.timeout.connect_timeout,
254 **self.conn_kw,
255 )
256 return conn
257
258 def _get_conn(self, timeout: float | None = None) -> BaseHTTPConnection:
259 """
260 Get a connection. Will return a pooled connection if one is available.
261
262 If no connections are available and :prop:`.block` is ``False``, then a
263 fresh connection is returned.
264
265 :param timeout:
266 Seconds to wait before giving up and raising
267 :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and
268 :prop:`.block` is ``True``.
269 """
270 conn = None
271
272 if self.pool is None:
273 raise ClosedPoolError(self, "Pool is closed.")
274
275 try:
276 conn = self.pool.get(block=self.block, timeout=timeout)
277
278 except AttributeError: # self.pool is None
279 raise ClosedPoolError(self, "Pool is closed.") from None # Defensive:
280
281 except queue.Empty:
282 if self.block:
283 raise EmptyPoolError(
284 self,
285 "Pool is empty and a new connection can't be opened due to blocking mode.",
286 ) from None
287 pass # Oh well, we'll create a new connection then
288
289 # If this is a persistent connection, check if it got disconnected
290 if conn and is_connection_dropped(conn):
291 log.debug("Resetting dropped connection: %s", self.host)
292 conn.close()
293
294 return conn or self._new_conn()
295
296 def _put_conn(self, conn: BaseHTTPConnection | None) -> None:
297 """
298 Put a connection back into the pool.
299
300 :param conn:
301 Connection object for the current host and port as returned by
302 :meth:`._new_conn` or :meth:`._get_conn`.
303
304 If the pool is already full, the connection is closed and discarded
305 because we exceeded maxsize. If connections are discarded frequently,
306 then maxsize should be increased.
307
308 If the pool is closed, then the connection will be closed and discarded.
309 """
310 if self.pool is not None:
311 try:
312 self.pool.put(conn, block=False)
313 return # Everything is dandy, done.
314 except AttributeError:
315 # self.pool is None.
316 pass
317 except queue.Full:
318 # Connection never got put back into the pool, close it.
319 if conn:
320 conn.close()
321
322 if self.block:
323 # This should never happen if you got the conn from self._get_conn
324 raise FullPoolError(
325 self,
326 "Pool reached maximum size and no more connections are allowed.",
327 ) from None
328
329 log.warning(
330 "Connection pool is full, discarding connection: %s. Connection pool size: %s",
331 self.host,
332 self.pool.qsize(),
333 )
334
335 # Connection never got put back into the pool, close it.
336 if conn:
337 conn.close()
338
339 def _validate_conn(self, conn: BaseHTTPConnection) -> None:
340 """
341 Called right before a request is made, after the socket is created.
342 """
343
344 def _prepare_proxy(self, conn: BaseHTTPConnection) -> None:
345 # Nothing to do for HTTP connections.
346 pass
347
348 def _get_timeout(self, timeout: _TYPE_TIMEOUT) -> Timeout:
349 """Helper that always returns a :class:`urllib3.util.Timeout`"""
350 if timeout is _DEFAULT_TIMEOUT:
351 return self.timeout.clone()
352
353 if isinstance(timeout, Timeout):
354 return timeout.clone()
355 else:
356 # User passed us an int/float. This is for backwards compatibility,
357 # can be removed later
358 return Timeout.from_float(timeout)
359
360 def _raise_timeout(
361 self,
362 err: BaseSSLError | OSError | SocketTimeout,
363 url: str,
364 timeout_value: _TYPE_TIMEOUT | None,
365 ) -> None:
366 """Is the error actually a timeout? Will raise a ReadTimeout or pass"""
367
368 if isinstance(err, SocketTimeout):
369 raise ReadTimeoutError(
370 self, url, f"Read timed out. (read timeout={timeout_value})"
371 ) from err
372
373 # See the above comment about EAGAIN in Python 3.
374 if hasattr(err, "errno") and err.errno in _blocking_errnos:
375 raise ReadTimeoutError(
376 self, url, f"Read timed out. (read timeout={timeout_value})"
377 ) from err
378
379 def _make_request(
380 self,
381 conn: BaseHTTPConnection,
382 method: str,
383 url: str,
384 body: _TYPE_BODY | None = None,
385 headers: typing.Mapping[str, str] | None = None,
386 retries: Retry | None = None,
387 timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,
388 chunked: bool = False,
389 response_conn: BaseHTTPConnection | None = None,
390 preload_content: bool = True,
391 decode_content: bool = True,
392 enforce_content_length: bool = True,
393 ) -> BaseHTTPResponse:
394 """
395 Perform a request on a given urllib connection object taken from our
396 pool.
397
398 :param conn:
399 a connection from one of our connection pools
400
401 :param method:
402 HTTP request method (such as GET, POST, PUT, etc.)
403
404 :param url:
405 The URL to perform the request on.
406
407 :param body:
408 Data to send in the request body, either :class:`str`, :class:`bytes`,
409 an iterable of :class:`str`/:class:`bytes`, or a file-like object.
410
411 :param headers:
412 Dictionary of custom headers to send, such as User-Agent,
413 If-None-Match, etc. If None, pool headers are used. If provided,
414 these headers completely replace any pool-specific headers.
415
416 :param retries:
417 Configure the number of retries to allow before raising a
418 :class:`~urllib3.exceptions.MaxRetryError` exception.
419
420 Pass ``None`` to retry until you receive a response. Pass a
421 :class:`~urllib3.util.retry.Retry` object for fine-grained control
422 over different types of retries.
423 Pass an integer number to retry connection errors that many times,
424 but no other types of errors. Pass zero to never retry.
425
426 If ``False``, then retries are disabled and any exception is raised
427 immediately. Also, instead of raising a MaxRetryError on redirects,
428 the redirect response will be returned.
429
430 :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.
431
432 :param timeout:
433 If specified, overrides the default timeout for this one
434 request. It may be a float (in seconds) or an instance of
435 :class:`urllib3.util.Timeout`.
436
437 :param chunked:
438 If True, urllib3 will send the body using chunked transfer
439 encoding. Otherwise, urllib3 will send the body using the standard
440 content-length form. Defaults to False.
441
442 :param response_conn:
443 Set this to ``None`` if you will handle releasing the connection or
444 set the connection to have the response release it.
445
446 :param preload_content:
447 If True, the response's body will be preloaded during construction.
448
449 :param decode_content:
450 If True, will attempt to decode the body based on the
451 'content-encoding' header.
452
453 :param enforce_content_length:
454 Enforce content length checking. Body returned by server must match
455 value of Content-Length header, if present. Otherwise, raise error.
456 """
457 self.num_requests += 1
458
459 timeout_obj = self._get_timeout(timeout)
460 timeout_obj.start_connect()
461 conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout)
462
463 try:
464 # Trigger any extra validation we need to do.
465 try:
466 self._validate_conn(conn)
467 except (SocketTimeout, BaseSSLError) as e:
468 self._raise_timeout(err=e, url=url, timeout_value=conn.timeout)
469 raise
470
471 # _validate_conn() starts the connection to an HTTPS proxy
472 # so we need to wrap errors with 'ProxyError' here too.
473 except (
474 OSError,
475 NewConnectionError,
476 TimeoutError,
477 BaseSSLError,
478 CertificateError,
479 SSLError,
480 ) as e:
481 new_e: Exception = e
482 if isinstance(e, (BaseSSLError, CertificateError)):
483 new_e = SSLError(e)
484 # If the connection didn't successfully connect to it's proxy
485 # then there
486 if isinstance(
487 new_e, (OSError, NewConnectionError, TimeoutError, SSLError)
488 ) and (conn and conn.proxy and not conn.has_connected_to_proxy):
489 new_e = _wrap_proxy_error(new_e, conn.proxy.scheme)
490 raise new_e
491
492 # conn.request() calls http.client.*.request, not the method in
493 # urllib3.request. It also calls makefile (recv) on the socket.
494 try:
495 conn.request(
496 method,
497 url,
498 body=body,
499 headers=headers,
500 chunked=chunked,
501 preload_content=preload_content,
502 decode_content=decode_content,
503 enforce_content_length=enforce_content_length,
504 )
505
506 # We are swallowing BrokenPipeError (errno.EPIPE) since the server is
507 # legitimately able to close the connection after sending a valid response.
508 # With this behaviour, the received response is still readable.
509 except BrokenPipeError:
510 pass
511 except OSError as e:
512 # MacOS/Linux
513 # EPROTOTYPE and ECONNRESET are needed on macOS
514 # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/
515 # Condition changed later to emit ECONNRESET instead of only EPROTOTYPE.
516 if e.errno != errno.EPROTOTYPE and e.errno != errno.ECONNRESET:
517 raise
518
519 # Reset the timeout for the recv() on the socket
520 read_timeout = timeout_obj.read_timeout
521
522 if not conn.is_closed:
523 # In Python 3 socket.py will catch EAGAIN and return None when you
524 # try and read into the file pointer created by http.client, which
525 # instead raises a BadStatusLine exception. Instead of catching
526 # the exception and assuming all BadStatusLine exceptions are read
527 # timeouts, check for a zero timeout before making the request.
528 if read_timeout == 0:
529 raise ReadTimeoutError(
530 self, url, f"Read timed out. (read timeout={read_timeout})"
531 )
532 conn.timeout = read_timeout
533
534 # Receive the response from the server
535 try:
536 response = conn.getresponse()
537 except (BaseSSLError, OSError) as e:
538 self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
539 raise
540
541 # Set properties that are used by the pooling layer.
542 response.retries = retries
543 response._connection = response_conn # type: ignore[attr-defined]
544 response._pool = self # type: ignore[attr-defined]
545
546 log.debug(
547 '%s://%s:%s "%s %s HTTP/%s" %s %s',
548 self.scheme,
549 self.host,
550 self.port,
551 method,
552 url,
553 response.version,
554 response.status,
555 response.length_remaining,
556 )
557
558 return response
559
560 def close(self) -> None:
561 """
562 Close all pooled connections and disable the pool.
563 """
564 if self.pool is None:
565 return
566 # Disable access to the pool
567 old_pool, self.pool = self.pool, None
568
569 # Close all the HTTPConnections in the pool.
570 _close_pool_connections(old_pool)
571
572 def is_same_host(self, url: str) -> bool:
573 """
574 Check if the given ``url`` is a member of the same host as this
575 connection pool.
576 """
577 if url.startswith("/"):
578 return True
579
580 # TODO: Add optional support for socket.gethostbyname checking.
581 scheme, _, host, port, *_ = parse_url(url)
582 scheme = scheme or "http"
583 if host is not None:
584 host = _normalize_host(host, scheme=scheme)
585
586 # Use explicit default port for comparison when none is given
587 if self.port and not port:
588 port = port_by_scheme.get(scheme)
589 elif not self.port and port == port_by_scheme.get(scheme):
590 port = None
591
592 return (scheme, host, port) == (self.scheme, self.host, self.port)
593
594 def urlopen( # type: ignore[override]
595 self,
596 method: str,
597 url: str,
598 body: _TYPE_BODY | None = None,
599 headers: typing.Mapping[str, str] | None = None,
600 retries: Retry | bool | int | None = None,
601 redirect: bool = True,
602 assert_same_host: bool = True,
603 timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,
604 pool_timeout: int | None = None,
605 release_conn: bool | None = None,
606 chunked: bool = False,
607 body_pos: _TYPE_BODY_POSITION | None = None,
608 preload_content: bool = True,
609 decode_content: bool = True,
610 **response_kw: typing.Any,
611 ) -> BaseHTTPResponse:
612 """
613 Get a connection from the pool and perform an HTTP request. This is the
614 lowest level call for making a request, so you'll need to specify all
615 the raw details.
616
617 .. note::
618
619 More commonly, it's appropriate to use a convenience method
620 such as :meth:`request`.
621
622 .. note::
623
624 `release_conn` will only behave as expected if
625 `preload_content=False` because we want to make
626 `preload_content=False` the default behaviour someday soon without
627 breaking backwards compatibility.
628
629 :param method:
630 HTTP request method (such as GET, POST, PUT, etc.)
631
632 :param url:
633 The URL to perform the request on.
634
635 :param body:
636 Data to send in the request body, either :class:`str`, :class:`bytes`,
637 an iterable of :class:`str`/:class:`bytes`, or a file-like object.
638
639 :param headers:
640 Dictionary of custom headers to send, such as User-Agent,
641 If-None-Match, etc. If None, pool headers are used. If provided,
642 these headers completely replace any pool-specific headers.
643
644 :param retries:
645 Configure the number of retries to allow before raising a
646 :class:`~urllib3.exceptions.MaxRetryError` exception.
647
648 If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a
649 :class:`~urllib3.util.retry.Retry` object for fine-grained control
650 over different types of retries.
651 Pass an integer number to retry connection errors that many times,
652 but no other types of errors. Pass zero to never retry.
653
654 If ``False``, then retries are disabled and any exception is raised
655 immediately. Also, instead of raising a MaxRetryError on redirects,
656 the redirect response will be returned.
657
658 :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.
659
660 :param redirect:
661 If True, automatically handle redirects (status codes 301, 302,
662 303, 307, 308). Each redirect counts as a retry. Disabling retries
663 will disable redirect, too.
664
665 :param assert_same_host:
666 If ``True``, will make sure that the host of the pool requests is
667 consistent else will raise HostChangedError. When ``False``, you can
668 use the pool on an HTTP proxy and request foreign hosts.
669
670 :param timeout:
671 If specified, overrides the default timeout for this one
672 request. It may be a float (in seconds) or an instance of
673 :class:`urllib3.util.Timeout`.
674
675 :param pool_timeout:
676 If set and the pool is set to block=True, then this method will
677 block for ``pool_timeout`` seconds and raise EmptyPoolError if no
678 connection is available within the time period.
679
680 :param bool preload_content:
681 If True, the response's body will be preloaded into memory.
682
683 :param bool decode_content:
684 If True, will attempt to decode the body based on the
685 'content-encoding' header.
686
687 :param release_conn:
688 If False, then the urlopen call will not release the connection
689 back into the pool once a response is received (but will release if
690 you read the entire contents of the response such as when
691 `preload_content=True`). This is useful if you're not preloading
692 the response's content immediately. You will need to call
693 ``r.release_conn()`` on the response ``r`` to return the connection
694 back into the pool. If None, it takes the value of ``preload_content``
695 which defaults to ``True``.
696
697 :param bool chunked:
698 If True, urllib3 will send the body using chunked transfer
699 encoding. Otherwise, urllib3 will send the body using the standard
700 content-length form. Defaults to False.
701
702 :param int body_pos:
703 Position to seek to in file-like body in the event of a retry or
704 redirect. Typically this won't need to be set because urllib3 will
705 auto-populate the value when needed.
706 """
707 parsed_url = parse_url(url)
708 destination_scheme = parsed_url.scheme
709
710 if headers is None:
711 headers = self.headers
712
713 if not isinstance(retries, Retry):
714 retries = Retry.from_int(retries, redirect=redirect, default=self.retries)
715
716 if release_conn is None:
717 release_conn = preload_content
718
719 # Check host
720 if assert_same_host and not self.is_same_host(url):
721 raise HostChangedError(self, url, retries)
722
723 # Ensure that the URL we're connecting to is properly encoded
724 if url.startswith("/"):
725 url = to_str(_encode_target(url))
726 else:
727 url = to_str(parsed_url.url)
728
729 conn = None
730
731 # Track whether `conn` needs to be released before
732 # returning/raising/recursing. Update this variable if necessary, and
733 # leave `release_conn` constant throughout the function. That way, if
734 # the function recurses, the original value of `release_conn` will be
735 # passed down into the recursive call, and its value will be respected.
736 #
737 # See issue #651 [1] for details.
738 #
739 # [1] <https://github.com/urllib3/urllib3/issues/651>
740 release_this_conn = release_conn
741
742 http_tunnel_required = connection_requires_http_tunnel(
743 self.proxy, self.proxy_config, destination_scheme
744 )
745
746 # Merge the proxy headers. Only done when not using HTTP CONNECT. We
747 # have to copy the headers dict so we can safely change it without those
748 # changes being reflected in anyone else's copy.
749 if not http_tunnel_required:
750 headers = headers.copy() # type: ignore[attr-defined]
751 headers.update(self.proxy_headers) # type: ignore[union-attr]
752
753 # Must keep the exception bound to a separate variable or else Python 3
754 # complains about UnboundLocalError.
755 err = None
756
757 # Keep track of whether we cleanly exited the except block. This
758 # ensures we do proper cleanup in finally.
759 clean_exit = False
760
761 # Rewind body position, if needed. Record current position
762 # for future rewinds in the event of a redirect/retry.
763 body_pos = set_file_position(body, body_pos)
764
765 try:
766 # Request a connection from the queue.
767 timeout_obj = self._get_timeout(timeout)
768 conn = self._get_conn(timeout=pool_timeout)
769
770 conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]
771
772 # Is this a closed/new connection that requires CONNECT tunnelling?
773 if self.proxy is not None and http_tunnel_required and conn.is_closed:
774 try:
775 self._prepare_proxy(conn)
776 except (BaseSSLError, OSError, SocketTimeout) as e:
777 self._raise_timeout(
778 err=e, url=self.proxy.url, timeout_value=conn.timeout
779 )
780 raise
781
782 # If we're going to release the connection in ``finally:``, then
783 # the response doesn't need to know about the connection. Otherwise
784 # it will also try to release it and we'll have a double-release
785 # mess.
786 response_conn = conn if not release_conn else None
787
788 # Make the request on the HTTPConnection object
789 response = self._make_request(
790 conn,
791 method,
792 url,
793 timeout=timeout_obj,
794 body=body,
795 headers=headers,
796 chunked=chunked,
797 retries=retries,
798 response_conn=response_conn,
799 preload_content=preload_content,
800 decode_content=decode_content,
801 **response_kw,
802 )
803
804 # Everything went great!
805 clean_exit = True
806
807 except EmptyPoolError:
808 # Didn't get a connection from the pool, no need to clean up
809 clean_exit = True
810 release_this_conn = False
811 raise
812
813 except (
814 TimeoutError,
815 HTTPException,
816 OSError,
817 ProtocolError,
818 BaseSSLError,
819 SSLError,
820 CertificateError,
821 ProxyError,
822 ) as e:
823 # Discard the connection for these exceptions. It will be
824 # replaced during the next _get_conn() call.
825 clean_exit = False
826 new_e: Exception = e
827 if isinstance(e, (BaseSSLError, CertificateError)):
828 new_e = SSLError(e)
829 if isinstance(
830 new_e,
831 (
832 OSError,
833 NewConnectionError,
834 TimeoutError,
835 SSLError,
836 HTTPException,
837 ),
838 ) and (conn and conn.proxy and not conn.has_connected_to_proxy):
839 new_e = _wrap_proxy_error(new_e, conn.proxy.scheme)
840 elif isinstance(new_e, (OSError, HTTPException)):
841 new_e = ProtocolError("Connection aborted.", new_e)
842
843 retries = retries.increment(
844 method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2]
845 )
846 retries.sleep()
847
848 # Keep track of the error for the retry warning.
849 err = e
850
851 finally:
852 if not clean_exit:
853 # We hit some kind of exception, handled or otherwise. We need
854 # to throw the connection away unless explicitly told not to.
855 # Close the connection, set the variable to None, and make sure
856 # we put the None back in the pool to avoid leaking it.
857 if conn:
858 conn.close()
859 conn = None
860 release_this_conn = True
861
862 if release_this_conn:
863 # Put the connection back to be reused. If the connection is
864 # expired then it will be None, which will get replaced with a
865 # fresh connection during _get_conn.
866 self._put_conn(conn)
867
868 if not conn:
869 # Try again
870 log.warning(
871 "Retrying (%r) after connection broken by '%r': %s", retries, err, url
872 )
873 return self.urlopen(
874 method,
875 url,
876 body,
877 headers,
878 retries,
879 redirect,
880 assert_same_host,
881 timeout=timeout,
882 pool_timeout=pool_timeout,
883 release_conn=release_conn,
884 chunked=chunked,
885 body_pos=body_pos,
886 preload_content=preload_content,
887 decode_content=decode_content,
888 **response_kw,
889 )
890
891 # Handle redirect?
892 redirect_location = redirect and response.get_redirect_location()
893 if redirect_location:
894 if response.status == 303:
895 # Change the method according to RFC 9110, Section 15.4.4.
896 method = "GET"
897 # And lose the body not to transfer anything sensitive.
898 body = None
899 headers = HTTPHeaderDict(headers)._prepare_for_method_change()
900
901 try:
902 retries = retries.increment(method, url, response=response, _pool=self)
903 except MaxRetryError:
904 if retries.raise_on_redirect:
905 response.drain_conn()
906 raise
907 return response
908
909 response.drain_conn()
910 retries.sleep_for_retry(response)
911 log.debug("Redirecting %s -> %s", url, redirect_location)
912 return self.urlopen(
913 method,
914 redirect_location,
915 body,
916 headers,
917 retries=retries,
918 redirect=redirect,
919 assert_same_host=assert_same_host,
920 timeout=timeout,
921 pool_timeout=pool_timeout,
922 release_conn=release_conn,
923 chunked=chunked,
924 body_pos=body_pos,
925 preload_content=preload_content,
926 decode_content=decode_content,
927 **response_kw,
928 )
929
930 # Check if we should retry the HTTP response.
931 has_retry_after = bool(response.headers.get("Retry-After"))
932 if retries.is_retry(method, response.status, has_retry_after):
933 try:
934 retries = retries.increment(method, url, response=response, _pool=self)
935 except MaxRetryError:
936 if retries.raise_on_status:
937 response.drain_conn()
938 raise
939 return response
940
941 response.drain_conn()
942 retries.sleep(response)
943 log.debug("Retry: %s", url)
944 return self.urlopen(
945 method,
946 url,
947 body,
948 headers,
949 retries=retries,
950 redirect=redirect,
951 assert_same_host=assert_same_host,
952 timeout=timeout,
953 pool_timeout=pool_timeout,
954 release_conn=release_conn,
955 chunked=chunked,
956 body_pos=body_pos,
957 preload_content=preload_content,
958 decode_content=decode_content,
959 **response_kw,
960 )
961
962 return response
963
964
965 class HTTPSConnectionPool(HTTPConnectionPool):
966 """
967 Same as :class:`.HTTPConnectionPool`, but HTTPS.
968
969 :class:`.HTTPSConnection` uses one of ``assert_fingerprint``,
970 ``assert_hostname`` and ``host`` in this order to verify connections.
971 If ``assert_hostname`` is False, no verification is done.
972
973 The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``,
974 ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl`
975 is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade
976 the connection socket into an SSL socket.
977 """
978
979 scheme = "https"
980 ConnectionCls: type[BaseHTTPSConnection] = HTTPSConnection
981
982 def __init__(
983 self,
984 host: str,
985 port: int | None = None,
986 timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT,
987 maxsize: int = 1,
988 block: bool = False,
989 headers: typing.Mapping[str, str] | None = None,
990 retries: Retry | bool | int | None = None,
991 _proxy: Url | None = None,
992 _proxy_headers: typing.Mapping[str, str] | None = None,
993 key_file: str | None = None,
994 cert_file: str | None = None,
995 cert_reqs: int | str | None = None,
996 key_password: str | None = None,
997 ca_certs: str | None = None,
998 ssl_version: int | str | None = None,
999 ssl_minimum_version: ssl.TLSVersion | None = None,
1000 ssl_maximum_version: ssl.TLSVersion | None = None,
1001 assert_hostname: str | typing.Literal[False] | None = None,
1002 assert_fingerprint: str | None = None,
1003 ca_cert_dir: str | None = None,
1004 **conn_kw: typing.Any,
1005 ) -> None:
1006 super().__init__(
1007 host,
1008 port,
1009 timeout,
1010 maxsize,
1011 block,
1012 headers,
1013 retries,
1014 _proxy,
1015 _proxy_headers,
1016 **conn_kw,
1017 )
1018
1019 self.key_file = key_file
1020 self.cert_file = cert_file
1021 self.cert_reqs = cert_reqs
1022 self.key_password = key_password
1023 self.ca_certs = ca_certs
1024 self.ca_cert_dir = ca_cert_dir
1025 self.ssl_version = ssl_version
1026 self.ssl_minimum_version = ssl_minimum_version
1027 self.ssl_maximum_version = ssl_maximum_version
1028 self.assert_hostname = assert_hostname
1029 self.assert_fingerprint = assert_fingerprint
1030
1031 def _prepare_proxy(self, conn: HTTPSConnection) -> None: # type: ignore[override]
1032 """Establishes a tunnel connection through HTTP CONNECT."""
1033 if self.proxy and self.proxy.scheme == "https":
1034 tunnel_scheme = "https"
1035 else:
1036 tunnel_scheme = "http"
1037
1038 conn.set_tunnel(
1039 scheme=tunnel_scheme,
1040 host=self._tunnel_host,
1041 port=self.port,
1042 headers=self.proxy_headers,
1043 )
1044 conn.connect()
1045
1046 def _new_conn(self) -> BaseHTTPSConnection:
1047 """
1048 Return a fresh :class:`urllib3.connection.HTTPConnection`.
1049 """
1050 self.num_connections += 1
1051 log.debug(
1052 "Starting new HTTPS connection (%d): %s:%s",
1053 self.num_connections,
1054 self.host,
1055 self.port or "443",
1056 )
1057
1058 if not self.ConnectionCls or self.ConnectionCls is DummyConnection: # type: ignore[comparison-overlap]
1059 raise ImportError(
1060 "Can't connect to HTTPS URL because the SSL module is not available."
1061 )
1062
1063 actual_host: str = self.host
1064 actual_port = self.port
1065 if self.proxy is not None and self.proxy.host is not None:
1066 actual_host = self.proxy.host
1067 actual_port = self.proxy.port
1068
1069 return self.ConnectionCls(
1070 host=actual_host,
1071 port=actual_port,
1072 timeout=self.timeout.connect_timeout,
1073 cert_file=self.cert_file,
1074 key_file=self.key_file,
1075 key_password=self.key_password,
1076 cert_reqs=self.cert_reqs,
1077 ca_certs=self.ca_certs,
1078 ca_cert_dir=self.ca_cert_dir,
1079 assert_hostname=self.assert_hostname,
1080 assert_fingerprint=self.assert_fingerprint,
1081 ssl_version=self.ssl_version,
1082 ssl_minimum_version=self.ssl_minimum_version,
1083 ssl_maximum_version=self.ssl_maximum_version,
1084 **self.conn_kw,
1085 )
1086
1087 def _validate_conn(self, conn: BaseHTTPConnection) -> None:
1088 """
1089 Called right before a request is made, after the socket is created.
1090 """
1091 super()._validate_conn(conn)
1092
1093 # Force connect early to allow us to validate the connection.
1094 if conn.is_closed:
1095 conn.connect()
1096
1097 # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791
1098 if not conn.is_verified and not conn.proxy_is_verified:
1099 warnings.warn(
1100 (
1101 f"Unverified HTTPS request is being made to host '{conn.host}'. "
1102 "Adding certificate verification is strongly advised. See: "
1103 "https://urllib3.readthedocs.io/en/latest/advanced-usage.html"
1104 "#tls-warnings"
1105 ),
1106 InsecureRequestWarning,
1107 )
1108
1109
1110 def connection_from_url(url: str, **kw: typing.Any) -> HTTPConnectionPool:
1111 """
1112 Given a url, return an :class:`.ConnectionPool` instance of its host.
1113
1114 This is a shortcut for not having to parse out the scheme, host, and port
1115 of the url before creating an :class:`.ConnectionPool` instance.
1116
1117 :param url:
1118 Absolute URL string that must include the scheme. Port is optional.
1119
1120 :param \\**kw:
1121 Passes additional parameters to the constructor of the appropriate
1122 :class:`.ConnectionPool`. Useful for specifying things like
1123 timeout, maxsize, headers, etc.
1124
1125 Example::
1126
1127 >>> conn = connection_from_url('http://google.com/')
1128 >>> r = conn.request('GET', '/')
1129 """
1130 scheme, _, host, port, *_ = parse_url(url)
1131 scheme = scheme or "http"
1132 port = port or port_by_scheme.get(scheme, 80)
1133 if scheme == "https":
1134 return HTTPSConnectionPool(host, port=port, **kw) # type: ignore[arg-type]
1135 else:
1136 return HTTPConnectionPool(host, port=port, **kw) # type: ignore[arg-type]
1137
1138
1139 @typing.overload
1140 def _normalize_host(host: None, scheme: str | None) -> None:
1141 ...
1142
1143
1144 @typing.overload
1145 def _normalize_host(host: str, scheme: str | None) -> str:
1146 ...
1147
1148
1149 def _normalize_host(host: str | None, scheme: str | None) -> str | None:
1150 """
1151 Normalize hosts for comparisons and use with sockets.
1152 """
1153
1154 host = normalize_host(host, scheme)
1155
1156 # httplib doesn't like it when we include brackets in IPv6 addresses
1157 # Specifically, if we include brackets but also pass the port then
1158 # httplib crazily doubles up the square brackets on the Host header.
1159 # Instead, we need to make sure we never pass ``None`` as the port.
1160 # However, for backward compatibility reasons we can't actually
1161 # *assert* that. See http://bugs.python.org/issue28539
1162 if host and host.startswith("[") and host.endswith("]"):
1163 host = host[1:-1]
1164 return host
1165
1166
1167 def _url_from_pool(
1168 pool: HTTPConnectionPool | HTTPSConnectionPool, path: str | None = None
1169 ) -> str:
1170 """Returns the URL from a given connection pool. This is mainly used for testing and logging."""
1171 return Url(scheme=pool.scheme, host=pool.host, port=pool.port, path=path).url
1172
1173
1174 def _close_pool_connections(pool: queue.LifoQueue[typing.Any]) -> None:
1175 """Drains a queue of connections and closes each one."""
1176 try:
1177 while True:
1178 conn = pool.get(block=False)
1179 if conn:
1180 conn.close()
1181 except queue.Empty:
1182 pass # Done.