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]

Reply via email to