Skip to content

Commit c0bb6bd

Browse files
fix: error parsing response cookies in FastAPI and awsgi [backport 2.18] (#11837)
Backport e29ccb0 from #11829 to 2.18. This fix resolves an issue parsing response cookies in FastAPI and awsgi issue: #11818 ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) Co-authored-by: Alberto Vara <[email protected]>
1 parent 0d6c79b commit c0bb6bd

File tree

3 files changed

+65
-9
lines changed

3 files changed

+65
-9
lines changed

ddtrace/contrib/internal/asgi/middleware.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ async def _blocked_asgi_app(scope, receive, send):
9191
await send({"type": "http.response.body", "body": b""})
9292

9393

94+
def _parse_response_cookies(response_headers):
95+
cookies = {}
96+
try:
97+
result = response_headers.get("set-cookie", "").split("=", maxsplit=1)
98+
if len(result) == 2:
99+
cookie_key, cookie_value = result
100+
cookies[cookie_key] = cookie_value
101+
except Exception:
102+
log.debug("failed to extract response cookies", exc_info=True)
103+
return cookies
104+
105+
94106
class TraceMiddleware:
95107
"""
96108
ASGI application middleware that traces the requests.
@@ -211,7 +223,6 @@ async def __call__(self, scope, receive, send):
211223
peer_ip = client[0]
212224
else:
213225
peer_ip = None
214-
215226
trace_utils.set_http_meta(
216227
span,
217228
self.integration_config,
@@ -234,15 +245,8 @@ async def wrapped_send(message):
234245
except Exception:
235246
log.warning("failed to extract response headers", exc_info=True)
236247
response_headers = None
237-
238248
if span and message.get("type") == "http.response.start" and "status" in message:
239-
cookies = {}
240-
try:
241-
cookie_key, cookie_value = response_headers.get("set-cookie", "").split("=", maxsplit=1)
242-
cookies[cookie_key] = cookie_value
243-
except Exception:
244-
log.debug("failed to extract response cookies", exc_info=True)
245-
249+
cookies = _parse_response_cookies(response_headers)
246250
status_code = message["status"]
247251
trace_utils.set_http_meta(
248252
span,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
ASGI: This fix resolves an issue parsing response cookies in FastAPI and awsgi

tests/contrib/asgi/test_asgi.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
from functools import partial
3+
import logging
34
import os
45
import random
56

@@ -10,6 +11,7 @@
1011
from ddtrace.constants import ERROR_MSG
1112
from ddtrace.contrib.asgi import TraceMiddleware
1213
from ddtrace.contrib.asgi import span_from_scope
14+
from ddtrace.contrib.internal.asgi.middleware import _parse_response_cookies
1315
from ddtrace.propagation import http as http_propagation
1416
from tests.conftest import DEFAULT_DDTRACE_SUBPROCESS_TEST_SERVICE_NAME
1517
from tests.utils import DummyTracer
@@ -634,6 +636,52 @@ async def test_tasks_asgi_without_more_body(scope, tracer, test_spans):
634636
assert request_span.duration < 1
635637

636638

639+
@pytest.mark.asyncio
640+
async def test_request_parse_response_cookies(tracer, test_spans, caplog):
641+
"""
642+
Regression test https://github.com/DataDog/dd-trace-py/issues/11818
643+
"""
644+
645+
async def tasks_cookies(scope, receive, send):
646+
message = await receive()
647+
if message.get("type") == "http.request":
648+
await send({"type": "http.response.start", "status": 200, "headers": [[b"set-cookie", b"test_cookie"]]})
649+
await send({"type": "http.response.body", "body": b"*"})
650+
await asyncio.sleep(1)
651+
652+
with caplog.at_level(logging.DEBUG):
653+
app = TraceMiddleware(tasks_cookies, tracer=tracer)
654+
async with httpx.AsyncClient(app=app) as client:
655+
response = await client.get("http://testserver/")
656+
assert response.status_code == 200
657+
658+
assert "failed to extract response cookies" not in caplog.text
659+
660+
661+
@pytest.mark.parametrize(
662+
"headers,expected_result",
663+
[
664+
({}, {}),
665+
({"cookie": "cookie1=value1"}, {}),
666+
({"header-1": ""}, {}),
667+
({"Set-cookie": "cookie1=value1"}, {}),
668+
({"set-Cookie": "cookie1=value1"}, {}),
669+
({"SET-cookie": "cookie1=value1"}, {}),
670+
({"set-cookie": "a"}, {}),
671+
({"set-cookie": "1234"}, {}),
672+
({"set-cookie": "cookie1=value1"}, {"cookie1": "value1"}),
673+
({"set-cookie": "cookie2=value1=value2"}, {"cookie2": "value1=value2"}),
674+
({"set-cookie": "cookie3=="}, {"cookie3": "="}),
675+
],
676+
)
677+
def test__parse_response_cookies(headers, expected_result, caplog):
678+
with caplog.at_level(logging.DEBUG):
679+
result = _parse_response_cookies(headers)
680+
681+
assert "failed to extract response cookies" not in caplog.text
682+
assert result == expected_result
683+
684+
637685
@pytest.mark.asyncio
638686
async def test_tasks_asgi_with_more_body(scope, tracer, test_spans):
639687
"""

0 commit comments

Comments
 (0)