Index: test/webapp-3.0/WEB-INF/web.xml
===================================================================
--- test/webapp-3.0/WEB-INF/web.xml (revision 1332331)
+++ test/webapp-3.0/WEB-INF/web.xml (working copy)
@@ -95,6 +95,26 @@
Bug49922
*.od
+
+ NoContentLengthFlushingServlet
+
+ org.apache.catalina.core.TestStandardContext$NoContentLengthFlushingServlet
+
+
+
+ NoContentLengthFlushingServlet
+ /noContentLengthFlushingServlet/servlet
+
+
+ NoContentLengthConnectionCloseFlushingServlet
+
+ org.apache.catalina.core.TestStandardContext$NoContentLengthConnectionCloseFlushingServlet
+
+
+
+ NoContentLengthConnectionCloseFlushingServlet
+ /noContentLengthConnectionCloseFlushingServlet/servlet
+
Index: test/org/apache/coyote/http11/TestAbstractHttp11Processor.java
===================================================================
--- test/org/apache/coyote/http11/TestAbstractHttp11Processor.java (revision 1332331)
+++ test/org/apache/coyote/http11/TestAbstractHttp11Processor.java (working copy)
@@ -16,20 +16,19 @@
*/
package org.apache.coyote.http11;
-import java.io.File;
-import java.io.IOException;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import org.junit.Test;
+import java.io.File;
+import java.io.IOException;
import org.apache.catalina.Context;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
+import org.junit.Test;
public class TestAbstractHttp11Processor extends TomcatBaseTest {
@@ -238,7 +237,62 @@
assertTrue(client.isResponse200());
assertEquals("OK", client.getResponseBody());
}
+
+ @Test
+ public void testChunking11NoContentLength() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // Use the normal Tomcat ROOT context
+ File root = new File("test/webapp-3.0");
+ tomcat.addWebapp("", root.getAbsolutePath());
+
+ tomcat.start();
+
+ String request =
+ "GET /noContentLengthFlushingServlet/servlet HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: any" + SimpleHttpClient.CRLF +
+ "Accept: text/event-stream" +
+ SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.getResponseHeaders().contains("Transfer-Encoding: chunked"));
+ }
+
+ @Test
+ public void testNoChunking11NoContentLengthConnectionClose() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // Use the normal Tomcat ROOT context
+ File root = new File("test/webapp-3.0");
+ tomcat.addWebapp("", root.getAbsolutePath());
+
+ tomcat.start();
+
+ String request =
+ "GET /noContentLengthConnectionCloseFlushingServlet/servlet HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: any" + SimpleHttpClient.CRLF +
+ "Accept: text/event-stream" +
+ SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ assertTrue(client.isResponse200());
+ assertTrue(client.getResponseHeaders().contains("Connection: close"));
+ assertFalse(client.getResponseHeaders().contains("Transfer-Encoding: chunked"));
+ assertEquals("OK", client.getResponseBody());
+ }
+
private static final class Client extends SimpleHttpClient {
public Client(int port) {
Index: test/org/apache/catalina/core/TestStandardContext.java
===================================================================
--- test/org/apache/catalina/core/TestStandardContext.java (revision 1332331)
+++ test/org/apache/catalina/core/TestStandardContext.java (working copy)
@@ -440,7 +440,43 @@
}
}
}
+
+ // flushes with no content-length set
+ // should result in chunking on HTTP 1.1
+ public static final class NoContentLengthFlushingServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/plain");
+ resp.getWriter().write("OK");
+ resp.flushBuffer();
+ }
+
+ }
+
+ // flushes with no content-length set but sets Connection: close header
+ // should no result in chunking on HTTP 1.1
+ public static final class NoContentLengthConnectionCloseFlushingServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/event-stream");
+ resp.addHeader("Connection", "close");
+ resp.flushBuffer();
+ resp.getWriter().write("OK");
+ resp.flushBuffer();
+ }
+
+ }
+
/**
* Test case for bug 49711: HttpServletRequest.getParts does not work
* in a filter.
Index: java/org/apache/coyote/http11/AbstractHttp11Processor.java
===================================================================
--- java/org/apache/coyote/http11/AbstractHttp11Processor.java (revision 1332331)
+++ java/org/apache/coyote/http11/AbstractHttp11Processor.java (working copy)
@@ -1131,7 +1131,7 @@
MimeHeaders headers = request.getMimeHeaders();
// Check connection header
- MessageBytes connectionValueMB = headers.getValue("connection");
+ MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);
if (connectionValueMB != null) {
ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
@@ -1376,7 +1376,10 @@
(outputFilters[Constants.IDENTITY_FILTER]);
contentDelimitation = true;
} else {
- if (entityBody && http11) {
+ // if the response code supports an entity body
+ // and we're on HTTP 1.1 then we chunk
+ // unless we have a Connection: close header
+ if (entityBody && http11 && !isConnectionClose(headers)) {
getOutputBuffer().addActiveFilter
(outputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
@@ -1446,6 +1449,14 @@
getOutputBuffer().endHeaders();
}
+
+ private boolean isConnectionClose(MimeHeaders headers) {
+ MessageBytes connection = headers.getValue(Constants.CONNECTION);
+ if (connection == null) {
+ return false;
+ }
+ return connection.equals(Constants.CLOSE);
+ }
abstract boolean prepareSendfile(OutputFilter[] outputFilters);