4.3.9.RELEASE Spring Framework
635
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
registerWebSocketHandlersWebSocketHandlerRegistry registry { registry.addHandlermyHandler,
myHandler .setAllowedOrigins
http:mydomain.com ;
} Bean
public WebSocketHandler myHandler {
return new
MyHandler; }
}
XML configuration equivalent:
beans xmlns
= http:www.springframework.orgschemabeans
xmlns:xsi =
http:www.w3.org2001XMLSchema-instance xmlns:websocket
= http:www.springframework.orgschemawebsocket
xsi:schemaLocation =
http:www.springframework.orgschemabeans http:www.springframework.orgschemabeansspring-beans.xsd
http:www.springframework.orgschemawebsocket http:www.springframework.orgschemawebsocketspring-websocket.xsd
websocket:handlers allowed-origins
= http:mydomain.com
websocket:mapping path
= myHandler
handler =
myHandler websocket:handlers
bean id
= myHandler
class =
org.springframework.samples.MyHandler beans
26.3 SockJS Fallback Options
As explained in the introduction
, WebSocket is not supported in all browsers yet and may be precluded by restrictive network proxies. This is why Spring provides fallback options that emulate the WebSocket
API as close as possible based on the SockJS protocol
version 0.3.3.
Overview of SockJS
The  goal  of  SockJS  is  to  let  applications  use  a  WebSocket  API  but  fall  back  to  non-WebSocket alternatives when necessary at runtime, i.e. without the need to change application code.
SockJS consists of: • The
SockJS protocol defined in the form of executable
narrated tests .
• The SockJS JavaScript client
- a client library for use in browsers. • SockJS  server  implementations  including  one  in  the  Spring  Framework
spring-websocket module.
• As of 4.1 spring-websocket
also provides a SockJS Java client. SockJS is designed for use in browsers. It goes to great lengths to support a wide range of browser
versions using a variety of techniques. For the full list of SockJS transport types and browsers see the
4.3.9.RELEASE Spring Framework
636
SockJS client page. Transports fall in 3 general categories: WebSocket, HTTP Streaming, and HTTP
Long Polling. For an overview of these categories see this blog post
. The SockJS client begins by sending
GET info to obtain basic information from the server. After
that it must decide what transport to use. If possible WebSocket is used. If not, in most browsers there is at least one HTTP streaming option and if not then HTTP long polling is used.
All transport requests have the following URL structure:
http:host:portmyAppmyEndpoint{server-id}{session-id}{transport}
• {server-id}
- useful for routing requests in a cluster but not used otherwise. •
{session-id} - correlates HTTP requests belonging to a SockJS session.
• {transport}
- indicates the transport type, e.g. websocket, xhr-streaming, etc. The  WebSocket  transport  needs  only  a  single  HTTP  request  to  do  the  WebSocket  handshake.  All
messages thereafter are exchanged on that socket. HTTP  transports  require  more  requests.  AjaxXHR  streaming  for  example  relies  on  one  long-
running request for server-to-client messages and additional HTTP POST requests for client-to-server messages. Long polling is similar except it ends the current request after each server-to-client send.
SockJS  adds  minimal  message  framing.  For  example  the  server  sends  the  letter  o  open  frame initially,  messages  are  sent  as  a[message1,message2]  JSON-encoded  array,  the  letter  h
heartbeat frame if no messages flow for 25 seconds by default, and the letter c close frame to close the session.
To learn more, run an example in a browser and watch the HTTP requests. The SockJS client allows fixing the list of transports so it is possible to see each transport one at a time. The SockJS client also
provides  a  debug  flag  which  enables  helpful  messages  in  the  browser  console.  On  the  server  side enable
TRACE logging for
org.springframework.web.socket . For even more detail refer to the
SockJS protocol narrated test
.
Enable SockJS
SockJS is easy to enable through Java configuration:
Configuration EnableWebSocket
public class
WebSocketConfig implements
WebSocketConfigurer { Override
public void
registerWebSocketHandlersWebSocketHandlerRegistry registry { registry.addHandlermyHandler,
myHandler .withSockJS;
} Bean
public WebSocketHandler myHandler {
return new
MyHandler; }
}
and the XML configuration equivalent:
4.3.9.RELEASE Spring Framework
637
beans xmlns
= http:www.springframework.orgschemabeans
xmlns:xsi =
http:www.w3.org2001XMLSchema-instance xmlns:websocket
= http:www.springframework.orgschemawebsocket
xsi:schemaLocation =
http:www.springframework.orgschemabeans http:www.springframework.orgschemabeansspring-beans.xsd
http:www.springframework.orgschemawebsocket http:www.springframework.orgschemawebsocketspring-websocket.xsd
websocket:handlers websocket:mapping
path =
myHandler handler
= myHandler
websocket:sockjs websocket:handlers
bean id
= myHandler
class =
org.springframework.samples.MyHandler beans
The  above  is  for  use  in  Spring  MVC  applications  and  should  be  included  in  the  configuration  of  a DispatcherServlet
.  However,  Spring’s  WebSocket  and  SockJS  support  does  not  depend  on  Spring MVC.  It  is  relatively  simple  to  integrate  into  other  HTTP  serving  environments  with  the  help  of
SockJsHttpRequestHandler .
On  the  browser  side,  applications  can  use  the sockjs-client
version  1.0.x  that  emulates  the  W3C WebSocket API and communicates with the server to select the best transport option depending on
the browser it’s running in. Review the sockjs-client
page and the list of transport types supported by browser. The client also provides several configuration options, for example, to specify which transports
to include.
HTTP Streaming in IE 8, 9: AjaxXHR vs IFrame
Internet Explorer 8 and 9 are and will remain common for some time. They are a key reason for having SockJS. This section covers important considerations about running in those browsers.
The SockJS client supports AjaxXHR streaming in IE 8 and 9 via Microsoft’s XDomainRequest
. That works across domains but does not support sending cookies. Cookies are very often essential for Java
applications. However since the SockJS client can be used with many server types not just Java ones, it  needs  to  know  whether  cookies  matter.  If  so  the  SockJS  client  prefers  AjaxXHR  for  streaming  or
otherwise it relies on a iframe-based technique. The very first
info request from the SockJS client is a request for information that can influence
the client’s choice of transports. One of those details is whether the server application relies on cookies, e.g. for authentication purposes or clustering with sticky sessions. Spring’s SockJS support includes
a property called sessionCookieNeeded
. It is enabled by default since most Java applications rely on the
JSESSIONID cookie. If your application does not need it, you can turn off this option and the
SockJS client should choose xdr-streaming
in IE 8 and 9. If  you  do  use  an  iframe-based  transport,  and  in  any  case,  it  is  good  to  know  that  browsers  can  be
instructed to block the use of IFrames on a given page by setting the HTTP response header X-Frame-
Options to
DENY ,
SAMEORIGIN , or
ALLOW-FROM origin . This is used to prevent
clickjacking .
Note
Spring  Security  3.2+  provides  support  for  setting X-Frame-Options
on  every  response.  By default the Spring Security Java config sets it to
DENY . In 3.2 the Spring Security XML namespace
does not set that header by default but may be configured to do so, and in the future it may set it by default.
4.3.9.RELEASE Spring Framework
638
See Section 7.1. Default Security Headers
of the Spring Security documentation for details on how to configure the setting of the
X-Frame-Options header. You may also check or watch
SEC-2501 for additional background.
If your application adds the X-Frame-Options
response header as it should and relies on an iframe- based transport, you will need to set the header value to
SAMEORIGIN or
ALLOW-FROM  origin .
Along with that the Spring SockJS support also needs to know the location of the SockJS client because it  is  loaded  from  the  iframe.  By  default  the  iframe  is  set  to  download  the  SockJS  client  from  a  CDN
location. It is a good idea to configure this option to a URL from the same origin as the application. In Java config this can be done as shown below. The XML namespace provides a similar option via
the websocket:sockjs
element:
Configuration EnableWebSocket
public class
WebSocketConfig implements
WebSocketConfigurer { Override
public void
registerStompEndpointsStompEndpointRegistry registry { registry.addEndpoint
portfolio .withSockJS
.setClientLibraryUrl http:localhost:8080myappjssockjs-client.js
; }
... }
Note
During initial development, do enable the SockJS client devel
mode that prevents the browser from caching SockJS requests like the iframe that would otherwise be cached. For details on
how to enable it see the SockJS client
page.
Heartbeat Messages
The SockJS protocol requires servers to send heartbeat messages to preclude proxies from concluding a  connection  is  hung.  The  Spring  SockJS  configuration  has  a  property  called
heartbeatTime that
can be used to customize the frequency. By default a heartbeat is sent after 25 seconds assuming no other messages were sent on that connection. This 25 seconds value is in line with the following
IETF recommendation
for public Internet applications.
Note
When  using  STOMP  over  WebSocketSockJS,  if  the  STOMP  client  and  server  negotiate heartbeats to be exchanged, the SockJS heartbeats are disabled.
The  Spring  SockJS  support  also  allows  configuring  the TaskScheduler
to  use  for  scheduling heartbeats  tasks.  The  task  scheduler  is  backed  by  a  thread  pool  with  default  settings  based  on  the
number of available processors. Applications should consider customizing the settings according to their specific needs.
Servlet 3 Async Requests
HTTP streaming and HTTP long polling SockJS transports require a connection to remain open longer than usual. For an overview of these techniques see
this blog post .
4.3.9.RELEASE Spring Framework
639
In Servlet containers this is done through Servlet 3 async support that allows exiting the Servlet container thread processing a request and continuing to write to the response from another thread.
A specific issue is that the Servlet API does not provide notifications for a client that has gone away, see SERVLET_SPEC-44
. However, Servlet containers raise an exception on subsequent attempts to write to the response. Since Spring’s SockJS Service supports sever-sent heartbeats every 25 seconds by
default, that means a client disconnect is usually detected within that time period or earlier if messages are sent more frequently.
Note
As  a  result  network  IO  failures  may  occur  simply  because  a  client  has  disconnected,  which can  fill  the  log  with  unnecessary  stack  traces.  Spring  makes  a  best  effort  to  identify  such
network  failures  that  represent  client  disconnects  specific  to  each  server  and  log  a  minimal message using the dedicated log category
DISCONNECTED_CLIENT_LOG_CATEGORY defined in
AbstractSockJsSession . If you need to see the stack traces, set that log category to TRACE.
CORS Headers for SockJS
If  you  allow  cross-origin  requests  see  the  section  called  “Configuring  allowed  origins”,  the  SockJS protocol uses CORS for cross-domain support in the XHR streaming and polling transports. Therefore
CORS  headers  are  added  automatically  unless  the  presence  of  CORS  headers  in  the  response  is detected. So if an application is already configured to provide CORS support, e.g. through a Servlet
Filter, Spring’s SockJsService will skip this part. It is also possible to disable the addition of these CORS headers via the
suppressCors property in
Spring’s SockJsService. The following is the list of headers and values expected by SockJS:
• Access-Control-Allow-Origin
- initialized from the value of the Origin request header. •
Access-Control-Allow-Credentials - always set to
true .
• Access-Control-Request-Headers
-  initialized  from  values  from  the  equivalent  request header.
• Access-Control-Allow-Methods
-  the  HTTP  methods  a  transport  supports  see TransportType
enum. •
Access-Control-Max-Age - set to 31536000 1 year.
For  the  exact  implementation  see addCorsHeaders
in AbstractSockJsService
as  well  as  the TransportType
enum in the source code. Alternatively  if  the  CORS  configuration  allows  it  consider  excluding  URLs  with  the  SockJS  endpoint
prefix thus letting Spring’s SockJsService
handle it.
SockJS Client
A  SockJS  Java  client  is  provided  in  order  to  connect  to  remote  SockJS  endpoints  without  using  a browser. This can be especially useful when there is a need for bidirectional communication between
2 servers over a public network, i.e. where network proxies may preclude the use of the WebSocket
4.3.9.RELEASE Spring Framework
640
protocol. A SockJS Java client is also very useful for testing purposes, for example to simulate a large number of concurrent users.
The  SockJS  Java  client  supports  the  websocket,  xhr-streaming,  and  xhr-polling  transports.  The remaining ones only make sense for use in a browser.
The WebSocketTransport
can be configured with: •
StandardWebSocketClient in a JSR-356 runtime
• JettyWebSocketClient
using the Jetty 9+ native WebSocket API • Any implementation of Spring’s
WebSocketClient An
XhrTransport by  definition  supports  both  xhr-streaming  and  xhr-polling  since  from  a  client
perspective there is no difference other than in the URL used to connect to the server. At present there are two implementations:
• RestTemplateXhrTransport
uses Spring’s RestTemplate
for HTTP requests. •
JettyXhrTransport uses Jetty’s
HttpClient for HTTP requests.
The example below shows how to create a SockJS client and connect to a SockJS endpoint:
ListTransport transports = new
ArrayList2; transports.add
new WebSocketTransport
new StandardWebSocketClient;
transports.add new
RestTemplateXhrTransport; SockJsClient sockJsClient =
new SockJsClienttransports;
sockJsClient.doHandshake new
MyWebSocketHandler, ws:example.com:8080sockjs
;
Note
SockJS  uses  JSON  formatted  arrays  for  messages.  By  default  Jackson  2  is  used  and needs  to  be  on  the  classpath.  Alternatively  you  can  configure  a  custom  implementation  of
SockJsMessageCodec and configure it on the
SockJsClient .
To use the SockJsClient for simulating a large number of concurrent users you will need to configure the underlying HTTP client for XHR transports to allow a sufficient number of connections and threads.
For example with Jetty:
HttpClient jettyHttpClient = new
HttpClient; jettyHttpClient.setMaxConnectionsPerDestination1000;
jettyHttpClient.setExecutor new
QueuedThreadPool1000;
Consider also customizing these server-side SockJS related properties see Javadoc for details:
Configuration
public class
WebSocketConfig extends
WebSocketMessageBrokerConfigurationSupport { Override
public void
registerStompEndpointsStompEndpointRegistry registry { registry.addEndpoint
sockjs .withSockJS
.setStreamBytesLimit512  1024 .setHttpMessageCacheSize1000
.setDisconnectDelay30  1000; }
... }
4.3.9.RELEASE Spring Framework
641
26.4 STOMP Over WebSocket Messaging Architecture