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.

Spring provides a base class 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 request has been created to resolve the previous limitation WebSocket API in Java, which can be found at eclipse-ee4j/websocket-api#211 . Tomcat, Undertow and WebSphere provide their own alternative APIs to do this, and it is also possible to do this using Jetty. Hopefully the same will be done for other servers.

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>
For client-side WebSocket configuration, use 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 to SAMEORIGIN 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:// or https://. 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>