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-Options
header is set toSAMEORIGIN
and 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>