Skip to content

Commit bd50c38

Browse files
szokeasaurusrexsentrivanaantonpirker
authored
fix(httpx): Prevent Sentry baggage duplication (#3728)
Sentry baggage will get added to an HTTPX request multiple times if the same request is repeated. To prevent this from occurring, we can strip any existing Sentry baggage before adding Sentry baggage to the request. Fixes #3709 --------- Co-authored-by: Ivana Kellyer <[email protected]> Co-authored-by: Anton Pirker <[email protected]>
1 parent e9ec6c1 commit bd50c38

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

sentry_sdk/integrations/httpx.py

+23-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from sentry_sdk.consts import OP, SPANDATA
33
from sentry_sdk.integrations import Integration, DidNotEnable
44
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
5-
from sentry_sdk.tracing_utils import should_propagate_trace
5+
from sentry_sdk.tracing_utils import Baggage, should_propagate_trace
66
from sentry_sdk.utils import (
77
SENSITIVE_DATA_SUBSTITUTE,
88
capture_internal_exceptions,
@@ -14,6 +14,7 @@
1414
from typing import TYPE_CHECKING
1515

1616
if TYPE_CHECKING:
17+
from collections.abc import MutableMapping
1718
from typing import Any
1819

1920

@@ -76,11 +77,9 @@ def send(self, request, **kwargs):
7677
key=key, value=value, url=request.url
7778
)
7879
)
79-
if key == BAGGAGE_HEADER_NAME and request.headers.get(
80-
BAGGAGE_HEADER_NAME
81-
):
82-
# do not overwrite any existing baggage, just append to it
83-
request.headers[key] += "," + value
80+
81+
if key == BAGGAGE_HEADER_NAME:
82+
_add_sentry_baggage_to_headers(request.headers, value)
8483
else:
8584
request.headers[key] = value
8685

@@ -148,3 +147,21 @@ async def send(self, request, **kwargs):
148147
return rv
149148

150149
AsyncClient.send = send
150+
151+
152+
def _add_sentry_baggage_to_headers(headers, sentry_baggage):
153+
# type: (MutableMapping[str, str], str) -> None
154+
"""Add the Sentry baggage to the headers.
155+
156+
This function directly mutates the provided headers. The provided sentry_baggage
157+
is appended to the existing baggage. If the baggage already contains Sentry items,
158+
they are stripped out first.
159+
"""
160+
existing_baggage = headers.get(BAGGAGE_HEADER_NAME, "")
161+
stripped_existing_baggage = Baggage.strip_sentry_baggage(existing_baggage)
162+
163+
separator = "," if len(stripped_existing_baggage) > 0 else ""
164+
165+
headers[BAGGAGE_HEADER_NAME] = (
166+
stripped_existing_baggage + separator + sentry_baggage
167+
)

sentry_sdk/tracing_utils.py

+15
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,21 @@ def serialize(self, include_third_party=False):
609609

610610
return ",".join(items)
611611

612+
@staticmethod
613+
def strip_sentry_baggage(header):
614+
# type: (str) -> str
615+
"""Remove Sentry baggage from the given header.
616+
617+
Given a Baggage header, return a new Baggage header with all Sentry baggage items removed.
618+
"""
619+
return ",".join(
620+
(
621+
item
622+
for item in header.split(",")
623+
if not Baggage.SENTRY_PREFIX_REGEX.match(item.strip())
624+
)
625+
)
626+
612627

613628
def should_propagate_trace(client, url):
614629
# type: (sentry_sdk.client.BaseClient, str) -> bool

tests/test_tracing_utils.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from dataclasses import asdict, dataclass
22
from typing import Optional, List
33

4-
from sentry_sdk.tracing_utils import _should_be_included
4+
from sentry_sdk.tracing_utils import _should_be_included, Baggage
55
import pytest
66

77

@@ -94,3 +94,24 @@ def test_should_be_included(test_case, expected):
9494
kwargs = asdict(test_case)
9595
kwargs.pop("id")
9696
assert _should_be_included(**kwargs) == expected
97+
98+
99+
@pytest.mark.parametrize(
100+
("header", "expected"),
101+
(
102+
("", ""),
103+
("foo=bar", "foo=bar"),
104+
(" foo=bar, baz = qux ", " foo=bar, baz = qux "),
105+
("sentry-trace_id=123", ""),
106+
(" sentry-trace_id = 123 ", ""),
107+
("sentry-trace_id=123,sentry-public_key=456", ""),
108+
("foo=bar,sentry-trace_id=123", "foo=bar"),
109+
("foo=bar,sentry-trace_id=123,baz=qux", "foo=bar,baz=qux"),
110+
(
111+
"foo=bar,sentry-trace_id=123,baz=qux,sentry-public_key=456",
112+
"foo=bar,baz=qux",
113+
),
114+
),
115+
)
116+
def test_strip_sentry_baggage(header, expected):
117+
assert Baggage.strip_sentry_baggage(header) == expected

0 commit comments

Comments
 (0)