Search code examples
javaspringwebsocketspring-websocket

How to implement a custom WebSocket subprotocol with Spring


I have to add support for a custom WebSocket subprotocol (so not STOMP) in a Spring Boot application, but I'm having a very hard time understanding what I need to provide and what Spring already has.

This is how far I got:

@Configuration
@EnableWebSocket
public class WebSocketAutoConfiguration implements WebSocketConfigurer {

    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
        webSocketHandlerRegistry.addHandler(this.webSocketHandler(), new String[]{endpointUrl});
    }

    @Bean
    public WebSocketHandler webSocketHandler() {
        ExecutorSubscribableChannel clientInboundChannel = new ExecutorSubscribableChannel();
        ExecutorSubscribableChannel clientOutboundChannel = new ExecutorSubscribableChannel();
        SubProtocolWebSocketHandler subProtocolWebSocketHandler = new SubProtocolWebSocketHandler(clientInboundChannel, clientOutboundChannel);
        subProtocolWebSocketHandler.addProtocolHandler(new SubProtocolHandler() {
            public List<String> getSupportedProtocols() {
                return Collections.singletonList("custom-protocol");
            }

            public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> message, MessageChannel outputChannel) throws Exception {
                session.sendMessage(new TextMessage("some message"));
            }

            public void handleMessageToClient(WebSocketSession session, Message<?> message) throws Exception {
            }

            public String resolveSessionId(Message<?> message) {
                return UUID.randomUUID().toString();
            }

            public void afterSessionStarted(WebSocketSession session, MessageChannel outputChannel) throws Exception {
                System.out.println("SESSION STARTED");
            }

            public void afterSessionEnded(WebSocketSession session, CloseStatus closeStatus, MessageChannel outputChannel) throws Exception {
                session.close();
                System.out.println("SESSION ENDED");
            }
        });
        return subProtocolWebSocketHandler;
    }
}

This works, in the sense that handleMessageFromClient does get triggered on a web socket message, but I fail to understand the purpose of MessageChannel outputChannel and handleMessageToClient.

Is it possible to get the PerConnectionWebSocketHandler semantics with SubProtocolWebSocketHandler?

The documentation around this is basically non-existent e.g. the docs for handleMessageToClient say:

Handle the given {@link Message} to the client associated with the given WebSocket session.

Well, fantastic. And the STOMP implementations are mind-boggling, so they're not very usable as a guideline.

Any example, broad steps or anything, really, would be much appreciated.


Solution

  • Turns out it is exceptionally easy. No need to mess with SubProtocolWebSocketHandler at all. The only requirement is that the provided WebSocketHandler implements SubProtocolCapable.

    public class CustomHandler implements WebSocketHandler, SubProtocolCapable {
       ...
    }
    

    That's all. To make a PerConnectionWebSocketHandler, it's enough to simply extend it and implement SubProtocolCapable:

    public class CustomHandler extends PerConnectionWebSocketHandler implements SubProtocolCapable {
       ...
    }