Skip to content

Commit 16f37f7

Browse files
committed
websocket tests
1 parent da0a00b commit 16f37f7

2 files changed

Lines changed: 59 additions & 53 deletions

File tree

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

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
from opentelemetry.ext.asgi.version import __version__ # noqa
2929
from opentelemetry.trace.status import Status, StatusCanonicalCode
3030

31-
_HTTP_VERSION_PREFIX = "HTTP/"
32-
3331

3432
def get_header_from_scope(scope: dict, header_name: str) -> typing.List[str]:
3533
"""Retrieve a HTTP header value from the ASGI scope.
@@ -88,14 +86,16 @@ def collect_request_attributes(scope):
8886

8987
result = {
9088
"component": scope.get("type"),
91-
"http.method": scope.get("method"),
9289
"http.scheme": scope.get("scheme"),
9390
"http.host": server_host,
9491
"host.port": port,
9592
"http.flavor": scope.get("http_version"),
9693
"http.target": scope.get("path"),
9794
"http.url": http_url,
9895
}
96+
http_method = scope.get("method")
97+
if http_method:
98+
result["http.method"] = http_method
9999
http_host_value = ",".join(get_header_from_scope(scope, "host"))
100100
if http_host_value:
101101
result["http.server_name"] = http_host_value
@@ -127,15 +127,10 @@ def set_status_code(span, status_code):
127127

128128

129129
def get_default_span_name(scope):
130-
"""Default implementation for name_callback, returns HTTP {METHOD_NAME or PATH}."""
131-
span_name = "HTTP"
132-
130+
"""Default implementation for name_callback"""
133131
method_or_path = scope.get("method") or scope.get("path")
134132

135-
if method_or_path:
136-
return span_name + " " + method_or_path
137-
138-
return span_name
133+
return method_or_path
139134

140135

141136
class OpenTelemetryMiddleware:
@@ -174,38 +169,32 @@ async def __call__(self, scope, receive, send):
174169

175170
try:
176171
with self.tracer.start_as_current_span(
177-
span_name + " (asgi.connection)",
172+
span_name + " asgi",
178173
kind=trace.SpanKind.SERVER,
179174
attributes=collect_request_attributes(scope),
180175
):
181176

182177
@wraps(receive)
183178
async def wrapped_receive():
184179
with self.tracer.start_as_current_span(
185-
span_name + " (asgi." + scope["type"] + ".receive)"
180+
span_name + " asgi." + scope["type"] + ".receive"
186181
) as receive_span:
187182
payload = await receive()
188183
if payload["type"] == "websocket.receive":
189184
set_status_code(receive_span, 200)
190-
receive_span.set_attribute(
191-
"http.status_text", payload["text"]
192-
)
193185
receive_span.set_attribute("type", payload["type"])
194186
return payload
195187

196188
@wraps(send)
197189
async def wrapped_send(payload):
198190
with self.tracer.start_as_current_span(
199-
span_name + " (asgi." + scope["type"] + ".send)"
191+
span_name + " asgi." + scope["type"] + ".send"
200192
) as send_span:
201193
if payload["type"] == "http.response.start":
202194
status_code = payload["status"]
203195
set_status_code(send_span, status_code)
204196
elif payload["type"] == "websocket.send":
205197
set_status_code(send_span, 200)
206-
send_span.set_attribute(
207-
"http.status_text", payload["text"]
208-
)
209198
send_span.set_attribute("type", payload["type"])
210199
await send(payload)
211200

ext/opentelemetry-ext-asgi/tests/test_asgi_middleware.py

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@
2424
)
2525

2626

27-
async def simple_asgi(scope, receive, send):
28-
assert isinstance(scope, dict)
29-
assert scope.get("type") == "http"
27+
async def http_app(scope, receive, send):
3028
payload = await receive()
3129
if payload.get("type") == "http.request":
3230
await send(
@@ -39,6 +37,28 @@ async def simple_asgi(scope, receive, send):
3937
await send({"type": "http.response.body", "body": b"*"})
4038

4139

40+
async def websocket_app(scope, receive, send):
41+
while True:
42+
payload = await receive()
43+
if payload.get("type") == "websocket.connect":
44+
await send({"type": "websocket.accept"})
45+
46+
if payload.get("type") == "websocket.receive":
47+
if payload.get("text") == "ping":
48+
await send({"type": "websocket.send", "text": "pong"})
49+
50+
if payload.get("type") == "websocket.disconnect":
51+
break
52+
53+
54+
async def simple_asgi(scope, receive, send):
55+
assert isinstance(scope, dict)
56+
if scope["type"] == "http":
57+
await http_app(scope, receive, send)
58+
elif scope["type"] == "websocket":
59+
await websocket_app(scope, receive, send)
60+
61+
4262
async def error_asgi(scope, receive, send):
4363
assert isinstance(scope, dict)
4464
assert scope.get("type") == "http"
@@ -91,25 +111,25 @@ def validate_outputs(self, outputs, error=None, modifiers=None):
91111
self.assertEqual(len(span_list), 4)
92112
expected = [
93113
{
94-
"name": "HTTP GET (asgi.http.receive)",
114+
"name": "GET asgi.http.receive",
95115
"kind": trace_api.SpanKind.INTERNAL,
96116
"attributes": {"type": "http.request"},
97117
},
98118
{
99-
"name": "HTTP GET (asgi.http.send)",
119+
"name": "GET asgi.http.send",
100120
"kind": trace_api.SpanKind.INTERNAL,
101121
"attributes": {
102122
"http.status_code": 200,
103123
"type": "http.response.start",
104124
},
105125
},
106126
{
107-
"name": "HTTP GET (asgi.http.send)",
127+
"name": "GET asgi.http.send",
108128
"kind": trace_api.SpanKind.INTERNAL,
109129
"attributes": {"type": "http.response.body"},
110130
},
111131
{
112-
"name": "HTTP GET (asgi.connection)",
132+
"name": "GET asgi",
113133
"kind": trace_api.SpanKind.SERVER,
114134
"attributes": {
115135
"component": "http",
@@ -228,41 +248,38 @@ def update_expected_user_agent(expected):
228248
self.validate_outputs(outputs, modifiers=[update_expected_user_agent])
229249

230250
def test_websocket(self):
231-
async def app_asgi(scope, receive, send):
232-
assert scope["type"] == "websocket"
233-
payload = await receive()
234-
if payload.get("type") == "http.request":
235-
await send(
236-
{
237-
"type": "http.response.start",
238-
"status": 200,
239-
"headers": [[b"Content-Type", b"text/plain"]],
240-
}
241-
)
242-
await send({"type": "http.response.body", "body": b"*"})
243-
244-
self.scope["type"] = "websocket"
245-
app = otel_asgi.OpenTelemetryMiddleware(app_asgi)
251+
self.scope = {
252+
"type": "websocket",
253+
"http_version": "1.1",
254+
"scheme": "ws",
255+
"path": "/",
256+
"query_string": b"",
257+
"headers": [],
258+
"client": ("127.0.0.1", 32767),
259+
"server": ("127.0.0.1", 80),
260+
}
261+
app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
246262
self.seed_app(app)
247-
self.send_default_request()
263+
self.send_input({"type": "websocket.connect"})
264+
self.send_input({"type": "websocket.receive", "text": "ping"})
265+
self.send_input({"type": "websocket.disconnect"})
266+
outputs = self.get_all_output()
248267
span_list = self.memory_exporter.get_finished_spans()
249-
self.assertEqual(len(span_list), 4)
268+
self.assertEqual(len(span_list), 6)
250269
expected = [
251-
"HTTP GET (asgi.websocket.receive)",
252-
"HTTP GET (asgi.websocket.send)",
253-
"HTTP GET (asgi.websocket.send)",
270+
"/ asgi.websocket.receive",
271+
"/ asgi.websocket.send",
272+
"/ asgi.websocket.receive",
273+
"/ asgi.websocket.send",
274+
"/ asgi.websocket.receive",
275+
"/ asgi",
254276
]
255277
actual = [span.name for span in span_list]
256-
self.assertListEqual(actual[:3], expected)
278+
self.assertListEqual(actual, expected)
257279

258280
def test_lifespan(self):
259-
async def app_asgi(scope, receive, send):
260-
assert scope["type"] == "lifespan"
261-
await receive()
262-
await send({})
263-
264281
self.scope["type"] = "lifespan"
265-
app = otel_asgi.OpenTelemetryMiddleware(app_asgi)
282+
app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
266283
self.seed_app(app)
267284
self.send_default_request()
268285
span_list = self.memory_exporter.get_finished_spans()

0 commit comments

Comments
 (0)