https://bz.apache.org/bugzilla/show_bug.cgi?id=69921

            Bug ID: 69921
           Summary: INTERACTION REPORT: getRequestURI() side effects and
                    Request reuse with processorCache
           Product: Tomcat 10
           Version: 10.1.49
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Catalina
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ------

SUMMARY

Since Tomcat 10.1.16, Request.getRequestURI() uses MessageBytes.toStringType(),
which mutates the internal state of the underlying MessageBytes instance by
converting its type to T_STR.

While this behavior is intentional for performance reasons, in certain
non-standard execution flows it can interact with Tomcat’s processorCache reuse
mechanism in a way that leads to unexpected behavior during request parsing.

This report is not claiming a general Tomcat bug, but documents an interaction
that can be surprising and difficult to diagnose in real-world production
environments, and may benefit from additional documentation or diagnostic
visibility.

ENVIRONMENT

Tomcat: 10.1.49
Java: JDK 17
Spring Boot: 3.4.12
Kotlin Coroutines: 1.8.1

BACKGROUND

MessageBytes behavior change

Bug 66627 made MessageBytes.toString() side-effect free.

Bug 68026 addressed a performance issue caused by repeated String conversions
by introducing toStringType(), which explicitly mutates the internal type to
cache the String representation.

Since Tomcat 10.1.16, Request.getRequestURI() uses toStringType() for
performance reasons.

As a result, calling getRequestURI() now mutates internal parsing state
associated with the request URI.

OBSERVED INTERACTION

Processor reuse

AbstractProtocol uses processorCache to reuse Http11Processor instances.

Each processor owns a Request instance that is recycled and reused across
requests.

Non-standard access scenarios

The same interaction can occur in more than one realistic scenario.

Application-level (coroutines / asynchronous execution)

In our application, Kotlin coroutines are used with explicit propagation of
RequestAttributes to background threads.

This results in concurrent access to a Request instance outside of the normal
request-handling thread, which is outside the servlet specification.

While this is application-level misuse, it exposes an interaction that is
difficult to reason about without detailed knowledge of Tomcat internals.

Infrastructure-level (Java Agents / APM / security agents)

Another realistic scenario involves Java Agents.

APM, security, logging, or tracing agents may use bytecode instrumentation.

Such agents may attempt to collect request metadata.

This can occur before CoyoteAdapter.postParseRequest() is invoked.

In doing so, the agent may call getRequestURI().

In this case, toStringType() is invoked and the internal MessageBytes type is
mutated to T_STR.

When postParseRequest() later processes the same Request instance, it may
observe the mutated state and follow a different parsing path.

Because these agents operate outside the application codebase and are often
configured at the platform or infrastructure level, their presence and timing
can be difficult for application developers to reason about, further
complicating diagnosis.

TIMING-SENSITIVE INTERACTION

A possible sequence is:

A request completes and its Request object is recycled and returned to
processorCache.

The same Request instance is reused for a subsequent request.

While CoyoteAdapter.postParseRequest() is processing the new request,

Either
a) a background thread originating from the previous request, or
b) a Java Agent executing before request parsing
calls getRequestURI().

getRequestURI() invokes toStringType(), mutating the underlying MessageBytes
type from T_BYTES to T_STR.

postParseRequest() observes the mutated state and follows a different parsing
path.

The request may not be mapped to a context, resulting in an intermittent 404
response.

WHY THIS IS SURPRISING

>From an application and operational perspective:

getRequestURI() appears to be a simple accessor.

Its observable side effect (mutating internal parsing state) is not obvious at
the API level.

When external components such as Java Agents are involved, the method may be
called before request parsing in ways that are difficult for users to predict
or control.

While this interaction still requires request-scoped objects to be accessed
outside their intended lifecycle, the triggering conditions are not necessarily
limited to application code.

EVIDENCE

Calling request.getRequestURI() before the undecodedURI.getType() == T_BYTES
branch in postParseRequest() causes the request to fail.

Calling it after that branch completes allows the request to be processed
normally.

This behavior is consistent with the internal MessageBytes type being mutated
during parsing.

RELATED ISSUES

Bug 66627 – MessageBytes.toString() side effects (fixed)

Bug 68026 – Performance issue caused by repeated conversions (fixed via
toStringType())

Bug 69665 – Similar report, closed as WORKSFORME due to lack of a reproducible
test case

This report aims to add additional operational context to that discussion.

SUGGESTIONS (NON-BREAKING)

The primary suggestion is documentation improvement.

In particular, clarifying in Javadoc that getRequestURI() has observable side
effects and mutates internal parsing state would significantly improve
diagnosability when request-scoped objects are accessed outside their intended
lifecycle.

In addition, when request parsing follows an unexpected code path due to prior
internal state changes, additional diagnostics could be helpful.

This could include optional, guarded logging to indicate that request parsing
did not proceed via the typical initial path (for example, when undecodedURI is
no longer in its expected initial state during parsing).

Such diagnostics would not indicate an error in Tomcat itself, but could help
operators more quickly identify unexpected request lifecycle interactions when
intermittent 404 responses are observed in production environments.

WORKAROUND (APPLICATION SIDE)

Avoid propagating Request or RequestAttributes outside the request-handling
thread.

Extract required values (URI, headers) eagerly and pass immutable data to
background tasks.

CLOSING NOTE

This interaction arises from request-scoped objects being accessed outside
their intended lifecycle.

However, because getRequestURI() now has observable side effects and processors
are aggressively reused, the resulting behavior can be non-obvious and
difficult to diagnose, especially when external components such as Java Agents
are involved.

This report is provided for awareness and potential improvements in
documentation and diagnostic visibility.

-- 
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to