Question

I am using Wicket with an embedded Jetty webserver. The application is using websockets, therefore the Jetty setup looks the following:

FilterHolder filterHolder = new FilterHolder(Jetty9WebSocketFilter.class);
filterHolder.setInitParameter("applicationClassName", "MyApplication");
filterHolder.setInitParameter("filterMappingUrlPattern", "/*");

WebAppContext context = new WebAppContext();
context.setResourceBase(".");
context.addFilter(filterHolder, "/*", null);
context.addServlet(DefaultServlet.class, "/*");

Server server = new Server(8083);
server.setHandler(context);

try {
    server.start();
} catch (Exception e) {
    e.printStackTrace();
}

Everything works fine, besides the websocket timeout. Research showed that Jetty's websocket connections time out, although this is not common for webservers.

After researching I stumbled upon the following init parameter, that I now pass to my filterHolder:

filterHolder.setInitParameter("maxIdleTime", "5000");

And apparently this parameter does something - because now the timeout occurs notably faster than before, exactly after 5 seconds.

But I couldn't figure out how I can disable the timeout completely. Using -1, 0 or even Integer.MIN_VALUE instead of 5000 does nothing, there is still a timeout after a while. The documentation I found says nothing about an according value.

What init parameter can I use to disable the websocket timeout? Or do I have to stick with setting the timeout to some ridiculously high value?

Was it helpful?

Solution

Jetty 9.1 has its own WebSocketUpgradeFilter, use that one, and then modify the default policy's idle timeout.

Example:

package jetty.websocket;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
import org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;

public class JettyWebSocketViaFilter
{
    @WebSocket
    public static class EchoSocket
    {
        @OnWebSocketMessage
        public void onMessage(Session session, String msg)
        {
            session.getRemote().sendStringByFuture(msg);
        }
    }

    public static class EchoCreator implements WebSocketCreator
    {
        private EchoSocket echoer = new EchoSocket();

        @Override
        public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
        {
            return echoer;
        }
    }

    public static void main(String[] args)
    {
        Server server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(8080);
        server.addConnector(connector);

        // Setup the basic application "context" for this application at "/"
        // This is also known as the handler tree (in jetty speak)
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);

        // Add the websocket filter
        WebSocketUpgradeFilter wsfilter = WebSocketUpgradeFilter.configureContext(context);
        wsfilter.getFactory().getPolicy().setIdleTimeout(5000);
        wsfilter.addMapping(new ServletPathSpec("/"),new EchoCreator());

        // The filesystem paths we will map
        String staticRoot = "src/main/webapp/websocket/protocol";

        ServletHolder holderDefault = new ServletHolder("default",DefaultServlet.class);
        holderDefault.setInitParameter("resourceBase",staticRoot);
        holderDefault.setInitParameter("dirAllowed","true");
        context.addServlet(holderDefault,"/");

        try
        {
            server.start();
            server.join();
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }
}

Timeouts in Jetty with WebSockets:

Since WebSocket is an upgraded HTTP/1.1 request you have essentially 2 timeouts to worry about.

First is the connector idle timeout, that will be used for the HTTP/1.1 initial portion of the incoming Upgrade request. This is configured at the server level, with the connector that it has. With Jetty, any value 0 or below is considered an infinite timeout.

Next, you have the websocket endpoint specific idle timeout. If it has a value greater than 0, then it is applied to already established connection's idle timeout (the one from the server side).

Some combinations and what it means ...

Server Connector IdleTimeout  | WebSocket Endpoint IdleTimeout | Actual Timeout
------------------------------+--------------------------------+----------------
   30,000 ms                  |       -1                       |   30,000 ms
   30,000 ms                  |   10,000 ms                    |   10,000 ms
   30,000 ms                  |  400,000 ms                    |  400,000 ms
  500,000 ms                  |       -1                       |  500,000 ms
  500,000 ms                  |      200 ms                    |      200 ms
       -1                     |       -1                       |  (infinite)
       -1                     |    1,000 ms                    |    1,000 ms

You can think of the Server Connector IdleTimeout as a TCP level timeout, while the WebSocket endpoint timeout is an application level idle timeout.

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top