This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new 9394e40506 Fix BZ 70091. Add allowSchemeMismatch to Http2Protocol
9394e40506 is described below
commit 9394e405067ea4d3ec79f32c3e532a13c6624b92
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 | 21 ++++++-----
.../apache/coyote/http2/TestHttp2Section_8_3.java | 41 +++++++++++++++++++---
webapps/docs/changelog.xml | 5 +++
webapps/docs/config/http2.xml | 8 +++++
5 files changed, 87 insertions(+), 15 deletions(-)
diff --git a/java/org/apache/coyote/http2/Http2Protocol.java
b/java/org/apache/coyote/http2/Http2Protocol.java
index ad5e4f590d..9f1fdaea8f 100644
--- a/java/org/apache/coyote/http2/Http2Protocol.java
+++ b/java/org/apache/coyote/http2/Http2Protocol.java
@@ -40,8 +40,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 {
@@ -107,6 +107,7 @@ public class Http2Protocol implements UpgradeProtocol {
private boolean initiatePingDisabled = false;
private boolean useSendfile = true;
+ private boolean allowSchemeMismatch = false;
// Reference to HTTP/1.1 protocol that this instance is configured under
private AbstractHttp11Protocol<?> http11Protocol = null;
@@ -193,6 +194,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 932b5a3de8..1502dc7509 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -397,12 +397,16 @@ 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) !=
handler.getProtocol().getHttp11Protocol().isSSLEnabled()) {
- headerException = new StreamException(
-
sm.getString("stream.header.inconsistentScheme", getConnectionId(),
getIdAsString(),
- value,
Boolean.toString(handler.getProtocol().getHttp11Protocol().isSSLEnabled())),
- Http2Error.PROTOCOL_ERROR, getIdAsInt());
+ // Check scheme is consistent with TLS usage when required
to be
+ if (!handler.getProtocol().getAllowSchemeMismatch() &&
+ "https".equals(value) !=
handler.getProtocol().getHttp11Protocol().isSSLEnabled()) {
+ headerException =
+ new StreamException(
+
sm.getString("stream.header.inconsistentScheme", getConnectionId(),
+ getIdAsString(), value,
+ Boolean.toString(
+
handler.getProtocol().getHttp11Protocol().isSSLEnabled())),
+ Http2Error.PROTOCOL_ERROR,
getIdAsInt());
}
} else {
headerException = new StreamException(
@@ -590,8 +594,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 e1acfe5e58..5476f6130c 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -263,6 +263,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 8b7beb26c9..ad1e2f1f5a 100644
--- a/webapps/docs/config/http2.xml
+++ b/webapps/docs/config/http2.xml
@@ -71,6 +71,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="discardRequestsAndResponses" required="false">
<p>A boolean value which can be used to enable or disable the recycling
of the container internal request and response processing objects. If set
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]