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 2020/05/04 20:07:44 UTC

[tomcat] branch 9.0.x updated: Fix BZ 64403 Compressed h2 responses should not have C-L header

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 1d9585b  Fix BZ 64403 Compressed h2 responses should not have C-L header
1d9585b is described below

commit 1d9585b86b13df8b069be36c2cc10c1fed94e756
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon May 4 21:06:16 2020 +0100

    Fix BZ 64403 Compressed h2 responses should not have C-L header
    
    https://bz.apache.org/bugzilla/show_bug.cgi?id=64403
---
 java/org/apache/coyote/http2/StreamProcessor.java  | 19 ++---
 .../apache/coyote/http2/TestStreamProcessor.java   | 81 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         |  5 ++
 3 files changed, 97 insertions(+), 8 deletions(-)

diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index 15bfcab..e03f307 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -147,6 +147,17 @@ class StreamProcessor extends AbstractProcessor {
         // Add the pseudo header for status
         headers.addValue(":status").setString(Integer.toString(statusCode));
 
+
+        // Compression can't be used with sendfile
+        // Need to check for compression (and set headers appropriately) before
+        // adding headers below
+        if (noSendfile && protocol != null &&
+                protocol.useCompression(coyoteRequest, coyoteResponse)) {
+            // Enable compression. Headers will have been set. Need to configure
+            // output filter at this point.
+            stream.addOutputFilter(new GzipOutputFilter());
+        }
+
         // Check to see if a response body is present
         if (!(statusCode < 200 || statusCode == 204 || statusCode == 205 || statusCode == 304)) {
             String contentType = coyoteResponse.getContentType();
@@ -178,14 +189,6 @@ class StreamProcessor extends AbstractProcessor {
         if (statusCode >= 200 && headers.getValue("date") == null) {
             headers.addValue("date").setString(FastHttpDateFormat.getCurrentDate());
         }
-
-        // Compression can't be used with sendfile
-        if (noSendfile && protocol != null &&
-                protocol.useCompression(coyoteRequest, coyoteResponse)) {
-            // Enable compression. Headers will have been set. Need to configure
-            // output filter at this point.
-            stream.addOutputFilter(new GzipOutputFilter());
-        }
     }
 
 
diff --git a/test/org/apache/coyote/http2/TestStreamProcessor.java b/test/org/apache/coyote/http2/TestStreamProcessor.java
index a4dd5f1..a46e1b2 100644
--- a/test/org/apache/coyote/http2/TestStreamProcessor.java
+++ b/test/org/apache/coyote/http2/TestStreamProcessor.java
@@ -18,6 +18,7 @@ package org.apache.coyote.http2;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -34,6 +35,7 @@ import org.junit.Test;
 
 import org.apache.catalina.Context;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Connector;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.tomcat.util.compat.JrePlatform;
 import org.apache.tomcat.util.http.FastHttpDateFormat;
@@ -223,4 +225,83 @@ public class TestStreamProcessor extends Http2TestBase {
             });
         }
     }
+
+
+    @Test
+    public void testCompression() throws Exception {
+        enableHttp2();
+
+        Tomcat tomcat = getTomcatInstance();
+
+        Context ctxt = tomcat.addContext("", null);
+        Tomcat.addServlet(ctxt, "simple", new SimpleServlet());
+        ctxt.addServletMappingDecoded("/simple", "simple");
+        Tomcat.addServlet(ctxt, "compression", new CompressionServlet());
+        ctxt.addServletMappingDecoded("/compression", "compression");
+
+        // Enable compression for the LargeServlet
+        Connector connector = tomcat.getConnector();
+        Assert.assertTrue(connector.setProperty("compression", "on"));
+
+        tomcat.start();
+
+        enableHttp2();
+        openClientConnection();
+        doHttpUpgrade();
+        sendClientPreface();
+        validateHttp2InitialResponse();
+
+
+        byte[] frameHeader = new byte[9];
+        ByteBuffer headersPayload = ByteBuffer.allocate(128);
+
+        List<Header> headers = new ArrayList<>(3);
+        headers.add(new Header(":method", "GET"));
+        headers.add(new Header(":scheme", "http"));
+        headers.add(new Header(":path", "/compression"));
+        headers.add(new Header(":authority", "localhost:" + getPort()));
+        headers.add(new Header("accept-encoding", "gzip"));
+
+        buildGetRequest(frameHeader, headersPayload, null, headers, 3);
+
+        writeFrame(frameHeader, headersPayload);
+
+        readSimpleGetResponse();
+
+        Assert.assertEquals(
+                "3-HeadersStart\n" +
+                "3-Header-[:status]-[200]\n" +
+                "3-Header-[vary]-[accept-encoding]\n" +
+                "3-Header-[content-encoding]-[gzip]\n" +
+                "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" +
+                "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" +
+                "3-HeadersEnd\n" +
+                "3-Body-97\n" +
+                "3-EndOfStream\n", output.getTrace());
+    }
+
+
+    private static class CompressionServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            // Generate content type that is compressible
+            resp.setContentType("text/plain");
+            resp.setCharacterEncoding("UTF-8");
+
+            // Make ir large enough to trigger compression
+            int count = 64 * 1024;
+
+            // One bytes per entry
+            resp.setContentLengthLong(count);
+
+            OutputStream os = resp.getOutputStream();
+            for (int i = 0; i < count; i++) {
+                os.write('X');
+            }
+        }
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ceff687..e981152 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -116,6 +116,11 @@
         keep-alive where the response has a non-2xx status code but the request
         body has been fully read. (rjung/markt)
       </fix>
+      <fix>
+        <bug>64403</bug>: Ensure that compressed HTTP/2 responses are not sent
+        with a content length header appropriate for the original, uncompressed
+        response. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org