Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot Websocket without SockJS

I have been struggling with this for at least two weeks now. I'm pretty new to websockets. I have good experience with rest endpoints.

My use case is simple. Client initiates a websocket connection sending some info to server, and server uses that info and, sends client some info back at some regular interval say every 5 seconds. I followed the tutorial here - https://spring.io/guides/gs/messaging-stomp-websocket/ It works perfectly as explained. As per the above tutorial, client initiates a http request, which gets upgraded to websocket.

In my case front end is an angular 10 application, and the front end developer prefers to use rxjs/websocket and doesn't want to use SockJS client, as he is sure we don't have to support any legacy browsers, and this is where I'm struck. Apparently rxjs/websocket needs url in ws:// protocol.

From the following snippet, I thought my equivalent ws protocol would be ws://localhost:8080/test However, it doesn't seem to work. I'm not sure what is wrong. Any help is greatly appreciated!

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
{

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config)
    {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/ws/");
    

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry)
    {
        registry.addEndpoint("/test");
    }

}

From the tutorial, I changed app.js, as follows to test this out.

function connect() {
    // var socket = new SockJS('http://localhost:8080/test'); This works perfectly
    // stompClient = Stomp.over(socket);
    ws = new WebSocket('ws://localhost:8080/test');
    stompClient = Stomp.client(ws);

    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/' + $("#site").val(), function (message) {
            showMessageSentFromServer(JSON.stringify(message.body));
        });
    });
}

When I open up the developer tool of chrome and inspect, I see the websocket connection has be established and upgraded or that is what I see. However, in the console, I see an error log as follows. I'm not sure what is wrong.

Screen shot of network:

enter image description here

Console failure log:

stomp.min.js:8 Uncaught DOMException: Failed to construct 'WebSocket': The URL '[object WebSocket]' is invalid.
    at Object.client (http://localhost:8080/webjars/stomp-websocket/stomp.min.js:8:7229)
    at connect (http://localhost:8080/app.js:18:25)
    at HTMLButtonElement.<anonymous> (http://localhost:8080/app.js:54:9)
    at HTMLButtonElement.dispatch (http://localhost:8080/webjars/jquery/jquery.min.js:3:10315)
    at HTMLButtonElement.q.handle (http://localhost:8080/webjars/jquery/jquery.min.js:3:8342)

Now long story short, I managed to disable SockJs on the server side, by removing withSockJS(). So what is my equivalent ws protocol URL?

Also, another challenge I have apart from this is, how do I set up a scheduled process that can send messages to a websocket topic that the client has subscribed, based on the input from the client. I know it is easy to set up a scheduled process using a @Scheduled annotation. But in my case, I wanted some inputs from the client which is required inside the scheduled process.

Also, please share any resources or examples that you have that explains how to implement a websocket stomp client subscription to a topic using rxjs

like image 888
Manikdn84 Avatar asked Feb 04 '23 13:02

Manikdn84


1 Answers

I was able to fix the problem by making two simple changes.

  1. I enabled my backend to support both ws and http protocols by adding two endpoints to the WebSocketConfig as shown below - one with and the other one without sockjs as shown below, which made my backend more flexible in terms of supporting both the protocols to establish websocket connection. I don't know why this wasn't mentioned anywhere in spring docs or else. Perhaps, people thought it is implied!
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
{

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

        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry)
        {
                registry.addEndpoint("/test"); // This will allow you to use ws://localhost:8080/test to establish websocket connection
                registry.addEndpoint("/test").withSockJS(); // This will allow you to use http://localhost:8080/test to establish websocket connection
        }

}
  1. As pointed out by @arturgrzesiak in previous comment, there was an error in the parameter passed to the Stomp.client(url) I was silly and passing wsobject instead of plain url.
stompClient = Stomp.client('ws://localhost:8080/test');

Finally,

If someone wants to use SockJS client to connect, they can connect using

var socket = new SockJS('http://localhost:8080/test');
stompClient = Stomp.over(socket);

If someone wants to use just a plain Websocket object to connect, use the following.

stompClient = Stomp.client('ws://localhost:8080/test');

I posted this solution as this will be useful to someone out there who had a similar painful experience, and they might find it useful.

like image 195
Manikdn84 Avatar answered Feb 11 '23 18:02

Manikdn84