Skip to content

Commit 2746715

Browse files
committed
changes W3CBaggagePropagator to handle multiple headers for baggage, using new getList() method on the TextMapGetter. Includes tests.
1 parent a4b8ae4 commit 2746715

File tree

3 files changed

+112
-11
lines changed

3 files changed

+112
-11
lines changed

api/all/src/main/java/io/opentelemetry/api/baggage/propagation/W3CBaggagePropagator.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ private static String encodeValue(String value) {
8686
return URL_ESCAPER.escape(value);
8787
}
8888

89+
/**
90+
* @param context the {@code Context} used to store the extracted value.
91+
* @param carrier holds propagation fields. For example, an outgoing message or http request.
92+
* @param getter invoked for each propagation key to get data from the carrier.
93+
* @return the extracted context
94+
*/
8995
@Override
9096
public <C> Context extract(Context context, @Nullable C carrier, TextMapGetter<C> getter) {
9197
if (context == null) {
@@ -95,21 +101,26 @@ public <C> Context extract(Context context, @Nullable C carrier, TextMapGetter<C
95101
return context;
96102
}
97103

98-
String baggageHeader = getter.get(carrier, FIELD);
99-
if (baggageHeader == null) {
100-
return context;
101-
}
102-
if (baggageHeader.isEmpty()) {
104+
List<String> baggageHeaders = getter.getList(carrier, FIELD);
105+
if (baggageHeaders == null || baggageHeaders.isEmpty()) {
103106
return context;
104107
}
105108

109+
boolean extracted = false;
106110
BaggageBuilder baggageBuilder = Baggage.builder();
107-
try {
108-
extractEntries(baggageHeader, baggageBuilder);
109-
} catch (RuntimeException e) {
110-
return context;
111+
for (String header : baggageHeaders) {
112+
if (header.isEmpty()) {
113+
continue;
114+
}
115+
116+
try {
117+
extractEntries(header, baggageBuilder);
118+
extracted = true;
119+
} catch (RuntimeException expected) {
120+
// invalid baggage header, continue
121+
}
111122
}
112-
return context.with(baggageBuilder.build());
123+
return extracted ? context.with(baggageBuilder.build()) : context;
113124
}
114125

115126
private static void extractEntries(String baggageHeader, BaggageBuilder baggageBuilder) {

api/all/src/test/java/io/opentelemetry/api/baggage/propagation/W3CBaggagePropagatorTest.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static java.util.Collections.singletonMap;
99
import static org.assertj.core.api.Assertions.assertThat;
1010

11+
import com.google.common.collect.ImmutableList;
1112
import com.google.common.collect.ImmutableMap;
1213
import io.opentelemetry.api.baggage.Baggage;
1314
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
@@ -16,6 +17,7 @@
1617
import java.util.Collections;
1718
import java.util.HashMap;
1819
import java.util.LinkedHashMap;
20+
import java.util.List;
1921
import java.util.Map;
2022
import javax.annotation.Nullable;
2123
import org.junit.jupiter.api.Test;
@@ -36,6 +38,27 @@ public String get(Map<String, String> carrier, String key) {
3638
}
3739
};
3840

41+
private static final TextMapGetter<Map<String, List<String>>> multiGetter =
42+
new TextMapGetter<Map<String, List<String>>>() {
43+
@Override
44+
public Iterable<String> keys(Map<String, List<String>> carrier) {
45+
return carrier.keySet();
46+
}
47+
48+
@Nullable
49+
@Override
50+
public String get(Map<String, List<String>> carrier, String key) {
51+
return carrier.getOrDefault(key, Collections.emptyList()).stream()
52+
.findFirst()
53+
.orElse(null);
54+
}
55+
56+
@Override
57+
public List<String> getList(Map<String, List<String>> carrier, String key) {
58+
return carrier.getOrDefault(key, Collections.emptyList());
59+
}
60+
};
61+
3962
@Test
4063
void fields() {
4164
assertThat(W3CBaggagePropagator.getInstance().fields()).containsExactly("baggage");
@@ -421,6 +444,73 @@ void extract_nullGetter() {
421444
.isSameAs(context);
422445
}
423446

447+
@Test
448+
void extract_multiple_headers() {
449+
W3CBaggagePropagator propagator = W3CBaggagePropagator.getInstance();
450+
451+
Context result =
452+
propagator.extract(
453+
Context.root(),
454+
ImmutableMap.of("baggage", ImmutableList.of("k1=v1", "k2=v2")),
455+
multiGetter);
456+
457+
Baggage expectedBaggage = Baggage.builder().put("k1", "v1").put("k2", "v2").build();
458+
assertThat(Baggage.fromContext(result)).isEqualTo(expectedBaggage);
459+
}
460+
461+
@Test
462+
void extract_multiple_headers_duplicate_key() {
463+
W3CBaggagePropagator propagator = W3CBaggagePropagator.getInstance();
464+
465+
Context result =
466+
propagator.extract(
467+
Context.root(),
468+
ImmutableMap.of("baggage", ImmutableList.of("k1=v1", "k1=v2")),
469+
multiGetter);
470+
471+
Baggage expectedBaggage = Baggage.builder().put("k1", "v2").build();
472+
assertThat(Baggage.fromContext(result)).isEqualTo(expectedBaggage);
473+
}
474+
475+
@Test
476+
void extract_multiple_headers_mixed_duplicates_non_duplicates() {
477+
W3CBaggagePropagator propagator = W3CBaggagePropagator.getInstance();
478+
479+
Context result =
480+
propagator.extract(
481+
Context.root(),
482+
ImmutableMap.of("baggage", ImmutableList.of("k1=v1,k2=v0", "k2=v2,k3=v3")),
483+
multiGetter);
484+
485+
Baggage expectedBaggage =
486+
Baggage.builder().put("k1", "v1").put("k2", "v2").put("k3", "v3").build();
487+
assertThat(Baggage.fromContext(result)).isEqualTo(expectedBaggage);
488+
}
489+
490+
@Test
491+
void extract_multiple_headers_all_empty() {
492+
W3CBaggagePropagator propagator = W3CBaggagePropagator.getInstance();
493+
494+
Context result =
495+
propagator.extract(
496+
Context.root(), ImmutableMap.of("baggage", ImmutableList.of("", "")), multiGetter);
497+
498+
Baggage expectedBaggage = Baggage.builder().build();
499+
assertThat(Baggage.fromContext(result)).isEqualTo(expectedBaggage);
500+
}
501+
502+
@Test
503+
void extract_multiple_headers_some_empty() {
504+
W3CBaggagePropagator propagator = W3CBaggagePropagator.getInstance();
505+
506+
Context result =
507+
propagator.extract(
508+
Context.root(), ImmutableMap.of("baggage", ImmutableList.of("", "k=v")), multiGetter);
509+
510+
Baggage expectedBaggage = Baggage.builder().put("k", "v").build();
511+
assertThat(Baggage.fromContext(result)).isEqualTo(expectedBaggage);
512+
}
513+
424514
@Test
425515
void inject_noBaggage() {
426516
W3CBaggagePropagator propagator = W3CBaggagePropagator.getInstance();

context/src/main/java/io/opentelemetry/context/propagation/TextMapGetter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public interface TextMapGetter<C> {
3737
String get(@Nullable C carrier, String key);
3838

3939
/**
40-
* If implemented, returns all values for a given {@code key}, in order.
40+
* If implemented, returns all values for a given {@code key} in order, or returns an empty list.
4141
*
4242
* <p>The default method returns the first value of the given propagation {@code key} as a
4343
* singleton list, or returns an empty list.

0 commit comments

Comments
 (0)