SockJS secure connection with IE8 - spring

Afternoon everyone. I'm having an issue with sockjs and Spring4. I don't know which side of the setup is causing the issue. The problem is I can't seem to get IE8 to open a connection to my Spring backend over HTTPS.
I'm trying to implament this example: https://demo.rasc.ch/spring4ws/
The link I'm trying is the chat.
The link to his source is here: https://github.com/ralscha/spring4ws-demos
The only change I made to his source is I'm using jquery-1.9.1 , Spring 4.0.0, and the full stomp.js and not the stomp.min.js
The sock and stomp code in the index page for the chat client is:
$(function() {
var username, lastUsername, stompClient, content = $("#content")[0],
input = $("#editor input")[0];
function notify(text) {
$('<p class="message notice"/>').text(text).appendTo(content);
content.scrollTop = content.scrollHeight;
}
$(input).keyup(function(event) {
if (event.which === 13 && $.trim(input.value)) {
if (!username) {
username = input.value;
$("#editor p").addClass("user").removeClass("guide").text(username);
var path = window.location.pathname.substring(0,
window.location.pathname.lastIndexOf('/')+1);
var sock = new SockJS(path + 'chat');
stompClient = Stomp.over(sock);
stompClient.connect({}, function(frame) {
notify("The connection has been opened");
$(input).removeAttr("disabled").focus();
stompClient.subscribe("/queue/chatmessage", function(msg) {
var data = JSON.parse(msg.body);
if (lastUsername !== data.username) {
lastUsername = data.username;
$('<p class="user"/>').text(data.username).appendTo(content);
}
$('<p class="message"/>').text(data.message).appendTo(content);
content.scrollTop = content.scrollHeight;
});
},
function(error) {
notify("An error occured: " + error);
$(input).attr("disabled", "disabled");
});
} else {
stompClient.send("/queue/chatmessage", {}, JSON.stringify({username: username, message: input.value}));
}
input.value = "";
}
});
$(input).focus();
$(window).resize(function() {
$(content).height($(window).height() - $("#editor").outerHeight(true) - 15).scrollTop(content.scrollHeight);
}).resize();
});
Sorry about the formatting.
In Spring all I did was separate the the webconfig java file into 2 files
WebConfig is standard. Extends WebMvcConfigurerAdapter :
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index.html");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
WebSocket implaments WebSocketMessageBrokerConfigurer:
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS().setSessionCookieNeeded(false);
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/");
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
// use default thread pool with 1 thread
}
#Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(2).maxPoolSize(3);
}
The initilizer is basic too.
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class, WebSocketConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/chatdemo/*" };
}
I'm also running this through Tomcat 7 using eclipse. So not the embedded tomcat.
The problem I'm having is the readystate inside sock is being set to permanent in IE. I don't fully understand xhr/xdr polling, but I'm assuming that's the problem.
Is there anything else I need to do to get IE to work over https on the sockjs side or the spring side?

Related

Websocket not working with spring boot application and angular frontend

I looked and tried a lot but I can not find the cause of my problem...
I have a JHipster generated application which consists out of a spring boot application and an angular frontend and I want to use websockets for updates. For that I use Stomp and SockJs
The connection itself is already not working.
I get the following error:
WebSocket connection to 'ws://localhost:9000/updates/websocket/447/xxudq4ni/websocket' failed: WebSocket is closed before the connection is established.
This is the call to port 9000, which is then proxied to the actual backend under Port 8080.
If I call the backend under port 8080 directly, I get:
WebSocket connection to 'ws://localhost:8080/updates/websocket/156/mg0dspp2/websocket' failed: Error during WebSocket handshake: Unexpected response code: 200
I do not really see what the actual response is but I suppose it is the JHIpster error message "an error has occured" and this html is returned with a http statuscode of 200.
I'm out of ideas what the actual problem is... I followed this intro here and several others...
here is my backend:
WebsocketConfig:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
public static final String IP_ADDRESS = "IP_ADDRESS";
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/updates/websocket")
//.setHandshakeHandler(defaultHandshakeHandler())
.setAllowedOrigins("*")
.withSockJS()
.setClientLibraryUrl("https://cdn.jsdelivr.net/npm/sockjs-client#1.5.0/dist/sockjs.min.js");
//.setInterceptors(httpSessionHandshakeInterceptor());
}
private DefaultHandshakeHandler defaultHandshakeHandler() {
return new DefaultHandshakeHandler() {
#Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
Principal principal = request.getPrincipal();
if (principal == null) {
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS));
principal = new AnonymousAuthenticationToken("WebsocketConfiguration", "anonymous", authorities);
}
return principal;
}
};
}
#Bean
public HandshakeInterceptor httpSessionHandshakeInterceptor() {
return new 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(IP_ADDRESS, servletRequest.getRemoteAddress());
}
return true;
}
#Override
public void afterHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Exception exception
) {}
};
}
}
Controller:
#Controller
public class UpdateController {
private static final Logger log = LoggerFactory.getLogger(UpdateController.class);
#MessageMapping("/updates/websocket")
#SendTo("/topic/trucks")
public UpdateDto send(UpdateDto dto) {
return dto;
}
}
Frontend:
connect(): void {
if (this.stompClient?.connected || this.called) {
return;
}
this.called = true;
// building absolute path so that websocket doesn't fail when deploying with a context path
let url = '/updates/websocket';
url = this.location.prepareExternalUrl(url);
var socket = new SockJS(url);
this.stompClient = Stomp.over(socket);
this.stompClient.connect({}, (frame) => {
this.connectionSubject.next();
this.sendActivity();
this.routerSubscription = this.router.events
.pipe(filter((event: Event) => event instanceof NavigationEnd))
.subscribe(() => this.sendActivity());
}, error => {
console.log(error);
});
}
Im on Windows and I use Chrome for the development. But it also does not work in FireFox, so I do not think it has something to do with the platform.
Any help would be very much appreciated. Thank you very much!

Spring boot push notification to vue.js

I want to send notification to all client connected using websocket and webstomp ,so I need to push notification when some row added to database , I tried something like this but did not work
my socket configuration
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/socket")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
controller
template.convertAndSend("/topic/notification", position);
front
methods: {
connect() {
this.socket = new SockJS("http://localhost:8082/socket");
this.stompClient = Stomp.over(this.socket);
this.stompClient.connect({},
frame => {
this.connected = true;
console.log(frame);
this.stompClient.subscribe("/topic/notification", tick => {
console.log(tick);
this.$notify({
group: 'foo',
title: '<h4>Nothing!</h4>',
text: 'car added',
type: 'success',
duration: 1000
})
});
},
error => {
console.log(error);
this.connected = false;
}
);
},
},

Controller component issue with Spring and SockJs

I have a problem with a configuration of a WebSocket using Spring and SockJs.
I have configured all my app like Spring Documentation, the connection seems to be fine, but when I send a message the Controller is never involved and it doesn't work.
This is the Configuration component:
#Configuration
#EnableWebMvc
#EnableWebSocketMessageBroker
public class MyWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/mySocket").withSockJS().setInterceptors(new MySocketHandshakeInterceptor());;
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app").enableSimpleBroker("/topic");
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
}
}
This is the HandshakeInterceptor implementation:
public class MySocketHandshakeInterceptor implements HandshakeInterceptor {
#Override
public void afterHandshake(ServerHttpRequest paramServerHttpRequest, ServerHttpResponse paramServerHttpResponse, WebSocketHandler paramWebSocketHandler, Exception paramException) {
// TODO Auto-generated method stub
System.out.println("MySocketHandshakeInterceptor afterHandshake");
}
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> paramMap)
throws Exception {
System.out.println("MySocketHandshakeInterceptor beforeHandshake");
return true;
}
}
This is the Controller component:
#Controller
public class MySocketController {
#MessageMapping("/testsocket")
#SendTo("/topic/testresponse")
public MessageOut test(MessageIn message) throws Exception {
System.out.println("MySocketController start");
System.out.println("MySocketController test "+ message);
return new MessageOut("test OK");
}
}
These are MessageIn and MessageOut:
public class MessageIn {
private String test;
/**
* #return the test
*/
public String getTest() {
return test;
}
}
public class MessageOut {
private String result;
public MessageOut(String result) {
super();
this.result = result;
}
/**
* #return the test
*/
public String getResult() {
return result;
}
/**
* #param result the result to set
*/
public void setResult(String result) {
this.result = result;
}
}
Finally, this is the client side (javascript):
var socketSession = {};
connectToMySocket();
function connectToVideoSocket() {
socketSession.socket = new SockJS("/mySocket");
socketSession.socket.stomp = Stomp.over(socketSession.socket);
socketSession.socket.stomp.debug = null;
socketSession.socket.stomp.connect({}, function () {
socketSession.socket.stomp.subscribe("/topic/testresponse", function (data) {
console.log(data);
});
});
}
This is the command that I launch to test the socket:
socketSession.socket.stomp.send("app/testsocket", {}, JSON.stringify({'test': 'test'}));
In the system out console I can only see the two rows:
MySocketHandshakeInterceptor beforeHandshake
MySocketHandshakeInterceptor afterHandshake
The interceptor works fine, but I don't see any print by the Controller component.
What's wrong?
Thanks.
I resolved by myself.
It was a problem of the client-side.
The correct script is:
var mySocket = undefined;
var stompClient = undefined;
connectToVideoSocket();
function connectToVideoSocket() {
mySocket = new SockJS('/mySocket');
stompClient = Stomp.over(mySocket);
stompClient.debug = null;
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/testresponse', function(data){
console.log(JSON.parse(data.body).result);
});
});
}
In this way it works fine.

Spring websocket timeout settings

I'm using the Spring websocket support. My question is how to set the websocket connection timeout. Now the connection is closed automatically after several minutes. I want the connection never to be closed.
Here is my websocket handler:
public class MyHandler implements WebSocketHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass());
class MyTimerTask extends TimerTask {
private WebSocketSession session;
public MyTimerTask(WebSocketSession session) {
this.session = session;
}
#Override
public void run() {
try {
String msg = ((int)(Math.random()*50)) + "";
this.session.sendMessage(new TextMessage(msg.toString()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Autowired
private UserDao userDao;
#Autowired
private JdbcDaoImpl jdbcDaoImpl;
private Timer timer;
#Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
System.out.println("websocket????");
timer = new Timer();
timer.schedule(new MyTimerTask(session), 0, 1000);
logger.info("logger connection");
}
#Override
public void handleMessage(WebSocketSession session,
WebSocketMessage<?> message) throws Exception { }
#Override
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception { }
#Override
public void afterConnectionClosed(WebSocketSession session,
CloseStatus closeStatus) throws Exception {
System.out.println("websocket????");
timer.cancel();
}
#Override
public boolean supportsPartialMessages() {
return false;
}
}
my websocket config:
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="com.sdp.websocket.MyHandler"/>
and javascript client:
var webserver = 'ws://localhost:8080/authtest/myHandler';
var websocket = new WebSocket(webserver);
websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };
function onOpen(evt) {
console.log("Connected to WebSocket server.");
}
function onClose(evt) {
console.log("Disconnected");
}
function onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
}
function onError(evt) {
console.log('Error occured: ' + evt.data);
}
debugger;
function sendMsg(){
websocket.send("{msg:'hello'}");
}
The websocket stays opened until either the server or the client decide to close it. However, websockets are affected by two timeouts:
HTTP session timeout;
proxy connection timeouts;
If all you have between your client and your server is a websocket connection, and you don't interact over HTTP with AJAX or requesting other pages, the HTTP session expires and some servers decide to invalidate it along with the websocket (Tomcat7 had a bug that did just that). Some other servers don't do that because they see there is activity on the websocket. See here for an extended discussion: Need some resolution or clarification for how and when HttpSession last access time gets updated.
The other timeout is with proxies. They see the connection and if there is no activity on the wire for a longer period of time, they just cut it because they think it hanged. To address this, while you don't send actual application data, you need to have a heartbeat or a ping/pong of messages from time to time to let the proxy know that the connection is still OK.
Other issues might also intervene, like a buggy browser support for websocket, how your network is configured, firewall rules, etc.
For available timeout options in Spring see the websocket documentation: Configuring the WebSocket Engine.
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
var container = new ServletServerContainerFactoryBean();
container.setMaxSessionIdleTimeout(...);
return container;
}
}

websockets spring glassfish 4

I am trying to build a simple didactic websocket application using spring 4.0, jsf and glassfish 4.0.
I have created a maven web project (because this app has another web component(jsf)), and from this app i`m trying to setup some websockets.
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoHandler(), "/echo");
}
#Bean
public WebSocketHandler echoHandler() {
return new EchoHandler();
}
}
and
public class EchoHandler extends TextWebSocketHandler {
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
session.sendMessage(message);
}
}
and on the client side a very simple connect:
<script>
/* <![CDATA[ */
var endPointURL = "ws://localhost:8080/liveasterisk/echo";
var chatClient = null;
function connect () {
chatClient = new WebSocket(endPointURL);
chatClient.onmessage = function (event) {
alert(event);
};
}
function disconnect () {
chatClient.close();
}
function sendMessage() {
chatClient.send("xxx");
}
connect();
/* ]]> */
</script>
The problem is that when the connect() method fires i get a 404 response.
I guess that i have to somehow train jsf to respond to handshake request.
All my *.xhtml are mapped to jsf servlet.
So what I`m I missing here ?
I have solved the problem like this:
#ServerEndpoint(value = "/keepalive", configurator = SpringConfigurator.class)
public class KeepAliveEndpoint {
private static Logger logger = Logger.getLogger(KeepAliveEndpoint.class);
#Autowired
private KeepAliveService keepAliveService;
private List<Session> sessions = new ArrayList<Session>();
#OnOpen
public void onOpen(Session session) {
sessions.add(session);
System.out.println("onOpen: " + session.getId()+" list size: " + sessions.size());
}
#OnClose
public void onClose(Session session) {
System.out.println("onClose: " + session.getId());
sessions.remove(session);
}
#OnMessage
public void handleMessage(Session session, String message) {
try{
Long userId = Long.parseLong(message);
keepAliveService.keepAlive(userId);
}catch(NumberFormatException nfe){
try {
session.getBasicRemote().sendText("Cannot perform live update for your status");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
so now I have a sockets exposed via jsf and I can inject "services" with #Autowired in this endpoint.
And with this js code:
<script type="text/javascript">
var host = "ws://localhost:8080/myApp/keepalive";
var wSocket = new WebSocket(host);
var browserSupport = ("WebSocket" in window) ? true : false;
// called body onLoad()
function initializeReception() {
if (browserSupport) {
wSocket.onopen = function(){
setInterval(function(){wSocket.send('<h:outputText value="#{managedBean.userDTO.id}" />')}, 300000);
};
// called when a message is received
wSocket.onmessage = function(event) {
alert(event.data);
};
// on error handler
wSocket.onError = function(event) {
alert('An error has occured '+event.data+'.');
};
// called when socket closes
wSocket.onclose = function(){
// websocket is closed.
//alert("Connection is closed...");
};
}
else {
// The browser doesn't support WebSocket
alert("WebSocket is NOT supported by your Browser!");
}
}
initializeReception();
</script>
The above configuration is for use with Spring MVC's DispatcherServlet. Do you have one configured in the web application? Depending on the servlet mapping (not shown above) you'll most likely need one more part added to the URL to match the servlet mapping.
The longer explanation is that #EnableWebSocket creates a HandlerMapping that maps "/echo" to the WebSocketHandler. That HandlerMapping needs to reside in the configuration of the DispatcherServlet in order for the HTTP handshake to be processed.

Resources