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]
