jpayne@69
|
1 """
|
jpayne@69
|
2 requests.adapters
|
jpayne@69
|
3 ~~~~~~~~~~~~~~~~~
|
jpayne@69
|
4
|
jpayne@69
|
5 This module contains the transport adapters that Requests uses to define
|
jpayne@69
|
6 and maintain connections.
|
jpayne@69
|
7 """
|
jpayne@69
|
8
|
jpayne@69
|
9 import os.path
|
jpayne@69
|
10 import socket # noqa: F401
|
jpayne@69
|
11 import typing
|
jpayne@69
|
12 import warnings
|
jpayne@69
|
13
|
jpayne@69
|
14 from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
|
jpayne@69
|
15 from urllib3.exceptions import HTTPError as _HTTPError
|
jpayne@69
|
16 from urllib3.exceptions import InvalidHeader as _InvalidHeader
|
jpayne@69
|
17 from urllib3.exceptions import (
|
jpayne@69
|
18 LocationValueError,
|
jpayne@69
|
19 MaxRetryError,
|
jpayne@69
|
20 NewConnectionError,
|
jpayne@69
|
21 ProtocolError,
|
jpayne@69
|
22 )
|
jpayne@69
|
23 from urllib3.exceptions import ProxyError as _ProxyError
|
jpayne@69
|
24 from urllib3.exceptions import ReadTimeoutError, ResponseError
|
jpayne@69
|
25 from urllib3.exceptions import SSLError as _SSLError
|
jpayne@69
|
26 from urllib3.poolmanager import PoolManager, proxy_from_url
|
jpayne@69
|
27 from urllib3.util import Timeout as TimeoutSauce
|
jpayne@69
|
28 from urllib3.util import parse_url
|
jpayne@69
|
29 from urllib3.util.retry import Retry
|
jpayne@69
|
30 from urllib3.util.ssl_ import create_urllib3_context
|
jpayne@69
|
31
|
jpayne@69
|
32 from .auth import _basic_auth_str
|
jpayne@69
|
33 from .compat import basestring, urlparse
|
jpayne@69
|
34 from .cookies import extract_cookies_to_jar
|
jpayne@69
|
35 from .exceptions import (
|
jpayne@69
|
36 ConnectionError,
|
jpayne@69
|
37 ConnectTimeout,
|
jpayne@69
|
38 InvalidHeader,
|
jpayne@69
|
39 InvalidProxyURL,
|
jpayne@69
|
40 InvalidSchema,
|
jpayne@69
|
41 InvalidURL,
|
jpayne@69
|
42 ProxyError,
|
jpayne@69
|
43 ReadTimeout,
|
jpayne@69
|
44 RetryError,
|
jpayne@69
|
45 SSLError,
|
jpayne@69
|
46 )
|
jpayne@69
|
47 from .models import Response
|
jpayne@69
|
48 from .structures import CaseInsensitiveDict
|
jpayne@69
|
49 from .utils import (
|
jpayne@69
|
50 DEFAULT_CA_BUNDLE_PATH,
|
jpayne@69
|
51 extract_zipped_paths,
|
jpayne@69
|
52 get_auth_from_url,
|
jpayne@69
|
53 get_encoding_from_headers,
|
jpayne@69
|
54 prepend_scheme_if_needed,
|
jpayne@69
|
55 select_proxy,
|
jpayne@69
|
56 urldefragauth,
|
jpayne@69
|
57 )
|
jpayne@69
|
58
|
jpayne@69
|
59 try:
|
jpayne@69
|
60 from urllib3.contrib.socks import SOCKSProxyManager
|
jpayne@69
|
61 except ImportError:
|
jpayne@69
|
62
|
jpayne@69
|
63 def SOCKSProxyManager(*args, **kwargs):
|
jpayne@69
|
64 raise InvalidSchema("Missing dependencies for SOCKS support.")
|
jpayne@69
|
65
|
jpayne@69
|
66
|
jpayne@69
|
67 if typing.TYPE_CHECKING:
|
jpayne@69
|
68 from .models import PreparedRequest
|
jpayne@69
|
69
|
jpayne@69
|
70
|
jpayne@69
|
71 DEFAULT_POOLBLOCK = False
|
jpayne@69
|
72 DEFAULT_POOLSIZE = 10
|
jpayne@69
|
73 DEFAULT_RETRIES = 0
|
jpayne@69
|
74 DEFAULT_POOL_TIMEOUT = None
|
jpayne@69
|
75
|
jpayne@69
|
76
|
jpayne@69
|
77 try:
|
jpayne@69
|
78 import ssl # noqa: F401
|
jpayne@69
|
79
|
jpayne@69
|
80 _preloaded_ssl_context = create_urllib3_context()
|
jpayne@69
|
81 _preloaded_ssl_context.load_verify_locations(
|
jpayne@69
|
82 extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
|
jpayne@69
|
83 )
|
jpayne@69
|
84 except ImportError:
|
jpayne@69
|
85 # Bypass default SSLContext creation when Python
|
jpayne@69
|
86 # interpreter isn't built with the ssl module.
|
jpayne@69
|
87 _preloaded_ssl_context = None
|
jpayne@69
|
88
|
jpayne@69
|
89
|
jpayne@69
|
90 def _urllib3_request_context(
|
jpayne@69
|
91 request: "PreparedRequest",
|
jpayne@69
|
92 verify: "bool | str | None",
|
jpayne@69
|
93 client_cert: "typing.Tuple[str, str] | str | None",
|
jpayne@69
|
94 poolmanager: "PoolManager",
|
jpayne@69
|
95 ) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])":
|
jpayne@69
|
96 host_params = {}
|
jpayne@69
|
97 pool_kwargs = {}
|
jpayne@69
|
98 parsed_request_url = urlparse(request.url)
|
jpayne@69
|
99 scheme = parsed_request_url.scheme.lower()
|
jpayne@69
|
100 port = parsed_request_url.port
|
jpayne@69
|
101
|
jpayne@69
|
102 # Determine if we have and should use our default SSLContext
|
jpayne@69
|
103 # to optimize performance on standard requests.
|
jpayne@69
|
104 poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {})
|
jpayne@69
|
105 has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context")
|
jpayne@69
|
106 should_use_default_ssl_context = (
|
jpayne@69
|
107 _preloaded_ssl_context is not None and not has_poolmanager_ssl_context
|
jpayne@69
|
108 )
|
jpayne@69
|
109
|
jpayne@69
|
110 cert_reqs = "CERT_REQUIRED"
|
jpayne@69
|
111 if verify is False:
|
jpayne@69
|
112 cert_reqs = "CERT_NONE"
|
jpayne@69
|
113 elif verify is True and should_use_default_ssl_context:
|
jpayne@69
|
114 pool_kwargs["ssl_context"] = _preloaded_ssl_context
|
jpayne@69
|
115 elif isinstance(verify, str):
|
jpayne@69
|
116 if not os.path.isdir(verify):
|
jpayne@69
|
117 pool_kwargs["ca_certs"] = verify
|
jpayne@69
|
118 else:
|
jpayne@69
|
119 pool_kwargs["ca_cert_dir"] = verify
|
jpayne@69
|
120 pool_kwargs["cert_reqs"] = cert_reqs
|
jpayne@69
|
121 if client_cert is not None:
|
jpayne@69
|
122 if isinstance(client_cert, tuple) and len(client_cert) == 2:
|
jpayne@69
|
123 pool_kwargs["cert_file"] = client_cert[0]
|
jpayne@69
|
124 pool_kwargs["key_file"] = client_cert[1]
|
jpayne@69
|
125 else:
|
jpayne@69
|
126 # According to our docs, we allow users to specify just the client
|
jpayne@69
|
127 # cert path
|
jpayne@69
|
128 pool_kwargs["cert_file"] = client_cert
|
jpayne@69
|
129 host_params = {
|
jpayne@69
|
130 "scheme": scheme,
|
jpayne@69
|
131 "host": parsed_request_url.hostname,
|
jpayne@69
|
132 "port": port,
|
jpayne@69
|
133 }
|
jpayne@69
|
134 return host_params, pool_kwargs
|
jpayne@69
|
135
|
jpayne@69
|
136
|
jpayne@69
|
137 class BaseAdapter:
|
jpayne@69
|
138 """The Base Transport Adapter"""
|
jpayne@69
|
139
|
jpayne@69
|
140 def __init__(self):
|
jpayne@69
|
141 super().__init__()
|
jpayne@69
|
142
|
jpayne@69
|
143 def send(
|
jpayne@69
|
144 self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
|
jpayne@69
|
145 ):
|
jpayne@69
|
146 """Sends PreparedRequest object. Returns Response object.
|
jpayne@69
|
147
|
jpayne@69
|
148 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
|
jpayne@69
|
149 :param stream: (optional) Whether to stream the request content.
|
jpayne@69
|
150 :param timeout: (optional) How long to wait for the server to send
|
jpayne@69
|
151 data before giving up, as a float, or a :ref:`(connect timeout,
|
jpayne@69
|
152 read timeout) <timeouts>` tuple.
|
jpayne@69
|
153 :type timeout: float or tuple
|
jpayne@69
|
154 :param verify: (optional) Either a boolean, in which case it controls whether we verify
|
jpayne@69
|
155 the server's TLS certificate, or a string, in which case it must be a path
|
jpayne@69
|
156 to a CA bundle to use
|
jpayne@69
|
157 :param cert: (optional) Any user-provided SSL certificate to be trusted.
|
jpayne@69
|
158 :param proxies: (optional) The proxies dictionary to apply to the request.
|
jpayne@69
|
159 """
|
jpayne@69
|
160 raise NotImplementedError
|
jpayne@69
|
161
|
jpayne@69
|
162 def close(self):
|
jpayne@69
|
163 """Cleans up adapter specific items."""
|
jpayne@69
|
164 raise NotImplementedError
|
jpayne@69
|
165
|
jpayne@69
|
166
|
jpayne@69
|
167 class HTTPAdapter(BaseAdapter):
|
jpayne@69
|
168 """The built-in HTTP Adapter for urllib3.
|
jpayne@69
|
169
|
jpayne@69
|
170 Provides a general-case interface for Requests sessions to contact HTTP and
|
jpayne@69
|
171 HTTPS urls by implementing the Transport Adapter interface. This class will
|
jpayne@69
|
172 usually be created by the :class:`Session <Session>` class under the
|
jpayne@69
|
173 covers.
|
jpayne@69
|
174
|
jpayne@69
|
175 :param pool_connections: The number of urllib3 connection pools to cache.
|
jpayne@69
|
176 :param pool_maxsize: The maximum number of connections to save in the pool.
|
jpayne@69
|
177 :param max_retries: The maximum number of retries each connection
|
jpayne@69
|
178 should attempt. Note, this applies only to failed DNS lookups, socket
|
jpayne@69
|
179 connections and connection timeouts, never to requests where data has
|
jpayne@69
|
180 made it to the server. By default, Requests does not retry failed
|
jpayne@69
|
181 connections. If you need granular control over the conditions under
|
jpayne@69
|
182 which we retry a request, import urllib3's ``Retry`` class and pass
|
jpayne@69
|
183 that instead.
|
jpayne@69
|
184 :param pool_block: Whether the connection pool should block for connections.
|
jpayne@69
|
185
|
jpayne@69
|
186 Usage::
|
jpayne@69
|
187
|
jpayne@69
|
188 >>> import requests
|
jpayne@69
|
189 >>> s = requests.Session()
|
jpayne@69
|
190 >>> a = requests.adapters.HTTPAdapter(max_retries=3)
|
jpayne@69
|
191 >>> s.mount('http://', a)
|
jpayne@69
|
192 """
|
jpayne@69
|
193
|
jpayne@69
|
194 __attrs__ = [
|
jpayne@69
|
195 "max_retries",
|
jpayne@69
|
196 "config",
|
jpayne@69
|
197 "_pool_connections",
|
jpayne@69
|
198 "_pool_maxsize",
|
jpayne@69
|
199 "_pool_block",
|
jpayne@69
|
200 ]
|
jpayne@69
|
201
|
jpayne@69
|
202 def __init__(
|
jpayne@69
|
203 self,
|
jpayne@69
|
204 pool_connections=DEFAULT_POOLSIZE,
|
jpayne@69
|
205 pool_maxsize=DEFAULT_POOLSIZE,
|
jpayne@69
|
206 max_retries=DEFAULT_RETRIES,
|
jpayne@69
|
207 pool_block=DEFAULT_POOLBLOCK,
|
jpayne@69
|
208 ):
|
jpayne@69
|
209 if max_retries == DEFAULT_RETRIES:
|
jpayne@69
|
210 self.max_retries = Retry(0, read=False)
|
jpayne@69
|
211 else:
|
jpayne@69
|
212 self.max_retries = Retry.from_int(max_retries)
|
jpayne@69
|
213 self.config = {}
|
jpayne@69
|
214 self.proxy_manager = {}
|
jpayne@69
|
215
|
jpayne@69
|
216 super().__init__()
|
jpayne@69
|
217
|
jpayne@69
|
218 self._pool_connections = pool_connections
|
jpayne@69
|
219 self._pool_maxsize = pool_maxsize
|
jpayne@69
|
220 self._pool_block = pool_block
|
jpayne@69
|
221
|
jpayne@69
|
222 self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
|
jpayne@69
|
223
|
jpayne@69
|
224 def __getstate__(self):
|
jpayne@69
|
225 return {attr: getattr(self, attr, None) for attr in self.__attrs__}
|
jpayne@69
|
226
|
jpayne@69
|
227 def __setstate__(self, state):
|
jpayne@69
|
228 # Can't handle by adding 'proxy_manager' to self.__attrs__ because
|
jpayne@69
|
229 # self.poolmanager uses a lambda function, which isn't pickleable.
|
jpayne@69
|
230 self.proxy_manager = {}
|
jpayne@69
|
231 self.config = {}
|
jpayne@69
|
232
|
jpayne@69
|
233 for attr, value in state.items():
|
jpayne@69
|
234 setattr(self, attr, value)
|
jpayne@69
|
235
|
jpayne@69
|
236 self.init_poolmanager(
|
jpayne@69
|
237 self._pool_connections, self._pool_maxsize, block=self._pool_block
|
jpayne@69
|
238 )
|
jpayne@69
|
239
|
jpayne@69
|
240 def init_poolmanager(
|
jpayne@69
|
241 self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs
|
jpayne@69
|
242 ):
|
jpayne@69
|
243 """Initializes a urllib3 PoolManager.
|
jpayne@69
|
244
|
jpayne@69
|
245 This method should not be called from user code, and is only
|
jpayne@69
|
246 exposed for use when subclassing the
|
jpayne@69
|
247 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
248
|
jpayne@69
|
249 :param connections: The number of urllib3 connection pools to cache.
|
jpayne@69
|
250 :param maxsize: The maximum number of connections to save in the pool.
|
jpayne@69
|
251 :param block: Block when no free connections are available.
|
jpayne@69
|
252 :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
|
jpayne@69
|
253 """
|
jpayne@69
|
254 # save these values for pickling
|
jpayne@69
|
255 self._pool_connections = connections
|
jpayne@69
|
256 self._pool_maxsize = maxsize
|
jpayne@69
|
257 self._pool_block = block
|
jpayne@69
|
258
|
jpayne@69
|
259 self.poolmanager = PoolManager(
|
jpayne@69
|
260 num_pools=connections,
|
jpayne@69
|
261 maxsize=maxsize,
|
jpayne@69
|
262 block=block,
|
jpayne@69
|
263 **pool_kwargs,
|
jpayne@69
|
264 )
|
jpayne@69
|
265
|
jpayne@69
|
266 def proxy_manager_for(self, proxy, **proxy_kwargs):
|
jpayne@69
|
267 """Return urllib3 ProxyManager for the given proxy.
|
jpayne@69
|
268
|
jpayne@69
|
269 This method should not be called from user code, and is only
|
jpayne@69
|
270 exposed for use when subclassing the
|
jpayne@69
|
271 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
272
|
jpayne@69
|
273 :param proxy: The proxy to return a urllib3 ProxyManager for.
|
jpayne@69
|
274 :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
|
jpayne@69
|
275 :returns: ProxyManager
|
jpayne@69
|
276 :rtype: urllib3.ProxyManager
|
jpayne@69
|
277 """
|
jpayne@69
|
278 if proxy in self.proxy_manager:
|
jpayne@69
|
279 manager = self.proxy_manager[proxy]
|
jpayne@69
|
280 elif proxy.lower().startswith("socks"):
|
jpayne@69
|
281 username, password = get_auth_from_url(proxy)
|
jpayne@69
|
282 manager = self.proxy_manager[proxy] = SOCKSProxyManager(
|
jpayne@69
|
283 proxy,
|
jpayne@69
|
284 username=username,
|
jpayne@69
|
285 password=password,
|
jpayne@69
|
286 num_pools=self._pool_connections,
|
jpayne@69
|
287 maxsize=self._pool_maxsize,
|
jpayne@69
|
288 block=self._pool_block,
|
jpayne@69
|
289 **proxy_kwargs,
|
jpayne@69
|
290 )
|
jpayne@69
|
291 else:
|
jpayne@69
|
292 proxy_headers = self.proxy_headers(proxy)
|
jpayne@69
|
293 manager = self.proxy_manager[proxy] = proxy_from_url(
|
jpayne@69
|
294 proxy,
|
jpayne@69
|
295 proxy_headers=proxy_headers,
|
jpayne@69
|
296 num_pools=self._pool_connections,
|
jpayne@69
|
297 maxsize=self._pool_maxsize,
|
jpayne@69
|
298 block=self._pool_block,
|
jpayne@69
|
299 **proxy_kwargs,
|
jpayne@69
|
300 )
|
jpayne@69
|
301
|
jpayne@69
|
302 return manager
|
jpayne@69
|
303
|
jpayne@69
|
304 def cert_verify(self, conn, url, verify, cert):
|
jpayne@69
|
305 """Verify a SSL certificate. This method should not be called from user
|
jpayne@69
|
306 code, and is only exposed for use when subclassing the
|
jpayne@69
|
307 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
308
|
jpayne@69
|
309 :param conn: The urllib3 connection object associated with the cert.
|
jpayne@69
|
310 :param url: The requested URL.
|
jpayne@69
|
311 :param verify: Either a boolean, in which case it controls whether we verify
|
jpayne@69
|
312 the server's TLS certificate, or a string, in which case it must be a path
|
jpayne@69
|
313 to a CA bundle to use
|
jpayne@69
|
314 :param cert: The SSL certificate to verify.
|
jpayne@69
|
315 """
|
jpayne@69
|
316 if url.lower().startswith("https") and verify:
|
jpayne@69
|
317 conn.cert_reqs = "CERT_REQUIRED"
|
jpayne@69
|
318
|
jpayne@69
|
319 # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use.
|
jpayne@69
|
320 # Otherwise, if verify is a boolean, we don't load anything since
|
jpayne@69
|
321 # the connection will be using a context with the default certificates already loaded,
|
jpayne@69
|
322 # and this avoids a call to the slow load_verify_locations()
|
jpayne@69
|
323 if verify is not True:
|
jpayne@69
|
324 # `verify` must be a str with a path then
|
jpayne@69
|
325 cert_loc = verify
|
jpayne@69
|
326
|
jpayne@69
|
327 if not os.path.exists(cert_loc):
|
jpayne@69
|
328 raise OSError(
|
jpayne@69
|
329 f"Could not find a suitable TLS CA certificate bundle, "
|
jpayne@69
|
330 f"invalid path: {cert_loc}"
|
jpayne@69
|
331 )
|
jpayne@69
|
332
|
jpayne@69
|
333 if not os.path.isdir(cert_loc):
|
jpayne@69
|
334 conn.ca_certs = cert_loc
|
jpayne@69
|
335 else:
|
jpayne@69
|
336 conn.ca_cert_dir = cert_loc
|
jpayne@69
|
337 else:
|
jpayne@69
|
338 conn.cert_reqs = "CERT_NONE"
|
jpayne@69
|
339 conn.ca_certs = None
|
jpayne@69
|
340 conn.ca_cert_dir = None
|
jpayne@69
|
341
|
jpayne@69
|
342 if cert:
|
jpayne@69
|
343 if not isinstance(cert, basestring):
|
jpayne@69
|
344 conn.cert_file = cert[0]
|
jpayne@69
|
345 conn.key_file = cert[1]
|
jpayne@69
|
346 else:
|
jpayne@69
|
347 conn.cert_file = cert
|
jpayne@69
|
348 conn.key_file = None
|
jpayne@69
|
349 if conn.cert_file and not os.path.exists(conn.cert_file):
|
jpayne@69
|
350 raise OSError(
|
jpayne@69
|
351 f"Could not find the TLS certificate file, "
|
jpayne@69
|
352 f"invalid path: {conn.cert_file}"
|
jpayne@69
|
353 )
|
jpayne@69
|
354 if conn.key_file and not os.path.exists(conn.key_file):
|
jpayne@69
|
355 raise OSError(
|
jpayne@69
|
356 f"Could not find the TLS key file, invalid path: {conn.key_file}"
|
jpayne@69
|
357 )
|
jpayne@69
|
358
|
jpayne@69
|
359 def build_response(self, req, resp):
|
jpayne@69
|
360 """Builds a :class:`Response <requests.Response>` object from a urllib3
|
jpayne@69
|
361 response. This should not be called from user code, and is only exposed
|
jpayne@69
|
362 for use when subclassing the
|
jpayne@69
|
363 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
|
jpayne@69
|
364
|
jpayne@69
|
365 :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
|
jpayne@69
|
366 :param resp: The urllib3 response object.
|
jpayne@69
|
367 :rtype: requests.Response
|
jpayne@69
|
368 """
|
jpayne@69
|
369 response = Response()
|
jpayne@69
|
370
|
jpayne@69
|
371 # Fallback to None if there's no status_code, for whatever reason.
|
jpayne@69
|
372 response.status_code = getattr(resp, "status", None)
|
jpayne@69
|
373
|
jpayne@69
|
374 # Make headers case-insensitive.
|
jpayne@69
|
375 response.headers = CaseInsensitiveDict(getattr(resp, "headers", {}))
|
jpayne@69
|
376
|
jpayne@69
|
377 # Set encoding.
|
jpayne@69
|
378 response.encoding = get_encoding_from_headers(response.headers)
|
jpayne@69
|
379 response.raw = resp
|
jpayne@69
|
380 response.reason = response.raw.reason
|
jpayne@69
|
381
|
jpayne@69
|
382 if isinstance(req.url, bytes):
|
jpayne@69
|
383 response.url = req.url.decode("utf-8")
|
jpayne@69
|
384 else:
|
jpayne@69
|
385 response.url = req.url
|
jpayne@69
|
386
|
jpayne@69
|
387 # Add new cookies from the server.
|
jpayne@69
|
388 extract_cookies_to_jar(response.cookies, req, resp)
|
jpayne@69
|
389
|
jpayne@69
|
390 # Give the Response some context.
|
jpayne@69
|
391 response.request = req
|
jpayne@69
|
392 response.connection = self
|
jpayne@69
|
393
|
jpayne@69
|
394 return response
|
jpayne@69
|
395
|
jpayne@69
|
396 def build_connection_pool_key_attributes(self, request, verify, cert=None):
|
jpayne@69
|
397 """Build the PoolKey attributes used by urllib3 to return a connection.
|
jpayne@69
|
398
|
jpayne@69
|
399 This looks at the PreparedRequest, the user-specified verify value,
|
jpayne@69
|
400 and the value of the cert parameter to determine what PoolKey values
|
jpayne@69
|
401 to use to select a connection from a given urllib3 Connection Pool.
|
jpayne@69
|
402
|
jpayne@69
|
403 The SSL related pool key arguments are not consistently set. As of
|
jpayne@69
|
404 this writing, use the following to determine what keys may be in that
|
jpayne@69
|
405 dictionary:
|
jpayne@69
|
406
|
jpayne@69
|
407 * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
|
jpayne@69
|
408 default Requests SSL Context
|
jpayne@69
|
409 * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
|
jpayne@69
|
410 ``"cert_reqs"`` will be set
|
jpayne@69
|
411 * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
|
jpayne@69
|
412 ``"ca_certs"`` will be set if the string is not a directory recognized
|
jpayne@69
|
413 by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be
|
jpayne@69
|
414 set.
|
jpayne@69
|
415 * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
|
jpayne@69
|
416 ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
|
jpayne@69
|
417 be present
|
jpayne@69
|
418
|
jpayne@69
|
419 To override these settings, one may subclass this class, call this
|
jpayne@69
|
420 method and use the above logic to change parameters as desired. For
|
jpayne@69
|
421 example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
|
jpayne@69
|
422 must both set ``"ssl_context"`` and based on what else they require,
|
jpayne@69
|
423 alter the other keys to ensure the desired behaviour.
|
jpayne@69
|
424
|
jpayne@69
|
425 :param request:
|
jpayne@69
|
426 The PreparedReqest being sent over the connection.
|
jpayne@69
|
427 :type request:
|
jpayne@69
|
428 :class:`~requests.models.PreparedRequest`
|
jpayne@69
|
429 :param verify:
|
jpayne@69
|
430 Either a boolean, in which case it controls whether
|
jpayne@69
|
431 we verify the server's TLS certificate, or a string, in which case it
|
jpayne@69
|
432 must be a path to a CA bundle to use.
|
jpayne@69
|
433 :param cert:
|
jpayne@69
|
434 (optional) Any user-provided SSL certificate for client
|
jpayne@69
|
435 authentication (a.k.a., mTLS). This may be a string (i.e., just
|
jpayne@69
|
436 the path to a file which holds both certificate and key) or a
|
jpayne@69
|
437 tuple of length 2 with the certificate file path and key file
|
jpayne@69
|
438 path.
|
jpayne@69
|
439 :returns:
|
jpayne@69
|
440 A tuple of two dictionaries. The first is the "host parameters"
|
jpayne@69
|
441 portion of the Pool Key including scheme, hostname, and port. The
|
jpayne@69
|
442 second is a dictionary of SSLContext related parameters.
|
jpayne@69
|
443 """
|
jpayne@69
|
444 return _urllib3_request_context(request, verify, cert, self.poolmanager)
|
jpayne@69
|
445
|
jpayne@69
|
446 def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
|
jpayne@69
|
447 """Returns a urllib3 connection for the given request and TLS settings.
|
jpayne@69
|
448 This should not be called from user code, and is only exposed for use
|
jpayne@69
|
449 when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
450
|
jpayne@69
|
451 :param request:
|
jpayne@69
|
452 The :class:`PreparedRequest <PreparedRequest>` object to be sent
|
jpayne@69
|
453 over the connection.
|
jpayne@69
|
454 :param verify:
|
jpayne@69
|
455 Either a boolean, in which case it controls whether we verify the
|
jpayne@69
|
456 server's TLS certificate, or a string, in which case it must be a
|
jpayne@69
|
457 path to a CA bundle to use.
|
jpayne@69
|
458 :param proxies:
|
jpayne@69
|
459 (optional) The proxies dictionary to apply to the request.
|
jpayne@69
|
460 :param cert:
|
jpayne@69
|
461 (optional) Any user-provided SSL certificate to be used for client
|
jpayne@69
|
462 authentication (a.k.a., mTLS).
|
jpayne@69
|
463 :rtype:
|
jpayne@69
|
464 urllib3.ConnectionPool
|
jpayne@69
|
465 """
|
jpayne@69
|
466 proxy = select_proxy(request.url, proxies)
|
jpayne@69
|
467 try:
|
jpayne@69
|
468 host_params, pool_kwargs = self.build_connection_pool_key_attributes(
|
jpayne@69
|
469 request,
|
jpayne@69
|
470 verify,
|
jpayne@69
|
471 cert,
|
jpayne@69
|
472 )
|
jpayne@69
|
473 except ValueError as e:
|
jpayne@69
|
474 raise InvalidURL(e, request=request)
|
jpayne@69
|
475 if proxy:
|
jpayne@69
|
476 proxy = prepend_scheme_if_needed(proxy, "http")
|
jpayne@69
|
477 proxy_url = parse_url(proxy)
|
jpayne@69
|
478 if not proxy_url.host:
|
jpayne@69
|
479 raise InvalidProxyURL(
|
jpayne@69
|
480 "Please check proxy URL. It is malformed "
|
jpayne@69
|
481 "and could be missing the host."
|
jpayne@69
|
482 )
|
jpayne@69
|
483 proxy_manager = self.proxy_manager_for(proxy)
|
jpayne@69
|
484 conn = proxy_manager.connection_from_host(
|
jpayne@69
|
485 **host_params, pool_kwargs=pool_kwargs
|
jpayne@69
|
486 )
|
jpayne@69
|
487 else:
|
jpayne@69
|
488 # Only scheme should be lower case
|
jpayne@69
|
489 conn = self.poolmanager.connection_from_host(
|
jpayne@69
|
490 **host_params, pool_kwargs=pool_kwargs
|
jpayne@69
|
491 )
|
jpayne@69
|
492
|
jpayne@69
|
493 return conn
|
jpayne@69
|
494
|
jpayne@69
|
495 def get_connection(self, url, proxies=None):
|
jpayne@69
|
496 """DEPRECATED: Users should move to `get_connection_with_tls_context`
|
jpayne@69
|
497 for all subclasses of HTTPAdapter using Requests>=2.32.2.
|
jpayne@69
|
498
|
jpayne@69
|
499 Returns a urllib3 connection for the given URL. This should not be
|
jpayne@69
|
500 called from user code, and is only exposed for use when subclassing the
|
jpayne@69
|
501 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
502
|
jpayne@69
|
503 :param url: The URL to connect to.
|
jpayne@69
|
504 :param proxies: (optional) A Requests-style dictionary of proxies used on this request.
|
jpayne@69
|
505 :rtype: urllib3.ConnectionPool
|
jpayne@69
|
506 """
|
jpayne@69
|
507 warnings.warn(
|
jpayne@69
|
508 (
|
jpayne@69
|
509 "`get_connection` has been deprecated in favor of "
|
jpayne@69
|
510 "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
|
jpayne@69
|
511 "will need to migrate for Requests>=2.32.2. Please see "
|
jpayne@69
|
512 "https://github.com/psf/requests/pull/6710 for more details."
|
jpayne@69
|
513 ),
|
jpayne@69
|
514 DeprecationWarning,
|
jpayne@69
|
515 )
|
jpayne@69
|
516 proxy = select_proxy(url, proxies)
|
jpayne@69
|
517
|
jpayne@69
|
518 if proxy:
|
jpayne@69
|
519 proxy = prepend_scheme_if_needed(proxy, "http")
|
jpayne@69
|
520 proxy_url = parse_url(proxy)
|
jpayne@69
|
521 if not proxy_url.host:
|
jpayne@69
|
522 raise InvalidProxyURL(
|
jpayne@69
|
523 "Please check proxy URL. It is malformed "
|
jpayne@69
|
524 "and could be missing the host."
|
jpayne@69
|
525 )
|
jpayne@69
|
526 proxy_manager = self.proxy_manager_for(proxy)
|
jpayne@69
|
527 conn = proxy_manager.connection_from_url(url)
|
jpayne@69
|
528 else:
|
jpayne@69
|
529 # Only scheme should be lower case
|
jpayne@69
|
530 parsed = urlparse(url)
|
jpayne@69
|
531 url = parsed.geturl()
|
jpayne@69
|
532 conn = self.poolmanager.connection_from_url(url)
|
jpayne@69
|
533
|
jpayne@69
|
534 return conn
|
jpayne@69
|
535
|
jpayne@69
|
536 def close(self):
|
jpayne@69
|
537 """Disposes of any internal state.
|
jpayne@69
|
538
|
jpayne@69
|
539 Currently, this closes the PoolManager and any active ProxyManager,
|
jpayne@69
|
540 which closes any pooled connections.
|
jpayne@69
|
541 """
|
jpayne@69
|
542 self.poolmanager.clear()
|
jpayne@69
|
543 for proxy in self.proxy_manager.values():
|
jpayne@69
|
544 proxy.clear()
|
jpayne@69
|
545
|
jpayne@69
|
546 def request_url(self, request, proxies):
|
jpayne@69
|
547 """Obtain the url to use when making the final request.
|
jpayne@69
|
548
|
jpayne@69
|
549 If the message is being sent through a HTTP proxy, the full URL has to
|
jpayne@69
|
550 be used. Otherwise, we should only use the path portion of the URL.
|
jpayne@69
|
551
|
jpayne@69
|
552 This should not be called from user code, and is only exposed for use
|
jpayne@69
|
553 when subclassing the
|
jpayne@69
|
554 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
555
|
jpayne@69
|
556 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
|
jpayne@69
|
557 :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
|
jpayne@69
|
558 :rtype: str
|
jpayne@69
|
559 """
|
jpayne@69
|
560 proxy = select_proxy(request.url, proxies)
|
jpayne@69
|
561 scheme = urlparse(request.url).scheme
|
jpayne@69
|
562
|
jpayne@69
|
563 is_proxied_http_request = proxy and scheme != "https"
|
jpayne@69
|
564 using_socks_proxy = False
|
jpayne@69
|
565 if proxy:
|
jpayne@69
|
566 proxy_scheme = urlparse(proxy).scheme.lower()
|
jpayne@69
|
567 using_socks_proxy = proxy_scheme.startswith("socks")
|
jpayne@69
|
568
|
jpayne@69
|
569 url = request.path_url
|
jpayne@69
|
570 if url.startswith("//"): # Don't confuse urllib3
|
jpayne@69
|
571 url = f"/{url.lstrip('/')}"
|
jpayne@69
|
572
|
jpayne@69
|
573 if is_proxied_http_request and not using_socks_proxy:
|
jpayne@69
|
574 url = urldefragauth(request.url)
|
jpayne@69
|
575
|
jpayne@69
|
576 return url
|
jpayne@69
|
577
|
jpayne@69
|
578 def add_headers(self, request, **kwargs):
|
jpayne@69
|
579 """Add any headers needed by the connection. As of v2.0 this does
|
jpayne@69
|
580 nothing by default, but is left for overriding by users that subclass
|
jpayne@69
|
581 the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
582
|
jpayne@69
|
583 This should not be called from user code, and is only exposed for use
|
jpayne@69
|
584 when subclassing the
|
jpayne@69
|
585 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
586
|
jpayne@69
|
587 :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to.
|
jpayne@69
|
588 :param kwargs: The keyword arguments from the call to send().
|
jpayne@69
|
589 """
|
jpayne@69
|
590 pass
|
jpayne@69
|
591
|
jpayne@69
|
592 def proxy_headers(self, proxy):
|
jpayne@69
|
593 """Returns a dictionary of the headers to add to any request sent
|
jpayne@69
|
594 through a proxy. This works with urllib3 magic to ensure that they are
|
jpayne@69
|
595 correctly sent to the proxy, rather than in a tunnelled request if
|
jpayne@69
|
596 CONNECT is being used.
|
jpayne@69
|
597
|
jpayne@69
|
598 This should not be called from user code, and is only exposed for use
|
jpayne@69
|
599 when subclassing the
|
jpayne@69
|
600 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
jpayne@69
|
601
|
jpayne@69
|
602 :param proxy: The url of the proxy being used for this request.
|
jpayne@69
|
603 :rtype: dict
|
jpayne@69
|
604 """
|
jpayne@69
|
605 headers = {}
|
jpayne@69
|
606 username, password = get_auth_from_url(proxy)
|
jpayne@69
|
607
|
jpayne@69
|
608 if username:
|
jpayne@69
|
609 headers["Proxy-Authorization"] = _basic_auth_str(username, password)
|
jpayne@69
|
610
|
jpayne@69
|
611 return headers
|
jpayne@69
|
612
|
jpayne@69
|
613 def send(
|
jpayne@69
|
614 self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
|
jpayne@69
|
615 ):
|
jpayne@69
|
616 """Sends PreparedRequest object. Returns Response object.
|
jpayne@69
|
617
|
jpayne@69
|
618 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
|
jpayne@69
|
619 :param stream: (optional) Whether to stream the request content.
|
jpayne@69
|
620 :param timeout: (optional) How long to wait for the server to send
|
jpayne@69
|
621 data before giving up, as a float, or a :ref:`(connect timeout,
|
jpayne@69
|
622 read timeout) <timeouts>` tuple.
|
jpayne@69
|
623 :type timeout: float or tuple or urllib3 Timeout object
|
jpayne@69
|
624 :param verify: (optional) Either a boolean, in which case it controls whether
|
jpayne@69
|
625 we verify the server's TLS certificate, or a string, in which case it
|
jpayne@69
|
626 must be a path to a CA bundle to use
|
jpayne@69
|
627 :param cert: (optional) Any user-provided SSL certificate to be trusted.
|
jpayne@69
|
628 :param proxies: (optional) The proxies dictionary to apply to the request.
|
jpayne@69
|
629 :rtype: requests.Response
|
jpayne@69
|
630 """
|
jpayne@69
|
631
|
jpayne@69
|
632 try:
|
jpayne@69
|
633 conn = self.get_connection_with_tls_context(
|
jpayne@69
|
634 request, verify, proxies=proxies, cert=cert
|
jpayne@69
|
635 )
|
jpayne@69
|
636 except LocationValueError as e:
|
jpayne@69
|
637 raise InvalidURL(e, request=request)
|
jpayne@69
|
638
|
jpayne@69
|
639 self.cert_verify(conn, request.url, verify, cert)
|
jpayne@69
|
640 url = self.request_url(request, proxies)
|
jpayne@69
|
641 self.add_headers(
|
jpayne@69
|
642 request,
|
jpayne@69
|
643 stream=stream,
|
jpayne@69
|
644 timeout=timeout,
|
jpayne@69
|
645 verify=verify,
|
jpayne@69
|
646 cert=cert,
|
jpayne@69
|
647 proxies=proxies,
|
jpayne@69
|
648 )
|
jpayne@69
|
649
|
jpayne@69
|
650 chunked = not (request.body is None or "Content-Length" in request.headers)
|
jpayne@69
|
651
|
jpayne@69
|
652 if isinstance(timeout, tuple):
|
jpayne@69
|
653 try:
|
jpayne@69
|
654 connect, read = timeout
|
jpayne@69
|
655 timeout = TimeoutSauce(connect=connect, read=read)
|
jpayne@69
|
656 except ValueError:
|
jpayne@69
|
657 raise ValueError(
|
jpayne@69
|
658 f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
|
jpayne@69
|
659 f"or a single float to set both timeouts to the same value."
|
jpayne@69
|
660 )
|
jpayne@69
|
661 elif isinstance(timeout, TimeoutSauce):
|
jpayne@69
|
662 pass
|
jpayne@69
|
663 else:
|
jpayne@69
|
664 timeout = TimeoutSauce(connect=timeout, read=timeout)
|
jpayne@69
|
665
|
jpayne@69
|
666 try:
|
jpayne@69
|
667 resp = conn.urlopen(
|
jpayne@69
|
668 method=request.method,
|
jpayne@69
|
669 url=url,
|
jpayne@69
|
670 body=request.body,
|
jpayne@69
|
671 headers=request.headers,
|
jpayne@69
|
672 redirect=False,
|
jpayne@69
|
673 assert_same_host=False,
|
jpayne@69
|
674 preload_content=False,
|
jpayne@69
|
675 decode_content=False,
|
jpayne@69
|
676 retries=self.max_retries,
|
jpayne@69
|
677 timeout=timeout,
|
jpayne@69
|
678 chunked=chunked,
|
jpayne@69
|
679 )
|
jpayne@69
|
680
|
jpayne@69
|
681 except (ProtocolError, OSError) as err:
|
jpayne@69
|
682 raise ConnectionError(err, request=request)
|
jpayne@69
|
683
|
jpayne@69
|
684 except MaxRetryError as e:
|
jpayne@69
|
685 if isinstance(e.reason, ConnectTimeoutError):
|
jpayne@69
|
686 # TODO: Remove this in 3.0.0: see #2811
|
jpayne@69
|
687 if not isinstance(e.reason, NewConnectionError):
|
jpayne@69
|
688 raise ConnectTimeout(e, request=request)
|
jpayne@69
|
689
|
jpayne@69
|
690 if isinstance(e.reason, ResponseError):
|
jpayne@69
|
691 raise RetryError(e, request=request)
|
jpayne@69
|
692
|
jpayne@69
|
693 if isinstance(e.reason, _ProxyError):
|
jpayne@69
|
694 raise ProxyError(e, request=request)
|
jpayne@69
|
695
|
jpayne@69
|
696 if isinstance(e.reason, _SSLError):
|
jpayne@69
|
697 # This branch is for urllib3 v1.22 and later.
|
jpayne@69
|
698 raise SSLError(e, request=request)
|
jpayne@69
|
699
|
jpayne@69
|
700 raise ConnectionError(e, request=request)
|
jpayne@69
|
701
|
jpayne@69
|
702 except ClosedPoolError as e:
|
jpayne@69
|
703 raise ConnectionError(e, request=request)
|
jpayne@69
|
704
|
jpayne@69
|
705 except _ProxyError as e:
|
jpayne@69
|
706 raise ProxyError(e)
|
jpayne@69
|
707
|
jpayne@69
|
708 except (_SSLError, _HTTPError) as e:
|
jpayne@69
|
709 if isinstance(e, _SSLError):
|
jpayne@69
|
710 # This branch is for urllib3 versions earlier than v1.22
|
jpayne@69
|
711 raise SSLError(e, request=request)
|
jpayne@69
|
712 elif isinstance(e, ReadTimeoutError):
|
jpayne@69
|
713 raise ReadTimeout(e, request=request)
|
jpayne@69
|
714 elif isinstance(e, _InvalidHeader):
|
jpayne@69
|
715 raise InvalidHeader(e, request=request)
|
jpayne@69
|
716 else:
|
jpayne@69
|
717 raise
|
jpayne@69
|
718
|
jpayne@69
|
719 return self.build_response(request, resp)
|