diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/SockJsServiceRegistration.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/SockJsServiceRegistration.java index 20507b4e762..28a29d8c6bf 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/SockJsServiceRegistration.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/SockJsServiceRegistration.java @@ -78,8 +78,9 @@ public class SockJsServiceRegistration { * "foreign" domain) from an invisible iframe. Code run from this iframe * doesn't need to worry about cross-domain issues since it is running from * a domain local to the SockJS server. The iframe does need to load the - * SockJS javascript client library and this option allows configuring its - * url. + * SockJS javascript client library and this option allows configuring its url. + * See the reference documentation for more details on this. + * *

By default this is set to point to * "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js". */ @@ -108,10 +109,12 @@ public class SockJsServiceRegistration { * from clients with a "cookie_needed" boolean property that indicates whether the use * of a JSESSIONID cookie is required for the application to function correctly, e.g. * for load balancing or in Java Servlet containers for the use of an HTTP session. + * *

This is especially important for IE 8,9 that support XDomainRequest -- a modified * AJAX/XHR -- that can do requests across domains but does not send any cookies. In * those cases, the SockJS client prefers the "iframe-htmlfile" transport over * "xdr-streaming" in order to be able to send cookies. + * *

The default value is "true" to maximize the chance for applications to work * correctly in IE 8,9 with support for cookies (and the JSESSIONID cookie in * particular). However, an application can choose to set this to "false" if the use diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java index 1baf7e88f4d..ae9639ed43c 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java @@ -44,6 +44,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.sockjs.SockJsException; import org.springframework.web.socket.sockjs.SockJsService; +import org.springframework.web.util.UriComponentsBuilder; /** * An abstract base class for {@link SockJsService} implementations that provides SockJS @@ -106,8 +107,9 @@ public abstract class AbstractSockJsService implements SockJsService { * "foreign" domain) from an invisible iframe. Code run from this iframe * doesn't need to worry about cross-domain issues since it is running from * a domain local to the SockJS server. The iframe does need to load the - * SockJS javascript client library and this option allows configuring its - * url. + * SockJS javascript client library and this option allows configuring that url. + * For more details see the reference documentation. + * *

By default this is set to point to * "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js". */ @@ -146,13 +148,16 @@ public abstract class AbstractSockJsService implements SockJsService { * clients with a "cookie_needed" boolean property that indicates whether the use of a * JSESSIONID cookie is required for the application to function correctly, e.g. for * load balancing or in Java Servlet containers for the use of an HTTP session. + * *

This is especially important for IE 8,9 that support XDomainRequest -- a modified * AJAX/XHR -- that can do requests across domains but does not send any cookies. In * those cases, the SockJS client prefers the "iframe-htmlfile" transport over * "xdr-streaming" in order to be able to send cookies. + * *

The SockJS protocol also expects a SockJS service to echo back the JSESSIONID * cookie when this property is set to true. However, when running in a Servlet * container this is not necessary since the container takes care of it. + * *

The default value is "true" to maximize the chance for applications to work * correctly in IE 8,9 with support for cookies (and the JSESSIONID cookie in * particular). However, an application can choose to set this to "false" if diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 923990103c6..cacfd865380 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -37188,7 +37188,7 @@ or WebSocket XML namespace: [[websocket-fallback]] -=== Fallback Options +=== SockJS Fallback Options As explained in the <>, 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 @@ -37258,6 +37258,102 @@ https://github.com/sockjs/sockjs-client[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. +[[websocket-fallback-sockjs-transport]] +==== SockJS Transports + +The SockJS client simulates the WebSocket API in a wide range of browsers. +For the full list of transports by browser see the +https://github.com/sockjs/sockjs-client[SockJS client] page. The transport types +fall in 3 categories: WebSocket, HTTP Streaming, and HTTP Long Polling. For more +background on those techniques see +https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[this blog post]. +An important goal of SockJS is to support at least one streaming transport +per browser, for efficiency reasons, but when necessary fall back on +long polling. + +The next few sections cover various aspects of confugring and using SockJS +in Spring applications. + +[[websocket-fallback-xhr-vs-iframe]] +==== HTTP Streaming in IE 8, 9: Ajax/XHR 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. + +SockJS client supports Ajax/XHR streaming in IE 8, 9 via Microsoft's +http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx[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 do matter. +If so the SockJS client prefers Ajax/XHR 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 the `xhr-streaming` transport 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 `. This is used to prevent +https://www.owasp.org/index.php/Clickjacking[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. + +See http://docs.spring.io/spring-security/site/docs/3.2.2.RELEASE/reference/htmlsingle/#headers[Section 7.1. "Default Security Headers"] +of the Spring Security documentation for details no how to configure the +setting of the `X-Frame-Options` header. You may also check or watch +https://jira.spring.io/browse/SEC-2501[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 `. 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 on the `` element: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Configuration + @EnableWebSocket + public class WebSocketConfig implements WebSocketConfigurer { + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/portfolio").withSockJS() + .setClientLibraryUrl("http://localhost:8080/myapp/js/sockjs-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 +https://github.com/sockjs/sockjs-client[SockJS client] page. +==== [[websocket-fallback-sockjs-servlet3-async]]