Spring boot STOMP websocket works locally, but not on deployed server - spring

I have a STOMP client and Spring backend, the code works fine when local but not when deployed to server, failed to connect to server.
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/tracker").setAllowedOrigins("*");
}
}
Javascript client initiates connection with :
var socket = new WebSocket("ws://localhost:8080/tracker");
When I try this after deployment
WebSocket("wss://myurl/tracker")
or
WebSocket("wss://myurl:8080/tracker")
The connection fails

Can you provide more details? What is the exact error code and message?
Is it possible that you have a reverse proxy or a load balancer in front of your deployment server? This could be blocking the websocket from connecting in the deployment environment. In that case, you need to configure the proxy/balancer to allow for websockets. In NGINX, these are the lines you are looking for:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
Websocket connection starts with an HTTP Upgrade request to upgrade the protocol to WS or WSS depending on security. The lines above instruct NGINX to pass that request further to the server.
I recommend you have an in-depth read here in NGINS's guide.

Related

Socket keepalive property

We use okhttp3 lib with spring boot on service side.
OktHttp is configured to use connection pool.
We have found that okhttp does not enable keepalive for socket.
Do you know a reason why?
Our linux machine is configured with
net.ipv4.tcp_keepalive_time=60
net.ipv4.tcp_keepalive_intvl=6
net.ipv4.tcp_keepalive_probes=10
And we expect that all tcp connections will inherit these settings.
But if you check connection state with netstat -town you will see that keepalive is not enabled.
It can be an issue for cases when your border controller (firewall, gateway) drops idle connections
I can solve this issue with custom SocketFactory like this
#Override
public Socket createSocket() throws IOException {
var socket = socketFactory.createSocket();
socket.setKeepAlive(true);
return socket;
}
but maybe I miss something
Thanks,

Spring Boot websocket RabbitMQ STOMP relay broker cannot send message when sent from instance without TCP connection to client

I've got this issue when I try to send websocket message from the instance that has no TCP connection to client.
My setup:
2 instances of spring boot app. Single external RabbitMQ broker.
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableStompBrokerRelay("/topic/", "/queue/", "/exchange/")
.setRelayHost(properties.getRelayHost())
.setRelayPort(properties.getRelayPort())
.setSystemLogin(properties.getRelaySystemLogin())
.setSystemPasscode(properties.getRelaySystemPassword())
.setClientLogin(properties.getRelayClientLogin())
.setClientPasscode(properties.getRelayClientPassword())
.setUserDestinationBroadcast(properties.getUserDestinationBroadcast())
.setUserRegistryBroadcast(properties.getUserRegistryBroadcast());
}
Client connected to instance-1, messages sent from instance-1 are sent to client correctly.
When message is sent from instance-2 I get the user session from SimpUserRegistry, but sending message fails with: No TCP connection for session ...
Am I doing something wrong here or what could be the issue?
You are missing some configuration
registry.setApplicationDestinationPrefixes("/app")
.enableStompBrokerRelay("/topic", "/queue")
.setAutoStartup(Boolean.TRUE)
.setClientLogin(username)
.setClientPasscode(password)
.setSystemLogin(username)
.setSystemPasscode(password)
.setUserDestinationBroadcast("/topic/unresolved.user.dest")
.setUserRegistryBroadcast("/topic/registry.broadcast")
.setRelayHost(relayHost)
.setRelayPort(relayPort);
Just adding 2 more configuration properties, It works perfectly for me, no need to work around
.setUserDestinationBroadcast("/topic/unresolved.user.dest")
.setUserRegistryBroadcast("/topic/registry.broadcast")

Spring Boot Cors behind reverse-proxy on non-default port

My application has a backend built with Spring Boot, and a frontend web application built with Angular and served by nginx.
Nginx is configured to reverse-proxy requests to /api/ to the backend :
location /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://back:8080;
}
If nginx is running on the default port (80), I can access my application at http://myserver/ without issues. XHR calls to http://myserver/api/some/REST/resource work perfectly.
If I change nginx to listen to some other port (randomly : 9043), then all XHR calls fail with an error "Invalid CORS request". Which is unexpected, because only my frontend application is making calls to the API, and thanks to the reverse-proxy this API is served on the same host as the javascript files.
For it to function, I need to add this to my Spring application :
#Configuration
public class CorsConfig {
#Value("${url.base:''}")
private String urlBase;
#Value("${cors.allowed.origins:[]}")
private String[] allowedOrigins;
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(urlBase + "/**")
.allowedOrigins(allowedOrigins)
.allowedMethods("*");
}
};
}
}
and then set cors.allowed.origins to http://myserver:9043/ in my configuration.
This works, but it is not practical if I'm going to :
make the hostname and/or port dynamic (port is derived from the branch name during CI build, then the containers are deployed to a rancher cluster)
hide the nginx behind a load-balancer (= another level of reverse-proxying)
Would there be a solution to fix this by doing any of the following :
Have spring boot to ignore the port number when validating CORS requests ?
Have nginx tweak the contents of the proxied request so that CORS validation by Spring succeeds ?
EDIT : some more details and example :
The services are dockerized :
Frontend : nginx listens on port 80 inside the container, docker exposes it on the host as port 9043
Backend : spring boot listens on 8080. The port is not exported by Docker, so it is only accessible from the frontend container, which has a link to the backend container.
It only works if the frontend is exposed to the outside world on port 80...
Add two http header in reverse request:
X-Forwarded-Proto
X-Forwarded-Port
to indecate the origin proto and ports (match the origin request).
see more: https://moi.vonos.net/java/spring-forward/

Spring 4 STOMP over Websockets- How to setup login and passcode properly

I'm playing around with Spring 4 Stomp over Websockets. Now I'm trying to put login and password in my configuration.
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//registry.enableSimpleBroker("/queue/", "/topic/");
//Enable MQ
StompBrokerRelayRegistration relay=registry.enableStompBrokerRelay("/queue/", "/topic/");
relay.setSystemLogin("login");
relay.setSystemPasscode("passcode");
//relay.setClientLogin("login");
//relay.setClientPasscode("passcode");
registry.setApplicationDestinationPrefixes("/app");
}
But then when I try to connect with different login and passcode, I can still connect. Here's my javascript.
$scope.initSockets = function() {
$scope.socket.client = new SockJS('/Html5GameApp');
$scope.socket.stomp = Stomp.over($scope.socket.client);
$scope.socket.stomp.connect("sample","sample",function(frame) {
console.log('Connected: ' + frame);
$scope.socket.stomp.subscribe("/queue/stomp.data", $scope.liveGameData);
});
$scope.socket.client.onclose = $scope.reconnect;
};
Am I doing wrong with my configuration?How will I setup it properly.Thanks
Your application is made of 3 "systems" or "actors" in this scenario:
the browsers
the Spring application
the broker (e.g. RabbitMQ)
If you take a look at StompBrokerRelayRegistration's javadoc, you'll see that:
system credentials are for the shared "system" connection and are used to send messages to the STOMP broker from within the application, i.e. messages not associated with a specific client session (e.g. REST/HTTP request handling method).
client credentials are used when creating connections to the STOMP broker on behalf of connected clients.
If you're actually trying to enforce access security in your application, you could take a look at the portfolio sample and its security config. In a nutshell, security is enforced during the HTTP Upgrade phase in this example.

Grizzly / Glassfish - Cant establish websockets handshake

I'm trying to get WebSockets working on top of Grizzly / Glassfish. I've cloned the sample WebSockets chat application, built it and deployed it to Glassfish 3.1.2. However, I cannot get WebSockets to connect. The WebSockets handshake is failing because I'm getting a 405 (Method Not Allowed) response. This makes sense because of what is in the Servlet:
public class WebSocketsServlet extends HttpServlet {
private final ChatApplication app = new ChatApplication();
#Override
public void init(ServletConfig config) throws ServletException {
WebSocketEngine.getEngine().register(app);
}
#Override
public void destroy() {
WebSocketEngine.getEngine().unregister(app);
}
}
There is no doGet method specified, so I'm wondering if there is more configuration required somewhere, or if you need to implement the handshake logic in the servlet doGet method yourself?
I was trying to use grizzly-websockets-chat-2.1.9.war on glassfish 3.1.2 and getting same error.
Followed the advice from this page http://www.java.net/forum/topic/glassfish/glassfish/websocket-connection-not-establishing-glasshfish-server-how-fix-it-0
which states to use the version found here (I would think the version would indicate it being older however the time stamps on the files are Jan 30 2012):
WAR
https://maven.java.net/service/local/artifact/maven/redirect?r=releases&g=com.sun.grizzly.samples&a=grizzly-websockets-chat&v=1.9.46&e=war
SOURCES
https://maven.java.net/service/local/artifact/maven/redirect?r=releases&g=com.sun.grizzly.samples&a=grizzly-websockets-chat&v=1.9.46&e=jar&c=sources
By using that war everything works.
For those that like using the glassfish web console. You can enable web-sockets by:
Configurations > server-config > Network Config > Protocols > http-listener-1, then HTTP tab > Scroll to bottom and check off Websockets Support.
Note Configurations > default-config > ... also has the same options
You might find this more continent that keeping a terminal around.
It appears you haven't enabled websocket support (disabled by default).
Issue the following command and then restart the server:
asadmin set configs.config.server-config.network-config.protocols.protocol.http-listener-1.http.websockets-support-enabled=true
You can replace http-listener-1 with whatever http-listener you wish to enable WS support for.

Resources