1

I created uber jar with embedded jetty server(I updated to 9.4.20.v20190813). The fat app includes two parts: the static front-end, and the api backend. The code skeleton copied from jetty server 9.1 multiple embeded ports and application in same server instance. And I want to add GzipHandler on both two web apps.

public class Main
{
    public static void main(String[] args)
    {
        Server server = new Server();

        ServerConnector connectorA = new ServerConnector(server);
        connectorA.setPort(8080);
        connectorA.setName("connA"); // connector name A (static web app)
        ServerConnector connectorB = new ServerConnector(server);
        connectorB.setPort(9090);
        connectorB.setName("connB"); // connector name B (api app)

        server.addConnector(connectorA);
        server.addConnector(connectorB);

        // Basic handler collection
        HandlerCollection contexts = new HandlerCollection();
        server.setHandler(contexts);

        // WebApp A
        WebAppContext appA = new WebAppContext();
        appA.setContextPath("/a");
        appA.setWar("./webapps/webapp-a.war");
        appA.setVirtualHosts(new String[]{"@connA"}); // connector name A
        contexts.addHandler(appA);

        // WebApp B
        WebAppContext appB = new WebAppContext();
        appB.setContextPath("/b");
        appB.setWar("./webapps/webapp-b.war");
        appB.setVirtualHosts(new String[]{"@connB"}); // connector name B
        contexts.addHandler(appB);

    GzipHandler gzipHandler = new GzipHandler();
    gzipHandler.setIncludedMethods("POST", "GET");
    gzipHandler.setIncludedMimeTypes("text/html", "text/plain", "text/xml", "text/css", "application/javascript",
                                     "text/javascript", "application/json");
    gzipHandler.setInflateBufferSize(2048);
    gzipHandler.setMinGzipSize(2048);

        contexts.addHandler(gzipHandler);

        try
        {
            server.start(); // start server thread
            server.join(); // wait for server thread to end
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }
}

When I visit http://localhost:8080, some resources return Status Code: 206 Partial Content. , the server throws exception:

17:16:31.447 [qtp60830820-16] WARN  org.eclipse.jetty.server.HttpChannel - /favicon.ico
java.lang.NullPointerException: null
    at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:725)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:502)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:370)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:267)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)

My question: favicon.ico's mime-type is not included in GzipHandler, and why GzipHandler processes it? and favicon.ico is in the static resources web app root dir (the same level as index.html).

How can I apply the gziphandler correctly? Thanks!

BTW: gzip filter works fine

EDITS:

  1. I upgraded jetty version from 9.4.12.v20180830 to 9.4.20.v20190813
  2. I added more settings on GzipHandler
Jack Tang
  • 59
  • 5

1 Answers1

2

Jetty 9.1 is EOL (End of Life).

https://www.eclipse.org/jetty/documentation/current/what-jetty-version.html

Your code works, as-is, just upgrade to a supported version of Jetty.

I tested with Jetty 9.4.20.v20190813 and it worked as designed.

The Status Code: 206 Partial Content is an expected status response code, when the response includes partial range data, which is only produced when the request includes the Range: header.

Your code has a handler tree that looks like the following ..

Server.setHandler
 \- HandlerCollection
     \- WebAppContext ("/a")
     \- WebAppContext ("/b")
     \- GzipHandler

Having the GzipHandler at the end will allow the GzipHandler to apply to requests that don't match contexts "/a" nor "/b", which is a legitimate configuration, but probably not what you want.

Since you mentioned favicon.ico, that's usually a root request, which wouldn't match either "/a" nor "/b", so that's what we can only assume is what you want.

But, you didn't actually say that you wanted the GzipHandler to apply to the WebAppContext, so I'm just going to assume that is what you really wanted to accomplish.

If that's the case, then we need GzipHandler to do what it needs BEFORE a WebAppContext is entered.

We want a handler tree that looks like this ...

Server.setHandler
 \- HandlerList
     \- GzipHandler
     |   \- ContextHandlerCollection
     |       \- WebAppContext ("/a")
     |       \- WebAppContext ("/b")
     \- DefaultHandler

Which would be a single GzipHandler configured for all contexts.

The rough code would look like this ...

HandlerList handlers = new HandlerList();
server.setHandler(handlers);

WebAppContext appA = new WebAppContext();
appA.setContext("/a");
WebAppContext appB = new WebAppContext();
appB.setContextPath("/b");

ContextHandlerCollection contexts = new ContextHandlerCollection(appA, appB);

GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setHandler(contexts);

handlers.addHandler(gzipHandler);
handlers.addHandler(new DefaultHandler());

But you can also have a unique GzipHandler per WebApp.

Looking like this ...

Server.setHandler
 \- HandlerList
     \- ContextHandlerCollection
     |   \- WebAppContext ("/a")
     |       \- GzipHandler (instance / config A)
     |   \- WebAppContext ("/b")
     |       \- GzipHandler (instance / config B)
     \- DefaultHandler

or like this ...

Server.setHandler
 \- HandlerList
     \- GzipHandler (instance / config A)
     |   \- WebAppContext ("/a")
     \- GzipHandler (instance / config B)
     |   \- WebAppContext ("/b")
     \- DefaultHandler
Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136