Skip to content

Commit 56bc94f

Browse files
fix: extract http.referer from resolved ALB headers
Reuse resolve_alb_request_headers for referer tagging so multiValueHeaders events and lowercase referer keys are handled consistently with other ALB HTTP facet tags. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9efab76 commit 56bc94f

2 files changed

Lines changed: 53 additions & 10 deletions

File tree

datadog_lambda/trigger.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,16 @@ def resolve_alb_request_headers(event):
316316
return resolved
317317

318318

319+
def _get_header_case_insensitive(headers, name):
320+
if not isinstance(headers, dict):
321+
return None
322+
name_lower = name.lower()
323+
for key, value in headers.items():
324+
if isinstance(key, str) and key.lower() == name_lower and value:
325+
return value
326+
return None
327+
328+
319329
def extract_http_tags(event):
320330
"""
321331
Extracts HTTP facet tags from the triggering event
@@ -329,6 +339,7 @@ def extract_http_tags(event):
329339

330340
path = event.get("path")
331341
method = event.get("httpMethod")
342+
request_headers = None
332343

333344
if request_context and request_context.get("stage"):
334345
domain_name = request_context.get("domainName")
@@ -350,13 +361,13 @@ def extract_http_tags(event):
350361
elif request_context and request_context.get("elb"):
351362
# ALB events have no requestContext.stage; derive the URL from the
352363
# forwarded host/proto headers and the top-level path.
353-
alb_headers = resolve_alb_request_headers(event)
354-
host = alb_headers.get("host")
364+
request_headers = resolve_alb_request_headers(event)
365+
host = request_headers.get("host")
355366
if host:
356-
proto = alb_headers.get("x-forwarded-proto", "http")
367+
proto = request_headers.get("x-forwarded-proto", "http")
357368
http_tags["http.url"] = proto + "://" + host
358369

359-
user_agent = alb_headers.get("user-agent")
370+
user_agent = request_headers.get("user-agent")
360371
if user_agent:
361372
http_tags["http.useragent"] = user_agent
362373

@@ -370,13 +381,14 @@ def extract_http_tags(event):
370381
if method:
371382
http_tags["http.method"] = method
372383

373-
# Safely get headers
374-
headers = event.get("headers", {})
375-
if not isinstance(headers, dict):
376-
headers = {}
384+
if request_headers is None:
385+
request_headers = event.get("headers")
386+
if not isinstance(request_headers, dict):
387+
request_headers = {}
377388

378-
if headers and headers.get("Referer"):
379-
http_tags["http.referer"] = headers.get("Referer")
389+
referer = _get_header_case_insensitive(request_headers, "referer")
390+
if referer:
391+
http_tags["http.referer"] = referer
380392

381393
# Try to get `routeKey` from API GW v2; otherwise try to get `resource` from API GW v1
382394
route = event.get("routeKey") or event.get("resource")

tests/test_trigger.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,26 @@ def test_extract_trigger_tags_application_load_balancer_multivalue_headers(self)
450450
)
451451
assert tags.get("http.useragent").startswith("Mozilla/5.0")
452452

453+
def test_extract_trigger_tags_alb_referer_from_lowercase_headers(self):
454+
event_sample_source = "application-load-balancer"
455+
with open(event_samples + event_sample_source + ".json") as event_file:
456+
event = json.load(event_file)
457+
event["headers"]["referer"] = "https://example.com/page"
458+
459+
tags = extract_trigger_tags(event, get_mock_context())
460+
461+
self.assertEqual(tags.get("http.referer"), "https://example.com/page")
462+
463+
def test_extract_trigger_tags_alb_referer_from_multivalue_headers(self):
464+
event_sample_source = "application-load-balancer-multivalue-headers"
465+
with open(event_samples + event_sample_source + ".json") as event_file:
466+
event = json.load(event_file)
467+
event["multiValueHeaders"]["referer"] = ["https://example.com/page"]
468+
469+
tags = extract_trigger_tags(event, get_mock_context())
470+
471+
self.assertEqual(tags.get("http.referer"), "https://example.com/page")
472+
453473
def test_extract_trigger_tags_cloudfront(self):
454474
event_sample_source = "cloudfront"
455475
test_file = event_samples + event_sample_source + ".json"
@@ -636,6 +656,17 @@ def test_extract_http_tags_with_invalid_headers(self):
636656
# Should not raise an exception
637657
self.assertEqual(http_tags, {"span.kind": "server"})
638658

659+
def test_extract_http_tags_referer_case_insensitive(self):
660+
from datadog_lambda.trigger import extract_http_tags
661+
662+
event = {"headers": {"Referer": "https://example.com/capitalized"}}
663+
http_tags = extract_http_tags(event)
664+
self.assertEqual(http_tags.get("http.referer"), "https://example.com/capitalized")
665+
666+
event = {"headers": {"referer": "https://example.com/lowercase"}}
667+
http_tags = extract_http_tags(event)
668+
self.assertEqual(http_tags.get("http.referer"), "https://example.com/lowercase")
669+
639670
def test_extract_http_tags_with_invalid_route(self):
640671
from datadog_lambda.trigger import extract_http_tags
641672

0 commit comments

Comments
 (0)