From 930e934f9e8490db2448a0d8b9fea39a5b7d4bfa Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 12 Jun 2024 12:15:16 -0700 Subject: [PATCH 1/2] Delegate compression to servlet container --- README.md | 1 + src/main/java/winstone/HostConfiguration.java | 17 ++++- .../winstone/cmdline/CompressionScheme.java | 10 +++ src/main/java/winstone/cmdline/Option.java | 26 ++++++++ .../winstone/LocalStrings.properties | 1 + src/test/java/winstone/LauncherTest.java | 63 +++++++++++++++++++ src/testwebapp/lipsum.txt | 9 +++ 7 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/main/java/winstone/cmdline/CompressionScheme.java create mode 100644 src/testwebapp/lipsum.txt diff --git a/README.md b/README.md index 8d565387..2cf79b6c 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ To run locally exploded web archive: "^.*_anon_.*$" --controlPort = set the shutdown/control port. -1 to disable, Default disabled + --compression = set the compression scheme (gzip or none to disable compression). Default is gzip. --sessionTimeout = set the http session timeout value in minutes. Default to what webapp specifies, and then to 60 minutes --sessionEviction = set the session eviction timeout for idle sessions in seconds. Default value is 180. -1 never evict, 0 evict on exit --mimeTypes=ARG = define additional MIME type mappings. ARG would be EXT=MIMETYPE:EXT=MIMETYPE:... diff --git a/src/main/java/winstone/HostConfiguration.java b/src/main/java/winstone/HostConfiguration.java index 31caca7b..b71a39da 100644 --- a/src/main/java/winstone/HostConfiguration.java +++ b/src/main/java/winstone/HostConfiguration.java @@ -36,8 +36,10 @@ import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.RequestLogHandler; +import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; +import winstone.cmdline.CompressionScheme; import winstone.cmdline.Option; /** @@ -103,7 +105,20 @@ public HostConfiguration(Server server, String hostname, ClassLoader commonLibCL } } - server.setHandler(handler); + CompressionScheme compressionScheme = Option.COMPRESSION.get(this.args); + switch (compressionScheme) { + case GZIP: + GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.setHandler(handler); + server.setHandler(gzipHandler); + break; + case NONE: + server.setHandler(handler); + break; + default: + throw new IllegalStateException("Unexpected compression scheme: " + compressionScheme); + } + Logger.log( Level.FINER, Launcher.RESOURCES, diff --git a/src/main/java/winstone/cmdline/CompressionScheme.java b/src/main/java/winstone/cmdline/CompressionScheme.java new file mode 100644 index 00000000..d1ac2ac0 --- /dev/null +++ b/src/main/java/winstone/cmdline/CompressionScheme.java @@ -0,0 +1,10 @@ +package winstone.cmdline; + +/** + * The compression schemes supported by the server. In the future, this list may be expanded as support for additional + * compression schemes is added to Jetty upstream. + */ +public enum CompressionScheme { + GZIP, + NONE, +} diff --git a/src/main/java/winstone/cmdline/Option.java b/src/main/java/winstone/cmdline/Option.java index ccdccf69..3dd413ab 100644 --- a/src/main/java/winstone/cmdline/Option.java +++ b/src/main/java/winstone/cmdline/Option.java @@ -88,6 +88,7 @@ public static List> all(Class clazz) { public static final OInt JETTY_ACCEPTORS = integer("jettyAcceptorsCount", -1); public static final OInt JETTY_SELECTORS = integer("jettySelectorsCount", 0); + public static final OCompression COMPRESSION = new OCompression("compression", CompressionScheme.GZIP); public static final OString MIME_TYPES = string("mimeTypes"); public static final OInt MAX_PARAM_COUNT = integer("maxParamCount", -1); public static final OBoolean USAGE = bool("usage", false); @@ -308,6 +309,31 @@ public Class get(Map args, Class expectedTyp } } + public static class OCompression extends Option { + public OCompression(String name, CompressionScheme defaultValue) { + super(name, CompressionScheme.class, defaultValue); + } + + public CompressionScheme get(Map args) { + return get(args, defaultValue); + } + + public CompressionScheme get(Map args, CompressionScheme defaultValue) { + String v = args.get(name); + CompressionScheme compressionScheme; + if (v == null) { + compressionScheme = defaultValue; + } else if (v.equalsIgnoreCase("gzip")) { + compressionScheme = CompressionScheme.GZIP; + } else if (v.equalsIgnoreCase("none")) { + compressionScheme = CompressionScheme.NONE; + } else { + throw new IllegalArgumentException("Unexpected compression scheme: " + v); + } + return compressionScheme; + } + } + // static { // String[] protocols = {"http","https"}; // for (int i=0; i args = new HashMap<>(); + args.put("warfile", "target/test-classes/test.war"); + args.put("prefix", "/"); + args.put("httpPort", "0"); + winstone = new Launcher(args); + verifyGzip(true); + } + + @Test + public void explicitGzip() throws Exception { + Map args = new HashMap<>(); + args.put("warfile", "target/test-classes/test.war"); + args.put("prefix", "/"); + args.put("httpPort", "0"); + args.put("compression", "gzip"); + winstone = new Launcher(args); + verifyGzip(true); + } + + @Test + public void noCompression() throws Exception { + Map args = new HashMap<>(); + args.put("warfile", "target/test-classes/test.war"); + args.put("prefix", "/"); + args.put("httpPort", "0"); + args.put("compression", "none"); + winstone = new Launcher(args); + verifyGzip(false); + } + + private void verifyGzip(boolean gzip) throws Exception { + int port = ((ServerConnector) winstone.server.getConnectors()[0]).getLocalPort(); + HttpRequest request = HttpRequest.newBuilder(new URI("http://127.0.0.2:" + port + "/lipsum.txt")) + .GET() + .header("Accept-Encoding", "gzip") + .build(); + HttpResponse response = + HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofInputStream()); + assertEquals(HttpURLConnection.HTTP_OK, response.statusCode()); + assertEquals("text/plain", response.headers().firstValue("Content-Type").orElseThrow()); + InputStream is; + if (gzip) { + assertEquals( + "gzip", response.headers().firstValue("Content-Encoding").orElseThrow()); + is = new GZIPInputStream(response.body()); + } else { + assertFalse(response.headers().firstValue("Content-Encoding").isPresent()); + is = response.body(); + } + try { + assertThat(new String(is.readAllBytes(), StandardCharsets.UTF_8), startsWith("Lorem ipsum dolor sit amet")); + } finally { + is.close(); + } + } + @Test public void mimeType() throws Exception { Map args = new HashMap<>(); diff --git a/src/testwebapp/lipsum.txt b/src/testwebapp/lipsum.txt new file mode 100644 index 00000000..ee114c49 --- /dev/null +++ b/src/testwebapp/lipsum.txt @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tristique vehicula bibendum. Aenean ut ligula vel risus suscipit hendrerit. Ut eu nibh quis purus bibendum semper nec non nibh. Mauris a sapien vel arcu lobortis interdum. Proin eget elit accumsan, aliquet lacus pharetra, pulvinar massa. Nullam mauris urna, venenatis at dolor at, vulputate consequat turpis. Aenean tempus dignissim pretium. Pellentesque lobortis tortor ut tristique sagittis. Suspendisse porttitor facilisis leo ac maximus. Sed dapibus metus pulvinar, aliquam leo ut, finibus odio. + +Nunc tristique ligula ac porttitor bibendum. Maecenas molestie sagittis dui ac molestie. Nullam et est ex. Nullam et nunc interdum, congue risus quis, viverra neque. In efficitur, mauris ac porta congue, tellus lacus mattis massa, eu pulvinar magna ex non lacus. Proin odio libero, placerat eget neque ut, tristique dignissim eros. Sed elementum feugiat efficitur. Nulla sit amet porta nibh. Vivamus placerat bibendum risus sit amet blandit. Aliquam erat volutpat. Cras orci tellus, posuere eget nunc non, elementum consectetur lacus. Nullam at hendrerit neque. + +Pellentesque non interdum est, vitae volutpat arcu. Mauris imperdiet elit sed sodales egestas. Curabitur viverra quis ante in convallis. In feugiat, est eget suscipit mollis, ante ipsum scelerisque ante, ac blandit urna augue non ex. Nam vel elit vel ante gravida dignissim. Duis quis viverra urna. Phasellus ultrices, nulla nec mollis dictum, nulla tortor gravida lorem, id maximus lorem nunc eget nisl. Nulla condimentum ligula massa, id ultrices enim ultrices in. Aliquam rutrum scelerisque lacus non accumsan. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam erat volutpat. In nec congue elit, vel mollis diam. Donec sed erat a massa elementum interdum aliquet at risus. Nulla non euismod eros, porta imperdiet ante. + +Aenean lectus est, maximus sed lectus nec, vehicula tincidunt velit. Donec vel ligula eget mi aliquam cursus. Proin laoreet dui eu sapien feugiat facilisis. Praesent nec est id diam pulvinar efficitur. Duis nec facilisis nisl, nec cursus nulla. Nulla lacinia felis ut euismod porta. Curabitur porta sem nec dui posuere, eget vestibulum enim euismod. Duis at ultrices nulla, ullamcorper scelerisque libero. Mauris vel auctor justo, imperdiet fermentum nunc. Integer nec eleifend ex. + +Aliquam consectetur tincidunt scelerisque. Nullam fermentum blandit lacus. Quisque aliquam venenatis fringilla. Sed at ultricies lectus. Donec arcu mi, dictum eu sem et, mattis malesuada ligula. Donec facilisis est non est porttitor, id ultrices turpis sollicitudin. In eleifend urna sed justo lobortis tincidunt. Phasellus feugiat venenatis elit vel varius. Aenean lorem erat, iaculis sit amet tincidunt ac, volutpat at ipsum. Donec eleifend ipsum dignissim orci iaculis, nec condimentum nisl lacinia. Donec nec felis in erat finibus consequat. From 1f9cd53555ee48c769d5378568e60bd9efc192e4 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Wed, 12 Jun 2024 13:14:42 -0700 Subject: [PATCH 2/2] fixup --- src/main/java/winstone/HostConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/winstone/HostConfiguration.java b/src/main/java/winstone/HostConfiguration.java index b71a39da..e2503b4f 100644 --- a/src/main/java/winstone/HostConfiguration.java +++ b/src/main/java/winstone/HostConfiguration.java @@ -116,7 +116,7 @@ public HostConfiguration(Server server, String hostname, ClassLoader commonLibCL server.setHandler(handler); break; default: - throw new IllegalStateException("Unexpected compression scheme: " + compressionScheme); + throw new IllegalArgumentException("Unexpected compression scheme: " + compressionScheme); } Logger.log(