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 ecf002731a Respond with 414 when the request target is too long (#1147)
ecf002731a is described below
commit ecf002731aa4bf6f4c65c8bdbcd652b339716f4a
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 | 11 ++-
.../apache/coyote/http11/LocalStrings.properties | 1 +
.../http11/RequestTargetTooLongException.java | 35 ++++++++
.../apache/catalina/startup/SimpleHttpClient.java | 5 ++
.../coyote/http11/TestHttp11InputBuffer.java | 96 +++++++++++++++++++++-
webapps/docs/changelog.xml | 6 ++
7 files changed, 153 insertions(+), 5 deletions(-)
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java
b/java/org/apache/coyote/http11/Http11InputBuffer.java
index e28182027e..2c2be515a8 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -775,6 +775,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 b30c220f3c..1c9f16cd8a 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -317,6 +317,9 @@ public class Http11Processor extends AbstractProcessor {
}
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
break;
+ } catch (RequestTargetTooLongException e) {
+
response.setStatus(HttpServletResponse.SC_REQUEST_URI_TOO_LONG);
+ setErrorState(ErrorState.CLOSE_CLEAN, e);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
UserDataHelper.Mode logMode = userDataHelper.getNextMode();
@@ -333,8 +336,7 @@ public class Http11Processor extends AbstractProcessor {
log.debug(message, t);
}
}
- // 400 - Bad Request
- response.setStatus(400);
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
setErrorState(ErrorState.CLOSE_CLEAN, t);
}
@@ -934,7 +936,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 73ae99607a..7019cf4780 100644
--- a/java/org/apache/coyote/http11/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/LocalStrings.properties
@@ -66,6 +66,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..b0ec52fe4d
--- /dev/null
+++ b/java/org/apache/coyote/http11/RequestTargetTooLongException.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Exception used to mark the specific error condition of the request target
exceeding the size of the buffer used to
+ * read the request line.
+ */
+public class RequestTargetTooLongException extends IllegalArgumentException {
+
+ 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 dcb0a4db91..e8b954083b 100644
--- a/test/org/apache/catalina/startup/SimpleHttpClient.java
+++ b/test/org/apache/catalina/startup/SimpleHttpClient.java
@@ -65,6 +65,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 ";
@@ -497,6 +498,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 77faf3f9f9..2292de5f1d 100644
--- a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
+++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
@@ -699,8 +699,8 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
public void testInvalidMethod() {
String[] request = new String[1];
- request[0] = Method.GET + (char) 0 + " /test HTTP/1.1" + CRLF + "Host:
localhost:8080" + CRLF + "Connection: close" +
- CRLF + CRLF;
+ request[0] = Method.GET + (char) 0 + " /test HTTP/1.1" + CRLF + "Host:
localhost:8080" + CRLF +
+ "Connection: close" + CRLF + CRLF;
InvalidClient client = new InvalidClient(request);
@@ -887,4 +887,96 @@ 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/" + new String(new
char[9000]).replace("\0", "a"), "X-Test: a");
+
+ Exception e = client.doRequest();
+ Assert.assertNull(e);
+ Assert.assertTrue(client.getResponseLine(), client.isResponse414());
+ }
+
+
+ @Test
+ public void testRequestTargetQueryTooLong() {
+ LongRequestClient client =
+ new LongRequestClient("/test?q=" + new String(new
char[9000]).replace("\0", "a"), "X-Test: a");
+
+ Exception e = client.doRequest();
+ Assert.assertNull(e);
+ Assert.assertTrue(client.getResponseLine(), client.isResponse414());
+ }
+
+
+ /*
+ * 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: " + new String(new
char[9000]).replace("\0", "a"));
+
+ Exception e = client.doRequest();
+ Assert.assertNull(e);
+ Assert.assertTrue(client.getResponseLine(), client.isResponse400());
+ }
+
+
+ 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 de5a52f526..cb7707b54f 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -143,6 +143,12 @@
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. Pull request
+ <pr>1147</pr> provided by Arturo Bernal. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]