Skip to content

Commit acb078b

Browse files
authored
Wrap request to avoid modifying attributes of the original request (#10389)
1 parent d823ffe commit acb078b

File tree

2 files changed

+69
-51
lines changed

2 files changed

+69
-51
lines changed

instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/v3_1/OpenTelemetryHandlerMappingFilter.java

+35-35
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import java.lang.invoke.MethodHandles;
1717
import java.lang.invoke.MethodType;
1818
import java.util.ArrayList;
19+
import java.util.HashMap;
1920
import java.util.List;
21+
import java.util.Map;
2022
import java.util.Objects;
2123
import javax.annotation.Nullable;
2224
import javax.servlet.Filter;
@@ -26,41 +28,29 @@
2628
import javax.servlet.ServletRequest;
2729
import javax.servlet.ServletResponse;
2830
import javax.servlet.http.HttpServletRequest;
31+
import javax.servlet.http.HttpServletRequestWrapper;
2932
import javax.servlet.http.HttpServletResponse;
3033
import org.springframework.core.Ordered;
3134
import org.springframework.web.servlet.HandlerExecutionChain;
3235
import org.springframework.web.servlet.HandlerMapping;
3336
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
3437

3538
public class OpenTelemetryHandlerMappingFilter implements Filter, Ordered {
36-
private static final String PATH_ATTRIBUTE = getRequestPathAttribute();
3739
private static final MethodHandle usesPathPatternsMh = getUsesPathPatternsMh();
3840
private static final MethodHandle parseAndCacheMh = parseAndCacheMh();
3941

4042
private final HttpServerRouteGetter<HttpServletRequest> serverSpanName =
4143
(context, request) -> {
42-
Object previousValue = null;
43-
if (this.parseRequestPath && PATH_ATTRIBUTE != null) {
44-
previousValue = request.getAttribute(PATH_ATTRIBUTE);
44+
if (this.parseRequestPath) {
4545
// sets new value for PATH_ATTRIBUTE of request
4646
parseAndCache(request);
4747
}
48-
try {
49-
if (findMapping(request)) {
50-
// Name the parent span based on the matching pattern
51-
// Let the parent span resource name be set with the attribute set in findMapping.
52-
return SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME.get(context, request);
53-
}
54-
} finally {
55-
// mimic spring DispatcherServlet and restore the previous value of PATH_ATTRIBUTE
56-
if (this.parseRequestPath && PATH_ATTRIBUTE != null) {
57-
if (previousValue == null) {
58-
request.removeAttribute(PATH_ATTRIBUTE);
59-
} else {
60-
request.setAttribute(PATH_ATTRIBUTE, previousValue);
61-
}
62-
}
48+
if (findMapping(request)) {
49+
// Name the parent span based on the matching pattern
50+
// Let the parent span resource name be set with the attribute set in findMapping.
51+
return SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME.get(context, request);
6352
}
53+
6454
return null;
6555
};
6656

@@ -84,14 +74,39 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
8474
} finally {
8575
if (handlerMappings != null) {
8676
Context context = Context.current();
87-
HttpServerRoute.update(context, CONTROLLER, serverSpanName, (HttpServletRequest) request);
77+
HttpServerRoute.update(context, CONTROLLER, serverSpanName, prepareRequest(request));
8878
}
8979
}
9080
}
9181

9282
@Override
9383
public void destroy() {}
9484

85+
private static HttpServletRequest prepareRequest(ServletRequest request) {
86+
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/10379
87+
// Finding the request handler modifies request attributes. We are wrapping the request to avoid
88+
// this.
89+
return new HttpServletRequestWrapper((HttpServletRequest) request) {
90+
private final Map<String, Object> attributes = new HashMap<>();
91+
92+
@Override
93+
public void setAttribute(String name, Object o) {
94+
attributes.put(name, o);
95+
}
96+
97+
@Override
98+
public Object getAttribute(String name) {
99+
Object value = attributes.get(name);
100+
return value != null ? value : super.getAttribute(name);
101+
}
102+
103+
@Override
104+
public void removeAttribute(String name) {
105+
attributes.remove(name);
106+
}
107+
};
108+
}
109+
95110
/**
96111
* When a HandlerMapping matches a request, it sets HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE
97112
* as an attribute on the request. This attribute is read by SpringWebMvcDecorator.onRequest and
@@ -186,19 +201,4 @@ private static void parseAndCache(HttpServletRequest request) {
186201
throw new IllegalStateException(throwable);
187202
}
188203
}
189-
190-
private static String getRequestPathAttribute() {
191-
try {
192-
Class<?> pathUtilsClass =
193-
Class.forName("org.springframework.web.util.ServletRequestPathUtils");
194-
return (String)
195-
MethodHandles.lookup()
196-
.findStaticGetter(pathUtilsClass, "PATH_ATTRIBUTE", String.class)
197-
.invoke();
198-
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException exception) {
199-
return null;
200-
} catch (Throwable throwable) {
201-
throw new IllegalStateException(throwable);
202-
}
203-
}
204204
}

instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/main/java/org/springframework/web/servlet/v6_0/OpenTelemetryHandlerMappingFilter.java

+34-16
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@
1818
import jakarta.servlet.ServletRequest;
1919
import jakarta.servlet.ServletResponse;
2020
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.HttpServletRequestWrapper;
2122
import jakarta.servlet.http.HttpServletResponse;
2223
import java.io.IOException;
2324
import java.util.ArrayList;
25+
import java.util.HashMap;
2426
import java.util.List;
27+
import java.util.Map;
2528
import java.util.Objects;
2629
import javax.annotation.Nullable;
2730
import org.springframework.core.Ordered;
28-
import org.springframework.http.server.RequestPath;
2931
import org.springframework.web.servlet.HandlerExecutionChain;
3032
import org.springframework.web.servlet.HandlerMapping;
3133
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -35,25 +37,16 @@ public class OpenTelemetryHandlerMappingFilter implements Filter, Ordered {
3537

3638
private final HttpServerRouteGetter<HttpServletRequest> serverSpanName =
3739
(context, request) -> {
38-
RequestPath previousValue = null;
3940
if (this.parseRequestPath) {
40-
previousValue =
41-
(RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
4241
// sets new value for PATH_ATTRIBUTE of request
4342
ServletRequestPathUtils.parseAndCache(request);
4443
}
45-
try {
46-
if (findMapping(request)) {
47-
// Name the parent span based on the matching pattern
48-
// Let the parent span resource name be set with the attribute set in findMapping.
49-
return SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME.get(context, request);
50-
}
51-
} finally {
52-
// mimic spring DispatcherServlet and restore the previous value of PATH_ATTRIBUTE
53-
if (this.parseRequestPath) {
54-
ServletRequestPathUtils.setParsedRequestPath(previousValue, request);
55-
}
44+
if (findMapping(request)) {
45+
// Name the parent span based on the matching pattern
46+
// Let the parent span resource name be set with the attribute set in findMapping.
47+
return SpringWebMvcServerSpanNaming.SERVER_SPAN_NAME.get(context, request);
5648
}
49+
5750
return null;
5851
};
5952

@@ -77,14 +70,39 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
7770
} finally {
7871
if (handlerMappings != null) {
7972
Context context = Context.current();
80-
HttpServerRoute.update(context, CONTROLLER, serverSpanName, (HttpServletRequest) request);
73+
HttpServerRoute.update(context, CONTROLLER, serverSpanName, prepareRequest(request));
8174
}
8275
}
8376
}
8477

8578
@Override
8679
public void destroy() {}
8780

81+
private static HttpServletRequest prepareRequest(ServletRequest request) {
82+
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/10379
83+
// Finding the request handler modifies request attributes. We are wrapping the request to avoid
84+
// this.
85+
return new HttpServletRequestWrapper((HttpServletRequest) request) {
86+
private final Map<String, Object> attributes = new HashMap<>();
87+
88+
@Override
89+
public void setAttribute(String name, Object o) {
90+
attributes.put(name, o);
91+
}
92+
93+
@Override
94+
public Object getAttribute(String name) {
95+
Object value = attributes.get(name);
96+
return value != null ? value : super.getAttribute(name);
97+
}
98+
99+
@Override
100+
public void removeAttribute(String name) {
101+
attributes.remove(name);
102+
}
103+
};
104+
}
105+
88106
/**
89107
* When a HandlerMapping matches a request, it sets HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE
90108
* as an attribute on the request. This attribute is read by SpringWebMvcDecorator.onRequest and

0 commit comments

Comments
 (0)