Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallback decoding for asgi headers #2837

Merged
merged 12 commits into from
Sep 25, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#2537](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2537))
- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi` Add ability to disable internal HTTP send and receive spans
([#2802](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2802))
- `opentelemetry-instrumentation-asgi`, Add fallback decoding for ASGI headers
xrmx marked this conversation as resolved.
Show resolved Hide resolved
([#2837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2837))

### Breaking changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,17 @@ def get(
# ASGI header keys are in lower case
key = key.lower()
decoded = [
_value.decode("utf8")
_decode_header_item(_value)
for (_key, _value) in headers
if _key.decode("utf8").lower() == key
if _decode_header_item(_key).lower() == key
]
if not decoded:
return None
return decoded

def keys(self, carrier: dict) -> typing.List[str]:
headers = carrier.get("headers") or []
return [_key.decode("utf8") for (_key, _value) in headers]
return [_decode_header_item(_key) for (_key, _value) in headers]


asgi_getter = ASGIGetter()
Expand Down Expand Up @@ -410,7 +410,9 @@ def collect_custom_headers_attributes(
if raw_headers:
for key, value in raw_headers:
# Decode headers before processing.
headers[key.decode()].append(value.decode())
headers[_decode_header_item(key)].append(
_decode_header_item(value)
)

return sanitize.sanitize_header_values(
headers,
Expand Down Expand Up @@ -979,3 +981,13 @@ def _parse_active_request_count_attrs(
_server_active_requests_count_attrs_new,
sem_conv_opt_in_mode,
)


def _decode_header_item(value):
try:
return value.decode("utf-8")
except ValueError:
# ASGI header encoding specs, see:
# - https://asgi.readthedocs.io/en/latest/specs/www.html#wsgi-encoding-differences (see: WSGI encoding differences)
# - https://docs.python.org/3/library/codecs.html#text-encodings (see: Text Encodings)
return value.decode("unicode_escape")
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ async def test_http_custom_request_headers_in_span_attributes(self):
(b"Regex-Test-Header-1", b"Regex Test Value 1"),
(b"regex-test-header-2", b"RegexTestValue2,RegexTestValue3"),
(b"My-Secret-Header", b"My Secret Value"),
(
b"non-utf8-header",
b"Moto Z\xb2",
),
rocky-ken marked this conversation as resolved.
Show resolved Hide resolved
]
)
self.seed_app(self.app)
Expand All @@ -147,6 +151,7 @@ async def test_http_custom_request_headers_in_span_attributes(self):
"http.request.header.regex_test_header_2": (
"RegexTestValue2,RegexTestValue3",
),
"http.request.header.non_utf8_header": ("Moto Z²",),
"http.request.header.my_secret_header": ("[REDACTED]",),
}
for span in span_list:
Expand Down Expand Up @@ -418,7 +423,7 @@ async def test_websocket_custom_response_headers_not_in_span_attributes(


SANITIZE_FIELDS_TEST_VALUE = ".*my-secret.*"
SERVER_REQUEST_TEST_VALUE = "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*"
SERVER_REQUEST_TEST_VALUE = "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*,non-utf8-header"
SERVER_RESPONSE_TEST_VALUE = "Custom-Test-Header-1,Custom-Test-Header-2,Custom-Test-Header-3,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*"


Expand Down
Loading