Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring websocket is automatically closed after 30 minutes (timeout)

Tags:

I'm trying to implement a websocket on using Spring Boot (1.5.13).

The messaging works fine but after about 30 minutes the connection is terminated by the server (Reason 1008 - "This connection was established under an authenticated HTTP session that has ended"). I've tried to set different timeouts but doesn't seem to have any effect.

@Service
@RequiredArgsConstructor
@Slf4j
public class OCPPSocketHandler extends TextWebSocketHandler {
    @Override
    public void handleTextMessage(WebSocketSession webSocketSession, TextMessage textMessage)
        throws IOException {
      ...
    }
}

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    public static final String ENDPOINT = "/pp/v2.0";

    @Autowired
    private CustomSocketHandler socketHandler;

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(
            new CustomExceptionWebSocketHandlerDecorator(socketHandler), ENDPOINT
        )
        .setAllowedOrigins("*");
    }
}

application.properties:

#6h as milliseconds
server.connection-timeout=3600000 
server.servlet.session.timeout=6h

A TextMessage (WebSocket) is sent every 30 minutes to keep the connection alive.

I've seen this question about session timeouts but I can't see a solution there

like image 566
Sebastian Avatar asked May 29 '18 15:05

Sebastian


1 Answers

I found my WebSocket closed after 30 minutes too.

The root reason is the http session will close after 30 minutes by default in SpringBoot.

In SpringBoot config property server.servlet.session.timeout will change the default behavior, but there might have some limit.

Besides, WebSocket connections have pingpong messages to keep alive, so the connection should never be closed, until the pingpong stops.

After some tracing, I found a way to solve this problem:

  1. The timer to close the connection is here io.undertow.server.session.InMemorySessionManager.SessionImpl in my case.
  2. As we can see io.undertow.server.session.InMemorySessionManager.SessionImpl#setMaxInactiveInterval will reset the timer.
  3. This method will be called by javax.servlet.http.HttpSession#setMaxInactiveInterval.
  4. So, once setMaxInactiveInterval is called before each timeout, the connection will never be closed.

Here is my implementation:

  1. Store the HandshakeRequest into the javax.websocket.EndpointConfig#getUserProperties in the Configurator javax.websocket.server.ServerEndpointConfig.Configurator#modifyHandshake
  2. Our clients will send String messages to keep alive, so in the onMessage method, fetch the HandshakeRequest from javax.websocket.Session#getUserProperties, then
HttpSession httpSession = (HttpSession) handshakeRequest.getHttpSession();
httpSession.setMaxInactiveInterval((int) (session.getMaxIdleTimeout() / 1000));

That's all, hope it helps.

like image 111
debugging Avatar answered Sep 28 '22 17:09

debugging