Skip to content

Fix Play server spans losing URL tags on a malformed Host port - #12790

Draft
dougqh wants to merge 6 commits into
masterfrom
dougqh/play-host-header-port
Draft

dougqh wants to merge 6 commits into
masterfrom
dougqh/play-host-header-port

Conversation

@dougqh

@dougqh dougqh commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Parses the port in Play's Host header without throwing.

RequestURIDataAdapter (Play 2.3, 2.4, 2.6) split request.host() on its last : and called Integer.parseInt on the remainder, with no catch. Any malformed port threw from the constructor, and so did a bracketed IPv6 host with no port ([::1], where the last : sits inside the brackets).

  • Adds HostHeader to internal-api (datadog.trace.bootstrap.instrumentation.api). It splits host, host:port, [ipv6] and [ipv6]:port without throwing, using IntStringUtils.parseNonNegativeInt for the port.
  • The three Play adapters use it. A missing or malformed port is reported as 0, which is what a missing port already produced.

Motivation

The Host header comes from the client. HttpServerDecorator.onRequest calls url(request) inside a try { ... } catch (Exception), so the exception didn't reach the application, but it skipped everything after it in that block:

  • http.url and http.hostname
  • the URL-based resource name
  • callIGCallbackURI, which passes the request URI to AppSec

So a request with a malformed Host port produced a server span without URL tags, and its URI was never passed to AppSec.

Additional Notes

  • Stacked on Fix Oracle and jTDS JDBC URL parsing failures (quick fix) #12780, which adds IntStringUtils. I'll retarget this to master once Fix Oracle and jTDS JDBC URL parsing failures (quick fix) #12780 merges.
  • HostHeaderTest (JUnit 5, @TableTest) covers the split, including IPv6, empty, non-numeric, negative and overflowing ports.
  • The Play 2.6 suite passes on Java 8 (294 tests, 0 failures), the only JVM it supports. Play 2.3 and 2.4 compile; their adapters are identical to 2.6.
  • No Play-level test sends a malformed Host header yet. The existing Play server tests are Groovy, and the adapter is now a two-line delegation to HostHeader.
  • LambdaEventParser.stripPort duplicates HostHeader.host. I left it alone here because it's on a different branch of the stack.

🤖 Generated with Claude Code

dougqh and others added 6 commits October 7, 2026 17:25
Oracle: a second ':' after the port (EZConnect :server_type, IPv6 literals,
EZConnect Plus ?params) or a protocol prefix other than ldap:// (tcp://,
tcps://) was read as host:port:sid, so a non-numeric segment was parsed as
the port and threw NumberFormatException. The service form now strips the
optional :server_type and /instance_name suffixes, any <proto>:// prefix is
skipped, ?params are dropped, and port parsing no longer throws.

jTDS: the port and database separators were searched across the whole URL
remainder from an offset derived from the type name, so a ':' or '/' in a
property value threw StringIndexOutOfBoundsException and short host names
lost their port. The address is now split at ';', then '/', then ':'.

In both cases the exception was caught in parse(), dropping host, port and
instance from DB spans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Numbers.parseNonNegativeInt(CharSequence[, start, end]), which returns -1
for empty, non-digit, signed or overflowing input instead of throwing, and
parses a range in place without a substring.

Route every port parse in JDBCConnectionUrlParser through it. This removes
the catch-and-ignore NumberFormatException blocks, and the sites that let
the exception escape to parse()'s catch-all (MODIFIED_URL_LIKE, DERBY, the
regex-matched ORACLE_AT_DESCRIPTION / MARIA_ADDRESS ports) now keep host and
instance when the port is malformed instead of discarding the whole URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tils

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirrors LongStringUtils naming. It lives in internal-api rather than next to
LongStringUtils in dd-trace-api so the published API jar does not grow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RequestURIDataAdapter split the client-supplied Host header on its last ':'
and ran Integer.parseInt on the rest, so a malformed port, or an IPv6 host
without a port ("[::1]"), threw from the constructor. HttpServerDecorator
catches that around url(request), which skipped http.url, http.hostname,
the URL resource name and the AppSec request URI callback for the request.

Add HostHeader (internal-api) to split host and port without throwing,
treating ':' inside an IPv6 literal as part of the host, and use it in the
Play 2.3, 2.4 and 2.6 adapters. A missing or malformed port is reported as 0,
as a missing port already was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dougqh dougqh added type: bug fix Bug fix inst: play framework Play Framework instrumentation tag: ai generated Largely based on code generated by an AI or LLM labels Oct 8, 2026
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
• Patch Coverage: 100.00%
• Overall Coverage: 59.40% (-0.05%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 18793b5 | Docs | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Oct 8, 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.76 s 14.63 s [-0.1%; +1.8%] (no difference)
startup:insecure-bank:tracing:Agent 13.63 s 13.64 s [-0.9%; +0.7%] (no difference)
startup:petclinic:appsec:Agent 17.24 s 16.70 s [-1.2%; +7.6%] (no difference)
startup:petclinic:iast:Agent 16.84 s 17.05 s [-2.0%; -0.4%] (maybe better)
startup:petclinic:profiling:Agent 16.88 s 16.94 s [-1.4%; +0.6%] (no difference)
startup:petclinic:sca:Agent 17.15 s 17.07 s [-0.3%; +1.3%] (no difference)
startup:petclinic:tracing:Agent 16.20 s 16.20 s [-1.0%; +1.0%] (no difference)

Commit: 18793b51 · 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.

Base automatically changed from dougqh/jdbc-url-parser-oracle-jtds to master October 9, 2026 21:02

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

inst: play framework Play Framework instrumentation tag: ai generated Largely based on code generated by an AI or LLM type: bug fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant