Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add custom headers to STOMP CREATED message in Spring Boot application?

I'm trying to add custom headers to the STOMP 'CREATED' message, which is received by client at the first connection. Here is the function which connects to the WebSocket using STOMP JavaScript:

function connect() {
    socket = new SockJS('/chat');
    stompClient = Stomp.over(socket);
    stompClient.connect('', '', function(frame) {
      whoami = frame.headers['user-name'];
      console.log(frame);
      stompClient.subscribe('/user/queue/messages', function(message) {
          console.log("MESSAGE RECEIVED:");
          console.log(message);

        showMessage(JSON.parse(message.body));
      });
      stompClient.subscribe('/topic/active', function(activeMembers) {
        showActive(activeMembers);
      });
    });
  }

This function prints the following to the browser's console:

body: ""
command: "CONNECTED"
headers: Object
    heart-beat: "0,0"
    user-name: "someuser"
    version: "1.1"

And i want to add custom header so output must look like:

body: ""
command: "CONNECTED"
headers: Object
    heart-beat: "0,0"
    user-name: "someuser"
    version: "1.1"
    custom-header: "foo"

I have the following WebSocket configuration in my Spring Boot app.

WebSocketConfig.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

  @Override
  public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/queue", "/topic");
    config.setApplicationDestinationPrefixes("/app");
  }

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/chat", "/activeUsers")
            .withSockJS()
            .setInterceptors(customHttpSessionHandshakeInterceptor());
  }

  ...

  @Bean
  public CustomHttpSessionHandshakeInterceptor 
        customHttpSessionHandshakeInterceptor() {
        return new CustomHttpSessionHandshakeInterceptor();

  }

}

I have tried to register the 'HandshakeInterceptor' to set custom header, but it didn't work. Here is 'CustomHttpSessionHandshakeInterceptor':

CustomHttpSessionHandshakeInterceptor.java

public class CustomHttpSessionHandshakeInterceptor implements 

HandshakeInterceptor {

     @Override
        public boolean beforeHandshake(ServerHttpRequest request,
        ServerHttpResponse response,
        WebSocketHandler wsHandler,
        Map<String, Object> attributes) throws Exception {
            if (request instanceof ServletServerHttpRequest) {


                 ServletServerHttpRequest servletRequest =
                    (ServletServerHttpRequest) request;
                 attributes.put("custom-header", "foo");
            }
            return true;
        }

        public void afterHandshake(ServerHttpRequest request,
            ServerHttpResponse response,
            WebSocketHandler wsHandler,
            Exception ex) { }
}

I have found this code snippet at https://dzone.com/articles/spring-boot-based-websocket
Can someone explain me why this approach does not work? Is there another way to set custom headers to the STOMP 'CREATED' message at server side in Spring Boot application?
Thanks!

like image 701
Glinskiy Vladislav Avatar asked Feb 10 '17 18:02

Glinskiy Vladislav


1 Answers

Maybe it's too late, but better late than never ...

Server messages (e.g. CONNECTED) are immutable, means that they cannot be modified.

What I would do is register a client outbound interceptor and trap the connected message by overriding the preSend(...) method and build a new message with my custom headers.

@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) 
{
    LOGGER.info("Outbound channel pre send ...");
    final StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(message);
    final StompCommand command = headerAccessor.getCommand();
    if (!isNull(command)) {
        switch (command) {
            case CONNECTED:
                final StompHeaderAccessor accessor = StompHeaderAccessor.create(headerAccessor.getCommand());
                accessor.setSessionId(headerAccessor.getSessionId());
                @SuppressWarnings("unchecked")
                final MultiValueMap<String, String> nativeHeaders = (MultiValueMap<String, String>) headerAccessor.getHeader(StompHeaderAccessor.NATIVE_HEADERS);
                accessor.addNativeHeaders(nativeHeaders);

                // add custom headers
                accessor.addNativeHeader("CUSTOM01", "CUSTOM01");

                final Message<?> newMessage = MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders());
                return newMessage;
            default:
                break;
            }
        }
        return message;
    }

@UPDATE:::

The interface needed is called ChannelInterceptor and to register your own implementation you need to add @Configuration annotated class

@Configuration
public class CustomMessageBrokerConfig extends WebSocketMessageBrokerConfigurationSupport
implements WebSocketMessageBrokerConfigurer{}

and override a method configureClientOutboundChannel as below

@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
    log.info("Configure client outbound channel started ...");
    registration.interceptors(new CustomOutboundChannelInterceptor());
    log.info("Configure client outbound channel completed ...");
}
like image 123
TheReALDeAL Avatar answered Nov 05 '22 00:11

TheReALDeAL