Ensure HEAD response is consistent with GET response for HttpServlet
Fix generation of the HEAD response when the GET response uses chunking
diff --git a/java/javax/servlet/http/HttpServlet.java b/java/javax/servlet/http/HttpServlet.java
index f7ea58a..12a537a 100644
--- a/java/javax/servlet/http/HttpServlet.java
+++ b/java/javax/servlet/http/HttpServlet.java
@@ -758,7 +758,7 @@
// file private
NoBodyResponse(HttpServletResponse r) {
super(r);
- noBody = new NoBodyOutputStream();
+ noBody = new NoBodyOutputStream(this);
}
// file private
@@ -847,11 +847,13 @@
private static final ResourceBundle lStrings =
ResourceBundle.getBundle(LSTRING_FILE);
+ private final HttpServletResponse response;
+ private boolean flushed = false;
private int contentLength = 0;
// file private
- NoBodyOutputStream() {
- // NOOP
+ NoBodyOutputStream(HttpServletResponse response) {
+ this.response = response;
}
// file private
@@ -860,8 +862,9 @@
}
@Override
- public void write(int b) {
+ public void write(int b) throws IOException {
contentLength++;
+ checkCommit();
}
@Override
@@ -882,6 +885,7 @@
}
contentLength += len;
+ checkCommit();
}
@Override
@@ -894,4 +898,11 @@
public void setWriteListener(javax.servlet.WriteListener listener) {
// TODO SERVLET 3.1
}
+
+ private void checkCommit() throws IOException {
+ if (!flushed && contentLength > response.getBufferSize()) {
+ response.flushBuffer();
+ flushed = true;
+ }
+ }
}
diff --git a/test/javax/servlet/http/TestHttpServlet.java b/test/javax/servlet/http/TestHttpServlet.java
index feca2c9..331a0f6 100644
--- a/test/javax/servlet/http/TestHttpServlet.java
+++ b/test/javax/servlet/http/TestHttpServlet.java
@@ -111,6 +111,51 @@
}
+ @Test
+ public void testChunkingWithHead() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ StandardContext ctx = (StandardContext) tomcat.addContext("", null);
+
+ ChunkingServlet s = new ChunkingServlet();
+ Tomcat.addServlet(ctx, "ChunkingServlet", s);
+ ctx.addServletMappingDecoded("/chunking", "ChunkingServlet");
+
+ tomcat.start();
+
+ Map<String,List<String>> getHeaders = new HashMap<>();
+ String path = "http://localhost:" + getPort() + "/chunking";
+ ByteChunk out = new ByteChunk();
+
+ int rc = getUrl(path, out, getHeaders);
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ out.recycle();
+
+ Map<String,List<String>> headHeaders = new HashMap<>();
+ rc = headUrl(path, out, headHeaders);
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+ // Headers should be the same (apart from Date)
+ Assert.assertEquals(getHeaders.size(), headHeaders.size());
+ for (Map.Entry<String, List<String>> getHeader : getHeaders.entrySet()) {
+ String headerName = getHeader.getKey();
+ if ("date".equalsIgnoreCase(headerName)) {
+ continue;
+ }
+ Assert.assertTrue(headerName, headHeaders.containsKey(headerName));
+ List<String> getValues = getHeader.getValue();
+ List<String> headValues = headHeaders.get(headerName);
+ Assert.assertEquals(getValues.size(), headValues.size());
+ for (String value : getValues) {
+ Assert.assertTrue(headValues.contains(value));
+ }
+ }
+
+ tomcat.stop();
+ }
+
+
private static class Bug57602ServletOuter extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -141,4 +186,21 @@
pw.println("Included");
}
}
+
+
+ private static class ChunkingServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ resp.setCharacterEncoding("UTF-8");
+ PrintWriter pw = resp.getWriter();
+ // Trigger chunking
+ pw.write(new char[8192 * 16]);
+ pw.println("Data");
+ }
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index bccc8b4..de6e67e 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -101,6 +101,11 @@
<fix>
Improve parsing of Content-Range headers. (markt)
</fix>
+ <fix>
+ Ensure that the HEAD response is consistent with the GET response when
+ <code>HttpServlet</code> is relied upon to generate the HEAD response
+ and the GET response uses chunking. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">