|
2 | 2 |
|
3 | 3 | from locust.event import EventHook |
4 | 4 |
|
| 5 | +import os |
5 | 6 | import re |
6 | 7 | import sys |
7 | 8 | import time |
|
10 | 11 | from urllib.parse import urlparse, urlunparse |
11 | 12 |
|
12 | 13 | import requests |
| 14 | +from packaging.version import Version |
13 | 15 | from requests import Response |
14 | 16 | from requests.adapters import HTTPAdapter |
15 | 17 | from requests.auth import HTTPBasicAuth |
| 18 | +from requests.compat import basestring |
16 | 19 | from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema, RequestException |
| 20 | +from requests.utils import DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths |
17 | 21 | from urllib3 import PoolManager |
| 22 | +from urllib3.util import create_urllib3_context |
18 | 23 |
|
19 | 24 | from .exception import CatchResponseError, LocustError, ResponseError |
20 | 25 |
|
|
23 | 28 | else: |
24 | 29 | from typing_extensions import override |
25 | 30 |
|
| 31 | +requests_version = Version(requests.__version__).release |
26 | 32 |
|
27 | 33 | if TYPE_CHECKING: |
28 | 34 | from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping |
@@ -60,6 +66,10 @@ class RESTKwargs(RequestKwargs, total=False): |
60 | 66 |
|
61 | 67 | absolute_http_url_regexp = re.compile(r"^https?://", re.IGNORECASE) |
62 | 68 |
|
| 69 | +if requests_version >= (2, 32, 5): |
| 70 | + _preloaded_ssl_context = create_urllib3_context() |
| 71 | + _preloaded_ssl_context.load_verify_locations(extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)) |
| 72 | + |
63 | 73 |
|
64 | 74 | class HttpSession(requests.Session): |
65 | 75 | """ |
@@ -482,6 +492,52 @@ def init_poolmanager(self, *args, **kwargs): |
482 | 492 | if self.poolmanager is None: |
483 | 493 | super().init_poolmanager(*args, **kwargs) |
484 | 494 |
|
| 495 | + # In python requests version 2.32.5 they reverted |
| 496 | + # https://github.com/psf/requests/pull/6667 |
| 497 | + # Without this change the root CA certificates are loaded on every request |
| 498 | + # We re-implement this change to increase the performance |
| 499 | + def cert_verify(self, conn, url, verify, cert): |
| 500 | + if requests_version < (2, 32, 5): |
| 501 | + return super().cert_verify(conn, url, verify, cert) |
| 502 | + |
| 503 | + if url.lower().startswith("https") and verify: |
| 504 | + conn.cert_reqs = "CERT_REQUIRED" |
| 505 | + |
| 506 | + if verify is not True: |
| 507 | + cert_loc = verify |
| 508 | + |
| 509 | + if not os.path.exists(cert_loc): |
| 510 | + raise OSError(f"Could not find a suitable TLS CA certificate bundle, invalid path: {cert_loc}") |
| 511 | + |
| 512 | + if not os.path.isdir(cert_loc): |
| 513 | + conn.ca_certs = cert_loc |
| 514 | + else: |
| 515 | + conn.ca_cert_dir = cert_loc |
| 516 | + else: |
| 517 | + conn.cert_reqs = "CERT_NONE" |
| 518 | + conn.ca_certs = None |
| 519 | + conn.ca_cert_dir = None |
| 520 | + |
| 521 | + if cert: |
| 522 | + if not isinstance(cert, basestring): |
| 523 | + conn.cert_file = cert[0] |
| 524 | + conn.key_file = cert[1] |
| 525 | + else: |
| 526 | + conn.cert_file = cert |
| 527 | + conn.key_file = None |
| 528 | + if conn.cert_file and not os.path.exists(conn.cert_file): |
| 529 | + raise OSError(f"Could not find the TLS certificate file, invalid path: {conn.cert_file}") |
| 530 | + if conn.key_file and not os.path.exists(conn.key_file): |
| 531 | + raise OSError(f"Could not find the TLS key file, invalid path: {conn.key_file}") |
| 532 | + |
| 533 | + def build_connection_pool_key_attributes(self, request, verify, cert=None): |
| 534 | + host_params, pool_kwargs = super().build_connection_pool_key_attributes(request, verify, cert) |
| 535 | + |
| 536 | + if requests_version >= (2, 32, 5) and verify is True: |
| 537 | + pool_kwargs["ssl_context"] = _preloaded_ssl_context |
| 538 | + |
| 539 | + return host_params, pool_kwargs |
| 540 | + |
485 | 541 |
|
486 | 542 | # Monkey patch Response class to give some guidance |
487 | 543 | def _missing_catch_response_True(self, *_args, **_kwargs): |
|
0 commit comments