Skip to content

Preserve shared servlet snippet state across response-body reuse - #21133

Draft
trask with Copilot wants to merge 4 commits into
copilot/task-fix-snippet-injection-issuefrom
copilot/trask-upper-shared-response-body-state
Draft

trask with Copilot wants to merge 4 commits into
copilot/task-fix-snippet-injection-issuefrom
copilot/trask-upper-shared-response-body-state

Conversation

Copilot AI commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Share servlet snippet matching and Content-Length state across wrappers that retain the same response body, including includes and async redispatch. Stacked on #21104.

Restart injection when a reset, forward, or error dispatch replaces the body. Preserve native writer/stream ownership and roll back Content-Length only after the old body is discarded.

Container-specific body-discard and writer-ownership behavior outside Jetty 10/11 still needs maintainer review.

@opentelemetry-pr-dashboard

Copy link
Copy Markdown

Pull request dashboard status

Waiting on the author · refreshed 2026-11-02 06:58 UTC

Move out of draft to request review.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

Copilot AI and others added 2 commits November 2, 2026 07:12
Co-authored-by: trask <218610+trask@users.noreply.github.com>
Co-authored-by: trask <218610+trask@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement upper shared response body state layer Preserve shared servlet snippet state across response-body reuse Nov 2, 2026
Copilot AI requested a review from trask November 2, 2026 07:29
@trask
trask requested a balanced review from Copilot November 2, 2026 07:33
resetAsyncResponse(req, resp, true);
return;
} else if ("stream-forward".equals(snippetScenario)) {
resp.setHeader("X-Snippet-Scenario", snippetScenario);
resp.setContentType("text/html");
resp.getOutputStream().write("<head>".getBytes(UTF_8));
} else if ("async-reset".equals(snippetScenario)) {
resp.setHeader("X-Snippet-Scenario", snippetScenario);
asyncContext.dispatch("/test.jsp");
return;
} else if ("error-dispatch".equals(snippetScenario)) {
resp.setHeader("X-Snippet-Scenario", snippetScenario);

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A cached response writer reused after a forward can leave writer ownership cleared, allowing both output paths to advance the shared matcher and miss snippet injection at <head>.

1 open issue
What changed in this PR

This PR restores shared snippet-injection state across Servlet 3 and Servlet 5 response-body reuse, building on #21104’s protection against recycled streams carrying stale state into later requests.

Changes:

  • Share matching, injection, and Content-Length state across wrappers while coordinating writer and stream output.
  • Handle body replacement through resets, forwards, and error dispatches, including cached-writer refresh.
  • Expand lifecycle regression coverage and deploy freshly built servlet WARs into Jetty 10/11 smoke tests.
File Description
smoke-tests/​src/​test/​java/​io/​opentelemetry/​smoketest/​appserver/​JettySmokeTest.java Adds lifecycle scenarios and current-WAR deployment.
smoke-tests/​images/​servlet/​servlet-5.0/​src/​main/​webapp/​WEB-INF/​web.xml Enables async processing and maps error dispatch.
smoke-tests/​images/​servlet/​servlet-5.0/​src/​main/​java/​io/​opentelemetry/​smoketest/​matrix/​SnippetErrorServlet.java Provides the replacement error-page body.
smoke-tests/​images/​servlet/​servlet-5.0/​src/​main/​java/​io/​opentelemetry/​smoketest/​matrix/​SnippetErrorException.java Supplies a dedicated error-dispatch trigger.
smoke-tests/​images/​servlet/​servlet-5.0/​src/​main/​java/​io/​opentelemetry/​smoketest/​matrix/​JspServlet.java Adds forward, reset, and error fixtures.
smoke-tests/​images/​servlet/​servlet-3.0/​src/​main/​webapp/​WEB-INF/​web.xml Enables async processing and maps error dispatch.
smoke-tests/​images/​servlet/​servlet-3.0/​src/​main/​java/​io/​opentelemetry/​smoketest/​matrix/​SnippetErrorServlet.java Provides the replacement error-page body.
smoke-tests/​images/​servlet/​servlet-3.0/​src/​main/​java/​io/​opentelemetry/​smoketest/​matrix/​SnippetErrorException.java Supplies a dedicated error-dispatch trigger.
smoke-tests/​images/​servlet/​servlet-3.0/​src/​main/​java/​io/​opentelemetry/​smoketest/​matrix/​JspServlet.java Adds forward, reset, and error fixtures.
smoke-tests/​build.gradle.kts Packages current servlet WARs as test resources.
instrumentation/​servlet/​servlet-common/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​common/​snippet/​SnippetInjectingPrintWriter.java Accepts shared state and marks writer ownership.
instrumentation/​servlet/​servlet-common/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​common/​snippet/​OutputStreamSnippetInjectionHelper.java Checks writer ownership before stream matching.
instrumentation/​servlet/​servlet-common/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​common/​service/​ServletResponseResetInstrumentation.java Matches response reset methods.
instrumentation/​servlet/​servlet-common/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​common/​service/​RequestDispatcherForwardInstrumentation.java Matches dispatcher forward methods.
instrumentation/​servlet/​servlet-common/​bootstrap/​src/​main/​java/​io/​opentelemetry/​javaagent/​bootstrap/​servlet/​SnippetInjectingResponseWrapper.java Adds Content-Length rollback support.
instrumentation/​servlet/​servlet-common/​bootstrap/​src/​main/​java/​io/​opentelemetry/​javaagent/​bootstrap/​servlet/​InjectionState.java Centralizes shared body and lifecycle state.
instrumentation/​servlet/​servlet-5.0/​testing/​src/​main/​java/​io/​opentelemetry/​instrumentation/​servlet/​v5_0/​TestServlet5.java Adds dispatch and output-ownership fixtures.
instrumentation/​servlet/​servlet-5.0/​jetty11-testing/​src/​test/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​jetty/​dispatch/​JettyServlet5ForwardTest.java Tests replacement bodies and rejected operations.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​unitTests/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​snippet/​InjectionStateLifetimeTest.java Expands shared-state and cached-writer coverage.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​snippet/​ServletOutputStreamInjectionState.java Attaches the shared body state to streams.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​snippet/​Servlet5SnippetInjectingResponseWrapper.java Reuses body state and refreshes cached writers.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​Servlet5ResponseResetAdvice.java Updates state after successful native resets.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​Servlet5ResponseInjectionState.java Associates responses with request-valid state.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​Servlet5RequestDispatcherForwardAdvice.java Tracks forward entry and completion.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​service/​JakartaServletServiceAdvice.java Applies pending forward resets at service entry.
instrumentation/​servlet/​servlet-5.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v5_0/​JakartaServletInstrumentationModule.java Registers reset and forward instrumentation.
instrumentation/​servlet/​servlet-3.0/​testing/​src/​main/​java/​io/​opentelemetry/​instrumentation/​servlet/​v3_0/​TestServlet3.java Adds dispatch and output-ownership fixtures.
instrumentation/​servlet/​servlet-3.0/​testing/​src/​main/​java/​io/​opentelemetry/​instrumentation/​servlet/​v3_0/​jetty/​BaseJettyDispatchTest.java Tests replacement bodies and rejected operations.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​unitTests/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​snippet/​InjectionStateLifetimeTest.java Expands shared-state and cached-writer coverage.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​snippet/​ServletOutputStreamInjectionState.java Attaches the shared body state to streams.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​snippet/​Servlet3SnippetInjectingResponseWrapper.java Reuses body state and refreshes cached writers.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​Servlet3ResponseResetAdvice.java Updates state after successful native resets.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​Servlet3ResponseInjectionState.java Associates responses with request-valid state.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​Servlet3RequestDispatcherForwardAdvice.java Tracks forward entry and completion.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​Servlet3InstrumentationModule.java Registers reset and forward instrumentation.
instrumentation/​servlet/​servlet-3.0/​javaagent/​src/​main/​java/​io/​opentelemetry/​javaagent/​instrumentation/​servlet/​v3_0/​Servlet3Advice.java Applies pending forward resets at service entry.

Review effort: Balanced

Co-authored-by: trask <218610+trask@users.noreply.github.com>
Copilot AI requested a review from trask November 2, 2026 08:11
@trask
trask requested a balanced review from Copilot November 2, 2026 08:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Regression evidence covers Jetty 10/11, but the shared-state reset/forward/error lifecycle changes require maintainer validation of body-discard and writer-ownership behavior on other supported servlet containers.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants