@@ -314,6 +314,36 @@ def custom_event_context_extractor(lambda_event):
314314 expected_baggage = MOCK_W3C_BAGGAGE_VALUE ,
315315 propagators = "tracecontext,baggage" ,
316316 ),
317+ TestCase (
318+ name = "case_insensitive_headers_uppercase" ,
319+ custom_extractor = None ,
320+ context = {
321+ "headers" : {
322+ TraceContextTextMapPropagator ._TRACEPARENT_HEADER_NAME .upper (): MOCK_W3C_TRACE_CONTEXT_SAMPLED ,
323+ TraceContextTextMapPropagator ._TRACESTATE_HEADER_NAME .upper (): f"{ MOCK_W3C_TRACE_STATE_KEY } ={ MOCK_W3C_TRACE_STATE_VALUE } ,foo=1,bar=2" ,
324+ }
325+ },
326+ expected_traceid = MOCK_W3C_TRACE_ID ,
327+ expected_parentid = MOCK_W3C_PARENT_SPAN_ID ,
328+ expected_trace_state_len = 3 ,
329+ expected_state_value = MOCK_W3C_TRACE_STATE_VALUE ,
330+ xray_traceid = MOCK_XRAY_TRACE_CONTEXT_NOT_SAMPLED ,
331+ ),
332+ TestCase (
333+ name = "case_insensitive_headers_mixedcase" ,
334+ custom_extractor = None ,
335+ context = {
336+ "headers" : {
337+ "TraceParent" : MOCK_W3C_TRACE_CONTEXT_SAMPLED ,
338+ "tRaCeStAtE" : f"{ MOCK_W3C_TRACE_STATE_KEY } ={ MOCK_W3C_TRACE_STATE_VALUE } ,foo=1,bar=2" ,
339+ }
340+ },
341+ expected_traceid = MOCK_W3C_TRACE_ID ,
342+ expected_parentid = MOCK_W3C_PARENT_SPAN_ID ,
343+ expected_trace_state_len = 3 ,
344+ expected_state_value = MOCK_W3C_TRACE_STATE_VALUE ,
345+ xray_traceid = MOCK_XRAY_TRACE_CONTEXT_NOT_SAMPLED ,
346+ ),
317347 ]
318348 for test in tests :
319349 with self .subTest (test_name = test .name ):
@@ -389,6 +419,57 @@ def test_lambda_no_error_with_invalid_flush_timeout(self):
389419
390420 test_env_patch .stop ()
391421
422+ def test_api_gateway_v1_attributes_case_insensitivity (self ):
423+ AwsLambdaInstrumentor ().instrument ()
424+
425+ mock_execute_lambda (
426+ {
427+ "httpMethod" : "GET" ,
428+ "headers" : {
429+ "user-agent" : "lowercase-agent" ,
430+ "host" : "lowercase-host" ,
431+ "x-forwarded-proto" : "http" ,
432+ },
433+ "resource" : "/test" ,
434+ "requestContext" : {
435+ "version" : "1.0" ,
436+ },
437+ }
438+ )
439+
440+ spans = self .memory_exporter .get_finished_spans ()
441+ self .assertEqual (len (spans ), 1 )
442+ span = spans [0 ]
443+ self .assertEqual (
444+ span .attributes .get (HTTP_USER_AGENT ), "lowercase-agent"
445+ )
446+ self .assertEqual (span .attributes .get (NET_HOST_NAME ), "lowercase-host" )
447+ self .assertEqual (span .attributes .get (HTTP_SCHEME ), "http" )
448+
449+ self .memory_exporter .clear ()
450+
451+ mock_execute_lambda (
452+ {
453+ "httpMethod" : "GET" ,
454+ "headers" : {
455+ "uSeR-aGeNt" : "mixed-agent" ,
456+ "hOsT" : "mixed-host" ,
457+ "X-fOrWaRdEd-PrOtO" : "https" ,
458+ },
459+ "resource" : "/test" ,
460+ "requestContext" : {
461+ "version" : "1.0" ,
462+ },
463+ }
464+ )
465+
466+ spans = self .memory_exporter .get_finished_spans ()
467+ self .assertEqual (len (spans ), 1 )
468+ span = spans [0 ]
469+ self .assertEqual (span .attributes .get (HTTP_USER_AGENT ), "mixed-agent" )
470+ self .assertEqual (span .attributes .get (NET_HOST_NAME ), "mixed-host" )
471+ self .assertEqual (span .attributes .get (HTTP_SCHEME ), "https" )
472+
392473 def test_lambda_handles_multiple_consumers (self ):
393474 test_env_patch = mock .patch .dict (
394475 "os.environ" ,
0 commit comments