Spring Framework provides an API for the WebSocket protocol that you can use to write client and server applications that handle WebSocket messages.
WebSocketHandler
Create a WebSocket server the same way simply how to implement WebSocketHandler or, more likely, extend TextWebSocketHandler or BinaryWebSocketHandler. The following example uses TextWebSocketHandler:
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
public class MyHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
}
}
There is a special Java configuration WebSocket and XML namespace support for mapping a previous WebSocket handler to a specific URL, as shown in the following example:
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
The following example shows the XML equivalent of the configuration from the previous example:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
The previous example is intended for use in Spring MVC applications and should be included in the DispatcherServlet configuration. However, Spring's WebSocket support is independent of Spring MVC. With WebSocketHandler it is relatively easy to integrate WebSocketHandler into other HTTP frameworks using WebSocketHttpRequestHandler.
When using the WebSocketHandler API directly or indirectly, such as through STOMP messaging, the application must synchronize the sending of messages because the underlying standard WebSocket session (JSR-356) does not allow simultaneous sending. One option is to wrap WebSocketSession with ConcurrentWebSocketSessionDecorator.
WebSocket handshake
The easiest way configure the initial HTTP handshake request for the WebSocket is a HandshakeInterceptor that exposes the "before" and "after" handshake methods. You can use such an interceptor to bypass handshake or expose some attributes to WebSocketSession. The following example uses a built-in interceptor to pass HTTP session attributes to a WebSocket session:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/myHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
The following example shows the XML equivalent of the configuration from the previous example:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
A more advanced option is the DefaultHandshakeHandler extension, which performs all handshake steps for a WebSocket, including checking client origin, subprotocol approval, etc. An application may also need to use this option if it needs to configure a custom RequestUpgradeStrategy to adapt to an engine and WebSocket server version that is not yet supported. Both the Java configuration and the XML namespace allow you to configure a custom HandshakeHandler.
WebSocketHandlerDecorator , which can be used to decorate
WebSocketHandler with additional operating logic. Implementations of logging and exception handling are provided and added by default when using a Java WebSocket configuration or an XML namespace.
ExceptionWebSocketHandlerDecorator catches all uncaught exceptions raised from any
WebSocketHandler method and closes the WebSocket session with status
1011, which indicates a server error.
Deployment
The Spring WebSocket API is easy to integrate into a Spring MVC application if the DispatcherServlet handles both the WebSocket HTTP handshake and other HTTP requests. It can also be easily integrated into other HTTP processing scripts by calling WebSocketHttpRequestHandler. It's convenient and understandable. However, there are special considerations for JSR-356 runtimes.
The Java WebSocket API (JSR-356) provides two deployment mechanisms. The first involves scanning the classpath of the servlet container (Servlet 3 feature) at startup. The other is a registration API for use when initializing a servlet container. Neither of these mechanisms allows for a single "front controller" to be used for all HTTP processing - including WebSocket handshake and all other HTTP requests - such as DispatcherServlet in Spring MVC .
This is a significant limitation of JSR-356 that Spring's WebSocket support addresses with server-specific RequestUpgradeStrategy implementations, even when running in the JSR-356 runtime. Such strategies currently exist for Tomcat, Jetty, GlassFish, WebLogic, WebSphere and Undertow (and WildFly).
A secondary point is that JSR-356 enabled servlet containers must perform a ServletContainerInitializer (SCI) scan, which can slow down the launch of the application - in some cases significantly. If you experience significant slowdown after upgrading to a JSR-356-enabled version of the servlet container, you should be able to selectively enable or disable web fragments (and SCI scanning) using the <absolute-ordering /> element in web.xml, as shown in the following example:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<absolute-ordering/>
</web-app>
You can then selectively activate web fragments by name, for example Spring's own SpringServletContainerInitializer, which provides support for the Java initialization API. for Servlet 3 The following example shows how to do this:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<absolute-ordering>
<name>spring_web</name>
</absolute-ordering>
</web-app>
Server Configuration
Each underlying WebSocket protocol mechanism exposes configuration properties that control runtime characteristics, such as message buffer size, idle timeout, and others.
For Tomcat, WildFly, and GlassFish, you can add ServletServerContainerFactoryBean to the WebSocket Java config, as shown in the following example:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}
The following example shows the XML equivalent of the configuration from the previous example:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<bean class="org.springframework...ServletServerContainerFactoryBean">
<property name="maxTextMessageBufferSize" value="8192"/>
<property name="maxBinaryMessageBufferSize" value="8192"/>
</bean>
</beans>
WebSocketContainerFactoryBean (XML) or
ContainerProvider.getWebSocketContainer() (Java configuration).
In case of Jetty, you need to provide a pre-configured WebSocketServerFactory for Jetty and connect it to DefaultHandshakeHandler from Spring via the Java config for WebSocket. The following example shows how to do this:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(),
"/echo").setHandshakeHandler(handshakeHandler());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
}
}
The following example shows the XML equivalent of the configuration from the previous example:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/echo" handler="echoHandler"/>
<websocket:handshake-handler ref="handshakeHandler"/>
</websocket:handlers>
<bean id="handshakeHandler" class="org.springframework...DefaultHandshakeHandler">
<constructor-arg ref="upgradeStrategy"/>
</bean>
<bean id="upgradeStrategy" class="org.springframework...JettyRequestUpgradeStrategy">
<constructor-arg ref="serverFactory"/>
</bean>
<bean id="serverFactory" class="org.eclipse.jetty...WebSocketServerFactory">
<constructor-arg>
<bean class="org.eclipse.jetty...WebSocketPolicy">
<constructor-arg value="SERVER"/>
<property name="inputBufferSize" value="8092"/>
<property name="idleTimeout" value="600000"/>
</bean>
</constructor-arg>
</bean>
</beans>
Acceptable sources
Starting with Spring Framework 4.1.5, default logic for WebSocket and SockJS is to only accept requests from the same source. You can also allow all or a specific list of sources. This check is primarily intended for browser clients. There is nothing to prevent other types of clients from changing the value of the Origin header (see " for details RFC 6454: Web Origin Concept).
The following three operational logic options are possible:
Allow only requests from the same origin ( default): In this mode, if SockJS is enabled, the HTTP response inline frame (Iframe) for the
X-Frame-Optionsheader is set toSAMEORIGINand the JSONP passing mechanism is disabled because it doesn't allow you to check the source of the request. As a result, there is no support for IE6 and IE7 if this mode is enabled.Allow specified list of sources: Each valid source must start with
http://orhttps://. In this mode, if SockJS is activated, iframe passing is disabled. As a result, there is no support for IE6 through IE9 if this mode is enabled.Allow any sources: To enable this mode, you must specify
*as valid source values. In this mode, all transmission mechanisms are available.
You can configure valid WebSocket and SockJS sources, as shown in the following example:
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
The following example shows the XML equivalent of the configuration from the previous example:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="https://mydomain.com">
<websocket:mapping path="/myHandler" handler="myHandler" />
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
GO TO FULL VERSION