Skip to content

Commit 24bfeea

Browse files
committed
Add mixed-classpath scenario and Tomcat http.headers collection
Two related follow-ups to the #812 servlet-commons unification, both about HTTP request-tag collection on the Tomcat/servlet path. 1. spring-6.x-mixed-servlet-scenario — an end-to-end repro of the #13938 field condition: a Jakarta Spring 6 WAR that ALSO carries javax.servlet-api:4.0.1 at compile scope, so both javax.servlet.* (from WEB-INF/lib) and jakarta.servlet.* (from the Tomcat 10.1 container) are loadable in the deployed webapp at once. The scenario asserts http.headers and http.params are still collected on the jakarta entry span, proving servlet-commons' runtime instanceof dispatch resolves the live request under a mixed classpath — previously only covered by the HttpRequestWrappersTest unit test. One version by design (the resolution is version-independent; version compatibility is already covered by spring-6.x-scenario). 2. Tomcat http.headers — http.headers was emitted only by the Spring MVC plugin, so plugin.http.include_http_headers was a no-op for plain servlet apps served by the Tomcat plugin. Add INCLUDE_HTTP_HEADERS + HTTP_HEADERS_LENGTH_THRESHOLD to TomcatPluginConfig.Plugin.Http — the shared plugin.http.* namespace already used by the Spring MVC and HttpClient plugins, so no new config key and no agent.config change — and collect the configured request headers in TomcatInvokeInterceptor, guarded no-op when the config is empty. Proven on the dedicated Tomcat scenarios, where the Tomcat plugin owns the entry span: tomcat-9x (javax, Tomcat 9) and tomcat-10x (jakarta, Tomcat 10) now carry a mock_header header and a q1 query param on their internal self-call and assert http.headers + http.params, which also fills the previously-missing jakarta http.params coverage.
1 parent 64035d6 commit 24bfeea

28 files changed

Lines changed: 1185 additions & 4 deletions

File tree

.github/workflows/plugins-jdk17-test.1.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ jobs:
7171
matrix:
7272
case:
7373
- spring-6.x-scenario
74+
- spring-6.x-mixed-servlet-scenario
7475
- spring-boot-3.x-scenario
7576
- struts2.7-scenario
7677
- resteasy-6.x-scenario

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Release Notes.
66
------------------
77

88
* Fix `plugin.http.include_http_headers` not working on Spring Boot 3.x / Jakarta EE (apache/skywalking#13938).
9+
* Support `plugin.http.include_http_headers` in the Tomcat plugin, collecting the configured request headers as the `http.headers` tag on Tomcat entry spans (e.g. RESTEasy on Tomcat), consistent with the Spring MVC plugin. Honors the shared `plugin.http.http_headers_length_threshold`.
910
* Unify the Tomcat plugins into a single `tomcat` plugin (Tomcat 7 - 10) and the Jetty server plugins into a single `jetty-server` plugin (Jetty 9 - 11), each supporting both `javax.servlet` and `jakarta.servlet`. Breaking change: plugin names `tomcat-7.x/8.x` and `tomcat-10.x` are replaced by `tomcat`, and `jetty-server-9.x` and `jetty-server-11.x` by `jetty-server`; update `plugin.exclude_plugins` if you reference the old names.
1011
* Add a Jetty 12 server plugin (`jetty-server-12.x`). Jetty 12 removed the `HttpChannel` handle target and moved request handling to the async `Server#handle(Request, Response, Callback)` core API, so it needs a separate plugin from the merged `jetty-server`.
1112
* Add a Struts 7 plugin (`struts2-7.x`) for Jakarta Struts, whose `DefaultActionInvocation` moved to `org.apache.struts2`.

apm-sniffer/apm-sdk-plugin/tomcat-plugin/src/main/java/org/apache/skywalking/apm/plugin/tomcat/TomcatInvokeInterceptor.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@
3737
import org.apache.tomcat.util.http.Parameters;
3838

3939
import java.lang.reflect.Method;
40+
import java.util.ArrayList;
41+
import java.util.Collections;
4042
import java.util.Enumeration;
4143
import java.util.HashMap;
44+
import java.util.List;
4245
import java.util.Map;
4346

4447
public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor {
@@ -64,6 +67,10 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr
6467
if (TomcatPluginConfig.Plugin.Tomcat.COLLECT_HTTP_PARAMS) {
6568
collectHttpParam(request, span);
6669
}
70+
71+
if (!CollectionUtil.isEmpty(TomcatPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS)) {
72+
collectHttpHeaders(request, span);
73+
}
6774
}
6875

6976
@Override
@@ -115,4 +122,25 @@ private void collectHttpParam(Request request, AbstractSpan span) {
115122
Tags.HTTP.PARAMS.set(span, tagValue);
116123
}
117124
}
125+
126+
private void collectHttpHeaders(Request request, AbstractSpan span) {
127+
final List<String> headersList = new ArrayList<>(TomcatPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.size());
128+
TomcatPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream()
129+
.filter(headerName -> request.getHeaders(headerName) != null)
130+
.forEach(headerName -> {
131+
Enumeration<String> headerValues = request.getHeaders(headerName);
132+
List<String> valueList = Collections.list(headerValues);
133+
if (!CollectionUtil.isEmpty(valueList)) {
134+
headersList.add(headerName + "=" + valueList);
135+
}
136+
});
137+
138+
if (!headersList.isEmpty()) {
139+
String tagValue = String.join("\n", headersList);
140+
tagValue = TomcatPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ?
141+
StringUtil.cut(tagValue, TomcatPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) :
142+
tagValue;
143+
Tags.HTTP.HEADERS.set(span, tagValue);
144+
}
145+
}
118146
}

apm-sniffer/apm-sdk-plugin/tomcat-plugin/src/main/java/org/apache/skywalking/apm/plugin/tomcat/TomcatPluginConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package org.apache.skywalking.apm.plugin.tomcat;
2020

21+
import java.util.List;
2122
import org.apache.skywalking.apm.agent.core.boot.PluginConfig;
2223

2324
public class TomcatPluginConfig {
@@ -38,6 +39,19 @@ public static class Http {
3839
* for the sake of performance
3940
*/
4041
public static int HTTP_PARAMS_LENGTH_THRESHOLD = 1024;
42+
43+
/**
44+
* When {@link Http#INCLUDE_HTTP_HEADERS} declares header names, this threshold controls the length
45+
* limitation of all header values. use negative values to keep and send the complete headers.
46+
* Note. this config item is added for the sake of performance.
47+
*/
48+
public static int HTTP_HEADERS_LENGTH_THRESHOLD = 2048;
49+
50+
/**
51+
* It controls what header data should be collected, this is for security purpose, values must be in lower
52+
* case. Shares the {@code plugin.http.include_http_headers} config key with the other HTTP server plugins.
53+
*/
54+
public static List<String> INCLUDE_HTTP_HEADERS;
4155
}
4256
}
4357
}

0 commit comments

Comments
 (0)