This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 3623d9cf1b Fix BZ 70091. Add allowSchemeMismatch to Http2Protocol
3623d9cf1b is described below

commit 3623d9cf1bc4acea492ba1e5fc8ee3c9147ebb0b
Author: Mark Thomas <[email protected]>
AuthorDate: Thu May 28 17:51:13 2026 +0100

    Fix BZ 70091. Add allowSchemeMismatch to Http2Protocol
---
 java/org/apache/coyote/http2/Http2Protocol.java    | 27 ++++++++++++--
 java/org/apache/coyote/http2/Stream.java           | 12 ++++---
 .../apache/coyote/http2/TestHttp2Section_8_3.java  | 41 +++++++++++++++++++---
 webapps/docs/changelog.xml                         |  5 +++
 webapps/docs/config/http2.xml                      |  8 +++++
 5 files changed, 81 insertions(+), 12 deletions(-)

diff --git a/java/org/apache/coyote/http2/Http2Protocol.java 
b/java/org/apache/coyote/http2/Http2Protocol.java
index 0db1a496f8..f99db50ff2 100644
--- a/java/org/apache/coyote/http2/Http2Protocol.java
+++ b/java/org/apache/coyote/http2/Http2Protocol.java
@@ -50,8 +50,8 @@ import org.apache.tomcat.util.net.SocketWrapperBase;
 import org.apache.tomcat.util.res.StringManager;
 
 /**
- * HTTP/2 protocol handler. Implements the {@link UpgradeProtocol} interface 
to allow HTTP/2 to be used as an
- * upgrade from HTTP/1.1 or via ALPN.
+ * HTTP/2 protocol handler. Implements the {@link UpgradeProtocol} interface 
to allow HTTP/2 to be used as an upgrade
+ * from HTTP/1.1 or via ALPN.
  */
 public class Http2Protocol implements UpgradeProtocol {
 
@@ -120,6 +120,7 @@ public class Http2Protocol implements UpgradeProtocol {
 
     private boolean initiatePingDisabled = false;
     private boolean useSendfile = true;
+    private boolean allowSchemeMismatch = false;
     // Compression
     private final CompressionConfig compressionConfig = new 
CompressionConfig();
     // Reference to HTTP/1.1 protocol that this instance is configured under
@@ -208,6 +209,28 @@ public class Http2Protocol implements UpgradeProtocol {
     }
 
 
+    /**
+     * Are HTTP/2 streams allowed to provide a scheme that is inconsistent 
with the transport over which the stream was
+     * received?
+     *
+     * @return {@code true} if a mismatched scheme is permitted, otherwise 
{@code false}
+     */
+    public boolean getAllowSchemeMismatch() {
+        return allowSchemeMismatch;
+    }
+
+
+    /**
+     * Configure whether HTTP/2 streams atr allowed to provide a scheme that 
is inconsistent with the transport over
+     * which the stream was received
+     *
+     * @param allowSchemeMismatch {@code true} if a mismatched scheme is 
permitted, otherwise {@code false}
+     */
+    public void setAllowSchemeMismatch(boolean allowSchemeMismatch) {
+        this.allowSchemeMismatch = allowSchemeMismatch;
+    }
+
+
     /**
      * Returns the read timeout in milliseconds.
      *
diff --git a/java/org/apache/coyote/http2/Stream.java 
b/java/org/apache/coyote/http2/Stream.java
index 8dcae75fdb..b34ad478b7 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -395,9 +395,10 @@ class Stream extends AbstractNonZeroStream implements 
HeaderEmitter {
             case ":scheme": {
                 if (coyoteRequest.scheme().isNull()) {
                     coyoteRequest.scheme().setString(value);
-                    // Check scheme is consistent with TLS usage
-                    if ("https".equals(value) != ((AbstractHttp11Protocol<?>) 
handler.getProtocol().getHttp11Protocol())
-                            .isSSLEnabled()) {
+                    // Check scheme is consistent with TLS usage when required 
to be
+                    if (!handler.getProtocol().getAllowSchemeMismatch() && 
"https"
+                            .equals(value) != ((AbstractHttp11Protocol<?>) 
handler.getProtocol().getHttp11Protocol())
+                                    .isSSLEnabled()) {
                         headerException =
                                 new StreamException(
                                         
sm.getString("stream.header.inconsistentScheme", getConnectionId(),
@@ -592,8 +593,9 @@ class Stream extends AbstractNonZeroStream implements 
HeaderEmitter {
         } else if (Method.CONNECT.equals(coyoteRequest.getMethod())) {
             // CONNECT only
             if (!coyoteRequest.scheme().isNull() || 
!coyoteRequest.requestURI().isNull()) {
-                throw new 
StreamException(sm.getString("stream.header.invalidConnect", getConnectionId(),
-                        getIdAsString()), Http2Error.PROTOCOL_ERROR, 
getIdAsInt());
+                throw new StreamException(
+                        sm.getString("stream.header.invalidConnect", 
getConnectionId(), getIdAsString()),
+                        Http2Error.PROTOCOL_ERROR, getIdAsInt());
             }
             if (coyoteRequest.serverName().isNull()) {
                 missingHeader = true;
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_8_3.java 
b/test/org/apache/coyote/http2/TestHttp2Section_8_3.java
index 790b409405..ae35f5ccb1 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_8_3.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_8_3.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.catalina.connector.Connector;
 import org.apache.tomcat.util.http.Method;
 
 /**
@@ -36,19 +37,42 @@ public class TestHttp2Section_8_3 extends Http2TestBase {
 
     @Test
     public void testSchemeInconsistencyNonTLS() throws Exception {
-        testSchemeInconsistency(false);
+        testSchemeInconsistency(false, false);
     }
 
 
     @Test
     public void testSchemeInconsistencyTLS() throws Exception {
-        testSchemeInconsistency(true);
+        testSchemeInconsistency(true, false);
     }
 
 
-    private void testSchemeInconsistency(boolean connectionUsesTls) throws 
Exception {
+    @Test
+    public void testSchemeInconsistencyNonTLSMismatchAllowed() throws 
Exception {
+        testSchemeInconsistency(false, true);
+    }
+
+
+    @Test
+    public void testSchemeInconsistencyTLSMismatchAllowed() throws Exception {
+        testSchemeInconsistency(true, true);
+    }
+
+
+    private void testSchemeInconsistency(boolean connectionUsesTls, boolean 
allowSchemeMismatch) throws Exception {
         // Start HTTP/2 over non-TLS connection
-        http2Connect(connectionUsesTls);
+        enableHttp2(connectionUsesTls);
+        if (allowSchemeMismatch) {
+            Connector connector = getTomcatInstance().getConnector();
+            Http2Protocol http2Protocol = (Http2Protocol) 
connector.findUpgradeProtocols()[0];
+            http2Protocol.setAllowSchemeMismatch(allowSchemeMismatch);
+        }
+
+        configureAndStartWebApplication();
+        openClientConnection(connectionUsesTls);
+        doHttpUpgrade();
+        sendClientPreface();
+        validateHttp2InitialResponse();
 
         byte[] frameHeader = new byte[9];
         ByteBuffer headersPayload = ByteBuffer.allocate(128);
@@ -67,8 +91,15 @@ public class TestHttp2Section_8_3 extends Http2TestBase {
 
         writeFrame(frameHeader, headersPayload);
 
+        // Read first response frame
         parser.readFrame();
 
-        Assert.assertEquals("3-RST-[1]\n", output.getTrace());
+        if (allowSchemeMismatch) {
+            // Normal response. There will be a data frame with the body too.
+            parser.readFrame();
+            Assert.assertEquals(output.getTrace(), getSimpleResponseTrace(3));
+        } else {
+            Assert.assertEquals("3-RST-[1]\n", output.getTrace());
+        }
     }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 5d14988bac..6cee7bc272 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -259,6 +259,11 @@
       <fix>
         Avoid overflow scenarios in Asn1Parser. (remm)
       </fix>
+      <fix>
+        <bug>70091</bug>: Add a new attribute, <code>allowSchemeMismatch</code>
+        to <code>Http2Protocol</code> that allows the consistency check for the
+        scheme provided by the user agent to be bypassed. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">
diff --git a/webapps/docs/config/http2.xml b/webapps/docs/config/http2.xml
index b8411a79aa..a87d1f7103 100644
--- a/webapps/docs/config/http2.xml
+++ b/webapps/docs/config/http2.xml
@@ -80,6 +80,14 @@
 
   <attributes>
 
+    <attribute name="allowSchemeMismatch" required="false">
+      <p>A boolean value which can be used to enable or disable the consistency
+      check for the scheme presented by the user agent in the HTTP/2 stream and
+      the channel over which the stream was received. This should only be set 
to
+      <code>true</code> where a trusted TLS terminating reverse proxy is used.
+      If not specified, this attribute is set to <code>false</code>.</p>
+    </attribute>
+
     <attribute name="allowedTrailerHeaders" required="false">
       <p>By default Tomcat will ignore all trailer headers when processing
       HTTP/2 connections. For a header to be processed, it must be added to 
this


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to