This is an automated email from the ASF dual-hosted git repository. markt 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 8e786a8eda Update the default HEAD response to exclude payload headers 8e786a8eda is described below commit 8e786a8eda188426dff97e9c997dfceb4eb469c2 Author: Mark Thomas <ma...@apache.org> AuthorDate: Thu Jan 19 20:40:10 2023 +0000 Update the default HEAD response to exclude payload headers First explicitly allowed in RFC 7231 and also in the current RFC 9110 --- java/org/apache/coyote/http11/Http11Processor.java | 10 +++++++- java/org/apache/coyote/http2/StreamProcessor.java | 6 +++++ .../servlet/http/HttpServletDoHeadBaseTest.java | 27 +++++++++++++++++----- test/jakarta/servlet/http/TestHttpServlet.java | 20 ++++++++++++++-- webapps/docs/changelog.xml | 4 ++++ 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java index c27ff911f4..951d276593 100644 --- a/java/org/apache/coyote/http11/Http11Processor.java +++ b/java/org/apache/coyote/http11/Http11Processor.java @@ -916,7 +916,8 @@ public class Http11Processor extends AbstractProcessor { } MessageBytes methodMB = request.method(); - if (methodMB.equals("HEAD")) { + boolean head = methodMB.equals("HEAD"); + if (head) { // No entity body outputBuffer.addActiveFilter (outputFilters[Constants.VOID_FILTER]); @@ -1056,6 +1057,13 @@ public class Http11Processor extends AbstractProcessor { headers.setValue("Server").setString(server); } + if (head) { + headers.removeHeader("content-length"); + headers.removeHeader("content-range"); + headers.removeHeader("trailer"); + headers.removeHeader("transfer-encoding"); + } + // Build the response header try { outputBuffer.sendStatus(); diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java index 3afda9037b..acd9dce237 100644 --- a/java/org/apache/coyote/http2/StreamProcessor.java +++ b/java/org/apache/coyote/http2/StreamProcessor.java @@ -220,6 +220,12 @@ class StreamProcessor extends AbstractProcessor { if (statusCode >= 200 && headers.getValue("date") == null) { headers.addValue("date").setString(FastHttpDateFormat.getCurrentDate()); } + + // Remove payload headers for HEAD requests + if (coyoteRequest != null && "HEAD".equals(coyoteRequest.method().toString())) { + headers.removeHeader("content-length"); + headers.removeHeader("content-range"); + } } diff --git a/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java b/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java index 21a962d48d..e513be2430 100644 --- a/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java +++ b/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java @@ -95,7 +95,15 @@ public class HttpServletDoHeadBaseTest extends Http2TestBase { rc = headUrl(path, out, headHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); - // Headers should be the same (apart from Date) + // Headers should be the same part from: + // - Date header may be different + // - HEAD requests don't include payload headers + // (RFC 7231, section 4.3.2) + getHeaders.remove("content-length"); + getHeaders.remove("content-range"); + getHeaders.remove("trailer"); + getHeaders.remove("transfer-encoding"); + Assert.assertEquals(getHeaders.size(), headHeaders.size()); for (Map.Entry<String, List<String>> getHeader : getHeaders.entrySet()) { String headerName = getHeader.getKey(); @@ -158,15 +166,22 @@ public class HttpServletDoHeadBaseTest extends Http2TestBase { String[] headHeaders = traceHead.split("\n"); int i = 0; + int j = 0; for (; i < getHeaders.length; i++) { - // Headers should be the same, ignoring the first character which is the steam ID - Assert.assertEquals(getHeaders[i] + "\n" + traceGet + traceHead, '3', getHeaders[i].charAt(0)); - Assert.assertEquals(headHeaders[i] + "\n" + traceGet + traceHead, '5', headHeaders[i].charAt(0)); - Assert.assertEquals(traceGet + traceHead, getHeaders[i].substring(1), headHeaders[i].substring(1)); + // Ignore payload headers + if (getHeaders[i].contains("content-length") || getHeaders[i].contains("content-range") ) { + // Skip + } else { + // Headers should be the same, ignoring the first character which is the steam ID + Assert.assertEquals(getHeaders[i] + "\n" + traceGet + traceHead, '3', getHeaders[i].charAt(0)); + Assert.assertEquals(headHeaders[j] + "\n" + traceGet + traceHead, '5', headHeaders[j].charAt(0)); + Assert.assertEquals(traceGet + traceHead, getHeaders[i].substring(1), headHeaders[j].substring(1)); + j++; + } } // Stream 5 should have one more trace entry - Assert.assertEquals("5-EndOfStream", headHeaders[i]); + Assert.assertEquals("5-EndOfStream", headHeaders[j]); } catch (Exception t) { System.out.println(debug.toString()); throw t; diff --git a/test/jakarta/servlet/http/TestHttpServlet.java b/test/jakarta/servlet/http/TestHttpServlet.java index e12bcd54be..077467454e 100644 --- a/test/jakarta/servlet/http/TestHttpServlet.java +++ b/test/jakarta/servlet/http/TestHttpServlet.java @@ -45,6 +45,10 @@ import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; public class TestHttpServlet extends TomcatBaseTest { + /* + * Nature of test has changed from original bug report since content-length + * is no longer returned for HEAD requests as allowed by RFC 7231. + */ @Test public void testBug53454() throws Exception { Tomcat tomcat = getTomcatInstance(); @@ -64,8 +68,7 @@ public class TestHttpServlet extends TomcatBaseTest { resHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); - Assert.assertEquals(LargeBodyServlet.RESPONSE_LENGTH, - resHeaders.get("Content-Length").get(0)); + Assert.assertNull(resHeaders.get("Content-Length")); } @@ -178,6 +181,7 @@ public class TestHttpServlet extends TomcatBaseTest { int rc = getUrl(path, out, getHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); + removePayloadHeaders(getHeaders); out.recycle(); Map<String,List<String>> headHeaders = new HashMap<>(); @@ -204,6 +208,18 @@ public class TestHttpServlet extends TomcatBaseTest { } + /* + * Removes headers that are not expected to appear in the response to the + * equivalent HEAD request. + */ + private void removePayloadHeaders(Map<String,List<String>> headers) { + headers.remove("content-length"); + headers.remove("content-range"); + headers.remove("trailer"); + headers.remove("transfer-encoding"); + } + + @Test public void testDoOptions() throws Exception { doTestDoOptions(new OptionsServlet(), "GET, HEAD, OPTIONS"); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 7570715faa..9fd2f679a3 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -125,6 +125,10 @@ Implement the new Servlet API methods for setting character encodings that accept {@code Charset} objects. (markt) </add> + <update> + The default HEAD response no longer includes the payload HTTP header + fields as per section 9.3.2 of RFC 9110. (markt) + </update> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org