Skip to content

Guard Undertow port() against internal NPE - #12373

Open
dougqh wants to merge 1 commit into
masterfrom
dougqh/fix-undertow-port-npe
Open

Guard Undertow port() against internal NPE#12373
dougqh wants to merge 1 commit into
masterfrom
dougqh/fix-undertow-port-npe

Conversation

@dougqh

@dougqh dougqh commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Guards HttpServerExchangeURIDataAdapter.port() against a NullPointerException thrown from inside Undertow's own HttpServerExchange.getHostPort().

getHostPort()'s internal fallback chain is: parse the Host header → default port for http/https scheme → getDestinationAddress().getPort(). That last fallback NPEs when ServerConnection.getLocalAddress(InetSocketAddress.class) returns null — which happens when there's no (parseable) Host header, the scheme is neither http nor https, and the connection isn't backed by a plain InetSocketAddress (e.g. AJP, a Unix domain socket transport, or a wrapped/detached ServerConnection).

Since this is a bug in Undertow's own fallback logic rather than something we can validate in advance without re-implementing that logic ourselves, the fix wraps the call and falls back to 0 — the same "no port" sentinel URIUtils already treats as "omit the port" (port <= 0).

Motivation

Fixes a NullPointerException reported in Error Tracking (issue, first seen 2025-10-16, regressed 2025-11-19, still recurring):

java.lang.NullPointerException
  at (redacted)
  at io.undertow.core@2.2.37.SP2-redhat-00001//datadog.trace.instrumentation.undertow.HttpServerExchangeURIDataAdapter.port(HttpServerExchangeURIDataAdapter.java:25)
  at datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.onRequest(HttpServerDecorator.java:329)

Additional Notes

No new unit test: HttpServerExchange is a final Undertow class, and this repo pins Mockito 4.4.0 (the inline mock-maker needed to mock final classes isn't a default/existing dependency here). Adding mockito-inline just to force an artificial NPE through a mock wouldn't meaningfully validate the real failure mode anyway (no Host header + non-http/https scheme + non-InetSocketAddress connection) — reliably reproducing that combination would need an integration test against a non-standard connector (e.g. AJP), which is out of scope for this quick fix.

🤖 Generated with Claude Code

Contributor Checklist

Jira ticket: [PROJ-IDENT]

HttpServerExchange.getHostPort() can NPE inside Undertow itself when
there's no Host header, the scheme isn't http/https, and the
connection's local address isn't an InetSocketAddress (e.g. AJP or a
Unix domain socket transport). Fall back to 0 (the existing "no port"
sentinel used throughout URIUtils) rather than letting the NPE
propagate into the instrumented application.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@datadog-official

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 69.74% (+10.72%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 37f9d28 | Docs | View more details | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.68 s 14.63 s [-0.4%; +1.1%] (no difference)
startup:insecure-bank:tracing:Agent 13.67 s 13.67 s [-0.9%; +0.8%] (no difference)
startup:petclinic:appsec:Agent 17.61 s 17.36 s [+0.7%; +2.2%] (maybe worse)
startup:petclinic:iast:Agent 17.48 s 17.42 s [-0.5%; +1.2%] (no difference)
startup:petclinic:profiling:Agent 17.26 s 16.81 s [-1.9%; +7.2%] (no difference)
startup:petclinic:sca:Agent 17.57 s 17.36 s [+0.1%; +2.5%] (maybe worse)
startup:petclinic:tracing:Agent 16.59 s 16.72 s [-1.6%; +0.0%] (no difference)

Commit: 37f9d28d · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

public int port() {
return httpServerExchange.getHostPort();
try {
return httpServerExchange.getHostPort();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm debating how best to add a test.
It could be done via a mock, but in my mind, that does prove much.
To create the problem for real, requires a rather specific undertow set-up, but maybe I (Claude) can figure out how to reconstruct that scenario.

@dougqh
dougqh marked this pull request as ready for review September 2, 2026 02:28
@dougqh
dougqh requested a review from a team as a code owner September 2, 2026 02:28
@dougqh
dougqh requested review from jordan-wong and removed request for a team September 2, 2026 02:28
@dd-octo-sts

dd-octo-sts Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Hi! 👋 Thanks for your pull request! 🎉

To help us review it, please make sure to:

  • Add at least one type, and one component or instrumentation label to the pull request

If you need help, please check our contributing guidelines.

@dd-octo-sts dd-octo-sts Bot added the tag: ai generated Largely based on code generated by an AI or LLM label Sep 2, 2026

@datadog-official datadog-official Bot 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.

Datadog Autotest: FAIL

The new port catch does not stop the null pointer failure. The same request then calls peerPort(), which calls getPort() on the same null destination address outside the URL catch.

Open Bits AI session

🤖 Datadog Autotest · Commit 37f9d28 · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

} catch (final NullPointerException e) {
// Undertow's getHostPort() can NPE internally (e.g. no Host header and a connection whose
// local address isn't an InetSocketAddress, such as AJP or a Unix domain socket transport).
return 0;

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.

P1 Guard the later peer port lookup

The original request still causes a null pointer exception and stops later request decoration.

Assertion details
  • Input: A request has no usable Host port, uses a scheme other than HTTP or HTTPS, and has a null destination address.
  • Expected: The decorator must omit the unavailable port and continue without an exception.
  • Actual: port() returns 0. onRequest() then calls peerPort(), which calls getPort() on the null destination address outside the URL catch.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest · Open Bits AI session

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.

This makes sense.. we should probably instead pass some indication to skip the unavailable port or pass the error on elsewhere?

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

Labels

tag: ai generated Largely based on code generated by an AI or LLM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants