Skip to content

Commit f7bd549

Browse files
authored
Merge pull request #3316 from amadeuppereira/fix-ssl-context-performance-issue
Support requests>=2.32.5, reimplement the fix previously there for only loading ssl certificates once
2 parents eb15516 + 8514a30 commit f7bd549

File tree

4 files changed

+76
-5
lines changed

4 files changed

+76
-5
lines changed

locust/clients.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from locust.event import EventHook
44

5+
import os
56
import re
67
import sys
78
import time
@@ -10,11 +11,15 @@
1011
from urllib.parse import urlparse, urlunparse
1112

1213
import requests
14+
from packaging.version import Version
1315
from requests import Response
1416
from requests.adapters import HTTPAdapter
1517
from requests.auth import HTTPBasicAuth
18+
from requests.compat import basestring
1619
from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema, RequestException
20+
from requests.utils import DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths
1721
from urllib3 import PoolManager
22+
from urllib3.util import create_urllib3_context
1823

1924
from .exception import CatchResponseError, LocustError, ResponseError
2025

@@ -23,6 +28,7 @@
2328
else:
2429
from typing_extensions import override
2530

31+
requests_version = Version(requests.__version__).release
2632

2733
if TYPE_CHECKING:
2834
from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping
@@ -60,6 +66,10 @@ class RESTKwargs(RequestKwargs, total=False):
6066

6167
absolute_http_url_regexp = re.compile(r"^https?://", re.IGNORECASE)
6268

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+
6373

6474
class HttpSession(requests.Session):
6575
"""
@@ -482,6 +492,52 @@ def init_poolmanager(self, *args, **kwargs):
482492
if self.poolmanager is None:
483493
super().init_poolmanager(*args, **kwargs)
484494

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+
485541

486542
# Monkey patch Response class to give some guidance
487543
def _missing_catch_response_True(self, *_args, **_kwargs):

locust/test/test_http.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
import time
66

7+
import urllib3
78
from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema, RequestException
9+
from urllib3.exceptions import SSLError
810

911
from .testcases import WebserverTestCase
1012

13+
urllib3.disable_warnings()
14+
1115

1216
class TestHttpSession(WebserverTestCase):
1317
def get_client(self, base_url=None):
@@ -315,3 +319,14 @@ def on_request(**kw):
315319
self.assertEqual("GET", kwargs["response"].text)
316320
user.client.request("get", "/request_method", context={"user": "bar"}) # override User context
317321
self.assertDictEqual({"user": "bar"}, kwargs["context"])
322+
323+
def test_verify_true_fails_with_bad_cert(self):
324+
s = self.get_client("https://expired.badssl.com")
325+
r = s.get("/", verify=True)
326+
self.assertTrue("exception" in r.request_meta)
327+
self.assertIsInstance(r.request_meta["exception"], SSLError)
328+
329+
def test_verify_false_succeeds_with_bad_cert(self):
330+
s = self.get_client("https://expired.badssl.com")
331+
r = s.get("/", verify=False)
332+
self.assertEqual(r.status_code, 200)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ classifiers = [
3535
dependencies = [
3636
"flask>=2.0.0",
3737
"Werkzeug>=2.0.0",
38-
"requests>=2.32.2,<2.32.5",
38+
"requests>=2.32.2",
3939
"msgpack>=1.0.0",
4040
"pyzmq>=25.0.0",
4141
"geventhttpclient>=2.3.1",

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)