Skip to content

Commit 4839004

Browse files
authored
fix(HTTP2Transport): Only enable HTTP2 when DSN is HTTPS (#3678)
1 parent 336b177 commit 4839004

File tree

2 files changed

+71
-71
lines changed

2 files changed

+71
-71
lines changed

sentry_sdk/transport.py

+42-61
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,7 @@ def __init__(self, options):
215215
) # type: DefaultDict[Tuple[EventDataCategory, str], int]
216216
self._last_client_report_sent = time.time()
217217

218-
self._pool = self._make_pool(
219-
self.parsed_dsn,
220-
http_proxy=options["http_proxy"],
221-
https_proxy=options["https_proxy"],
222-
ca_certs=options["ca_certs"],
223-
cert_file=options["cert_file"],
224-
key_file=options["key_file"],
225-
proxy_headers=options["proxy_headers"],
226-
)
218+
self._pool = self._make_pool()
227219

228220
# Backwards compatibility for deprecated `self.hub_class` attribute
229221
self._hub_cls = sentry_sdk.Hub
@@ -532,8 +524,8 @@ def _serialize_envelope(self, envelope):
532524

533525
return content_encoding, body
534526

535-
def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
536-
# type: (Self, Optional[Any], Optional[Any], Optional[Any]) -> Dict[str, Any]
527+
def _get_pool_options(self):
528+
# type: (Self) -> Dict[str, Any]
537529
raise NotImplementedError()
538530

539531
def _in_no_proxy(self, parsed_dsn):
@@ -547,17 +539,8 @@ def _in_no_proxy(self, parsed_dsn):
547539
return True
548540
return False
549541

550-
def _make_pool(
551-
self,
552-
parsed_dsn, # type: Dsn
553-
http_proxy, # type: Optional[str]
554-
https_proxy, # type: Optional[str]
555-
ca_certs, # type: Optional[Any]
556-
cert_file, # type: Optional[Any]
557-
key_file, # type: Optional[Any]
558-
proxy_headers, # type: Optional[Dict[str, str]]
559-
):
560-
# type: (...) -> Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
542+
def _make_pool(self):
543+
# type: (Self) -> Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
561544
raise NotImplementedError()
562545

563546
def _request(
@@ -631,8 +614,8 @@ class HttpTransport(BaseHttpTransport):
631614
if TYPE_CHECKING:
632615
_pool: Union[PoolManager, ProxyManager]
633616

634-
def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
635-
# type: (Self, Any, Any, Any) -> Dict[str, Any]
617+
def _get_pool_options(self):
618+
# type: (Self) -> Dict[str, Any]
636619

637620
num_pools = self.options.get("_experiments", {}).get("transport_num_pools")
638621
options = {
@@ -658,42 +641,43 @@ def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
658641
options["socket_options"] = socket_options
659642

660643
options["ca_certs"] = (
661-
ca_certs # User-provided bundle from the SDK init
644+
self.options["ca_certs"] # User-provided bundle from the SDK init
662645
or os.environ.get("SSL_CERT_FILE")
663646
or os.environ.get("REQUESTS_CA_BUNDLE")
664647
or certifi.where()
665648
)
666649

667-
options["cert_file"] = cert_file or os.environ.get("CLIENT_CERT_FILE")
668-
options["key_file"] = key_file or os.environ.get("CLIENT_KEY_FILE")
650+
options["cert_file"] = self.options["cert_file"] or os.environ.get(
651+
"CLIENT_CERT_FILE"
652+
)
653+
options["key_file"] = self.options["key_file"] or os.environ.get(
654+
"CLIENT_KEY_FILE"
655+
)
669656

670657
return options
671658

672-
def _make_pool(
673-
self,
674-
parsed_dsn, # type: Dsn
675-
http_proxy, # type: Optional[str]
676-
https_proxy, # type: Optional[str]
677-
ca_certs, # type: Any
678-
cert_file, # type: Any
679-
key_file, # type: Any
680-
proxy_headers, # type: Optional[Dict[str, str]]
681-
):
682-
# type: (...) -> Union[PoolManager, ProxyManager]
659+
def _make_pool(self):
660+
# type: (Self) -> Union[PoolManager, ProxyManager]
661+
if self.parsed_dsn is None:
662+
raise ValueError("Cannot create HTTP-based transport without valid DSN")
663+
683664
proxy = None
684-
no_proxy = self._in_no_proxy(parsed_dsn)
665+
no_proxy = self._in_no_proxy(self.parsed_dsn)
685666

686667
# try HTTPS first
687-
if parsed_dsn.scheme == "https" and (https_proxy != ""):
668+
https_proxy = self.options["https_proxy"]
669+
if self.parsed_dsn.scheme == "https" and (https_proxy != ""):
688670
proxy = https_proxy or (not no_proxy and getproxies().get("https"))
689671

690672
# maybe fallback to HTTP proxy
673+
http_proxy = self.options["http_proxy"]
691674
if not proxy and (http_proxy != ""):
692675
proxy = http_proxy or (not no_proxy and getproxies().get("http"))
693676

694-
opts = self._get_pool_options(ca_certs, cert_file, key_file)
677+
opts = self._get_pool_options()
695678

696679
if proxy:
680+
proxy_headers = self.options["proxy_headers"]
697681
if proxy_headers:
698682
opts["proxy_headers"] = proxy_headers
699683

@@ -783,10 +767,11 @@ def _request(
783767
)
784768
return response
785769

786-
def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
787-
# type: (Any, Any, Any) -> Dict[str, Any]
770+
def _get_pool_options(self):
771+
# type: (Self) -> Dict[str, Any]
788772
options = {
789-
"http2": True,
773+
"http2": self.parsed_dsn is not None
774+
and self.parsed_dsn.scheme == "https",
790775
"retries": 3,
791776
} # type: Dict[str, Any]
792777

@@ -805,45 +790,41 @@ def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
805790

806791
ssl_context = ssl.create_default_context()
807792
ssl_context.load_verify_locations(
808-
ca_certs # User-provided bundle from the SDK init
793+
self.options["ca_certs"] # User-provided bundle from the SDK init
809794
or os.environ.get("SSL_CERT_FILE")
810795
or os.environ.get("REQUESTS_CA_BUNDLE")
811796
or certifi.where()
812797
)
813-
cert_file = cert_file or os.environ.get("CLIENT_CERT_FILE")
814-
key_file = key_file or os.environ.get("CLIENT_KEY_FILE")
798+
cert_file = self.options["cert_file"] or os.environ.get("CLIENT_CERT_FILE")
799+
key_file = self.options["key_file"] or os.environ.get("CLIENT_KEY_FILE")
815800
if cert_file is not None:
816801
ssl_context.load_cert_chain(cert_file, key_file)
817802

818803
options["ssl_context"] = ssl_context
819804

820805
return options
821806

822-
def _make_pool(
823-
self,
824-
parsed_dsn, # type: Dsn
825-
http_proxy, # type: Optional[str]
826-
https_proxy, # type: Optional[str]
827-
ca_certs, # type: Any
828-
cert_file, # type: Any
829-
key_file, # type: Any
830-
proxy_headers, # type: Optional[Dict[str, str]]
831-
):
832-
# type: (...) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
807+
def _make_pool(self):
808+
# type: (Self) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
809+
if self.parsed_dsn is None:
810+
raise ValueError("Cannot create HTTP-based transport without valid DSN")
833811
proxy = None
834-
no_proxy = self._in_no_proxy(parsed_dsn)
812+
no_proxy = self._in_no_proxy(self.parsed_dsn)
835813

836814
# try HTTPS first
837-
if parsed_dsn.scheme == "https" and (https_proxy != ""):
815+
https_proxy = self.options["https_proxy"]
816+
if self.parsed_dsn.scheme == "https" and (https_proxy != ""):
838817
proxy = https_proxy or (not no_proxy and getproxies().get("https"))
839818

840819
# maybe fallback to HTTP proxy
820+
http_proxy = self.options["http_proxy"]
841821
if not proxy and (http_proxy != ""):
842822
proxy = http_proxy or (not no_proxy and getproxies().get("http"))
843823

844-
opts = self._get_pool_options(ca_certs, cert_file, key_file)
824+
opts = self._get_pool_options()
845825

846826
if proxy:
827+
proxy_headers = self.options["proxy_headers"]
847828
if proxy_headers:
848829
opts["proxy_headers"] = proxy_headers
849830

tests/test_transport.py

+29-10
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def test_transport_num_pools(make_client, num_pools, expected_num_pools):
219219

220220
client = make_client(_experiments=_experiments)
221221

222-
options = client.transport._get_pool_options([])
222+
options = client.transport._get_pool_options()
223223
assert options["num_pools"] == expected_num_pools
224224

225225

@@ -231,12 +231,15 @@ def test_two_way_ssl_authentication(make_client, http2):
231231
if http2:
232232
_experiments["transport_http2"] = True
233233

234-
client = make_client(_experiments=_experiments)
235-
236234
current_dir = os.path.dirname(__file__)
237235
cert_file = f"{current_dir}/test.pem"
238236
key_file = f"{current_dir}/test.key"
239-
options = client.transport._get_pool_options([], cert_file, key_file)
237+
client = make_client(
238+
cert_file=cert_file,
239+
key_file=key_file,
240+
_experiments=_experiments,
241+
)
242+
options = client.transport._get_pool_options()
240243

241244
if http2:
242245
assert options["ssl_context"] is not None
@@ -254,23 +257,39 @@ def test_socket_options(make_client):
254257

255258
client = make_client(socket_options=socket_options)
256259

257-
options = client.transport._get_pool_options([])
260+
options = client.transport._get_pool_options()
258261
assert options["socket_options"] == socket_options
259262

260263

261264
def test_keep_alive_true(make_client):
262265
client = make_client(keep_alive=True)
263266

264-
options = client.transport._get_pool_options([])
267+
options = client.transport._get_pool_options()
265268
assert options["socket_options"] == KEEP_ALIVE_SOCKET_OPTIONS
266269

267270

268271
def test_keep_alive_on_by_default(make_client):
269272
client = make_client()
270-
options = client.transport._get_pool_options([])
273+
options = client.transport._get_pool_options()
271274
assert "socket_options" not in options
272275

273276

277+
@pytest.mark.skipif(not PY38, reason="HTTP2 libraries are only available in py3.8+")
278+
def test_http2_with_https_dsn(make_client):
279+
client = make_client(_experiments={"transport_http2": True})
280+
client.transport.parsed_dsn.scheme = "https"
281+
options = client.transport._get_pool_options()
282+
assert options["http2"] is True
283+
284+
285+
@pytest.mark.skipif(not PY38, reason="HTTP2 libraries are only available in py3.8+")
286+
def test_no_http2_with_http_dsn(make_client):
287+
client = make_client(_experiments={"transport_http2": True})
288+
client.transport.parsed_dsn.scheme = "http"
289+
options = client.transport._get_pool_options()
290+
assert options["http2"] is False
291+
292+
274293
def test_socket_options_override_keep_alive(make_client):
275294
socket_options = [
276295
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
@@ -280,7 +299,7 @@ def test_socket_options_override_keep_alive(make_client):
280299

281300
client = make_client(socket_options=socket_options, keep_alive=False)
282301

283-
options = client.transport._get_pool_options([])
302+
options = client.transport._get_pool_options()
284303
assert options["socket_options"] == socket_options
285304

286305

@@ -292,7 +311,7 @@ def test_socket_options_merge_with_keep_alive(make_client):
292311

293312
client = make_client(socket_options=socket_options, keep_alive=True)
294313

295-
options = client.transport._get_pool_options([])
314+
options = client.transport._get_pool_options()
296315
try:
297316
assert options["socket_options"] == [
298317
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 42),
@@ -314,7 +333,7 @@ def test_socket_options_override_defaults(make_client):
314333
# socket option defaults, so we need to set this and not ignore it.
315334
client = make_client(socket_options=[])
316335

317-
options = client.transport._get_pool_options([])
336+
options = client.transport._get_pool_options()
318337
assert options["socket_options"] == []
319338

320339

0 commit comments

Comments
 (0)