Skip to content

Commit 5a392fd

Browse files
authored
Fix spring-webflux header parsing for 7+ (#15502)
1 parent 4b41870 commit 5a392fd

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxTextMapGetter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ enum WebfluxTextMapGetter implements TextMapGetter<ServerWebExchange> {
1818

1919
@Override
2020
public Iterable<String> keys(ServerWebExchange exchange) {
21-
return exchange.getRequest().getHeaders().keySet();
21+
return HeaderUtil.getKeys(exchange.getRequest().getHeaders());
2222
}
2323

2424
@Nullable

instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/HeaderUtil.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
77

88
import static java.util.Collections.emptyList;
9+
import static java.util.Collections.emptySet;
910

1011
import java.lang.invoke.MethodHandle;
1112
import java.lang.invoke.MethodHandles;
1213
import java.lang.invoke.MethodType;
1314
import java.util.List;
15+
import java.util.Map;
16+
import java.util.Set;
1417
import java.util.function.Supplier;
1518
import javax.annotation.Nullable;
1619
import org.springframework.http.HttpHeaders;
@@ -23,6 +26,7 @@
2326
public final class HeaderUtil {
2427

2528
@Nullable private static final MethodHandle GET_HEADERS;
29+
@Nullable private static final MethodHandle HEADER_NAMES;
2630

2731
static {
2832
GET_HEADERS =
@@ -32,6 +36,17 @@ public final class HeaderUtil {
3236
() ->
3337
findGetHeadersMethod(
3438
MethodType.methodType(List.class, Object.class))); // before spring web 7.0
39+
40+
// Spring Web 7+
41+
MethodHandle headerNames = null;
42+
try {
43+
headerNames =
44+
MethodHandles.lookup()
45+
.findVirtual(HttpHeaders.class, "headerNames", MethodType.methodType(Set.class));
46+
} catch (Throwable t) {
47+
// ignore - will fall back to casting to Map
48+
}
49+
HEADER_NAMES = headerNames;
3550
}
3651

3752
@Nullable
@@ -64,5 +79,24 @@ public static List<String> getHeader(HttpHeaders headers, String name) {
6479
return emptyList();
6580
}
6681

82+
@SuppressWarnings("unchecked") // HttpHeaders is a Map in Spring Web 6 and earlier
83+
public static Set<String> getKeys(HttpHeaders headers) {
84+
if (HEADER_NAMES != null) {
85+
// Spring Web 7: HttpHeaders has headerNames() method
86+
try {
87+
Set<String> result = (Set<String>) HEADER_NAMES.invoke(headers);
88+
if (result != null) {
89+
return result;
90+
}
91+
} catch (Throwable t) {
92+
// ignore
93+
}
94+
} else {
95+
// Spring Web 6 and earlier: HttpHeaders extends Map
96+
return ((Map<String, List<String>>) headers).keySet();
97+
}
98+
return emptySet();
99+
}
100+
67101
private HeaderUtil() {}
68102
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.webflux.v5_3;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import org.junit.jupiter.api.Test;
13+
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
14+
import org.springframework.mock.web.server.MockServerWebExchange;
15+
16+
class WebfluxTextMapGetterTest {
17+
18+
@Test
19+
void testGet() {
20+
MockServerHttpRequest request =
21+
MockServerHttpRequest.get("/test")
22+
.header("traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01")
23+
.header("custom-header", "custom-value")
24+
.build();
25+
26+
MockServerWebExchange exchange = MockServerWebExchange.from(request);
27+
28+
String traceparent = WebfluxTextMapGetter.INSTANCE.get(exchange, "traceparent");
29+
assertThat(traceparent).isEqualTo("00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01");
30+
31+
String customHeader = WebfluxTextMapGetter.INSTANCE.get(exchange, "custom-header");
32+
assertThat(customHeader).isEqualTo("custom-value");
33+
}
34+
35+
@Test
36+
void testGetAll() {
37+
MockServerHttpRequest request =
38+
MockServerHttpRequest.get("/test")
39+
.header("accept", "application/json")
40+
.header("accept", "text/html")
41+
.build();
42+
43+
MockServerWebExchange exchange = MockServerWebExchange.from(request);
44+
45+
List<String> acceptHeaders = new ArrayList<>();
46+
WebfluxTextMapGetter.INSTANCE.getAll(exchange, "accept").forEachRemaining(acceptHeaders::add);
47+
48+
assertThat(acceptHeaders).containsExactly("application/json", "text/html");
49+
}
50+
51+
@Test
52+
void testKeysWithBaggageHeader() {
53+
MockServerHttpRequest request =
54+
MockServerHttpRequest.get("/test")
55+
.header("traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01")
56+
.header("baggage", "test-baggage-key-1=test-baggage-value-1")
57+
.build();
58+
59+
MockServerWebExchange exchange = MockServerWebExchange.from(request);
60+
61+
Iterable<String> keys = WebfluxTextMapGetter.INSTANCE.keys(exchange);
62+
assertThat(keys).contains("traceparent", "baggage");
63+
64+
String baggageValue = WebfluxTextMapGetter.INSTANCE.get(exchange, "baggage");
65+
assertThat(baggageValue).isEqualTo("test-baggage-key-1=test-baggage-value-1");
66+
}
67+
68+
@Test
69+
void testKeysWithMultipleBaggageHeaders() {
70+
MockServerHttpRequest request =
71+
MockServerHttpRequest.get("/test")
72+
.header("traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01")
73+
.header("baggage", "test-baggage-key-1=test-baggage-value-1")
74+
.header("baggage", "test-baggage-key-2=test-baggage-value-2")
75+
.header("x-custom", "custom-value")
76+
.build();
77+
78+
MockServerWebExchange exchange = MockServerWebExchange.from(request);
79+
80+
Iterable<String> keys = WebfluxTextMapGetter.INSTANCE.keys(exchange);
81+
assertThat(keys).contains("traceparent", "baggage", "x-custom");
82+
83+
List<String> baggageValues = new ArrayList<>();
84+
WebfluxTextMapGetter.INSTANCE.getAll(exchange, "baggage").forEachRemaining(baggageValues::add);
85+
86+
assertThat(baggageValues)
87+
.containsExactly(
88+
"test-baggage-key-1=test-baggage-value-1", "test-baggage-key-2=test-baggage-value-2");
89+
}
90+
}

0 commit comments

Comments
 (0)