|
18 | 18 |
|
19 | 19 | import opentelemetry.instrumentation.asgi as otel_asgi
|
20 | 20 | from opentelemetry import trace as trace_api
|
| 21 | +from opentelemetry.instrumentation.propagators import ( |
| 22 | + TraceResponsePropagator, |
| 23 | + get_global_response_propagator, |
| 24 | + set_global_response_propagator, |
| 25 | +) |
21 | 26 | from opentelemetry.sdk import resources
|
22 | 27 | from opentelemetry.semconv.trace import SpanAttributes
|
23 | 28 | from opentelemetry.test.asgitestutil import (
|
24 | 29 | AsgiTestBase,
|
25 | 30 | setup_testing_defaults,
|
26 | 31 | )
|
27 | 32 | from opentelemetry.test.test_base import TestBase
|
| 33 | +from opentelemetry.trace import format_span_id, format_trace_id |
28 | 34 |
|
29 | 35 |
|
30 | 36 | async def http_app(scope, receive, send):
|
@@ -287,6 +293,45 @@ def update_expected_user_agent(expected):
|
287 | 293 | outputs = self.get_all_output()
|
288 | 294 | self.validate_outputs(outputs, modifiers=[update_expected_user_agent])
|
289 | 295 |
|
| 296 | + def test_traceresponse_header(self): |
| 297 | + """Test a traceresponse header is sent when a global propagator is set.""" |
| 298 | + |
| 299 | + orig = get_global_response_propagator() |
| 300 | + set_global_response_propagator(TraceResponsePropagator()) |
| 301 | + |
| 302 | + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) |
| 303 | + self.seed_app(app) |
| 304 | + self.send_default_request() |
| 305 | + |
| 306 | + # traceresponse header corresponds to http.response.start span |
| 307 | + span = self.memory_exporter.get_finished_spans()[1] |
| 308 | + self.assertDictEqual( |
| 309 | + dict(span.attributes), |
| 310 | + { |
| 311 | + SpanAttributes.HTTP_STATUS_CODE: 200, |
| 312 | + "type": "http.response.start", |
| 313 | + }, |
| 314 | + ) |
| 315 | + |
| 316 | + response_start, response_body, *_ = self.get_all_output() |
| 317 | + self.assertEqual(response_body["body"], b"*") |
| 318 | + self.assertEqual(response_start["status"], 200) |
| 319 | + |
| 320 | + traceresponse = "00-{0}-{1}-01".format( |
| 321 | + format_trace_id(span.get_span_context().trace_id), |
| 322 | + format_span_id(span.get_span_context().span_id), |
| 323 | + ) |
| 324 | + self.assertListEqual( |
| 325 | + response_start["headers"], |
| 326 | + [ |
| 327 | + [b"Content-Type", b"text/plain"], |
| 328 | + [b"traceresponse", f"{traceresponse}".encode()], |
| 329 | + [b"access-control-expose-headers", b"traceresponse"], |
| 330 | + ], |
| 331 | + ) |
| 332 | + |
| 333 | + set_global_response_propagator(orig) |
| 334 | + |
290 | 335 | def test_websocket(self):
|
291 | 336 | self.scope = {
|
292 | 337 | "type": "websocket",
|
@@ -359,6 +404,50 @@ def test_websocket(self):
|
359 | 404 | self.assertEqual(span.kind, expected["kind"])
|
360 | 405 | self.assertDictEqual(dict(span.attributes), expected["attributes"])
|
361 | 406 |
|
| 407 | + def test_websocket_traceresponse_header(self): |
| 408 | + """Test a traceresponse header is set for websocket messages""" |
| 409 | + |
| 410 | + orig = get_global_response_propagator() |
| 411 | + set_global_response_propagator(TraceResponsePropagator()) |
| 412 | + |
| 413 | + self.scope = { |
| 414 | + "type": "websocket", |
| 415 | + "http_version": "1.1", |
| 416 | + "scheme": "ws", |
| 417 | + "path": "/", |
| 418 | + "query_string": b"", |
| 419 | + "headers": [], |
| 420 | + "client": ("127.0.0.1", 32767), |
| 421 | + "server": ("127.0.0.1", 80), |
| 422 | + } |
| 423 | + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) |
| 424 | + self.seed_app(app) |
| 425 | + self.send_input({"type": "websocket.connect"}) |
| 426 | + self.send_input({"type": "websocket.receive", "text": "ping"}) |
| 427 | + self.send_input({"type": "websocket.disconnect"}) |
| 428 | + _, socket_send, *_ = self.get_all_output() |
| 429 | + |
| 430 | + # traceresponse header corresponds to the 2nd websocket.send span |
| 431 | + span = self.memory_exporter.get_finished_spans()[3] |
| 432 | + self.assertDictEqual( |
| 433 | + dict(span.attributes), |
| 434 | + {SpanAttributes.HTTP_STATUS_CODE: 200, "type": "websocket.send"}, |
| 435 | + ) |
| 436 | + |
| 437 | + traceresponse = "00-{0}-{1}-01".format( |
| 438 | + format_trace_id(span.get_span_context().trace_id), |
| 439 | + format_span_id(span.get_span_context().span_id), |
| 440 | + ) |
| 441 | + self.assertListEqual( |
| 442 | + socket_send["headers"], |
| 443 | + [ |
| 444 | + [b"traceresponse", f"{traceresponse}".encode()], |
| 445 | + [b"access-control-expose-headers", b"traceresponse"], |
| 446 | + ], |
| 447 | + ) |
| 448 | + |
| 449 | + set_global_response_propagator(orig) |
| 450 | + |
362 | 451 | def test_lifespan(self):
|
363 | 452 | self.scope["type"] = "lifespan"
|
364 | 453 | app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
|
|
0 commit comments