diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index a2387ce34969..fe650d4a1330 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.http.GzipHttpContent; import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; @@ -443,6 +444,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques int i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE); if (i>0) { + baseRequest.setAttribute("o.e.j.s.h.gzip.GzipHandler.etag",etag); while (i>=0) { etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length()); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java index 17a1e25fb460..b96930b0df59 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java @@ -157,6 +157,19 @@ protected void commit(ByteBuffer content, boolean complete, Callback callback) { LOG.debug("{} exclude by status {}",this,sc); noCompression(); + + if (sc==304) + { + String request_etags = (String)_channel.getRequest().getAttribute("o.e.j.s.h.gzip.GzipHandler.etag"); + String response_etag = response.getHttpFields().get(HttpHeader.ETAG); + if (request_etags!=null && response_etag!=null) + { + String response_etag_gzip=etagGzip(response_etag); + if (request_etags.contains(response_etag_gzip)) + response.getHttpFields().put(HttpHeader.ETAG,response_etag_gzip); + } + } + _interceptor.write(content, complete, callback); return; } @@ -216,11 +229,7 @@ protected void commit(ByteBuffer content, boolean complete, Callback callback) response.setContentLength(-1); String etag=fields.get(HttpHeader.ETAG); if (etag!=null) - { - int end = etag.length()-1; - etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP; - fields.put(HttpHeader.ETAG,etag); - } + fields.put(HttpHeader.ETAG,etagGzip(etag)); LOG.debug("{} compressing {}",this,_deflater); _state.set(GZState.COMPRESSING); @@ -231,6 +240,12 @@ protected void commit(ByteBuffer content, boolean complete, Callback callback) callback.failed(new WritePendingException()); } + private String etagGzip(String etag) + { + int end = etag.length()-1; + return (etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP; + } + public void noCompression() { while (true) diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java index 99ca52f754c4..7bea760914ae 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java @@ -19,10 +19,14 @@ package org.eclipse.jetty.servlet; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -42,6 +46,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.util.IO; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -62,6 +67,8 @@ public class GzipHandlerTest "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+ "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque."; + private static String __contentETag = String.format("W/\"%x\"",__content.hashCode()); + private static String __contentETagGzip = String.format("W/\"%x--gzip\"",__content.hashCode()); private static String __icontent = "BEFORE"+__content+"AFTER"; private Server _server; @@ -75,6 +82,7 @@ public void init() throws Exception _server.addConnector(_connector); GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.setExcludedAgentPatterns(); ServletContextHandler context = new ServletContextHandler(gzipHandler,"/ctx"); ServletHandler servlets = context.getServletHandler(); @@ -93,8 +101,15 @@ public static class TestServlet extends HttpServlet @Override protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { - PrintWriter writer = response.getWriter(); - writer.write(__content); + response.setHeader("ETag",__contentETag); + String ifnm = req.getHeader("If-None-Match"); + if (ifnm!=null && ifnm.equals(__contentETag)) + response.sendError(304); + else + { + PrintWriter writer = response.getWriter(); + writer.write(__content); + } } } @@ -125,6 +140,33 @@ public void destroy() throws Exception _server.join(); } + @Test + public void testNotGzipHandler() throws Exception + { + // generated and parsed test + HttpTester.Request request = HttpTester.newRequest(); + HttpTester.Response response; + + request.setMethod("GET"); + request.setURI("/ctx/content"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host","tester"); + + response = HttpTester.parseResponse(_connector.getResponses(request.generate())); + + assertThat(response.getStatus(),is(200)); + assertThat(response.get("Content-Encoding"),not(equalToIgnoringCase("gzip"))); + assertThat(response.get("ETag"),is(__contentETag)); + assertThat(response.get("Vary"),is("Accept-Encoding")); + + InputStream testIn = new ByteArrayInputStream(response.getContentBytes()); + ByteArrayOutputStream testOut = new ByteArrayOutputStream(); + IO.copy(testIn,testOut); + + assertEquals(__content, testOut.toString("UTF8")); + + } + @Test public void testGzipHandler() throws Exception { @@ -133,22 +175,65 @@ public void testGzipHandler() throws Exception HttpTester.Response response; request.setMethod("GET"); + request.setURI("/ctx/content"); request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); request.setHeader("accept-encoding","gzip"); - request.setURI("/ctx/content"); response = HttpTester.parseResponse(_connector.getResponses(request.generate())); - assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip")); - assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + assertThat(response.getStatus(),is(200)); + assertThat(response.get("Content-Encoding"),Matchers.equalToIgnoringCase("gzip")); + assertThat(response.get("ETag"),is(__contentETagGzip)); + assertThat(response.get("Vary"),is("Accept-Encoding")); InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes())); ByteArrayOutputStream testOut = new ByteArrayOutputStream(); IO.copy(testIn,testOut); assertEquals(__content, testOut.toString("UTF8")); + } + + @Test + public void testETagNotGzipHandler() throws Exception + { + // generated and parsed test + HttpTester.Request request = HttpTester.newRequest(); + HttpTester.Response response; + + request.setMethod("GET"); + request.setURI("/ctx/content"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host","tester"); + request.setHeader("If-None-Match",__contentETag); + request.setHeader("accept-encoding","gzip"); + + response = HttpTester.parseResponse(_connector.getResponses(request.generate())); + + assertThat(response.getStatus(),is(304)); + assertThat(response.get("Content-Encoding"),not(Matchers.equalToIgnoringCase("gzip"))); + assertThat(response.get("ETag"),is(__contentETag)); + } + + @Test + public void testETagGzipHandler() throws Exception + { + // generated and parsed test + HttpTester.Request request = HttpTester.newRequest(); + HttpTester.Response response; + + request.setMethod("GET"); + request.setURI("/ctx/content"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host","tester"); + request.setHeader("If-None-Match",__contentETagGzip); + request.setHeader("accept-encoding","gzip"); + response = HttpTester.parseResponse(_connector.getResponses(request.generate())); + + assertThat(response.getStatus(),is(304)); + assertThat(response.get("Content-Encoding"),not(Matchers.equalToIgnoringCase("gzip"))); + assertThat(response.get("ETag"),is(__contentETagGzip)); } @Test @@ -166,8 +251,10 @@ public void testForwardGzipHandler() throws Exception response = HttpTester.parseResponse(_connector.getResponses(request.generate())); - assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip")); - assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + assertThat(response.getStatus(),is(200)); + assertThat(response.get("Content-Encoding"),Matchers.equalToIgnoringCase("gzip")); + assertThat(response.get("ETag"),is(__contentETagGzip)); + assertThat(response.get("Vary"),is("Accept-Encoding")); InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes())); ByteArrayOutputStream testOut = new ByteArrayOutputStream(); @@ -190,9 +277,11 @@ public void testIncludeGzipHandler() throws Exception request.setURI("/ctx/include"); response = HttpTester.parseResponse(_connector.getResponses(request.generate())); - - assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip")); - assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + + assertThat(response.getStatus(),is(200)); + assertThat(response.get("Content-Encoding"),Matchers.equalToIgnoringCase("gzip")); + assertThat(response.get("ETag"),nullValue()); + assertThat(response.get("Vary"),is("Accept-Encoding")); InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes())); ByteArrayOutputStream testOut = new ByteArrayOutputStream();