[[websocket-server]] = WebSocket API [.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server[See equivalent in the Reactive stack]# The Spring Framework provides a WebSocket API that you can use to write client- and server-side applications that handle WebSocket messages. [[websocket-server-handler]] == `WebSocketHandler` [.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-handler[See equivalent in the Reactive stack]# Creating a WebSocket server is as simple as implementing `WebSocketHandler` or, more likely, extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`. The following example uses `TextWebSocketHandler`: [source,java,indent=0,subs="verbatim,quotes"] ---- 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 dedicated WebSocket Java configuration and XML namespace support for mapping the preceding WebSocket handler to a specific URL, as the following example shows: [source,java,indent=0,subs="verbatim,quotes"] ---- 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 configuration equivalent of the preceding example: [source,xml,indent=0,subs="verbatim,quotes,attributes"] ---- ---- The preceding example is for use in Spring MVC applications and should be included in the configuration of a xref:web/webmvc/mvc-servlet.adoc[`DispatcherServlet`]. However, Spring's WebSocket support does not depend on Spring MVC. It is relatively simple to integrate a `WebSocketHandler` into other HTTP-serving environments with the help of {spring-framework-api}/web/socket/server/support/WebSocketHttpRequestHandler.html[`WebSocketHttpRequestHandler`]. When using the `WebSocketHandler` API directly vs indirectly, e.g. through the xref:web/websocket/stomp.adoc[STOMP] messaging, the application must synchronize the sending of messages since the underlying standard WebSocket session (JSR-356) does not allow concurrent sending. One option is to wrap the `WebSocketSession` with {spring-framework-api}/web/socket/handler/ConcurrentWebSocketSessionDecorator.html[`ConcurrentWebSocketSessionDecorator`]. [[websocket-server-handshake]] == WebSocket Handshake [.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-handshake[See equivalent in the Reactive stack]# The easiest way to customize the initial HTTP WebSocket handshake request is through a `HandshakeInterceptor`, which exposes methods for "`before`" and "`after`" the handshake. You can use such an interceptor to preclude the handshake or to make any attributes available to the `WebSocketSession`. The following example uses a built-in interceptor to pass HTTP session attributes to the WebSocket session: [source,java,indent=0,subs="verbatim,quotes"] ---- @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 configuration equivalent of the preceding example: [source,xml,indent=0,subs="verbatim,quotes,attributes"] ---- ---- A more advanced option is to extend the `DefaultHandshakeHandler` that performs the steps of the WebSocket handshake, including validating the client origin, negotiating a sub-protocol, and other details. An application may also need to use this option if it needs to configure a custom `RequestUpgradeStrategy` in order to adapt to a WebSocket server engine and version that is not yet supported (see xref:web/websocket/server.adoc#websocket-server-deployment[Deployment] for more on this subject). Both the Java configuration and XML namespace make it possible to configure a custom `HandshakeHandler`. TIP: Spring provides a `WebSocketHandlerDecorator` base class that you can use to decorate a `WebSocketHandler` with additional behavior. Logging and exception handling implementations are provided and added by default when using the WebSocket Java configuration or XML namespace. The `ExceptionWebSocketHandlerDecorator` catches all uncaught exceptions that arise from any `WebSocketHandler` method and closes the WebSocket session with status `1011`, which indicates a server error. [[websocket-server-deployment]] == Deployment The Spring WebSocket API is easy to integrate into a Spring MVC application where the `DispatcherServlet` serves both HTTP WebSocket handshake and other HTTP requests. It is also easy to integrate into other HTTP processing scenarios by invoking `WebSocketHttpRequestHandler`. This is convenient and easy to understand. However, special considerations apply with regards to JSR-356 runtimes. The Jakarta WebSocket API (JSR-356) provides two deployment mechanisms. The first involves a Servlet container classpath scan (a Servlet 3 feature) at startup. The other is a registration API to use at Servlet container initialization. Neither of these mechanism makes it possible to use a single "`front controller`" for all HTTP processing -- including WebSocket handshake and all other HTTP requests -- such as Spring MVC's `DispatcherServlet`. This is a significant limitation of JSR-356 that Spring's WebSocket support addresses with server-specific `RequestUpgradeStrategy` implementations even when running in a JSR-356 runtime. Such strategies currently exist for Tomcat, Jetty, GlassFish, WebLogic, WebSphere, and Undertow (and WildFly). As of Jakarta WebSocket 2.1, a standard request upgrade strategy is available which Spring chooses on Jakarta EE 10 based web containers such as Tomcat 10.1 and Jetty 12. A secondary consideration is that Servlet containers with JSR-356 support are expected to perform a `ServletContainerInitializer` (SCI) scan that can slow down application startup -- in some cases, dramatically. If a significant impact is observed after an upgrade to a Servlet container version with JSR-356 support, it should be possible to selectively enable or disable web fragments (and SCI scanning) through the use of the `` element in `web.xml`, as the following example shows: [source,xml,indent=0,subs="verbatim,quotes,attributes"] ---- ---- You can then selectively enable web fragments by name, such as Spring's own `SpringServletContainerInitializer` that provides support for the Servlet 3 Java initialization API. The following example shows how to do so: [source,xml,indent=0,subs="verbatim,quotes,attributes"] ---- spring_web ---- [[websocket-server-runtime-configuration]] == Configuring the Server [.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-config[See equivalent in the Reactive stack]# You can configure of the underlying WebSocket server such as input message buffer size, idle timeout, and more. For Jakarta WebSocket servers, you can add a `ServletServerContainerFactoryBean` to your Java configuration. For example: [source,java,indent=0,subs="verbatim,quotes"] ---- @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(8192); container.setMaxBinaryMessageBufferSize(8192); return container; } ---- Or to your XML configuration: [source,xml,indent=0,subs="verbatim,quotes,attributes"] ---- ---- NOTE: For client Jakarta WebSocket configuration, use ContainerProvider.getWebSocketContainer() in Java configuration, or `WebSocketContainerFactoryBean` in XML. For Jetty, you can supply a `Consumer` callback to configure the WebSocket server: [source,java,indent=0,subs="verbatim,quotes"] ---- @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(echoWebSocketHandler(), "/echo").setHandshakeHandler(handshakeHandler()); } @Bean public DefaultHandshakeHandler handshakeHandler() { JettyRequestUpgradeStrategy strategy = new JettyRequestUpgradeStrategy(); strategy.addWebSocketConfigurer(configurable -> { policy.setInputBufferSize(8192); policy.setIdleTimeout(600000); }); return new DefaultHandshakeHandler(strategy); } } ---- TIP: When using STOMP over WebSocket, you will also need to configure xref:web/websocket/stomp/server-config.adoc[STOMP WebSocket transport] properties. [[websocket-server-allowed-origins]] == Allowed Origins [.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-cors[See equivalent in the Reactive stack]# As of Spring Framework 4.1.5, the default behavior for WebSocket and SockJS is to accept only same-origin requests. It is also possible to allow all or a specified list of origins. This check is mostly designed for browser clients. Nothing prevents other types of clients from modifying the `Origin` header value (see {rfc-site}/rfc6454[RFC 6454: The Web Origin Concept] for more details). The three possible behaviors are: * Allow only same-origin requests (default): In this mode, when SockJS is enabled, the Iframe HTTP response header `X-Frame-Options` is set to `SAMEORIGIN`, and JSONP transport is disabled, since it does not allow checking the origin of a request. As a consequence, IE6 and IE7 are not supported when this mode is enabled. * Allow a specified list of origins: Each allowed origin must start with `http://` or `https://`. In this mode, when SockJS is enabled, IFrame transport is disabled. As a consequence, IE6 through IE9 are not supported when this mode is enabled. * Allow all origins: To enable this mode, you should provide `{asterisk}` as the allowed origin value. In this mode, all transports are available. You can configure WebSocket and SockJS allowed origins, as the following example shows: [source,java,indent=0,subs="verbatim,quotes"] ---- 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 configuration equivalent of the preceding example: [source,xml,indent=0,subs="verbatim,quotes,attributes"] ---- ----