diff --git a/api/all/src/main/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagator.java b/api/all/src/main/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagator.java index 393439518df..f37aaf0f19a 100644 --- a/api/all/src/main/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagator.java +++ b/api/all/src/main/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagator.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Logger; @@ -165,8 +166,21 @@ private static SpanContext extractImpl(@Nullable C carrier, TextMapGetter return contextFromParentHeader; } - String traceStateHeader = getter.get(carrier, TRACE_STATE); - if (traceStateHeader == null || traceStateHeader.isEmpty()) { + Iterator traceStateHeaders = getter.getAll(carrier, TRACE_STATE); + if (traceStateHeaders == null || !traceStateHeaders.hasNext()) { + return contextFromParentHeader; + } + + String traceStateHeader = traceStateHeaders.next(); + if (traceStateHeaders.hasNext()) { + // W3C Trace Context requires combining repeated fields in the order received. + StringBuilder combined = new StringBuilder(traceStateHeader); + while (traceStateHeaders.hasNext()) { + combined.append(',').append(traceStateHeaders.next()); + } + traceStateHeader = combined.toString(); + } + if (traceStateHeader.isEmpty()) { return contextFromParentHeader; } diff --git a/api/all/src/test/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagatorTest.java b/api/all/src/test/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagatorTest.java index 851d278e73f..87429cc737a 100644 --- a/api/all/src/test/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagatorTest.java +++ b/api/all/src/test/java/io/opentelemetry/api/trace/propagation/W3CTraceContextPropagatorTest.java @@ -21,7 +21,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -249,6 +251,37 @@ void extract_SampledContext_WithTraceState() { TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TRACE_STATE)); } + @Test + void extract_multipleTraceStateHeaders() { + Map> carrier = new HashMap<>(); + carrier.put( + W3CTraceContextPropagator.TRACE_PARENT, + Collections.singletonList(TRACEPARENT_HEADER_SAMPLED)); + carrier.put(W3CTraceContextPropagator.TRACE_STATE, Arrays.asList("bar=baz", "foo=bar")); + TextMapGetter>> multiGetter = + new TextMapGetter>>() { + @Override + @Nullable + public String get(Map> headers, String key) { + return headers.getOrDefault(key, Collections.emptyList()).stream() + .findFirst() + .orElse(null); + } + + @Override + public Iterator getAll(Map> headers, String key) { + return headers.getOrDefault(key, Collections.emptyList()).iterator(); + } + }; + + Context context = w3cTraceContextPropagator.extract(Context.root(), carrier, multiGetter); + + assertThat(getSpanContext(context)) + .isEqualTo( + SpanContext.createFromRemoteParent( + TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TRACE_STATE)); + } + @Test void extract_NotSampledContext_WithTraceState() { Map carrier = new LinkedHashMap<>();