Search code examples
javaspringjettyspring-websocketembedded-jetty

Setting WebSocket session idle timeout using Spring's Websocket API & Jetty 10


I have embedded Jetty 10 and Spring 5.3.37 (non-spring boot) application on it.
While integrating WS server (non-sockjs/stomp) into the application I faced the idle timeout issue, it's too short for my needs and i wanted to increase it but couldn't find a way.

The official Spring documentation has an article on the WebSocket API.
It says that it's possible to configure idle timeout using JettyRequestUpgradeStrategy.addWebSocketConfigurer.
But i guess, it's valid only for recent versions of Spring 6+ and Jetty 11+.

The thing is that I'm using Jetty 10 and Jetty10RequestUpgradeStrategy.
Spring 5's JettyRequestUpgradeStrategy won't work due to Jetty's 10 WebAppClassLoader restrictions (only for Jetty <= 9).
Jetty10RequestUpgradeStrategy looks pretty much different and doesn't have anything for customization.
An empty constructor without WebSocketPolicy and WebSocketServerFactory unlike the JettyRequestUpgradeStrategy.

My WS configuration:

@Configuration
@EnableWebSocket
public class WSConfig implements WebSocketConfigurer
{
    private final WSNotifyHandler wsNotifyHandler;

    public WSConfig(WSNotifyHandler wsNotifyHandler) {
        this.wsNotifyHandler = wsNotifyHandler;
    }

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(mapWsHandler, "/ws")
                .addInterceptors(new HttpSessionHandshakeInterceptor()).setHandshakeHandler(handshakeHandler());
    }

    @Bean
    public DefaultHandshakeHandler handshakeHandler() {
        return new DefaultHandshakeHandler(
                new Jetty10RequestUpgradeStrategy());
    }
}

So far I've run out of ideas on how to work around this issue without migrating to newer versions or "pinging" connection.
Is there a way to configure JettyWebSocketServerContainer that i missed out?


Solution

  • With my combination of Jetty 10 & Spring 5 it was impossible to do this directly.
    Switching to Jetty 12 won't help either due to the lack of necessary API for Jetty 12 in Spring 5.
    Moreover, Spring 6's baseline was set to Java 17 & EE9+.

    So, my final solution was to finally move to Jetty 12 and upgrade the app to Spring 6 and EE10.

    Meanwhile, i quickly dug out a very dirty solution for Jetty 10 that involves reflection and is generally discouraged to use:

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception
    {
        final var nativeSessionFld = AbstractWebSocketSession.class.getDeclaredField("nativeSession");
        nativeSessionFld.setAccessible(true);
        final var nativeSessionObj = nativeSessionFld.get(session);
        final var setIdleTimeout = nativeSessionObj.getClass().getDeclaredMethod("setIdleTimeout", Duration.class);
        setIdleTimeout.invoke(nativeSessionObj, Duration.ofMinutes(2));
    }