This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new b2b86f64f9 Respond with 414 when the request target is too long (#1147)
b2b86f64f9 is described below
commit b2b86f64f9365fd1134414e28b3b0d171dab4fbf
Author: Arturo Bernal <[email protected]>
AuthorDate: Fri Nov 13 13:30:48 2026 +0100
Respond with 414 when the request target is too long (#1147)
RFC 9112, section 3 requires a server that receives a request-target
longer than any URI it wishes to parse to respond with a 414 (URI Too
Long) status code. When the request target did not fit in the buffer used
to read the request line, Tomcat responded with a 400 status. Respond with
414 in that case. A request header field that is too large continues to
trigger a 400 response.
---
.../apache/coyote/http11/Http11InputBuffer.java | 4 +
java/org/apache/coyote/http11/Http11Processor.java | 9 +-
.../apache/coyote/http11/LocalStrings.properties | 1 +
.../http11/RequestTargetTooLongException.java | 40 +++++++++
.../apache/catalina/startup/SimpleHttpClient.java | 5 ++
.../coyote/http11/TestHttp11InputBuffer.java | 95 ++++++++++++++++++++++
webapps/docs/changelog.xml | 5 ++
7 files changed, 156 insertions(+), 3 deletions(-)
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java
b/java/org/apache/coyote/http11/Http11InputBuffer.java
index 05ebe9df94..916c9cd53c 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -746,6 +746,10 @@ public class Http11InputBuffer implements InputBuffer,
ApplicationBufferHandler,
if (parsingRequestLine) {
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
+ if (parsingRequestLinePhase == 4) {
+ // The buffer filled while reading the request target
(RFC 9112, section 3)
+ throw new
RequestTargetTooLongException(sm.getString("iib.requesttargettoolong.error"));
+ }
}
throw new
IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
}
diff --git a/java/org/apache/coyote/http11/Http11Processor.java
b/java/org/apache/coyote/http11/Http11Processor.java
index 3fd7c93608..6981d4541e 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -325,8 +325,8 @@ public class Http11Processor extends AbstractProcessor {
log.debug(message, t);
}
}
- // 400 - Bad Request
- response.setStatus(400);
+ // 414 - URI Too Long (RFC 9112, section 3) or 400 - Bad
Request
+ response.setStatus(t instanceof RequestTargetTooLongException
? 414 : 400);
setErrorState(ErrorState.CLOSE_CLEAN, t);
}
@@ -903,7 +903,10 @@ public class Http11Processor extends AbstractProcessor {
private void badRequest(String errorKey) {
- response.setStatus(400);
+ if (!getErrorState().isError()) {
+ // Do not replace the status of an error that has already been
identified
+ response.setStatus(400);
+ }
setErrorState(ErrorState.CLOSE_CLEAN, null);
if (log.isDebugEnabled()) {
log.debug(sm.getString(errorKey));
diff --git a/java/org/apache/coyote/http11/LocalStrings.properties
b/java/org/apache/coyote/http11/LocalStrings.properties
index 309e9ac105..a3c5c883f9 100644
--- a/java/org/apache/coyote/http11/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/LocalStrings.properties
@@ -58,6 +58,7 @@ iib.invalidmethod=Invalid character found in method name
[{0}]. HTTP method name
iib.parseheaders.ise.error=Unexpected state: headers already parsed. Buffer
not recycled?
iib.readtimeout=Timeout attempting to read data from the socket
iib.requestheadertoolarge.error=Request header is too large
+iib.requesttargettoolong.error=Request target is too long
iob.failedwrite=Failed write
iob.failedwrite.ack=Failed to send HTTP 100 continue response
diff --git a/java/org/apache/coyote/http11/RequestTargetTooLongException.java
b/java/org/apache/coyote/http11/RequestTargetTooLongException.java
new file mode 100644
index 0000000000..51e05086e8
--- /dev/null
+++ b/java/org/apache/coyote/http11/RequestTargetTooLongException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http11;
+
+import java.io.Serial;
+
+/**
+ * Exception used to mark the specific error condition of the request target
exceeding the size of the buffer used to
+ * read the request line.
+ *
+ * @since 12.0.x
+ */
+public class RequestTargetTooLongException extends IllegalArgumentException {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an exception with the specified detail message.
+ *
+ * @param message The detail message
+ */
+ public RequestTargetTooLongException(String message) {
+ super(message);
+ }
+}
diff --git a/test/org/apache/catalina/startup/SimpleHttpClient.java
b/test/org/apache/catalina/startup/SimpleHttpClient.java
index 770c0c778a..c06aea0750 100644
--- a/test/org/apache/catalina/startup/SimpleHttpClient.java
+++ b/test/org/apache/catalina/startup/SimpleHttpClient.java
@@ -67,6 +67,7 @@ public abstract class SimpleHttpClient {
public static final String FAIL_405 = "HTTP/1.1 405 ";
public static final String TIMEOUT_408 = "HTTP/1.1 408 ";
public static final String FAIL_413 = "HTTP/1.1 413 ";
+ public static final String FAIL_414 = "HTTP/1.1 414 ";
public static final String FAIL_417 = "HTTP/1.1 417 ";
public static final String FAIL_50X = "HTTP/1.1 50";
public static final String FAIL_500 = "HTTP/1.1 500 ";
@@ -496,6 +497,10 @@ public abstract class SimpleHttpClient {
return responseLineStartsWith(FAIL_413);
}
+ public boolean isResponse414() {
+ return responseLineStartsWith(FAIL_414);
+ }
+
public boolean isResponse417() {
return responseLineStartsWith(FAIL_417);
}
diff --git a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
index 54f00dbdce..3c4eedba9d 100644
--- a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
+++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
@@ -793,4 +793,99 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
// Expected response is a 400 response.
Assert.assertTrue(client.getResponseLine(), client.isResponse400());
}
+
+ /*
+ * RFC 9112, section 3: a server that receives a request-target longer
than any URI it wishes to parse MUST respond
+ * with a 414 (URI Too Long) status code.
+ */
+ @Test
+ public void testRequestTargetTooLong() {
+ LongRequestClient client = new LongRequestClient("/test/" +
"a".repeat(9000), "X-Test: a");
+
+ Exception e = client.doRequest();
+ if (e == null) {
+ Assert.assertTrue(client.getResponseLine(),
client.isResponse414());
+ }
+ Assert.assertFalse(client.isResponseBodyOK());
+ }
+
+
+ @Test
+ public void testRequestTargetQueryTooLong() {
+ LongRequestClient client = new LongRequestClient("/test?q=" +
"a".repeat(9000), "X-Test: a");
+
+ Exception e = client.doRequest();
+ if (e == null) {
+ Assert.assertTrue(client.getResponseLine(),
client.isResponse414());
+ }
+ Assert.assertFalse(client.isResponseBodyOK());
+ }
+
+
+ /*
+ * A header field that is too large is not a request-target problem so the
response remains a 400.
+ */
+ @Test
+ public void testRequestHeaderTooLarge() {
+ LongRequestClient client = new LongRequestClient("/test", "X-Test: " +
"a".repeat(9000));
+
+ Exception e = client.doRequest();
+ if (e == null) {
+ Assert.assertTrue(client.getResponseLine(),
client.isResponse400());
+ }
+ Assert.assertFalse(client.isResponseBodyOK());
+ }
+
+
+ private class LongRequestClient extends SimpleHttpClient {
+
+ private final String requestTarget;
+ private final String headerLine;
+
+ private LongRequestClient(String requestTarget, String headerLine) {
+ this.requestTarget = requestTarget;
+ this.headerLine = headerLine;
+ }
+
+ private Exception doRequest() {
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context root = tomcat.addContext("", TEMP_DIR);
+ Tomcat.addServlet(root, "test", new TesterServlet());
+ root.addServletMapping("/test", "test");
+
+ try {
+ tomcat.start();
+ setPort(tomcat.getConnector().getLocalPort());
+
+ // Open connection
+ connect();
+
+ String[] request = new String[1];
+ // @formatter:off
+ request[0] =
+ "GET " + requestTarget + " HTTP/1.1" + CRLF +
+ "Host: localhost:8080" + CRLF +
+ headerLine + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+ // @formatter:on
+
+ setRequest(request);
+ processRequest(); // blocks until response has been read
+
+ // Close the connection
+ disconnect();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResponseBodyOK() {
+ return false;
+ }
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 569a354fd5..35895d7aac 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -284,6 +284,11 @@
in the <code>Accept-Language</code> header) as case insensitive. Pull
request <pr>1144</pr> provided by Arturo Bernal. (markt)
</fix>
+ <fix>
+ Respond with a 414 (URI Too Long) status rather than a 400 (Bad
Request)
+ status when the request target is too long to fit in the buffer used to
+ read the request line, as required by RFC 9112, section 3. (abernal)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]