6

I'm have to implement custom API over Websockets that requires:

  • Custom WAMP-like subprotocol
  • Path parameters in socket URI

So I've following questions:

  • Is there any documentation or guides on implementing custom subprotocols in Spring? Protocol requires that exact version must be specified in the Sec-Websocket-Protocol field. Where this field could be read on server side?
  • What is a proper way to pass path parameters into a message handler? I could use ant patterns in handler registration

        @Override 
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(customHandler(), "/api/custom/{clientId}");
        }
    

    but those seems not available at TextWebSocketHandler. I'm solved this for now by extending default HttpSessionHandshakeInterceptor in a following way:

    public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
        private static final UriTemplate URI_TEMPLATE = new UriTemplate("/api/custom/{clientId}");
    
        @Override
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                       WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
            Map<String, String> segments = URI_TEMPLATE.match(request.getURI().getPath());
            attributes.put("CLIENTID", segments.get("clientId"));
    
            return super.beforeHandshake(request, response, wsHandler, attributes);
        }
    }
    

    and then accessing it in TextWebSocketHandler:

    public class CustomHandler extends TextWebSocketHandler {
    
        @Override
        protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
            super.handleTextMessage(session, message);
    
            String clientId = session.getAttributes().get("CLIENTID");
    
            ...
    
            session.sendMessage(response);
        }
    
    }
    

    but this method, in my opinion, is a bit clunky. Is there more proper way to solve this?

Thanks.

Gris
  • 303
  • 1
  • 3
  • 10

1 Answers1

2

The best advice I could give is to follow the example of the sub-protocol support that's built in -- starting with SubProtocolWebSocketHandler and the SubProtocolHandler's it delegates to including the StompSubProtocolHandler implementation. The SubProtocolWebSocketHandler is further connected to "clientInbound" and "clientOutbound" channels which are then used to form a processing flow as well as to provide thread boundaries.

There is a description for the processing flow for STOMP http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp-message-flow which includes delegating messages to annotated controllers and/or to a message broker which can also send messages back downstream to clients.

Essentially the StompSubProtocolHandler translates to and from a WebSocketMessage and a Spring Message with protocol-specific content. So that controllers, message brokers, or any other consumer of the messages from the client inbound channel are decoupled and unaware from the WebSocket transport layer. Many of the facilities built around the building, sending, and processing of such sub-protocol messages are meant to be potentially usable for support of other STOMP-like protocols. That includes all the classes in the org.springframework.messaging.simp package.

As for URL path parameters, Spring doesn't provide anything at the WebSocket level which is mostly a transport layer. Most of the interesting stuff happens at the sub-protocol level. For example for STOMP a MessageMapping is supported based on the destination header along with a @DestinationVariable which is comparable to using @PathVariable in Spring MVC but based on the destination header, not the URL.

Rossen Stoyanchev
  • 4,910
  • 23
  • 26