diff --git a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ServerWebSocketContainer.java b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ServerWebSocketContainer.java
index 6d29ac4a2a..25dc055b66 100644
--- a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ServerWebSocketContainer.java
+++ b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ServerWebSocketContainer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,6 +58,8 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
private SockJsServiceOptions sockJsServiceOptions;
+ private String[] origins;
+
public ServerWebSocketContainer(String... paths) {
this.paths = paths;
}
@@ -91,6 +93,18 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
return this;
}
+ /**
+ * Configure allowed {@code Origin} header values.
+ * @param origins the origins to allow.
+ * @return the current ServerWebSocketContainer
+ * @since 4.3
+ * @see WebSocketHandlerRegistration#setAllowedOrigins(String...)
+ */
+ public ServerWebSocketContainer setAllowedOrigins(String... origins) {
+ this.origins = origins; //NOSONAR - fully delegated
+ return this;
+ }
+
public ServerWebSocketContainer withSockJs(SockJsServiceOptions... sockJsServiceOptions) {
if (ObjectUtils.isEmpty(sockJsServiceOptions)) {
this.sockJsServiceOptions = new SockJsServiceOptions();
@@ -118,7 +132,8 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
WebSocketHandlerRegistration registration = registry.addHandler(webSocketHandler, this.paths)
.setHandshakeHandler(this.handshakeHandler)
- .addInterceptors(this.interceptors);
+ .addInterceptors(this.interceptors)
+ .setAllowedOrigins(this.origins);
if (this.sockJsServiceOptions != null) {
SockJsServiceRegistration sockJsServiceRegistration = registration.withSockJS();
@@ -155,6 +170,10 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
if (this.sockJsServiceOptions.messageCodec != null) {
sockJsServiceRegistration.setMessageCodec(this.sockJsServiceOptions.messageCodec);
}
+ if (this.sockJsServiceOptions.suppressCors != null) {
+ sockJsServiceRegistration.setSupressCors(this.sockJsServiceOptions.suppressCors);
+ }
+
}
}
@@ -184,6 +203,8 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
private SockJsMessageCodec messageCodec;
+ private Boolean suppressCors;
+
public SockJsServiceOptions setTaskScheduler(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
return this;
@@ -234,6 +255,11 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
return this;
}
+ public SockJsServiceOptions setSuppressCors(boolean suppressCors) {
+ this.suppressCors = suppressCors;
+ return this;
+ }
+
}
}
diff --git a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/ServerWebSocketContainerParser.java b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/ServerWebSocketContainerParser.java
index 481f37b0b6..c6c2edef31 100644
--- a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/ServerWebSocketContainerParser.java
+++ b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/ServerWebSocketContainerParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -87,6 +87,7 @@ public class ServerWebSocketContainerParser extends AbstractSingleBeanDefinition
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(sockjsBuilder, sockjs, "scheduler",
"taskScheduler");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(sockjsBuilder, sockjs, "message-codec");
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(sockjsBuilder, sockjs, "suppress-cors");
String transportHandlers = sockjs.getAttribute("transport-handlers");
if (StringUtils.hasText(transportHandlers)) {
@@ -103,6 +104,7 @@ public class ServerWebSocketContainerParser extends AbstractSingleBeanDefinition
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "handshake-handler");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-buffer-size-limit");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-time-limit");
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "allowed-origins");
}
}
diff --git a/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.3.xsd b/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.3.xsd
index e6308ebdda..ba95916fac 100644
--- a/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.3.xsd
+++ b/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.3.xsd
@@ -248,6 +248,15 @@
+
+
+
+ This option can be used to disable automatic addition
+ of CORS headers for SockJS requests.
+ The default value is 'false'.
+
+
+
@@ -311,6 +320,24 @@
+
+
+
+ Configure allowed Origin header values. Multiple origins may be specified
+ as a comma-separated list.
+
+ This check is mostly designed for browser clients. There is noting preventing other
+ types of client to modify the Origin header value.
+
+ When SockJS is enabled and allowed origins are restricted, transport types that do not
+ use Origin headers for cross origin requests (jsonp-polling, iframe-xhr-polling,
+ iframe-eventsource and iframe-htmlfile) are disabled. As a consequence, IE6/IE7 are not
+ supported and IE8/IE9 will only be supported without cookies.
+
+ By default, all origins are allowed.
+
+
+
diff --git a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests-context.xml b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests-context.xml
index fdf9f48a35..fddbc981b0 100644
--- a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests-context.xml
+++ b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests-context.xml
@@ -19,7 +19,8 @@
send-time-limit="100"
handshake-handler="handshakeHandler"
handshake-interceptors="handshakeInterceptor"
- decorator-factories="decoratorFactory">
+ decorator-factories="decoratorFactory"
+ allowed-origins="http://foo.com">
+ message-codec="sockJsMessageCodec"
+ suppress-cors="true" />
-
+
-
+
-
+
-
-
+
+
+ use-broker="true" />
-
+
-
+
-
+
+ class="org.springframework.integration.websocket.support.PassThruSubProtocolHandler" />
-
+
-
+
+ phase="200" />
-
+
-
+
+ channel="clientOutboundChannel" />
-
+
+ uri="ws://foo.bar" />
diff --git a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests.java b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests.java
index 62d6d86f67..88b5c1d980 100644
--- a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests.java
+++ b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/config/WebSocketParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2015 the original author or authors.
+ * Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.integration.websocket.config;
import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -164,6 +165,8 @@ public class WebSocketParserTests {
assertSame(this.handshakeInterceptor, interceptors[0]);
assertEquals(100, TestUtils.getPropertyValue(this.serverWebSocketContainer, "sendTimeLimit"));
assertEquals(100000, TestUtils.getPropertyValue(this.serverWebSocketContainer, "sendBufferSizeLimit"));
+ assertArrayEquals(new String[] {"http://foo.com"},
+ TestUtils.getPropertyValue(this.serverWebSocketContainer, "origins", String[].class));
WebSocketHandlerDecoratorFactory[] decoratorFactories =
TestUtils.getPropertyValue(this.serverWebSocketContainer, "decoratorFactories",
@@ -189,6 +192,7 @@ public class WebSocketParserTests {
assertEquals("https://foo.sock.js", sockJsService.getSockJsClientLibraryUrl());
assertFalse(sockJsService.isSessionCookieNeeded());
assertFalse(sockJsService.isWebSocketEnabled());
+ assertTrue(sockJsService.shouldSuppressCors());
assertSame(this.serverWebSocketContainer,
TestUtils.getPropertyValue(this.defaultInboundAdapter, "webSocketContainer"));
diff --git a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/server/WebSocketServerTests.java b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/server/WebSocketServerTests.java
index abcf4580ec..7b6f9cf53a 100644
--- a/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/server/WebSocketServerTests.java
+++ b/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/server/WebSocketServerTests.java
@@ -188,7 +188,10 @@ public class WebSocketServerTests {
@Bean
public IntegrationWebSocketContainer clientWebSocketContainer() {
- return new ClientWebSocketContainer(webSocketClient(), server().getWsBaseUrl() + "/ws");
+ ClientWebSocketContainer clientWebSocketContainer =
+ new ClientWebSocketContainer(webSocketClient(), server().getWsBaseUrl() + "/ws");
+ clientWebSocketContainer.setOrigin("http://foo.com");
+ return clientWebSocketContainer;
}
@Bean
@@ -251,6 +254,7 @@ public class WebSocketServerTests {
public ServerWebSocketContainer serverWebSocketContainer() {
return new ServerWebSocketContainer("/ws")
.setDecoratorFactories(testWebSocketHandlerDecoratorFactory())
+ .setAllowedOrigins("http://foo.com")
.withSockJs();
}
diff --git a/src/reference/asciidoc/web-sockets.adoc b/src/reference/asciidoc/web-sockets.adoc
index 0180761b32..c87ef1f087 100644
--- a/src/reference/asciidoc/web-sockets.adoc
+++ b/src/reference/asciidoc/web-sockets.adoc
@@ -41,18 +41,18 @@ That allows us to reuse many configuration items and infrastructure hooks to bui
//Client side
@Bean
public WebSocketClient webSocketClient() {
- return new SockJsClient(Collections.singletonList(new WebSocketTransport(new JettyWebSocketClient())));
+ return new SockJsClient(Collections.singletonList(new WebSocketTransport(new JettyWebSocketClient())));
}
@Bean
public IntegrationWebSocketContainer clientWebSocketContainer() {
- return new ClientWebSocketContainer(webSocketClient(), "ws://my.server.com/endpoint");
+ return new ClientWebSocketContainer(webSocketClient(), "ws://my.server.com/endpoint");
}
//Server side
@Bean
public IntegrationWebSocketContainer serverWebSocketContainer() {
- return new ServerWebSocketContainer("/endpoint").withSockJs();
+ return new ServerWebSocketContainer("/endpoint").withSockJs();
}
----
@@ -131,18 +131,18 @@ To include it in your configuration, simply provide the following namespace decl
[source,xml]
----
- client="" <2>
- uri="" <3>
- uri-variables="" <4>
- origin="" <5>
- send-time-limit="" <6>
- send-buffer-size-limit="" <7>
- auto-startup="" <8>
- phase=""> <9>
-
-
- <10>
+ id="" <1>
+ client="" <2>
+ uri="" <3>
+ uri-variables="" <4>
+ origin="" <5>
+ send-time-limit="" <6>
+ send-buffer-size-limit="" <7>
+ auto-startup="" <8>
+ phase=""> <9>
+
+
+ <10>
----
@@ -197,18 +197,20 @@ See `SmartLifeCycle`.
handshake-interceptors="" <4>
decorator-factories="" <5>
send-time-limit="" <6>
- send-buffer-size-limit=""> <7>
+ send-buffer-size-limit="" <7>
+ allowed-origins=""> <8>
- stream-bytes-limit="" <9>
- session-cookie-needed="" <10>
- heartbeat-time="" <11>
- disconnect-delay="" <12>
- message-cache-size="" <13>
- websocket-enabled="" <14>
- scheduler="" <15>
- message-codec="" <16>
- transport-handlers="" /> <17>
+ client-library-url="" <9>
+ stream-bytes-limit="" <10>
+ session-cookie-needed="" <11>
+ heartbeat-time="" <12>
+ disconnect-delay="" <13>
+ message-cache-size="" <14>
+ websocket-enabled="" <15>
+ scheduler="" <16>
+ message-codec="" <17>
+ transport-handlers="" <18>
+ suppress-cors="true"="" /> <19>
----
@@ -239,7 +241,15 @@ for more information.
<7> See the same option on the ``.
-<8> Transports with no native cross-domain communication (e.g.
+<8> Configure allowed Origin header values. Multiple origins may be specified as a comma-separated list.
+This check is mostly designed for browser clients.
+There is noting preventing other types of client to modify the Origin header value.
+When SockJS is enabled and allowed origins are restricted, transport types that do not use Origin headers for cross origin requests (jsonp-polling, iframe-xhr-polling, iframe-eventsource and iframe-htmlfile) are disabled.
+As a consequence, IE6/IE7 are not supported and IE8/IE9 will only be supported without cookies.
+By default, all origins are allowed.
+
+
+<9> Transports with no native cross-domain communication (e.g.
"eventsource", "htmlfile") must get a simple page from the "foreign" domain in an invisible iframe so that code in the iframe can run from a domain local to the SockJS server.
Since the iframe needs to load the SockJS javascript client library, this property allows specifying where to load it from.
By default this is set to point to `https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js`.
@@ -249,62 +259,66 @@ For example assuming a SockJS endpoint mapped to "/sockjs", and resulting iframe
In case of a prefix-based Servlet mapping one more traversal may be needed.
-<9> Minimum number of bytes that can be send over a single HTTP streaming request before it will be closed.
+<10> Minimum number of bytes that can be send over a single HTTP streaming request before it will be closed.
Defaults to `128K` (i.e.
128*1024 bytes).
-<10> The "cookie_needed" value in the response from the SockJs `"/info"` endpoint.
+<11> The "cookie_needed" value in the response from the SockJs `"/info"` endpoint.
This property 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.
-<11> The amount of time in milliseconds when the server has not sent any messages and after which the server should
+<12> The amount of time in milliseconds when the server has not sent any messages and after which the server should
send a heartbeat frame to the client in order to keep the connection from breaking.
The default value is `25,000` (25 seconds).
-<12> The amount of time in milliseconds before a client is considered disconnected after not having a receiving
+<13> The amount of time in milliseconds before a client is considered disconnected after not having a receiving
connection, i.e.
an active connection over which the server can send data to the client.
The default value is `5000`.
-<13> The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request
+<14> The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request
from the client.
The default size is `100`.
-<14> Some load balancers don't support websockets.
+<15> Some load balancers don't support websockets.
Set this option to `false` to disable the WebSocket transport on the server side.
The default value is `true`.
-<15> The `TaskScheduler` bean reference; a new `ThreadPoolTaskScheduler` instance will be created if no value is
+<16> The `TaskScheduler` bean reference; a new `ThreadPoolTaskScheduler` instance will be created if no value is
provided.
This scheduler instance will be used for scheduling heart-beat messages.
-<16> The `SockJsMessageCodec` bean reference to use for encoding and decoding SockJS messages.
+<17> The `SockJsMessageCodec` bean reference to use for encoding and decoding SockJS messages.
By default `Jackson2SockJsMessageCodec` is used requiring the Jackson library to be present on the classpath.
-<17> List of `TransportHandler` bean references.
+<18> List of `TransportHandler` bean references.
+
+
+<19> The option to disable automatic addition of CORS headers for SockJS requests.
+The default value is `false`.
**
[source,xml]
----
- channel="" <2>
- container="" <3>
- default-protocol-handler="" <4>
- protocol-handlers="" <5>
- message-converters="" <6>
- merge-with-default-converters="" <7>
- auto-startup="" <8>
- phase=""/> <9>
+ id="" <1>
+ channel="" <2>
+ container="" <3>
+ default-protocol-handler="" <4>
+ protocol-handlers="" <5>
+ message-converters="" <6>
+ merge-with-default-converters="" <7>
+ auto-startup="" <8>
+ phase=""/> <9>
----
@@ -357,19 +371,19 @@ See `SmartLifeCycle`.
[source,xml]
----
- channel="" <2>
- error-channel="" <3>
- container="" <4>
- default-protocol-handler="" <5>
- protocol-handlers="" <6>
- message-converters="" <7>
- merge-with-default-converters="" <8>
- send-timeout="" <9>
- payload-type="" <10>
- use-broker="" <11>
- auto-startup="" <12>
- phase=""/> <13>
+ id="" <1>
+ channel="" <2>
+ error-channel="" <3>
+ container="" <4>
+ default-protocol-handler="" <5>
+ protocol-handlers="" <6>
+ message-converters="" <7>
+ merge-with-default-converters="" <8>
+ send-timeout="" <9>
+ payload-type="" <10>
+ use-broker="" <11>
+ auto-startup="" <12>
+ phase=""/> <13>
----
diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc
index 0c5346d8aa..e316643ffb 100644
--- a/src/reference/asciidoc/whats-new.adoc
+++ b/src/reference/asciidoc/whats-new.adoc
@@ -270,3 +270,8 @@ See <> for more information.
The `ChannelMessageStoreQueryProvider` now supports H2 database.
See <> for more information.
+
+==== WebSocket Changes
+
+The `ServerWebSocketContainer` now exposes `allowedOrigins` option and `SockJsServiceOptions` a `suppressCors` option.
+See <> for more information.