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

--- Comment #4 from Ivaylo Zhelev <[email protected]> ---
NOTE ON THE ORIGINAL DESCRIPTION
================================

Maintainers: please treat the following as the authoritative restatement of
the Description and disregard the product-specific identifiers in the
original (buildpack version, proxy product names, JRE distribution, route
configuration syntax). They are not relevant to the bug; the issue is
generic.

Generic restatement
-------------------

Tomcat 10.1.55 added (Coyote changelog):

  "Add validation that the HTTP/2 :scheme pseudo-header is consistent with
   the use (or not) of TLS. (markt)"

Implementation in java/org/apache/coyote/http2/Stream.java (~line 551):

    if ("https".equals(value) !=
handler.getProtocol().getHttp11Protocol().isSSLEnabled()) {
        headerException = new StreamException(
            sm.getString("stream.header.inconsistentScheme", ...),
            Http2Error.PROTOCOL_ERROR, getIdAsInt());
    }

The check raises PROTOCOL_ERROR and resets the stream when the inbound
:scheme does not match the connector's SSLEnabled flag.

This breaks a common deployment topology: a TLS-terminating reverse proxy
that forwards HTTP/2 cleartext (h2c) to a backend Tomcat while preserving
the original request's :scheme: https. The pattern applies to any edge
proxy doing TLS termination + h2c upstream (Envoy, nginx, HAProxy, and
others). On upgrade from 10.1.54 to 10.1.55, all HTTP/2 requests through
such a proxy begin failing with PROTOCOL_ERROR; the application logs are
silent because the error is sent on the H2 stream.

Per RFC 9113 section 8.3.1, :scheme reflects the request target's URI
scheme, not the wire's TLS state -- and the spec explicitly contemplates
proxy/gateway translation. So strict equivalence between :scheme: https
and connector.isSSLEnabled() is over-strict for proxied deployments.

Minimal reproducer (no proxy needed)
------------------------------------

Plain Tomcat 10.1.55, default <Connector> with <UpgradeProtocol
Http2Protocol/>,
no TLS:

    curl --http2-prior-knowledge -H ':scheme: https' http://127.0.0.1:8080/
    # curl: (55) Failed sending HTTP request

Same against 10.1.54 -> 200 OK.

Why connector-side workarounds don't help
-----------------------------------------

- scheme="https" on the <Connector> does NOT affect this check; the codec
  compares against connector.isSSLEnabled(), not the scheme attribute.
- SSLEnabled="true" forces a real TLS handshake on the plaintext socket
  and fails at connect time.
- RemoteIpValve runs after stream-header validation and so cannot prevent
  the PROTOCOL_ERROR.

Operators currently have only two options: pin to 10.1.54, or reconfigure
the proxy to forward HTTP/1.1 (losing h2c upstream).

Proposed fix
------------

Provide an explicit operator opt-in that lets the codec accept a :scheme
mismatch when the operator has declared the connection is behind a
TLS terminator. Two reasonable shapes:

(a) New attribute on Http2Protocol:

    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol"
                     relaxedSchemeValidation="true" />

(b) Honor the existing Connector secure="true" attribute in the check,
since secure="true" is already the documented way to say "treat this
connection as TLS-secured even though SSL is not terminated here":

    boolean secureConnection =
        handler.getProtocol().getHttp11Protocol().isSSLEnabled()
        || handler.getProtocol().getHttp11Protocol().getSecure();

    if ("https".equals(value) != secureConnection) { ... }

(b) is preferable: no new attribute, no new docs, and existing
TLS-offload deployments that already have secure="true" recover
automatically once the patch ships.

Severity
--------

Major: silent regression in a very common deployment pattern; symptom is
"all HTTP/2 requests fail/hang" with no obvious indicator pointing to
:scheme. Several days of bisecting were needed to locate it.

Environment
-----------

- Tomcat 10.1.55 (regression); 10.1.54 last good
- A TLS-terminating reverse proxy forwarding h2c to the backend connector
- Connector: plain (non-TLS) HTTP/1.1 with <UpgradeProtocol Http2Protocol/>

-- 
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