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 fa8aacca2b Send an Allow header with the 405 response from HttpServlet
fa8aacca2b is described below

commit fa8aacca2b996ea259f6f8a98fcb86570fd38d09
Author: Arturo Bernal <[email protected]>
AuthorDate: Fri Oct 23 18:11:08 2026 +0200

    Send an Allow header with the 405 response from HttpServlet
    
    Section 15.5.6 of RFC 9110 requires an origin server to generate an
    Allow header field in a 405 response that lists the methods supported by
    the target resource. The 405 response sent for a method the servlet does
    not implement did not include one. Use the value that the default
    OPTIONS handling generates.
---
 java/jakarta/servlet/http/HttpServlet.java     |  8 +++++-
 test/jakarta/servlet/http/TestHttpServlet.java | 39 ++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/java/jakarta/servlet/http/HttpServlet.java 
b/java/jakarta/servlet/http/HttpServlet.java
index e11006e055..0e31f4b136 100644
--- a/java/jakarta/servlet/http/HttpServlet.java
+++ b/java/jakarta/servlet/http/HttpServlet.java
@@ -394,6 +394,8 @@ public abstract class HttpServlet extends GenericServlet {
         if (protocol.isEmpty() || protocol.endsWith("0.9") || 
protocol.endsWith("1.0")) {
             resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
         } else {
+            // RFC 9110, section 15.5.6: a 405 response must include an Allow 
header field
+            resp.setHeader("Allow", getAllowHeaderValue(req));
             resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
         }
     }
@@ -538,7 +540,11 @@ public abstract class HttpServlet extends GenericServlet {
      * @exception ServletException if the request for the OPTIONS cannot be 
handled
      */
     protected void doOptions(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
+        resp.setHeader("Allow", getAllowHeaderValue(req));
+    }
+
 
+    private String getAllowHeaderValue(HttpServletRequest req) {
         String allow = getCachedAllowHeaderValue();
 
         // Tomcat specific hack to see if TRACE is allowed
@@ -550,7 +556,7 @@ public abstract class HttpServlet extends GenericServlet {
             }
         }
 
-        resp.setHeader("Allow", allow);
+        return allow;
     }
 
 
diff --git a/test/jakarta/servlet/http/TestHttpServlet.java 
b/test/jakarta/servlet/http/TestHttpServlet.java
index e78b4a45e4..eb4d3cc546 100644
--- a/test/jakarta/servlet/http/TestHttpServlet.java
+++ b/test/jakarta/servlet/http/TestHttpServlet.java
@@ -231,6 +231,18 @@ public class TestHttpServlet extends TomcatBaseTest {
     }
 
 
+    @Test
+    public void testMethodNotAllowed() throws Exception {
+        doTestMethodNotAllowed(new OptionsServlet(), "GET, HEAD, OPTIONS");
+    }
+
+
+    @Test
+    public void testMethodNotAllowedSub() throws Exception {
+        doTestMethodNotAllowed(new OptionsServletSub(), "GET, HEAD, POST, 
OPTIONS");
+    }
+
+
     @Test
     public void testQueryMethod() throws Exception {
         Tomcat tomcat = getTomcatInstance();
@@ -363,6 +375,33 @@ public class TestHttpServlet extends TomcatBaseTest {
     }
 
 
+    /*
+     * RFC 9110, section 15.5.6: a 405 response must contain an Allow header 
field that lists the methods supported by
+     * the target resource.
+     */
+    private void doTestMethodNotAllowed(Servlet servlet, String expectedAllow) 
throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        // No file system docBase required
+        StandardContext ctx = (StandardContext) getProgrammaticRootContext();
+
+        // Map the test Servlet
+        Tomcat.addServlet(ctx, "servlet", servlet);
+        ctx.addServletMapping("/", "servlet");
+
+        tomcat.start();
+
+        Map<String,List<String>> resHeaders = new HashMap<>();
+        int rc = methodUrl("http://localhost:"; + getPort() + "/", new 
ByteChunk(), DEFAULT_CLIENT_TIMEOUT_MS, null,
+                resHeaders, Method.PUT);
+
+        Assert.assertEquals(HttpServletResponse.SC_METHOD_NOT_ALLOWED, rc);
+        List<String> allow = resHeaders.get("Allow");
+        Assert.assertNotNull(allow);
+        Assert.assertEquals(expectedAllow, allow.get(0));
+    }
+
+
     @Test
     public void testUnimplementedMethodHttp10() throws Exception {
         doTestUnimplementedMethod("1.0");


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

Reply via email to