Skip to content

Fix empty response body on Jetty HttpClient 9.4.24–9.4.43#16406

Merged
trask merged 3 commits intoopen-telemetry:mainfrom
imrahul23:fix/jetty-httpclient-demanded-content-listener
Mar 9, 2026
Merged

Fix empty response body on Jetty HttpClient 9.4.24–9.4.43#16406
trask merged 3 commits intoopen-telemetry:mainfrom
imrahul23:fix/jetty-httpclient-demanded-content-listener

Conversation

@imrahul23
Copy link
Copy Markdown
Contributor

Fixes #16405

Summary

  • Reflectively load Response.DemandedContentListener (introduced in Jetty 9.4.24) and include it in the proxy's interface list in JettyClientWrapUtil
  • Add regression test covering both ContentResponse and InputStreamResponseListener content delivery

Problem

In Jetty 9.4.24–~9.4.43, Response.DemandedContentListener is a standalone interface — AsyncContentListener and ContentListener do not extend it. Jetty's HttpReceiver.ContentListeners filters listeners via instanceof DemandedContentListener before delivering content.

JettyClientWrapUtil wraps response listeners in a java.lang.reflect.Proxy whose interface list does not include DemandedContentListener. The proxy fails the instanceof check, gets filtered out, and response content is never delivered — resulting in empty response bodies.

In later Jetty versions (~9.4.44+), AsyncContentListener extends DemandedContentListener, so the proxy inherits it automatically and the bug does not manifest.

Fix

DemandedContentListener does not exist in Jetty 9.2 (the compile target), so it is loaded reflectively via Class.forName(). If present, it is added to the listenerInterfaces array. If absent (pre-9.4.24), it is a no-op.

Test plan

  • JettyHttpClient9HttpCallTest.contentResponseBodyIsDelivered — synchronous ContentResponse path
  • JettyHttpClient9HttpCallTest.inputStreamListenerBodyIsDelivered — async InputStreamResponseListener path
  • Existing JettyHttpClient9LibraryTest suite passes
  • Verified against Jetty 9.2 base (no DemandedContentListener — reflective load returns null, no-op)
  • Verified against Jetty 9.4.24 + agent (previously failed, now passes)
  • Verified against Jetty 9.4.56 (latest 9.x — already worked, still works)

Made with Cursor

@imrahul23 imrahul23 requested a review from a team as a code owner March 6, 2026 20:23
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Mar 6, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: imrahul23 / name: imrahul23 (7d026ea)
  • ✅ login: laurit / name: Lauri Tulmin (9595a6e)

In Jetty 9.4.24, Response.DemandedContentListener was introduced as a
standalone interface. HttpReceiver.ContentListeners filters listeners via
instanceof DemandedContentListener before delivering content. In versions
9.4.24–9.4.43, AsyncContentListener and ContentListener do not extend
DemandedContentListener, so the Proxy created by JettyClientWrapUtil
fails the instanceof check and content is never delivered.

Fix by reflectively loading DemandedContentListener (when available) and
including it in the proxy's interface list.

Fixes open-telemetry#16405

Made-with: Cursor
@imrahul23 imrahul23 force-pushed the fix/jetty-httpclient-demanded-content-listener branch from b4974f7 to 6a9b2b0 Compare March 6, 2026 20:29
@laurit laurit added this to the v2.26.0 milestone Mar 9, 2026
@trask trask merged commit 6abd079 into open-telemetry:main Mar 9, 2026
177 of 179 checks passed
@otelbot
Copy link
Copy Markdown
Contributor

otelbot Bot commented Mar 9, 2026

Thank you for your contribution @imrahul23! 🎉 We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this survey.

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.

jetty-httpclient: empty response body on Jetty 9.4.24–9.4.43 due to missing DemandedContentListener on proxy

3 participants