You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/07/02 20:17:51 UTC
[tomcat] 02/03: Ensure HEAD response is consistent with GET
response for HttpServlet
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 8e204b92c45f2c2014af295b03c35cb1fab64878
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Jul 2 21:15:56 2019 +0100
Ensure HEAD response is consistent with GET response for HttpServlet
Fix generation of the HEAD response when the GET response uses chunking
---
java/javax/servlet/http/HttpServlet.java | 19 +++++++--
test/javax/servlet/http/TestHttpServlet.java | 62 ++++++++++++++++++++++++++++
webapps/docs/changelog.xml | 5 +++
3 files changed, 82 insertions(+), 4 deletions(-)
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 @@ class NoBodyResponse extends HttpServletResponseWrapper {
// file private
NoBodyResponse(HttpServletResponse r) {
super(r);
- noBody = new NoBodyOutputStream();
+ noBody = new NoBodyOutputStream(this);
}
// file private
@@ -847,11 +847,13 @@ class NoBodyOutputStream extends ServletOutputStream {
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 @@ class NoBodyOutputStream extends ServletOutputStream {
}
@Override
- public void write(int b) {
+ public void write(int b) throws IOException {
contentLength++;
+ checkCommit();
}
@Override
@@ -882,6 +885,7 @@ class NoBodyOutputStream extends ServletOutputStream {
}
contentLength += len;
+ checkCommit();
}
@Override
@@ -894,4 +898,11 @@ class NoBodyOutputStream extends ServletOutputStream {
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 @@ public class TestHttpServlet extends TomcatBaseTest {
}
+ @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 @@ public class TestHttpServlet extends TomcatBaseTest {
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">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org