[[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"]
----
	<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 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"]
----
	<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 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 `<absolute-ordering />` element in `web.xml`, as the following example shows:

[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
	<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="
			https://jakarta.ee/xml/ns/jakartaee
			https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
		version="5.0">

		<absolute-ordering/>

	</web-app>
----

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"]
----
	<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="
			https://jakarta.ee/xml/ns/jakartaee
			https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
		version="5.0">

		<absolute-ordering>
			<name>spring_web</name>
		</absolute-ordering>

	</web-app>
----



[[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"]
----
	<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>
----

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"]
----
	<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>
----




