Skip to content

Commit b7d362f

Browse files
committed
avoid loosing repeated HTTP headers
1 parent adfb1c7 commit b7d362f

File tree

2 files changed

+66
-5
lines changed

2 files changed

+66
-5
lines changed

instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,17 @@ def collect_custom_headers_attributes(
347347
- https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers
348348
"""
349349
# Decode headers before processing.
350-
headers: dict[str, str] = {
351-
_key.decode("utf8"): _value.decode("utf8")
352-
for (_key, _value) in scope_or_response_message.get("headers")
353-
or cast("list[tuple[bytes, bytes]]", [])
354-
}
350+
headers: dict[str, str] = {}
351+
raw_headers = scope_or_response_message.get("headers")
352+
if raw_headers:
353+
for _key, _value in raw_headers:
354+
key = _key.decode().lower()
355+
value = _value.decode()
356+
if key in headers:
357+
headers[key] += f",{value}"
358+
else:
359+
headers[key] = value
360+
355361
return sanitize.sanitize_header_values(
356362
headers,
357363
header_regexes,

instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_custom_headers.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,24 @@ async def http_app_with_custom_headers(scope, receive, send):
4040
await send({"type": "http.response.body", "body": b"*"})
4141

4242

43+
async def http_app_with_repeat_headers(scope, receive, send):
44+
message = await receive()
45+
assert scope["type"] == "http"
46+
if message.get("type") == "http.request":
47+
await send(
48+
{
49+
"type": "http.response.start",
50+
"status": 200,
51+
"headers": [
52+
(b"Content-Type", b"text/plain"),
53+
(b"custom-test-header-1", b"test-header-value-1"),
54+
(b"custom-test-header-1", b"test-header-value-2"),
55+
],
56+
}
57+
)
58+
await send({"type": "http.response.body", "body": b"*"})
59+
60+
4361
async def websocket_app_with_custom_headers(scope, receive, send):
4462
assert scope["type"] == "websocket"
4563
while True:
@@ -121,6 +139,25 @@ def test_http_custom_request_headers_in_span_attributes(self):
121139
if span.kind == SpanKind.SERVER:
122140
self.assertSpanHasAttributes(span, expected)
123141

142+
def test_http_repeat_request_headers_in_span_attributes(self):
143+
self.scope["headers"].extend(
144+
[
145+
(b"custom-test-header-1", b"test-header-value-1"),
146+
(b"custom-test-header-1", b"test-header-value-2"),
147+
]
148+
)
149+
self.seed_app(self.app)
150+
self.send_default_request()
151+
self.get_all_output()
152+
span_list = self.exporter.get_finished_spans()
153+
expected = {
154+
"http.request.header.custom_test_header_1": (
155+
"test-header-value-1,test-header-value-2",
156+
),
157+
}
158+
span = next(span for span in span_list if span.kind == SpanKind.SERVER)
159+
self.assertSpanHasAttributes(span, expected)
160+
124161
def test_http_custom_request_headers_not_in_span_attributes(self):
125162
self.scope["headers"].extend(
126163
[
@@ -176,6 +213,24 @@ def test_http_custom_response_headers_in_span_attributes(self):
176213
if span.kind == SpanKind.SERVER:
177214
self.assertSpanHasAttributes(span, expected)
178215

216+
def test_http_repeat_response_headers_in_span_attributes(self):
217+
self.app = otel_asgi.OpenTelemetryMiddleware(
218+
http_app_with_repeat_headers,
219+
tracer_provider=self.tracer_provider,
220+
**self.constructor_params,
221+
)
222+
self.seed_app(self.app)
223+
self.send_default_request()
224+
self.get_all_output()
225+
span_list = self.exporter.get_finished_spans()
226+
expected = {
227+
"http.response.header.custom_test_header_1": (
228+
"test-header-value-1,test-header-value-2",
229+
),
230+
}
231+
span = next(span for span in span_list if span.kind == SpanKind.SERVER)
232+
self.assertSpanHasAttributes(span, expected)
233+
179234
def test_http_custom_response_headers_not_in_span_attributes(self):
180235
self.app = otel_asgi.OpenTelemetryMiddleware(
181236
http_app_with_custom_headers,

0 commit comments

Comments
 (0)