Skip to content

Commit 8e786a8

Browse files
committed
Update the default HEAD response to exclude payload headers
First explicitly allowed in RFC 7231 and also in the current RFC 9110
1 parent 47508ce commit 8e786a8

File tree

5 files changed

+58
-9
lines changed

5 files changed

+58
-9
lines changed

java/org/apache/coyote/http11/Http11Processor.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,8 @@ protected final void prepareResponse() throws IOException {
916916
}
917917

918918
MessageBytes methodMB = request.method();
919-
if (methodMB.equals("HEAD")) {
919+
boolean head = methodMB.equals("HEAD");
920+
if (head) {
920921
// No entity body
921922
outputBuffer.addActiveFilter
922923
(outputFilters[Constants.VOID_FILTER]);
@@ -1056,6 +1057,13 @@ protected final void prepareResponse() throws IOException {
10561057
headers.setValue("Server").setString(server);
10571058
}
10581059

1060+
if (head) {
1061+
headers.removeHeader("content-length");
1062+
headers.removeHeader("content-range");
1063+
headers.removeHeader("trailer");
1064+
headers.removeHeader("transfer-encoding");
1065+
}
1066+
10591067
// Build the response header
10601068
try {
10611069
outputBuffer.sendStatus();

java/org/apache/coyote/http2/StreamProcessor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@ static void prepareHeaders(Request coyoteRequest, Response coyoteResponse, boole
220220
if (statusCode >= 200 && headers.getValue("date") == null) {
221221
headers.addValue("date").setString(FastHttpDateFormat.getCurrentDate());
222222
}
223+
224+
// Remove payload headers for HEAD requests
225+
if (coyoteRequest != null && "HEAD".equals(coyoteRequest.method().toString())) {
226+
headers.removeHeader("content-length");
227+
headers.removeHeader("content-range");
228+
}
223229
}
224230

225231

test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ public void testDoHead() throws Exception {
9595
rc = headUrl(path, out, headHeaders);
9696
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
9797

98-
// Headers should be the same (apart from Date)
98+
// Headers should be the same part from:
99+
// - Date header may be different
100+
// - HEAD requests don't include payload headers
101+
// (RFC 7231, section 4.3.2)
102+
getHeaders.remove("content-length");
103+
getHeaders.remove("content-range");
104+
getHeaders.remove("trailer");
105+
getHeaders.remove("transfer-encoding");
106+
99107
Assert.assertEquals(getHeaders.size(), headHeaders.size());
100108
for (Map.Entry<String, List<String>> getHeader : getHeaders.entrySet()) {
101109
String headerName = getHeader.getKey();
@@ -158,15 +166,22 @@ public void testDoHeadHttp2() throws Exception {
158166
String[] headHeaders = traceHead.split("\n");
159167

160168
int i = 0;
169+
int j = 0;
161170
for (; i < getHeaders.length; i++) {
162-
// Headers should be the same, ignoring the first character which is the steam ID
163-
Assert.assertEquals(getHeaders[i] + "\n" + traceGet + traceHead, '3', getHeaders[i].charAt(0));
164-
Assert.assertEquals(headHeaders[i] + "\n" + traceGet + traceHead, '5', headHeaders[i].charAt(0));
165-
Assert.assertEquals(traceGet + traceHead, getHeaders[i].substring(1), headHeaders[i].substring(1));
171+
// Ignore payload headers
172+
if (getHeaders[i].contains("content-length") || getHeaders[i].contains("content-range") ) {
173+
// Skip
174+
} else {
175+
// Headers should be the same, ignoring the first character which is the steam ID
176+
Assert.assertEquals(getHeaders[i] + "\n" + traceGet + traceHead, '3', getHeaders[i].charAt(0));
177+
Assert.assertEquals(headHeaders[j] + "\n" + traceGet + traceHead, '5', headHeaders[j].charAt(0));
178+
Assert.assertEquals(traceGet + traceHead, getHeaders[i].substring(1), headHeaders[j].substring(1));
179+
j++;
180+
}
166181
}
167182

168183
// Stream 5 should have one more trace entry
169-
Assert.assertEquals("5-EndOfStream", headHeaders[i]);
184+
Assert.assertEquals("5-EndOfStream", headHeaders[j]);
170185
} catch (Exception t) {
171186
System.out.println(debug.toString());
172187
throw t;

test/jakarta/servlet/http/TestHttpServlet.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545

4646
public class TestHttpServlet extends TomcatBaseTest {
4747

48+
/*
49+
* Nature of test has changed from original bug report since content-length
50+
* is no longer returned for HEAD requests as allowed by RFC 7231.
51+
*/
4852
@Test
4953
public void testBug53454() throws Exception {
5054
Tomcat tomcat = getTomcatInstance();
@@ -64,8 +68,7 @@ public void testBug53454() throws Exception {
6468
resHeaders);
6569

6670
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
67-
Assert.assertEquals(LargeBodyServlet.RESPONSE_LENGTH,
68-
resHeaders.get("Content-Length").get(0));
71+
Assert.assertNull(resHeaders.get("Content-Length"));
6972
}
7073

7174

@@ -178,6 +181,7 @@ private void doTestHead(Servlet servlet) throws Exception {
178181

179182
int rc = getUrl(path, out, getHeaders);
180183
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
184+
removePayloadHeaders(getHeaders);
181185
out.recycle();
182186

183187
Map<String,List<String>> headHeaders = new HashMap<>();
@@ -204,6 +208,18 @@ private void doTestHead(Servlet servlet) throws Exception {
204208
}
205209

206210

211+
/*
212+
* Removes headers that are not expected to appear in the response to the
213+
* equivalent HEAD request.
214+
*/
215+
private void removePayloadHeaders(Map<String,List<String>> headers) {
216+
headers.remove("content-length");
217+
headers.remove("content-range");
218+
headers.remove("trailer");
219+
headers.remove("transfer-encoding");
220+
}
221+
222+
207223
@Test
208224
public void testDoOptions() throws Exception {
209225
doTestDoOptions(new OptionsServlet(), "GET, HEAD, OPTIONS");

webapps/docs/changelog.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@
125125
Implement the new Servlet API methods for setting character encodings
126126
that accept {@code Charset} objects. (markt)
127127
</add>
128+
<update>
129+
The default HEAD response no longer includes the payload HTTP header
130+
fields as per section 9.3.2 of RFC 9110. (markt)
131+
</update>
128132
</changelog>
129133
</subsection>
130134
<subsection name="Coyote">

0 commit comments

Comments
 (0)