Browse Source

Add spring-websocket module tests

pull/292/head
Rossen Stoyanchev 12 years ago
parent
commit
05084d504b
  1. 1
      spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java
  2. 32
      spring-websocket/src/main/java/org/springframework/web/socket/client/ConnectionManagerSupport.java
  3. 2
      spring-websocket/src/main/java/org/springframework/web/socket/client/WebSocketConnectionManager.java
  4. 76
      spring-websocket/src/main/java/org/springframework/web/socket/client/endpoint/StandardWebSocketClient.java
  5. 8
      spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/ServerEndpointExporter.java
  6. 93
      spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/ServerEndpointRegistration.java
  7. 7
      spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/ServletServerContainerFactoryBean.java
  8. 6
      spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/SpringConfigurator.java
  9. 6
      spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/Test.java
  10. 4
      spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/package-info.java
  11. 4
      spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java
  12. 4
      spring-websocket/src/main/java/org/springframework/web/socket/server/support/TomcatRequestUpgradeStrategy.java
  13. 162
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/AbstractServerSockJsSession.java
  14. 2
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/AbstractSockJsService.java
  15. 161
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/AbstractSockJsSession.java
  16. 17
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/SockJsFrame.java
  17. 2
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/TransportType.java
  18. 19
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/DefaultSockJsService.java
  19. 20
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpReceivingTransportHandler.java
  20. 9
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpSendingTransportHandler.java
  21. 8
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpSockJsSession.java
  22. 4
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/EventSourceTransportHandler.java
  23. 6
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/HtmlFileTransportHandler.java
  24. 6
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/JsonpPollingTransportHandler.java
  25. 18
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/JsonpTransportHandler.java
  26. 4
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/PollingSockJsSession.java
  27. 4
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/StreamingSockJsSession.java
  28. 9
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/WebSocketServerSockJsSession.java
  29. 4
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/XhrPollingTransportHandler.java
  30. 4
      spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/XhrStreamingTransportHandler.java
  31. 2
      spring-websocket/src/main/java/org/springframework/web/socket/support/WebSocketHandlerDecorator.java
  32. 13
      spring-websocket/src/test/java/org/springframework/web/socket/AbstractHttpRequestTests.java
  33. 71
      spring-websocket/src/test/java/org/springframework/web/socket/adapter/JettyWebSocketListenerAdapterTests.java
  34. 82
      spring-websocket/src/test/java/org/springframework/web/socket/adapter/StandardEndpointAdapterTests.java
  35. 159
      spring-websocket/src/test/java/org/springframework/web/socket/client/WebSocketConnectionManagerTests.java
  36. 89
      spring-websocket/src/test/java/org/springframework/web/socket/client/endpoint/StandardWebSocketClientTests.java
  37. 110
      spring-websocket/src/test/java/org/springframework/web/socket/server/endpoint/ServerEndpointExporterTests.java
  38. 93
      spring-websocket/src/test/java/org/springframework/web/socket/server/endpoint/ServerEndpointRegistrationTests.java
  39. 127
      spring-websocket/src/test/java/org/springframework/web/socket/server/endpoint/SpringConfiguratorTests.java
  40. 151
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/AbstractSockJsServiceTests.java
  41. 269
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/AbstractSockJsSessionTests.java
  42. 74
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/BaseAbstractSockJsSessionTests.java
  43. 107
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/TestSockJsSession.java
  44. 146
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/support/DefaultSockJsServiceTests.java
  45. 176
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/AbstractHttpSockJsSessionTests.java
  46. 139
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/HttpReceivingTransportHandlerTests.java
  47. 186
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/HttpSendingTransportHandlerTests.java
  48. 153
      spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/WebSocketServerSockJsSessionTests.java
  49. 96
      spring-websocket/src/test/java/org/springframework/web/socket/support/BeanCreatingHandlerProviderTests.java
  50. 102
      spring-websocket/src/test/java/org/springframework/web/socket/support/ExceptionWebSocketHandlerDecoratorTests.java
  51. 82
      spring-websocket/src/test/java/org/springframework/web/socket/support/PerConnectionWebSocketHandlerTests.java
  52. 185
      spring-websocket/src/test/java/org/springframework/web/socket/support/TestWebSocketSession.java

1
spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java

@ -166,6 +166,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest { @@ -166,6 +166,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
@Override
public MultiValueMap<String, String> getQueryParams() {
if (this.queryParams == null) {
// TODO: extract from query string
this.queryParams = new LinkedMultiValueMap<String, String>(this.servletRequest.getParameterMap().size());
for (String name : this.servletRequest.getParameterMap().keySet()) {
for (String value : this.servletRequest.getParameterValues(name)) {

32
spring-websocket/src/main/java/org/springframework/web/socket/client/ConnectionManagerSupport.java

@ -26,7 +26,7 @@ import org.springframework.core.task.TaskExecutor; @@ -26,7 +26,7 @@ import org.springframework.core.task.TaskExecutor;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Abstract base class for WebSocketConnection managers.
* Abstract base class for WebSocket connection managers.
*
* @author Rossen Stoyanchev
* @since 4.0
@ -147,25 +147,25 @@ public abstract class ConnectionManagerSupport implements SmartLifecycle { @@ -147,25 +147,25 @@ public abstract class ConnectionManagerSupport implements SmartLifecycle {
public final void stop() {
synchronized (this.lifecycleMonitor) {
if (isRunning()) {
stopInternal();
if (logger.isDebugEnabled()) {
logger.debug("Stopping " + this.getClass().getSimpleName());
}
try {
stopInternal();
}
catch (Throwable e) {
logger.error("Failed to stop WebSocket connection", e);
}
finally {
this.isRunning = false;
}
}
}
}
protected void stopInternal() {
if (logger.isDebugEnabled()) {
logger.debug("Stopping " + this.getClass().getSimpleName());
}
try {
if (isConnected()) {
closeConnection();
}
}
catch (Throwable e) {
logger.error("Failed to stop WebSocket connection", e);
}
finally {
this.isRunning = false;
protected void stopInternal() throws Exception {
if (isConnected()) {
closeConnection();
}
}

2
spring-websocket/src/main/java/org/springframework/web/socket/client/WebSocketConnectionManager.java

@ -84,7 +84,7 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport { @@ -84,7 +84,7 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
}
@Override
public void stopInternal() {
public void stopInternal() throws Exception {
if (this.syncClientLifecycle) {
((SmartLifecycle) client).stop();
}

76
spring-websocket/src/main/java/org/springframework/web/socket/client/endpoint/StandardWebSocketClient.java

@ -52,12 +52,15 @@ public class StandardWebSocketClient implements WebSocketClient { @@ -52,12 +52,15 @@ public class StandardWebSocketClient implements WebSocketClient {
private static final Log logger = LogFactory.getLog(StandardWebSocketClient.class);
private static final Set<String> EXCLUDED_HEADERS = new HashSet<String>(
Arrays.asList("Sec-WebSocket-Accept", "Sec-WebSocket-Extensions", "Sec-WebSocket-Key",
"Sec-WebSocket-Protocol", "Sec-WebSocket-Version"));
private WebSocketContainer webSocketContainer;
private WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
public WebSocketContainer getWebSocketContainer() {
if (this.webSocketContainer == null) {
this.webSocketContainer = ContainerProvider.getWebSocketContainer();
}
return this.webSocketContainer;
}
public void setWebSocketContainer(WebSocketContainer container) {
this.webSocketContainer = container;
@ -72,8 +75,8 @@ public class StandardWebSocketClient implements WebSocketClient { @@ -72,8 +75,8 @@ public class StandardWebSocketClient implements WebSocketClient {
}
@Override
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler,
final HttpHeaders httpHeaders, URI uri) throws WebSocketConnectFailureException {
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders httpHeaders, URI uri)
throws WebSocketConnectFailureException {
StandardWebSocketSessionAdapter session = new StandardWebSocketSessionAdapter();
session.setUri(uri);
@ -86,29 +89,7 @@ public class StandardWebSocketClient implements WebSocketClient { @@ -86,29 +89,7 @@ public class StandardWebSocketClient implements WebSocketClient {
if (!protocols.isEmpty()) {
configBuidler.preferredSubprotocols(protocols);
}
configBuidler.configurator(new Configurator() {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
for (String headerName : httpHeaders.keySet()) {
if (!EXCLUDED_HEADERS.contains(headerName)) {
List<String> value = httpHeaders.get(headerName);
if (logger.isTraceEnabled()) {
logger.trace("Adding header [" + headerName + "=" + value + "]");
}
headers.put(headerName, value);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Handshake request headers: " + headers);
}
}
@Override
public void afterResponse(HandshakeResponse handshakeResponse) {
if (logger.isTraceEnabled()) {
logger.trace("Handshake response headers: " + handshakeResponse.getHeaders());
}
}
});
configBuidler.configurator(new StandardWebSocketClientConfigurator(httpHeaders));
}
try {
@ -121,4 +102,41 @@ public class StandardWebSocketClient implements WebSocketClient { @@ -121,4 +102,41 @@ public class StandardWebSocketClient implements WebSocketClient {
}
}
private static class StandardWebSocketClientConfigurator extends Configurator {
private static final Set<String> EXCLUDED_HEADERS = new HashSet<String>(
Arrays.asList("Sec-WebSocket-Accept", "Sec-WebSocket-Extensions", "Sec-WebSocket-Key",
"Sec-WebSocket-Protocol", "Sec-WebSocket-Version"));
private final HttpHeaders httpHeaders;
public StandardWebSocketClientConfigurator(HttpHeaders httpHeaders) {
this.httpHeaders = httpHeaders;
}
@Override
public void beforeRequest(Map<String, List<String>> headers) {
for (String headerName : this.httpHeaders.keySet()) {
if (!EXCLUDED_HEADERS.contains(headerName)) {
List<String> value = this.httpHeaders.get(headerName);
if (logger.isTraceEnabled()) {
logger.trace("Adding header [" + headerName + "=" + value + "]");
}
headers.put(headerName, value);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Handshake request headers: " + headers);
}
}
@Override
public void afterResponse(HandshakeResponse handshakeResponse) {
if (logger.isTraceEnabled()) {
logger.trace("Handshake response headers: " + handshakeResponse.getHeaders());
}
}
}
}

8
spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/EndpointExporter.java → spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/ServerEndpointExporter.java

@ -48,12 +48,13 @@ import org.springframework.util.ReflectionUtils; @@ -48,12 +48,13 @@ import org.springframework.util.ReflectionUtils;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class EndpointExporter implements InitializingBean, BeanPostProcessor, ApplicationContextAware {
public class ServerEndpointExporter implements InitializingBean, BeanPostProcessor, ApplicationContextAware {
private static final boolean isServletApiPresent =
ClassUtils.isPresent("javax.servlet.ServletContext", EndpointExporter.class.getClassLoader());
ClassUtils.isPresent("javax.servlet.ServletContext", ServerEndpointExporter.class.getClassLoader());
private static Log logger = LogFactory.getLog(ServerEndpointExporter.class);
private static Log logger = LogFactory.getLog(EndpointExporter.class);
private final List<Class<?>> annotatedEndpointClasses = new ArrayList<Class<?>>();
@ -63,6 +64,7 @@ public class EndpointExporter implements InitializingBean, BeanPostProcessor, Ap @@ -63,6 +64,7 @@ public class EndpointExporter implements InitializingBean, BeanPostProcessor, Ap
private ServerContainer serverContainer;
/**
* TODO
* @param annotatedEndpointClasses

93
spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/EndpointRegistration.java → spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/ServerEndpointRegistration.java

@ -38,16 +38,14 @@ import org.springframework.web.socket.support.BeanCreatingHandlerProvider; @@ -38,16 +38,14 @@ import org.springframework.web.socket.support.BeanCreatingHandlerProvider;
/**
* An implementation of {@link javax.websocket.server.ServerEndpointConfig} that also
* holds the target {@link javax.websocket.Endpoint} as a reference or a bean name.
*
* <p>
* Beans of this type are detected by {@link EndpointExporter} and
* registered with a Java WebSocket runtime at startup.
* holds the target {@link javax.websocket.Endpoint} provided as a reference or as a bean
* name. Beans of this type are detected by {@link ServerEndpointExporter} and registered
* with a Java WebSocket runtime at startup.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAware {
public class ServerEndpointRegistration implements ServerEndpointConfig, BeanFactoryAware {
private final String path;
@ -65,7 +63,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw @@ -65,7 +63,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
private final Map<String, Object> userProperties = new HashMap<String, Object>();
private Configurator configurator = new Configurator() {};
private Configurator configurator = new EndpointRegistrationConfigurator();
/**
@ -74,7 +72,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw @@ -74,7 +72,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
* @param path
* @param endpointClass
*/
public EndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
public ServerEndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpointClass, "endpointClass is required");
this.path = path;
@ -82,7 +80,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw @@ -82,7 +80,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
this.endpoint = null;
}
public EndpointRegistration(String path, Endpoint endpoint) {
public ServerEndpointRegistration(String path, Endpoint endpoint) {
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpoint, "endpoint is required");
this.path = path;
@ -152,38 +150,9 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw @@ -152,38 +150,9 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
return this.decoders;
}
/**
* The {@link Configurator#getEndpointInstance(Class)} method is always ignored.
*/
public void setConfigurator(Configurator configurator) {
this.configurator = configurator;
}
@Override
public Configurator getConfigurator() {
return new Configurator() {
@SuppressWarnings("unchecked")
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
return (T) EndpointRegistration.this.getEndpoint();
}
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
EndpointRegistration.this.configurator.modifyHandshake(sec, request, response);
}
@Override
public boolean checkOrigin(String originHeaderValue) {
return EndpointRegistration.this.configurator.checkOrigin(originHeaderValue);
}
@Override
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) {
return EndpointRegistration.this.configurator.getNegotiatedSubprotocol(supported, requested);
}
@Override
public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) {
return EndpointRegistration.this.configurator.getNegotiatedExtensions(installed, requested);
}
};
return this.configurator;
}
@Override
@ -193,4 +162,50 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw @@ -193,4 +162,50 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
}
}
protected void modifyHandshake(HandshakeRequest request, HandshakeResponse response) {
this.configurator.modifyHandshake(this, request, response);
}
protected boolean checkOrigin(String originHeaderValue) {
return this.configurator.checkOrigin(originHeaderValue);
}
protected String getNegotiatedSubprotocol(List<String> supported, List<String> requested) {
return this.configurator.getNegotiatedSubprotocol(supported, requested);
}
protected List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) {
return this.configurator.getNegotiatedExtensions(installed, requested);
}
private class EndpointRegistrationConfigurator extends Configurator {
@SuppressWarnings("unchecked")
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
return (T) ServerEndpointRegistration.this.getEndpoint();
}
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
@Override
public boolean checkOrigin(String originHeaderValue) {
return super.checkOrigin(originHeaderValue);
}
@Override
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) {
return super.getNegotiatedSubprotocol(supported, requested);
}
@Override
public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) {
return super.getNegotiatedExtensions(installed, requested);
}
}
}

7
spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/ServletServerContainerFactoryBean.java

@ -34,7 +34,7 @@ import org.springframework.web.socket.sockjs.SockJsService; @@ -34,7 +34,7 @@ import org.springframework.web.socket.sockjs.SockJsService;
* using its setters allows configuring the {@code ServerContainer} through Spring
* configuration. This is useful even if the ServerContainer is not injected into any
* other bean. For example, an application can configure a {@link DefaultHandshakeHandler}
* , a {@link SockJsService}, or {@link EndpointExporter}, and separately declare this
* , a {@link SockJsService}, or {@link ServerEndpointExporter}, and separately declare this
* FactoryBean in order to customize the properties of the (one and only)
* {@code ServerContainer} instance.
*
@ -44,9 +44,6 @@ import org.springframework.web.socket.sockjs.SockJsService; @@ -44,9 +44,6 @@ import org.springframework.web.socket.sockjs.SockJsService;
public class ServletServerContainerFactoryBean
implements FactoryBean<WebSocketContainer>, InitializingBean, ServletContextAware {
private static final String SERVER_CONTAINER_ATTR_NAME = "javax.websocket.server.ServerContainer";
private Long asyncSendTimeout;
private Long maxSessionIdleTimeout;
@ -92,7 +89,7 @@ public class ServletServerContainerFactoryBean @@ -92,7 +89,7 @@ public class ServletServerContainerFactoryBean
@Override
public void setServletContext(ServletContext servletContext) {
this.serverContainer = (ServerContainer) servletContext.getAttribute(SERVER_CONTAINER_ATTR_NAME);
this.serverContainer = (ServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer");
}
@Override

6
spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/SpringConfigurator.java

@ -27,9 +27,9 @@ import org.springframework.web.context.ContextLoader; @@ -27,9 +27,9 @@ import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
/**
* This should be used in conjuction with {@link ServerEndpoint @ServerEndpoint} classes.
* This should be used in conjunction with {@link ServerEndpoint @ServerEndpoint} classes.
*
* <p>For {@link javax.websocket.Endpoint}, see {@link EndpointExporter}.
* <p>For {@link javax.websocket.Endpoint}, see {@link ServerEndpointExporter}.
*
* @author Rossen Stoyanchev
* @since 4.0
@ -56,7 +56,7 @@ public class SpringConfigurator extends Configurator { @@ -56,7 +56,7 @@ public class SpringConfigurator extends Configurator {
}
return wac.getAutowireCapableBeanFactory().createBean(endpointClass);
}
if (beans.size() == 1) {
else if (beans.size() == 1) {
if (logger.isTraceEnabled()) {
logger.trace("Using @ServerEndpoint singleton " + beans.keySet().iterator().next());
}

6
spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/Test.java

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
package org.springframework.web.socket.server.endpoint;
public class Test {
}

4
spring-websocket/src/main/java/org/springframework/web/socket/server/endpoint/package-info.java

@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
/**
* Server classes for use with standard Java WebSocket endpoints including
* {@link org.springframework.web.socket.server.endpoint.EndpointRegistration} and
* {@link org.springframework.web.socket.server.endpoint.EndpointExporter} for
* {@link org.springframework.web.socket.server.endpoint.ServerEndpointRegistration} and
* {@link org.springframework.web.socket.server.endpoint.ServerEndpointExporter} for
* registering type-based endpoints,
* {@link org.springframework.web.socket.server.endpoint.SpringConfigurator} for
* instantiating annotated endpoints through Spring.

4
spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java

@ -50,7 +50,7 @@ import org.springframework.util.ClassUtils; @@ -50,7 +50,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.endpoint.EndpointRegistration;
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
/**
* GlassFish support for upgrading an {@link HttpServletRequest} during a WebSocket
@ -136,7 +136,7 @@ public class GlassFishRequestUpgradeStrategy extends AbstractEndpointUpgradeStra @@ -136,7 +136,7 @@ public class GlassFishRequestUpgradeStrategy extends AbstractEndpointUpgradeStra
String randomValue = String.valueOf(random.nextLong());
String endpointPath = requestUri.endsWith("/") ? requestUri + randomValue : requestUri + "/" + randomValue;
EndpointRegistration endpointConfig = new EndpointRegistration(endpointPath, endpoint);
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
endpointConfig.setSubprotocols(Arrays.asList(selectedProtocol));
return new TyrusEndpoint(new EndpointWrapper(endpoint, endpointConfig,

4
spring-websocket/src/main/java/org/springframework/web/socket/server/support/TomcatRequestUpgradeStrategy.java

@ -34,7 +34,7 @@ import org.springframework.http.server.ServletServerHttpRequest; @@ -34,7 +34,7 @@ import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.endpoint.EndpointRegistration;
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
/**
* Tomcat support for upgrading an {@link HttpServletRequest} during a WebSocket handshake.
@ -77,7 +77,7 @@ public class TomcatRequestUpgradeStrategy extends AbstractEndpointUpgradeStrateg @@ -77,7 +77,7 @@ public class TomcatRequestUpgradeStrategy extends AbstractEndpointUpgradeStrateg
// TODO: use ServletContext attribute when Tomcat is updated
WsServerContainer serverContainer = WsServerContainer.getServerContainer();
ServerEndpointConfig endpointConfig = new EndpointRegistration("/shouldntmatter", endpoint);
ServerEndpointConfig endpointConfig = new ServerEndpointRegistration("/shouldntmatter", endpoint);
upgradeHandler.preInit(endpoint, endpointConfig, serverContainer, webSocketRequest,
selectedProtocol, Collections.<String, String> emptyMap(), servletRequest.isSecure());

162
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/AbstractServerSockJsSession.java

@ -1,162 +0,0 @@ @@ -1,162 +0,0 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
import java.io.EOFException;
import java.io.IOException;
import java.net.SocketException;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
/**
* Provides partial implementations of {@link SockJsSession} methods to send messages,
* including heartbeat messages and to manage session state.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractServerSockJsSession extends AbstractSockJsSession {
private final SockJsConfiguration sockJsConfig;
private ScheduledFuture<?> heartbeatTask;
public AbstractServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
super(sessionId, handler);
this.sockJsConfig = config;
}
protected SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
}
@Override
public final synchronized void sendMessage(WebSocketMessage message) throws IOException {
Assert.isTrue(!isClosed(), "Cannot send a message when session is closed");
Assert.isInstanceOf(TextMessage.class, message, "Expected text message: " + message);
sendMessageInternal(((TextMessage) message).getPayload());
}
protected abstract void sendMessageInternal(String message) throws IOException;
@Override
public void connectionClosedInternal(CloseStatus status) {
updateLastActiveTime();
cancelHeartbeat();
}
@Override
public final synchronized void closeInternal(CloseStatus status) throws IOException {
if (isActive()) {
// TODO: deliver messages "in flight" before sending close frame
try {
// bypass writeFrame
writeFrameInternal(SockJsFrame.closeFrame(status.getCode(), status.getReason()));
}
catch (Throwable ex) {
logger.warn("Failed to send SockJS close frame: " + ex.getMessage());
}
}
updateLastActiveTime();
cancelHeartbeat();
disconnect(status);
}
protected abstract void disconnect(CloseStatus status) throws IOException;
/**
* For internal use within a TransportHandler and the (TransportHandler-specific)
* session sub-class.
*/
protected void writeFrame(SockJsFrame frame) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Preparing to write " + frame);
}
try {
writeFrameInternal(frame);
}
catch (IOException ex) {
if (ex instanceof EOFException || ex instanceof SocketException) {
logger.warn("Client went away. Terminating connection");
}
else {
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage());
}
disconnect(CloseStatus.SERVER_ERROR);
close(CloseStatus.SERVER_ERROR);
throw ex;
}
catch (Throwable ex) {
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage());
disconnect(CloseStatus.SERVER_ERROR);
close(CloseStatus.SERVER_ERROR);
throw new SockJsRuntimeException("Failed to write " + frame, ex);
}
}
protected abstract void writeFrameInternal(SockJsFrame frame) throws Exception;
public synchronized void sendHeartbeat() throws Exception {
if (isActive()) {
writeFrame(SockJsFrame.heartbeatFrame());
scheduleHeartbeat();
}
}
protected void scheduleHeartbeat() {
Assert.notNull(getSockJsConfig().getTaskScheduler(), "heartbeatScheduler not configured");
cancelHeartbeat();
if (!isActive()) {
return;
}
Date time = new Date(System.currentTimeMillis() + getSockJsConfig().getHeartbeatTime());
this.heartbeatTask = getSockJsConfig().getTaskScheduler().schedule(new Runnable() {
@Override
public void run() {
try {
sendHeartbeat();
}
catch (Throwable t) {
// ignore
}
}
}, time);
if (logger.isTraceEnabled()) {
logger.trace("Scheduled heartbeat after " + getSockJsConfig().getHeartbeatTime()/1000 + " seconds");
}
}
protected void cancelHeartbeat() {
if ((this.heartbeatTask != null) && !this.heartbeatTask.isDone()) {
if (logger.isTraceEnabled()) {
logger.trace("Cancelling heartbeat");
}
this.heartbeatTask.cancel(false);
}
this.heartbeatTask = null;
}
}

2
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/AbstractSockJsService.java

@ -332,6 +332,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf @@ -332,6 +332,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return path.substring(index + prefix.length());
}
}
return null;
}
// SockJS info request?
@ -519,5 +520,4 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf @@ -519,5 +520,4 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
}
};
}

161
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/AbstractSockJsSession.java

@ -16,9 +16,13 @@ @@ -16,9 +16,13 @@
package org.springframework.web.socket.sockjs;
import java.io.EOFException;
import java.io.IOException;
import java.net.SocketException;
import java.net.URI;
import java.security.Principal;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -26,6 +30,7 @@ import org.springframework.util.Assert; @@ -26,6 +30,7 @@ import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.adapter.ConfigurableWebSocketSession;
@ -50,24 +55,29 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -50,24 +55,29 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
private Principal principal;
private final SockJsConfiguration sockJsConfig;
private WebSocketHandler handler;
private State state = State.NEW;
private long timeCreated = System.currentTimeMillis();
private long timeLastActive = System.currentTimeMillis();
private long timeLastActive = timeCreated;
private ScheduledFuture<?> heartbeatTask;
/**
* @param sessionId
* @param webSocketHandler the recipient of SockJS messages
*/
public AbstractSockJsSession(String sessionId, WebSocketHandler webSocketHandler) {
public AbstractSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler webSocketHandler) {
Assert.notNull(sessionId, "sessionId is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required");
this.id = sessionId;
this.handler = webSocketHandler;
this.sockJsConfig = config;
}
@Override
@ -120,6 +130,10 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -120,6 +130,10 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
this.principal = principal;
}
public SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
}
public boolean isNew() {
return State.NEW.equals(this.state);
}
@ -167,35 +181,12 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -167,35 +181,12 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
this.handler.afterConnectionEstablished(this);
}
/**
* Close due to error arising from SockJS transport handling.
*/
protected void tryCloseWithSockJsTransportError(Throwable ex, CloseStatus closeStatus) {
logger.error("Closing due to transport error for " + this, ex);
try {
delegateError(ex);
}
catch (Throwable delegateEx) {
logger.error("Unhandled error for " + this, delegateEx);
try {
close(closeStatus);
}
catch (Throwable closeEx) {
logger.error("Unhandled error for " + this, closeEx);
}
}
}
public void delegateMessages(String[] messages) throws Exception {
for (String message : messages) {
this.handler.handleMessage(this, new TextMessage(message));
}
}
public void delegateError(Throwable ex) throws Exception {
this.handler.handleTransportError(this, ex);
}
/**
* Invoked in reaction to the underlying connection being closed by the remote side
* (or the WebSocket container) in order to perform cleanup and notify the
@ -208,7 +199,8 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -208,7 +199,8 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
logger.debug(this + " was closed, " + status);
}
try {
connectionClosedInternal(status);
updateLastActiveTime();
cancelHeartbeat();
}
finally {
this.state = State.CLOSED;
@ -217,9 +209,18 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -217,9 +209,18 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
}
}
protected void connectionClosedInternal(CloseStatus status) {
public void delegateError(Throwable ex) throws Exception {
this.handler.handleTransportError(this, ex);
}
public final synchronized void sendMessage(WebSocketMessage message) throws IOException {
Assert.isTrue(!isClosed(), "Cannot send a message when session is closed");
Assert.isInstanceOf(TextMessage.class, message, "Expected text message: " + message);
sendMessageInternal(((TextMessage) message).getPayload());
}
protected abstract void sendMessageInternal(String message) throws IOException;
/**
* {@inheritDoc}
* <p>Performs cleanup and notifies the {@link SockJsHandler}.
@ -240,7 +241,19 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -240,7 +241,19 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
logger.debug("Closing " + this + ", " + status);
}
try {
closeInternal(status);
if (isActive()) {
// TODO: deliver messages "in flight" before sending close frame
try {
// bypass writeFrame
writeFrameInternal(SockJsFrame.closeFrame(status.getCode(), status.getReason()));
}
catch (Throwable ex) {
logger.warn("Failed to send SockJS close frame: " + ex.getMessage());
}
}
updateLastActiveTime();
cancelHeartbeat();
disconnect(status);
}
finally {
this.state = State.CLOSED;
@ -254,7 +267,97 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess @@ -254,7 +267,97 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
}
}
protected abstract void closeInternal(CloseStatus status) throws IOException;
protected abstract void disconnect(CloseStatus status) throws IOException;
/**
* Close due to error arising from SockJS transport handling.
*/
protected void tryCloseWithSockJsTransportError(Throwable ex, CloseStatus closeStatus) {
logger.error("Closing due to transport error for " + this, ex);
try {
delegateError(ex);
}
catch (Throwable delegateEx) {
logger.error("Unhandled error for " + this, delegateEx);
try {
close(closeStatus);
}
catch (Throwable closeEx) {
logger.error("Unhandled error for " + this, closeEx);
}
}
}
/**
* For internal use within a TransportHandler and the (TransportHandler-specific)
* session sub-class.
*/
protected void writeFrame(SockJsFrame frame) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Preparing to write " + frame);
}
try {
writeFrameInternal(frame);
}
catch (IOException ex) {
if (ex instanceof EOFException || ex instanceof SocketException) {
logger.warn("Client went away. Terminating connection");
}
else {
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage());
}
disconnect(CloseStatus.SERVER_ERROR);
close(CloseStatus.SERVER_ERROR);
throw ex;
}
catch (Throwable ex) {
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage());
disconnect(CloseStatus.SERVER_ERROR);
close(CloseStatus.SERVER_ERROR);
throw new SockJsRuntimeException("Failed to write " + frame, ex);
}
}
protected abstract void writeFrameInternal(SockJsFrame frame) throws Exception;
public synchronized void sendHeartbeat() throws Exception {
if (isActive()) {
writeFrame(SockJsFrame.heartbeatFrame());
scheduleHeartbeat();
}
}
protected void scheduleHeartbeat() {
Assert.notNull(this.sockJsConfig.getTaskScheduler(), "heartbeatScheduler not configured");
cancelHeartbeat();
if (!isActive()) {
return;
}
Date time = new Date(System.currentTimeMillis() + this.sockJsConfig.getHeartbeatTime());
this.heartbeatTask = this.sockJsConfig.getTaskScheduler().schedule(new Runnable() {
public void run() {
try {
sendHeartbeat();
}
catch (Throwable t) {
// ignore
}
}
}, time);
if (logger.isTraceEnabled()) {
logger.trace("Scheduled heartbeat after " + this.sockJsConfig.getHeartbeatTime()/1000 + " seconds");
}
}
protected void cancelHeartbeat() {
if ((this.heartbeatTask != null) && !this.heartbeatTask.isDone()) {
if (logger.isTraceEnabled()) {
logger.trace("Cancelling heartbeat");
}
this.heartbeatTask.cancel(false);
}
this.heartbeatTask = null;
}
@Override

17
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/SockJsFrame.java

@ -41,6 +41,7 @@ public class SockJsFrame { @@ -41,6 +41,7 @@ public class SockJsFrame {
private SockJsFrame(String content) {
Assert.notNull("content is required");
this.content = content;
}
@ -116,6 +117,22 @@ public class SockJsFrame { @@ -116,6 +117,22 @@ public class SockJsFrame {
return "SockJsFrame content='" + result.replace("\n", "\\n").replace("\r", "\\r") + "'";
}
@Override
public int hashCode() {
return this.content.hashCode();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof SockJsFrame)) {
return false;
}
return this.content.equals(((SockJsFrame) other).content);
}
private static class MessageFrame extends SockJsFrame {

2
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/TransportType.java

@ -79,7 +79,7 @@ public enum TransportType { @@ -79,7 +79,7 @@ public enum TransportType {
return this.httpMethod;
}
public boolean setsNoCache() {
public boolean sendsNoCacheInstruction() {
return this.headerHints.contains("no_cache");
}

19
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/DefaultSockJsService.java

@ -35,6 +35,7 @@ import org.springframework.http.server.ServerHttpRequest; @@ -35,6 +35,7 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.DefaultHandshakeHandler;
import org.springframework.web.socket.server.HandshakeHandler;
@ -84,7 +85,8 @@ public class DefaultSockJsService extends AbstractSockJsService { @@ -84,7 +85,8 @@ public class DefaultSockJsService extends AbstractSockJsService {
* application stops.
*/
public DefaultSockJsService(TaskScheduler taskScheduler) {
this(taskScheduler, null);
super(taskScheduler);
addTransportHandlers(getDefaultTransportHandlers());
}
/**
@ -105,9 +107,16 @@ public class DefaultSockJsService extends AbstractSockJsService { @@ -105,9 +107,16 @@ public class DefaultSockJsService extends AbstractSockJsService {
super(taskScheduler);
transportHandlers = CollectionUtils.isEmpty(transportHandlers) ? getDefaultTransportHandlers() : transportHandlers;
addTransportHandlers(transportHandlers);
addTransportHandlers(Arrays.asList(transportHandlerOverrides));
if (!CollectionUtils.isEmpty(transportHandlers)) {
addTransportHandlers(transportHandlers);
}
if (!ObjectUtils.isEmpty(transportHandlerOverrides)) {
addTransportHandlers(Arrays.asList(transportHandlerOverrides));
}
if (this.transportHandlers.isEmpty()) {
logger.warn("No transport handlers");
}
}
protected final Set<TransportHandler> getDefaultTransportHandlers() {
@ -194,7 +203,7 @@ public class DefaultSockJsService extends AbstractSockJsService { @@ -194,7 +203,7 @@ public class DefaultSockJsService extends AbstractSockJsService {
transportHandler, request, response);
if (session != null) {
if (transportType.setsNoCache()) {
if (transportType.sendsNoCacheInstruction()) {
addNoCacheHeaders(response);
}

20
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpReceivingTransportHandler.java

@ -28,8 +28,6 @@ import org.springframework.http.server.ServerHttpRequest; @@ -28,8 +28,6 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsRuntimeException;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
@ -38,7 +36,7 @@ import com.fasterxml.jackson.databind.JsonMappingException; @@ -38,7 +36,7 @@ import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* TODO
* Base class for HTTP-based transports that read input messages.
*
* @author Rossen Stoyanchev
* @since 4.0
@ -57,8 +55,7 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport @@ -57,8 +55,7 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session)
throws TransportErrorException {
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);
@ -77,21 +74,26 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport @@ -77,21 +74,26 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
messages = readMessages(request);
}
catch (JsonMappingException ex) {
logger.error("Failed to read message: ", ex);
logger.error("Failed to read message: " + ex.getMessage());
sendInternalServerError(response, "Payload expected.", session.getId());
return;
}
catch (IOException ex) {
logger.error("Failed to read message: ", ex);
logger.error("Failed to read message: " + ex.getMessage());
sendInternalServerError(response, "Broken JSON encoding.", session.getId());
return;
}
catch (Throwable t) {
logger.error("Failed to read message: ", t);
logger.error("Failed to read message: " + t.getMessage());
sendInternalServerError(response, "Failed to process messages", session.getId());
return;
}
if (messages == null) {
sendInternalServerError(response, "Payload expected.", session.getId());
return;
}
if (logger.isTraceEnabled()) {
logger.trace("Received message(s): " + Arrays.asList(messages));
}
@ -104,7 +106,7 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport @@ -104,7 +106,7 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(session, t, logger);
throw new SockJsRuntimeException("Unhandled WebSocketHandler error in " + this, t);
throw new TransportErrorException("Unhandled WebSocketHandler error in " + this, t, session.getId());
}
}

9
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpSendingTransportHandler.java

@ -28,9 +28,9 @@ import org.springframework.web.socket.sockjs.AbstractSockJsSession; @@ -28,9 +28,9 @@ import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.ConfigurableTransportHandler;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
/**
* TODO
@ -57,18 +57,17 @@ public abstract class AbstractHttpSendingTransportHandler @@ -57,18 +57,17 @@ public abstract class AbstractHttpSendingTransportHandler
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session)
throws TransportErrorException {
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
// Set content type before writing
response.getHeaders().setContentType(getContentType());
AbstractHttpServerSockJsSession httpServerSession = (AbstractHttpServerSockJsSession) session;
AbstractHttpSockJsSession httpServerSession = (AbstractHttpSockJsSession) session;
handleRequestInternal(request, response, httpServerSession);
}
protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractHttpServerSockJsSession httpServerSession) throws TransportErrorException {
AbstractHttpSockJsSession httpServerSession) throws TransportErrorException {
if (httpServerSession.isNew()) {
logger.debug("Opening " + getTransportType() + " connection");

8
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpServerSockJsSession.java → spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/AbstractHttpSockJsSession.java

@ -26,11 +26,11 @@ import org.springframework.http.server.ServerHttpResponse; @@ -26,11 +26,11 @@ import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractServerSockJsSession;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
/**
@ -39,7 +39,7 @@ import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator @@ -39,7 +39,7 @@ import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractHttpServerSockJsSession extends AbstractServerSockJsSession {
public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private FrameFormat frameFormat;
@ -50,7 +50,7 @@ public abstract class AbstractHttpServerSockJsSession extends AbstractServerSock @@ -50,7 +50,7 @@ public abstract class AbstractHttpServerSockJsSession extends AbstractServerSock
private ServerHttpResponse response;
public AbstractHttpServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public AbstractHttpSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

4
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/EventSourceTransportHandler.java

@ -46,9 +46,9 @@ public class EventSourceTransportHandler extends AbstractHttpSendingTransportHan @@ -46,9 +46,9 @@ public class EventSourceTransportHandler extends AbstractHttpSendingTransportHan
}
@Override
public StreamingServerSockJsSession createSession(String sessionId, WebSocketHandler handler) {
public StreamingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new StreamingServerSockJsSession(sessionId, getSockJsConfig(), handler) {
return new StreamingSockJsSession(sessionId, getSockJsConfig(), handler) {
@Override
protected void writePrelude() throws IOException {
getResponse().getBody().write('\r');

6
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/HtmlFileTransportHandler.java

@ -80,10 +80,10 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle @@ -80,10 +80,10 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle
}
@Override
public StreamingServerSockJsSession createSession(String sessionId, WebSocketHandler handler) {
public StreamingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new StreamingServerSockJsSession(sessionId, getSockJsConfig(), handler) {
return new StreamingSockJsSession(sessionId, getSockJsConfig(), handler) {
@Override
protected void writePrelude() throws IOException {
@ -99,7 +99,7 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle @@ -99,7 +99,7 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle
@Override
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractHttpServerSockJsSession session) throws TransportErrorException {
AbstractHttpSockJsSession session) throws TransportErrorException {
try {
String callback = request.getQueryParams().getFirst("c");

6
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/JsonpPollingTransportHandler.java

@ -50,14 +50,14 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa @@ -50,14 +50,14 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
}
@Override
public PollingServerSockJsSession createSession(String sessionId, WebSocketHandler handler) {
public PollingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new PollingServerSockJsSession(sessionId, getSockJsConfig(), handler);
return new PollingSockJsSession(sessionId, getSockJsConfig(), handler);
}
@Override
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractHttpServerSockJsSession session) throws TransportErrorException {
AbstractHttpSockJsSession session) throws TransportErrorException {
try {
String callback = request.getQueryParams().getFirst("c");

18
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/JsonpTransportHandler.java

@ -20,14 +20,20 @@ import java.io.IOException; @@ -20,14 +20,20 @@ import java.io.IOException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportType;
public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler {
private final FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
@Override
public TransportType getTransportType() {
return TransportType.JSONP_SEND;
@ -37,13 +43,6 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler @@ -37,13 +43,6 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractSockJsSession sockJsSession) throws TransportErrorException {
if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) {
if (request.getQueryParams().getFirst("d") == null) {
sendInternalServerError(response, "Payload expected.", sockJsSession.getId());
return;
}
}
super.handleRequestInternal(request, response, sockJsSession);
try {
@ -57,8 +56,9 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler @@ -57,8 +56,9 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler
@Override
protected String[] readMessages(ServerHttpRequest request) throws IOException {
if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) {
String d = request.getQueryParams().getFirst("d");
return getObjectMapper().readValue(d, String[].class);
MultiValueMap<String, String> map = this.formConverter.read(null, request);
String d = map.getFirst("d");
return (StringUtils.hasText(d)) ? getObjectMapper().readValue(d, String[].class) : null;
}
else {
return getObjectMapper().readValue(request.getBody(), String[].class);

4
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/PollingServerSockJsSession.java → spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/PollingSockJsSession.java

@ -22,9 +22,9 @@ import org.springframework.web.socket.sockjs.SockJsConfiguration; @@ -22,9 +22,9 @@ import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
public class PollingServerSockJsSession extends AbstractHttpServerSockJsSession {
public class PollingSockJsSession extends AbstractHttpSockJsSession {
public PollingServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public PollingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

4
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/StreamingServerSockJsSession.java → spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/StreamingSockJsSession.java

@ -26,12 +26,12 @@ import org.springframework.web.socket.sockjs.SockJsFrame; @@ -26,12 +26,12 @@ import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
public class StreamingServerSockJsSession extends AbstractHttpServerSockJsSession {
public class StreamingSockJsSession extends AbstractHttpSockJsSession {
private int byteCount;
public StreamingServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public StreamingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

9
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/WebSocketServerSockJsSession.java

@ -23,7 +23,7 @@ import org.springframework.web.socket.CloseStatus; @@ -23,7 +23,7 @@ import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.AbstractServerSockJsSession;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
@ -31,10 +31,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,10 +31,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
/**
* A WebSocket implementation of {@link AbstractSockJsSession}. Delegates to a
* {@link WebSocketSession}.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class WebSocketServerSockJsSession extends AbstractServerSockJsSession {
public class WebSocketServerSockJsSession extends AbstractSockJsSession {
private WebSocketSession webSocketSession;
@ -103,5 +106,5 @@ public class WebSocketServerSockJsSession extends AbstractServerSockJsSession { @@ -103,5 +106,5 @@ public class WebSocketServerSockJsSession extends AbstractServerSockJsSession {
protected void disconnect(CloseStatus status) throws IOException {
this.webSocketSession.close(status);
}
}
}

4
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/XhrPollingTransportHandler.java

@ -51,9 +51,9 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand @@ -51,9 +51,9 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand
}
@Override
public PollingServerSockJsSession createSession(String sessionId, WebSocketHandler handler) {
public PollingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new PollingServerSockJsSession(sessionId, getSockJsConfig(), handler);
return new PollingSockJsSession(sessionId, getSockJsConfig(), handler);
}
}

4
spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/XhrStreamingTransportHandler.java

@ -47,10 +47,10 @@ public class XhrStreamingTransportHandler extends AbstractHttpSendingTransportHa @@ -47,10 +47,10 @@ public class XhrStreamingTransportHandler extends AbstractHttpSendingTransportHa
}
@Override
public StreamingServerSockJsSession createSession(String sessionId, WebSocketHandler handler) {
public StreamingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new StreamingServerSockJsSession(sessionId, getSockJsConfig(), handler) {
return new StreamingSockJsSession(sessionId, getSockJsConfig(), handler) {
@Override
protected void writePrelude() throws IOException {

2
spring-websocket/src/main/java/org/springframework/web/socket/support/WebSocketHandlerDecorator.java

@ -38,7 +38,7 @@ public class WebSocketHandlerDecorator implements WebSocketHandler { @@ -38,7 +38,7 @@ public class WebSocketHandlerDecorator implements WebSocketHandler {
}
protected WebSocketHandler getDelegate() {
public WebSocketHandler getDelegate() {
return this.delegate;
}

13
spring-websocket/src/test/java/org/springframework/web/socket/AbstractHttpRequestTests.java

@ -41,18 +41,21 @@ public class AbstractHttpRequestTests { @@ -41,18 +41,21 @@ public class AbstractHttpRequestTests {
@Before
public void setUp() {
this.servletRequest = new MockHttpServletRequest();
this.servletResponse = new MockHttpServletResponse();
this.request = new AsyncServletServerHttpRequest(this.servletRequest, this.servletResponse);
this.response = new ServletServerHttpResponse(this.servletResponse);
resetRequestAndResponse();
}
protected void setRequest(String method, String requestUri) {
this.servletRequest.setMethod(method);
this.servletRequest.setRequestURI(requestUri);
}
protected void resetRequestAndResponse() {
resetResponse();
this.servletRequest = new MockHttpServletRequest();
this.servletRequest.setAsyncSupported(true);
this.request = new AsyncServletServerHttpRequest(this.servletRequest, this.servletResponse);
}
protected void resetResponse() {
this.servletResponse = new MockHttpServletResponse();
this.response = new ServletServerHttpResponse(this.servletResponse);

71
spring-websocket/src/test/java/org/springframework/web/socket/adapter/JettyWebSocketListenerAdapterTests.java

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.adapter;
import org.eclipse.jetty.websocket.api.Session;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link JettyWebSocketListenerAdapter}.
*
* @author Rossen Stoyanchev
*/
public class JettyWebSocketListenerAdapterTests {
private JettyWebSocketListenerAdapter adapter;
private WebSocketHandler webSocketHandler;
private JettyWebSocketSessionAdapter webSocketSession;
private Session session;
@Before
public void setup() {
this.session = mock(Session.class);
this.webSocketHandler = mock(WebSocketHandler.class);
this.webSocketSession = new JettyWebSocketSessionAdapter();
this.adapter = new JettyWebSocketListenerAdapter(this.webSocketHandler, this.webSocketSession);
}
@Test
public void onOpen() throws Throwable {
this.adapter.onWebSocketConnect(this.session);
verify(this.webSocketHandler).afterConnectionEstablished(this.webSocketSession);
}
@Test
public void onClose() throws Throwable {
this.adapter.onWebSocketClose(1000, "reason");
verify(this.webSocketHandler).afterConnectionClosed(this.webSocketSession, CloseStatus.NORMAL.withReason("reason"));
}
@Test
public void onError() throws Throwable {
Exception exception = new Exception();
this.adapter.onWebSocketError(exception);
verify(this.webSocketHandler).handleTransportError(this.webSocketSession, exception);
}
}

82
spring-websocket/src/test/java/org/springframework/web/socket/adapter/StandardEndpointAdapterTests.java

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.adapter;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link StandardEndpointAdapter}.
*
* @author Rossen Stoyanchev
*/
public class StandardEndpointAdapterTests {
private StandardEndpointAdapter adapter;
private WebSocketHandler webSocketHandler;
private StandardWebSocketSessionAdapter webSocketSession;
private Session session;
@Before
public void setup() {
this.session = mock(Session.class);
this.webSocketHandler = mock(WebSocketHandler.class);
this.webSocketSession = new StandardWebSocketSessionAdapter();
this.adapter = new StandardEndpointAdapter(webSocketHandler, webSocketSession);
}
@Test
public void onOpen() throws Throwable {
this.adapter.onOpen(session, null);
verify(this.webSocketHandler).afterConnectionEstablished(this.webSocketSession);
verify(session, atLeast(2)).addMessageHandler(any(MessageHandler.Whole.class));
when(session.getId()).thenReturn("123");
assertEquals("123", this.webSocketSession.getId());
}
@Test
public void onClose() throws Throwable {
this.adapter.onClose(session, new CloseReason(CloseCodes.NORMAL_CLOSURE, "reason"));
verify(this.webSocketHandler).afterConnectionClosed(this.webSocketSession, CloseStatus.NORMAL.withReason("reason"));
}
@Test
public void onError() throws Throwable {
Exception exception = new Exception();
this.adapter.onError(session, exception);
verify(this.webSocketHandler).handleTransportError(this.webSocketSession, exception);
}
}

159
spring-websocket/src/test/java/org/springframework/web/socket/client/WebSocketConnectionManagerTests.java

@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.client;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.SmartLifecycle;
import org.springframework.http.HttpHeaders;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.WebSocketHandlerAdapter;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
import org.springframework.web.socket.support.LoggingWebSocketHandlerDecorator;
import org.springframework.web.socket.support.WebSocketHandlerDecorator;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link WebSocketConnectionManager}.
*
* @author Rossen Stoyanchev
*/
public class WebSocketConnectionManagerTests {
@Test
public void openConnection() throws Exception {
List<String> subprotocols = Arrays.asList("abc");
HttpHeaders headers = new HttpHeaders();
headers.setSecWebSocketProtocol(subprotocols);
WebSocketClient client = mock(WebSocketClient.class);
WebSocketHandler handler = new WebSocketHandlerAdapter();
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler , "/path/{id}", "123");
manager.setSubProtocols(subprotocols);
manager.openConnection();
ArgumentCaptor<WebSocketHandlerDecorator> captor = ArgumentCaptor.forClass(WebSocketHandlerDecorator.class);
ArgumentCaptor<HttpHeaders> headersCaptor = ArgumentCaptor.forClass(HttpHeaders.class);
ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
verify(client).doHandshake(captor.capture(), headersCaptor.capture(), uriCaptor.capture());
assertEquals(headers, headersCaptor.getValue());
assertEquals(new URI("/path/123"), uriCaptor.getValue());
WebSocketHandlerDecorator loggingHandler = captor.getValue();
assertEquals(LoggingWebSocketHandlerDecorator.class, loggingHandler.getClass());
WebSocketHandlerDecorator exceptionHandler = (WebSocketHandlerDecorator) loggingHandler.getDelegate();
assertNotNull(exceptionHandler);
assertEquals(ExceptionWebSocketHandlerDecorator.class, exceptionHandler.getClass());
assertSame(handler, exceptionHandler.getDelegate());
}
@Test
public void syncClientLifecycle() throws Exception {
TestLifecycleWebSocketClient client = new TestLifecycleWebSocketClient(false);
WebSocketHandler handler = new WebSocketHandlerAdapter();
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler , "/a");
manager.startInternal();
assertTrue(client.isRunning());
manager.stopInternal();
assertFalse(client.isRunning());
}
@Test
public void dontSyncClientLifecycle() throws Exception {
TestLifecycleWebSocketClient client = new TestLifecycleWebSocketClient(true);
WebSocketHandler handler = new WebSocketHandlerAdapter();
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler , "/a");
manager.startInternal();
assertTrue(client.isRunning());
manager.stopInternal();
assertTrue(client.isRunning());
}
private static class TestLifecycleWebSocketClient implements WebSocketClient, SmartLifecycle {
private boolean running;
public TestLifecycleWebSocketClient(boolean running) {
this.running = running;
}
@Override
public void start() {
this.running = true;
}
@Override
public void stop() {
this.running = false;
}
@Override
public boolean isRunning() {
return this.running;
}
@Override
public int getPhase() {
return 0;
}
@Override
public boolean isAutoStartup() {
return false;
}
@Override
public void stop(Runnable callback) {
this.running = false;
}
@Override
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables)
throws WebSocketConnectFailureException {
return null;
}
@Override
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, URI uri)
throws WebSocketConnectFailureException {
return null;
}
}
}

89
spring-websocket/src/test/java/org/springframework/web/socket/client/endpoint/StandardWebSocketClientTests.java

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.client.endpoint;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Endpoint;
import javax.websocket.WebSocketContainer;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.StandardEndpointAdapter;
import org.springframework.web.socket.adapter.WebSocketHandlerAdapter;
import org.springframework.web.socket.client.endpoint.StandardWebSocketClient;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link StandardWebSocketClient}.
*
* @author Rossen Stoyanchev
*/
public class StandardWebSocketClientTests {
@Test
public void doHandshake() throws Exception {
URI uri = new URI("ws://example.com/abc");
List<String> subprotocols = Arrays.asList("abc");
HttpHeaders headers = new HttpHeaders();
headers.setSecWebSocketProtocol(subprotocols);
headers.add("foo", "bar");
WebSocketHandler handler = new WebSocketHandlerAdapter();
WebSocketContainer webSocketContainer = mock(WebSocketContainer.class);
StandardWebSocketClient client = new StandardWebSocketClient();
client.setWebSocketContainer(webSocketContainer);
WebSocketSession session = client.doHandshake(handler, headers, uri);
ArgumentCaptor<Endpoint> endpointArg = ArgumentCaptor.forClass(Endpoint.class);
ArgumentCaptor<ClientEndpointConfig> configArg = ArgumentCaptor.forClass(ClientEndpointConfig.class);
ArgumentCaptor<URI> uriArg = ArgumentCaptor.forClass(URI.class);
verify(webSocketContainer).connectToServer(endpointArg.capture(), configArg.capture(), uriArg.capture());
assertNotNull(endpointArg.getValue());
assertEquals(StandardEndpointAdapter.class, endpointArg.getValue().getClass());
ClientEndpointConfig config = configArg.getValue();
assertEquals(subprotocols, config.getPreferredSubprotocols());
Map<String, List<String>> map = new HashMap<>();
config.getConfigurator().beforeRequest(map);
assertEquals(Collections.singletonMap("foo", Arrays.asList("bar")), map);
assertEquals(uri, uriArg.getValue());
assertEquals(uri, session.getUri());
assertEquals("example.com", session.getRemoteHostName());
}
}

110
spring-websocket/src/test/java/org/springframework/web/socket/server/endpoint/ServerEndpointExporterTests.java

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.endpoint;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link ServerEndpointExporter}.
*
* @author Rossen Stoyanchev
*/
public class ServerEndpointExporterTests {
private ServerContainer serverContainer;
private ServerEndpointExporter exporter;
private AnnotationConfigWebApplicationContext webAppContext;
@Before
public void setup() {
this.serverContainer = mock(ServerContainer.class);
MockServletContext servletContext = new MockServletContext();
servletContext.setAttribute("javax.websocket.server.ServerContainer", serverContainer);
this.webAppContext = new AnnotationConfigWebApplicationContext();
this.webAppContext.register(Config.class);
this.webAppContext.setServletContext(servletContext);
this.webAppContext.refresh();
this.exporter = new ServerEndpointExporter();
this.exporter.setApplicationContext(this.webAppContext);
}
@Test
public void addAnnotatedEndpointBean() throws Exception {
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class);
this.exporter.afterPropertiesSet();
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
}
@Test
public void addServerEndpointConfigBean() throws Exception {
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
verify(this.serverContainer).addEndpoint(endpointRegistration);
}
private static class DummyEndpoint extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig config) {
}
}
@ServerEndpoint("/path")
private static class AnnotatedDummyEndpoint {
}
@ServerEndpoint("/path")
private static class AnnotatedDummyEndpointBean {
}
@Configuration
static class Config {
@Bean
public AnnotatedDummyEndpointBean annotatedEndpoint1() {
return new AnnotatedDummyEndpointBean();
}
}
}

93
spring-websocket/src/test/java/org/springframework/web/socket/server/endpoint/ServerEndpointRegistrationTests.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.endpoint;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.*;
/**
* Test fixture for {@link ServerEndpointRegistration}.
*
* @author Rossen Stoyanchev
*/
public class ServerEndpointRegistrationTests {
@Test
public void endpointPerConnection() throws Exception {
@SuppressWarnings("resource")
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
ServerEndpointRegistration registration = new ServerEndpointRegistration("/path", EchoEndpoint.class);
registration.setBeanFactory(context.getBeanFactory());
EchoEndpoint endpoint = registration.getConfigurator().getEndpointInstance(EchoEndpoint.class);
assertNotNull(endpoint);
}
@Test
public void endpointSingleton() throws Exception {
EchoEndpoint endpoint = new EchoEndpoint(new EchoService());
ServerEndpointRegistration registration = new ServerEndpointRegistration("/path", endpoint);
EchoEndpoint actual = registration.getConfigurator().getEndpointInstance(EchoEndpoint.class);
assertSame(endpoint, actual);
}
@Configuration
static class Config {
@Bean
public EchoService echoService() {
return new EchoService();
}
}
private static class EchoEndpoint extends Endpoint {
@SuppressWarnings("unused")
private final EchoService service;
@Autowired
public EchoEndpoint(EchoService service) {
this.service = service;
}
@Override
public void onOpen(Session session, EndpointConfig config) {
}
}
private static class EchoService { }
}

127
spring-websocket/src/test/java/org/springframework/web/socket/server/endpoint/SpringConfiguratorTests.java

@ -0,0 +1,127 @@ @@ -0,0 +1,127 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.endpoint;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.junit.Assert.*;
public class SpringConfiguratorTests {
private MockServletContext servletContext;
private ContextLoader contextLoader;
private AnnotationConfigWebApplicationContext webAppContext;
@Before
public void setup() {
this.servletContext = new MockServletContext();
this.webAppContext = new AnnotationConfigWebApplicationContext();
this.webAppContext.register(Config.class);
this.contextLoader = new ContextLoader(webAppContext);
this.contextLoader.initWebApplicationContext(this.servletContext);
}
@After
public void destroy() {
this.contextLoader.closeWebApplicationContext(this.servletContext);
}
@Test
public void getEndpointInstanceCreateBean() throws Exception {
PerConnectionEchoEndpoint endpoint = new SpringConfigurator().getEndpointInstance(PerConnectionEchoEndpoint.class);
assertNotNull(endpoint);
}
@Test
public void getEndpointInstanceUseBean() throws Exception {
EchoEndpointBean expected = this.webAppContext.getBean(EchoEndpointBean.class);
EchoEndpointBean actual = new SpringConfigurator().getEndpointInstance(EchoEndpointBean.class);
assertSame(expected, actual);
}
@Configuration
static class Config {
@Bean
public EchoEndpointBean echoEndpointBean() {
return new EchoEndpointBean(echoService());
}
@Bean
public EchoService echoService() {
return new EchoService();
}
}
private static class EchoEndpointBean extends Endpoint {
@SuppressWarnings("unused")
private final EchoService service;
@Autowired
public EchoEndpointBean(EchoService service) {
this.service = service;
}
@Override
public void onOpen(Session session, EndpointConfig config) {
}
}
private static class PerConnectionEchoEndpoint extends Endpoint {
@SuppressWarnings("unused")
private final EchoService service;
@Autowired
public PerConnectionEchoEndpoint(EchoService service) {
this.service = service;
}
@Override
public void onOpen(Session session, EndpointConfig config) {
}
}
private static class EchoService { }
}

151
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/AbstractSockJsServiceTests.java

@ -31,6 +31,8 @@ import org.springframework.web.socket.WebSocketHandler; @@ -31,6 +31,8 @@ import org.springframework.web.socket.WebSocketHandler;
import static org.junit.Assert.*;
/**
* Test fixture for {@link AbstractSockJsService}.
*
* @author Rossen Stoyanchev
*/
public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
@ -48,65 +50,156 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests { @@ -48,65 +50,156 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
}
@Test
public void getSockJsPath() throws Exception {
public void getSockJsPathForGreetingRequest() throws Exception {
handleRequest("/echo", HttpStatus.OK);
handleRequest("GET", "/a", HttpStatus.OK);
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
handleRequest("/echo/info", HttpStatus.OK);
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
handleRequest("GET", "/a/", HttpStatus.OK);
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
this.service.setValidSockJsPrefixes("/b");
handleRequest("/echo/", HttpStatus.OK);
handleRequest("GET", "/a", HttpStatus.NOT_FOUND);
handleRequest("GET", "/a/", HttpStatus.NOT_FOUND);
handleRequest("GET", "/b", HttpStatus.OK);
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
}
handleRequest("/echo/iframe.html", HttpStatus.OK);
assertTrue(this.servletResponse.getContentAsString().startsWith("<!DOCTYPE html>\n"));
@Test
public void getSockJsPathForInfoRequest() throws Exception {
handleRequest("GET", "/a/info", HttpStatus.OK);
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
this.service.setValidSockJsPrefixes("/b");
handleRequest("GET", "/a/info", HttpStatus.NOT_FOUND);
handleRequest("GET", "/b/info", HttpStatus.OK);
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
}
@Test
public void getSockJsPathForTransportRequest() throws Exception {
handleRequest("/echo/websocket", HttpStatus.OK);
assertNull(this.service.sessionId);
// Info or greeting requests must be first so "/a" is cached as a known prefix
handleRequest("GET", "/a/info", HttpStatus.OK);
handleRequest("GET", "/a/server/session/xhr", HttpStatus.OK);
assertEquals("session", this.service.sessionId);
assertEquals(TransportType.XHR, this.service.transportType);
assertSame(this.handler, this.service.handler);
}
handleRequest("/echo/server1/session2/xhr", HttpStatus.OK);
assertEquals("session2", this.service.sessionId);
@Test
public void getSockJsPathForTransportRequestWithConfiguredPrefix() throws Exception {
this.service.setValidSockJsPrefixes("/a");
handleRequest("GET", "/a/server/session/xhr", HttpStatus.OK);
assertEquals("session", this.service.sessionId);
assertEquals(TransportType.XHR, this.service.transportType);
assertSame(this.handler, this.service.handler);
}
@Test
public void validateRequest() throws Exception {
handleRequest("/echo/other", HttpStatus.NOT_FOUND);
handleRequest("/echo//", HttpStatus.NOT_FOUND);
handleRequest("/echo///", HttpStatus.NOT_FOUND);
this.service.setValidSockJsPrefixes("/echo");
this.service.setWebSocketsEnabled(false);
handleRequest("GET", "/echo/server/session/websocket", HttpStatus.NOT_FOUND);
this.service.setWebSocketsEnabled(true);
handleRequest("GET", "/echo/server/session/websocket", HttpStatus.OK);
handleRequest("GET", "/echo//", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo///", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo/other", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo//service/websocket", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo/server//websocket", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo/server/session/", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo/s.erver/session/websocket", HttpStatus.NOT_FOUND);
handleRequest("GET", "/echo/server/s.ession/websocket", HttpStatus.NOT_FOUND);
}
@Test
public void handleInfoGet() throws Exception {
handleRequest("GET", "/a/info", HttpStatus.OK);
assertEquals("application/json;charset=UTF-8", this.servletResponse.getContentType());
assertEquals("*", this.servletResponse.getHeader("Access-Control-Allow-Origin"));
assertEquals("true", this.servletResponse.getHeader("Access-Control-Allow-Credentials"));
assertEquals("no-store, no-cache, must-revalidate, max-age=0", this.servletResponse.getHeader("Cache-Control"));
String body = this.servletResponse.getContentAsString();
assertEquals("{\"entropy\"", body.substring(0, body.indexOf(':')));
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":true,\"websocket\":true}",
body.substring(body.indexOf(',')));
this.service.setJsessionIdCookieRequired(false);
this.service.setWebSocketsEnabled(false);
handleRequest("GET", "/a/info", HttpStatus.OK);
body = this.servletResponse.getContentAsString();
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":false,\"websocket\":false}",
body.substring(body.indexOf(',')));
}
@Test
public void getSockJsPathGreetingRequest() throws Exception {
handleRequest("/echo", HttpStatus.OK);
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
public void handleInfoOptions() throws Exception {
this.servletRequest.addHeader("Access-Control-Request-Headers", "Last-Modified");
handleRequest("OPTIONS", "/a/info", HttpStatus.NO_CONTENT);
assertEquals("*", this.servletResponse.getHeader("Access-Control-Allow-Origin"));
assertEquals("true", this.servletResponse.getHeader("Access-Control-Allow-Credentials"));
assertEquals("Last-Modified", this.servletResponse.getHeader("Access-Control-Allow-Headers"));
assertEquals("OPTIONS, GET", this.servletResponse.getHeader("Access-Control-Allow-Methods"));
assertEquals("31536000", this.servletResponse.getHeader("Access-Control-Max-Age"));
}
@Test
public void getSockJsPathInfoRequest() throws Exception {
handleRequest("/echo/info", HttpStatus.OK);
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
public void handleIframeRequest() throws Exception {
this.service.setValidSockJsPrefixes("/a");
handleRequest("GET", "/a/iframe.html", HttpStatus.OK);
assertEquals("text/html;charset=UTF-8", this.servletResponse.getContentType());
assertTrue(this.servletResponse.getContentAsString().startsWith("<!DOCTYPE html>\n"));
assertEquals(496, this.servletResponse.getContentLength());
assertEquals("public, max-age=31536000", this.response.getHeaders().getCacheControl());
assertEquals("\"0da1ed070012f304e47b83c81c48ad620\"", response.getHeaders().getETag());
}
@Test
public void getSockJsPathWithConfiguredPrefix() throws Exception {
this.service.setValidSockJsPrefixes("/echo");
handleRequest("/echo/s1/s2/xhr", HttpStatus.OK);
public void handleIframeRequestNotModified() throws Exception {
this.servletRequest.addHeader("If-None-Match", "\"0da1ed070012f304e47b83c81c48ad620\"");
this.service.setValidSockJsPrefixes("/a");
handleRequest("GET", "/a/iframe.html", HttpStatus.NOT_MODIFIED);
}
@Test
public void getInfoOptions() throws Exception {
setRequest("OPTIONS", "/echo/info");
this.service.handleRequest(this.request, this.response, this.handler);
public void handleRawWebSocketRequest() throws Exception {
assertEquals(204, servletResponse.getStatus());
handleRequest("GET", "/a", HttpStatus.OK);
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
handleRequest("GET", "/a/websocket", HttpStatus.OK);
assertNull("Raw WebSocket should not open a SockJS session", this.service.sessionId);
assertSame(this.handler, this.service.handler);
}
private void handleRequest(String uri, HttpStatus httpStatus) throws IOException {
private void handleRequest(String httpMethod, String uri, HttpStatus httpStatus) throws IOException {
resetResponse();
setRequest("GET", uri);
setRequest(httpMethod, uri);
this.service.handleRequest(this.request, this.response, this.handler);
assertEquals(httpStatus.value(), this.servletResponse.getStatus());

269
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/AbstractSockJsSessionTests.java

@ -0,0 +1,269 @@ @@ -0,0 +1,269 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
import java.io.IOException;
import java.sql.Date;
import java.util.Collections;
import java.util.concurrent.ScheduledFuture;
import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link AbstractSockJsSession}.
*
* @author Rossen Stoyanchev
*/
public class AbstractSockJsSessionTests extends BaseAbstractSockJsSessionTests<TestSockJsSession> {
@Override
protected TestSockJsSession initSockJsSession() {
return new TestSockJsSession("1", this.sockJsConfig, this.webSocketHandler);
}
@Test
public void getTimeSinceLastActive() throws Exception {
Thread.sleep(1);
long time1 = this.session.getTimeSinceLastActive();
assertTrue(time1 > 0);
Thread.sleep(1);
long time2 = this.session.getTimeSinceLastActive();
assertTrue(time2 > time1);
this.session.delegateConnectionEstablished();
Thread.sleep(1);
this.session.setActive(false);
assertTrue(this.session.getTimeSinceLastActive() > 0);
this.session.setActive(true);
assertEquals(0, this.session.getTimeSinceLastActive());
}
@Test
public void delegateConnectionEstablished() throws Exception {
assertNew();
this.session.delegateConnectionEstablished();
assertOpen();
verify(this.webSocketHandler).afterConnectionEstablished(this.session);
}
@Test
public void delegateError() throws Exception {
Exception ex = new Exception();
this.session.delegateError(ex);
verify(this.webSocketHandler).handleTransportError(this.session, ex);
}
@Test
public void delegateMessages() throws Exception {
String msg1 = "message 1";
String msg2 = "message 2";
this.session.delegateMessages(new String[] { msg1, msg2 });
verify(this.webSocketHandler).handleMessage(this.session, new TextMessage(msg1));
verify(this.webSocketHandler).handleMessage(this.session, new TextMessage(msg2));
verifyNoMoreInteractions(this.webSocketHandler);
}
@Test
public void delegateConnectionClosed() throws Exception {
this.session.delegateConnectionEstablished();
this.session.delegateConnectionClosed(CloseStatus.GOING_AWAY);
assertClosed();
assertEquals(1, this.session.getNumberOfLastActiveTimeUpdates());
assertTrue(this.session.didCancelHeartbeat());
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.GOING_AWAY);
}
@Test
public void closeWhenNotOpen() throws Exception {
assertNew();
this.session.close();
assertNull("Close not ignored for a new session", this.session.getStatus());
this.session.delegateConnectionEstablished();
assertOpen();
this.session.close();
assertClosed();
assertEquals(3000, this.session.getStatus().getCode());
this.session.close(CloseStatus.SERVER_ERROR);
assertEquals("Close should be ignored if already closed", 3000, this.session.getStatus().getCode());
}
@Test
public void closeWhenNotActive() throws Exception {
this.session.delegateConnectionEstablished();
assertOpen();
this.session.setActive(false);
this.session.close();
assertEquals(Collections.emptyList(), this.session.getSockJsFramesWritten());
}
@Test
public void close() throws Exception {
this.session.delegateConnectionEstablished();
assertOpen();
this.session.setActive(true);
this.session.close();
assertEquals(1, this.session.getSockJsFramesWritten().size());
assertEquals(SockJsFrame.closeFrameGoAway(), this.session.getSockJsFramesWritten().get(0));
assertEquals(1, this.session.getNumberOfLastActiveTimeUpdates());
assertTrue(this.session.didCancelHeartbeat());
assertEquals(new CloseStatus(3000, "Go away!"), this.session.getStatus());
assertClosed();
verify(this.webSocketHandler).afterConnectionClosed(this.session, new CloseStatus(3000, "Go away!"));
}
@Test
public void closeWithWriteFrameExceptions() throws Exception {
this.session.setExceptionOnWriteFrame(new IOException());
this.session.delegateConnectionEstablished();
this.session.setActive(true);
this.session.close();
assertEquals(new CloseStatus(3000, "Go away!"), this.session.getStatus());
assertClosed();
}
@Test
public void closeWithWebSocketHandlerExceptions() throws Exception {
doThrow(new Exception()).when(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.NORMAL);
this.session.delegateConnectionEstablished();
this.session.setActive(true);
this.session.close(CloseStatus.NORMAL);
assertEquals(CloseStatus.NORMAL, this.session.getStatus());
assertClosed();
}
@Test
public void writeFrame() throws Exception {
this.session.writeFrame(SockJsFrame.openFrame());
assertEquals(1, this.session.getSockJsFramesWritten().size());
assertEquals(SockJsFrame.openFrame(), this.session.getSockJsFramesWritten().get(0));
}
@Test
public void writeFrameIoException() throws Exception {
this.session.setExceptionOnWriteFrame(new IOException());
this.session.delegateConnectionEstablished();
try {
this.session.writeFrame(SockJsFrame.openFrame());
fail("expected exception");
}
catch (IOException ex) {
assertEquals(CloseStatus.SERVER_ERROR, this.session.getStatus());
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.SERVER_ERROR);
}
}
@Test
public void writeFrameThrowable() throws Exception {
this.session.setExceptionOnWriteFrame(new NullPointerException());
this.session.delegateConnectionEstablished();
try {
this.session.writeFrame(SockJsFrame.openFrame());
fail("expected exception");
}
catch (SockJsRuntimeException ex) {
assertEquals(CloseStatus.SERVER_ERROR, this.session.getStatus());
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.SERVER_ERROR);
}
}
@Test
public void sendHeartbeatWhenNotActive() throws Exception {
this.session.setActive(false);
this.session.sendHeartbeat();
assertEquals(Collections.emptyList(), this.session.getSockJsFramesWritten());
}
@Test
public void sendHeartbeat() throws Exception {
this.session.setActive(true);
this.session.sendHeartbeat();
assertEquals(1, this.session.getSockJsFramesWritten().size());
assertEquals(SockJsFrame.heartbeatFrame(), this.session.getSockJsFramesWritten().get(0));
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class));
verifyNoMoreInteractions(this.taskScheduler);
}
@Test
public void scheduleHeartbeatNotActive() throws Exception {
this.session.setActive(false);
this.session.scheduleHeartbeat();
verifyNoMoreInteractions(this.taskScheduler);
}
@Test
public void scheduleAndCancelHeartbeat() throws Exception {
ScheduledFuture<?> task = mock(ScheduledFuture.class);
doReturn(task).when(this.taskScheduler).schedule(any(Runnable.class), any(Date.class));
this.session.setActive(true);
this.session.scheduleHeartbeat();
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class));
verifyNoMoreInteractions(this.taskScheduler);
doReturn(false).when(task).isDone();
this.session.cancelHeartbeat();
verify(task).isDone();
verify(task).cancel(false);
verifyNoMoreInteractions(task);
}
}

74
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/BaseAbstractSockJsSessionTests.java

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
import org.junit.Before;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.WebSocketHandler;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Base class for {@link AbstractSockJsSession} classes.
*
* @author Rossen Stoyanchev
*/
public abstract class BaseAbstractSockJsSessionTests<S extends AbstractSockJsSession> {
protected WebSocketHandler webSocketHandler;
protected StubSockJsConfig sockJsConfig;
protected TaskScheduler taskScheduler;
protected S session;
@Before
public void setUp() {
this.webSocketHandler = mock(WebSocketHandler.class);
this.taskScheduler = mock(TaskScheduler.class);
this.sockJsConfig = new StubSockJsConfig();
this.sockJsConfig.setTaskScheduler(this.taskScheduler);
this.session = initSockJsSession();
}
protected abstract S initSockJsSession();
protected void assertNew() {
assertState(true, false, false);
}
protected void assertOpen() {
assertState(false, true, false);
}
protected void assertClosed() {
assertState(false, false, true);
}
private void assertState(boolean isNew, boolean isOpen, boolean isClosed) {
assertEquals(isNew, this.session.isNew());
assertEquals(isOpen, this.session.isOpen());
assertEquals(isClosed, this.session.isClosed());
}
}

107
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/TestSockJsSession.java

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
/**
* @author Rossen Stoyanchev
*/
public class TestSockJsSession extends AbstractSockJsSession {
private boolean active;
private final List<SockJsFrame> sockJsFramesWritten = new ArrayList<>();
private CloseStatus status;
private Exception exceptionOnWriteFrame;
private int numberOfLastActiveTimeUpdates;
private boolean cancelledHeartbeat;
public TestSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
super(sessionId, config, handler);
}
public CloseStatus getStatus() {
return this.status;
}
@Override
public boolean isActive() {
return this.active;
}
public void setActive(boolean active) {
this.active = active;
}
public List<SockJsFrame> getSockJsFramesWritten() {
return this.sockJsFramesWritten;
}
public void setExceptionOnWriteFrame(Exception exceptionOnWriteFrame) {
this.exceptionOnWriteFrame = exceptionOnWriteFrame;
}
public int getNumberOfLastActiveTimeUpdates() {
return this.numberOfLastActiveTimeUpdates;
}
public boolean didCancelHeartbeat() {
return this.cancelledHeartbeat;
}
@Override
protected void updateLastActiveTime() {
this.numberOfLastActiveTimeUpdates++;
super.updateLastActiveTime();
}
@Override
protected void cancelHeartbeat() {
this.cancelledHeartbeat = true;
super.cancelHeartbeat();
}
@Override
protected void sendMessageInternal(String message) throws IOException {
}
@Override
protected void writeFrameInternal(SockJsFrame frame) throws Exception {
this.sockJsFramesWritten.add(frame);
if (this.exceptionOnWriteFrame != null) {
throw exceptionOnWriteFrame;
}
}
@Override
protected void disconnect(CloseStatus status) throws IOException {
this.status = status;
}
}

146
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/support/DefaultSockJsServiceTests.java

@ -16,17 +16,30 @@ @@ -16,17 +16,30 @@
package org.springframework.web.socket.sockjs.support;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.StubSockJsConfig;
import org.springframework.web.socket.sockjs.TestSockJsSession;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link DefaultSockJsService}.
@ -35,20 +48,17 @@ import static org.junit.Assert.*; @@ -35,20 +48,17 @@ import static org.junit.Assert.*;
*/
public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
private DefaultSockJsService service;
@Override
@Before
public void setUp() {
super.setUp();
this.service = new DefaultSockJsService(new ThreadPoolTaskScheduler());
this.service.setValidSockJsPrefixes("/echo");
}
@Test
public void defaultTransportHandlers() {
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class));
Map<TransportType, TransportHandler> handlers = service.getTransportHandlers();
assertEquals(8, handlers.size());
@ -62,4 +72,128 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests { @@ -62,4 +72,128 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
assertNotNull(handlers.get(TransportType.EVENT_SOURCE));
}
@Test
public void handleTransportRequestXhr() throws Exception {
setRequest("POST", "/a/server/session/xhr");
TaskScheduler taskScheduler = mock(TaskScheduler.class);
StubXhrTransportHandler xhrHandler = new StubXhrTransportHandler();
Set<TransportHandler> transportHandlers = Collections.<TransportHandler>singleton(xhrHandler);
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
DefaultSockJsService service = new DefaultSockJsService(taskScheduler, transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, webSocketHandler);
assertEquals(200, this.servletResponse.getStatus());
assertNotNull(xhrHandler.session);
assertSame(webSocketHandler, xhrHandler.webSocketHandler);
verify(taskScheduler).scheduleAtFixedRate(any(Runnable.class), eq(service.getDisconnectDelay()));
assertEquals("no-store, no-cache, must-revalidate, max-age=0", this.response.getHeaders().getCacheControl());
assertEquals("JSESSIONID=dummy;path=/", this.response.getHeaders().getFirst("Set-Cookie"));
assertEquals("*", this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
assertEquals("true", this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
}
@Test
public void handleTransportRequestXhrOptions() throws Exception {
setRequest("OPTIONS", "/a/server/session/xhr");
TaskScheduler taskScheduler = mock(TaskScheduler.class);
StubXhrTransportHandler xhrHandler = new StubXhrTransportHandler();
Set<TransportHandler> transportHandlers = Collections.<TransportHandler>singleton(xhrHandler);
DefaultSockJsService service = new DefaultSockJsService(taskScheduler, transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, null);
assertEquals(204, this.servletResponse.getStatus());
assertEquals("*", this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
assertEquals("true", this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
assertEquals("OPTIONS, POST", this.response.getHeaders().getFirst("Access-Control-Allow-Methods"));
}
@Test
public void handleTransportRequestNoSuitableHandler() throws Exception {
setRequest("POST", "/a/server/session/xhr");
Set<TransportHandler> transportHandlers = new HashSet<>();
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, null);
assertEquals(404, this.servletResponse.getStatus());
}
@Test
public void handleTransportRequestXhrSend() throws Exception {
this.servletRequest.setMethod("POST");
Set<TransportHandler> transportHandlers = new HashSet<>();
transportHandlers.add(new StubXhrTransportHandler());
transportHandlers.add(new StubXhrSendTransportHandler());
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR_SEND, webSocketHandler);
assertEquals(404, this.servletResponse.getStatus()); // dropped (no session)
resetResponse();
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, webSocketHandler);
assertEquals(200, this.servletResponse.getStatus());
resetResponse();
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR_SEND, webSocketHandler);
assertEquals(200, this.servletResponse.getStatus());
}
private static class StubXhrTransportHandler implements TransportHandler, SockJsSessionFactory {
WebSocketHandler webSocketHandler;
AbstractSockJsSession session;
@Override
public TransportType getTransportType() {
return TransportType.XHR;
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, AbstractSockJsSession session) throws TransportErrorException {
this.webSocketHandler = handler;
this.session = session;
}
@Override
public AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
return new TestSockJsSession(sessionId, new StubSockJsConfig(), webSocketHandler);
}
}
private static class StubXhrSendTransportHandler implements TransportHandler {
@Override
public TransportType getTransportType() {
return TransportType.XHR_SEND;
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, AbstractSockJsSession session) throws TransportErrorException {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);
}
}
}
}

176
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/AbstractHttpSockJsSessionTests.java

@ -0,0 +1,176 @@ @@ -0,0 +1,176 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.server.AsyncServletServerHttpRequest;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.BaseAbstractSockJsSessionTests;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.AbstractHttpSockJsSessionTests.TestAbstractHttpSockJsSession;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link AbstractHttpSockJsSession}.
*
* @author Rossen Stoyanchev
*/
public class AbstractHttpSockJsSessionTests extends BaseAbstractSockJsSessionTests<TestAbstractHttpSockJsSession> {
protected ServerHttpRequest request;
protected ServerHttpResponse response;
protected MockHttpServletRequest servletRequest;
protected MockHttpServletResponse servletResponse;
private FrameFormat frameFormat;
@Before
public void setup() {
super.setUp();
this.frameFormat = new DefaultFrameFormat("%s");
this.servletResponse = new MockHttpServletResponse();
this.response = new ServletServerHttpResponse(this.servletResponse);
this.servletRequest = new MockHttpServletRequest();
this.servletRequest.setAsyncSupported(true);
this.request = new AsyncServletServerHttpRequest(this.servletRequest, this.servletResponse);
}
@Override
protected TestAbstractHttpSockJsSession initSockJsSession() {
return new TestAbstractHttpSockJsSession(this.sockJsConfig, this.webSocketHandler);
}
@Test
public void setInitialRequest() throws Exception {
this.session.setInitialRequest(this.request, this.response, this.frameFormat);
assertTrue(this.session.hasRequest());
assertTrue(this.session.hasResponse());
assertEquals("o", this.servletResponse.getContentAsString());
assertFalse(this.servletRequest.isAsyncStarted());
verify(this.webSocketHandler).afterConnectionEstablished(this.session);
}
@Test
public void setLongPollingRequest() throws Exception {
this.session.getMessageCache().add("x");
this.session.setLongPollingRequest(this.request, this.response, this.frameFormat);
assertTrue(this.session.hasRequest());
assertTrue(this.session.hasResponse());
assertTrue(this.servletRequest.isAsyncStarted());
assertTrue(this.session.wasHeartbeatScheduled());
assertTrue(this.session.wasCacheFlushed());
verifyNoMoreInteractions(this.webSocketHandler);
}
@Test
public void setLongPollingRequestWhenClosed() throws Exception {
this.session.delegateConnectionClosed(CloseStatus.NORMAL);
assertClosed();
this.session.setLongPollingRequest(this.request, this.response, this.frameFormat);
assertEquals("c[3000,\"Go away!\"]", this.servletResponse.getContentAsString());
assertFalse(this.servletRequest.isAsyncStarted());
}
static class TestAbstractHttpSockJsSession extends AbstractHttpSockJsSession {
private IOException exceptionOnWriteFrame;
private boolean cacheFlushed;
private boolean heartbeatScheduled;
public TestAbstractHttpSockJsSession(SockJsConfiguration config, WebSocketHandler handler) {
super("1", config, handler);
}
public boolean wasCacheFlushed() {
return this.cacheFlushed;
}
public boolean wasHeartbeatScheduled() {
return this.heartbeatScheduled;
}
public boolean hasRequest() {
return getRequest() != null;
}
public boolean hasResponse() {
return getResponse() != null;
}
public void setExceptionOnWriteFrame(IOException exceptionOnWriteFrame) {
this.exceptionOnWriteFrame = exceptionOnWriteFrame;
}
@Override
protected void flushCache() throws IOException {
this.cacheFlushed = true;
}
@Override
protected void scheduleHeartbeat() {
this.heartbeatScheduled = true;
}
@Override
protected synchronized void writeFrameInternal(SockJsFrame frame) throws IOException {
if (this.exceptionOnWriteFrame != null) {
throw this.exceptionOnWriteFrame;
}
else {
super.writeFrameInternal(frame);
}
}
}
}

139
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/HttpReceivingTransportHandlerTests.java

@ -0,0 +1,139 @@ @@ -0,0 +1,139 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.StubSockJsConfig;
import org.springframework.web.socket.sockjs.TestSockJsSession;
import org.springframework.web.socket.sockjs.TransportErrorException;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link AbstractHttpReceivingTransportHandler} and sub-classes
* {@link XhrTransportHandler} and {@link JsonpTransportHandler}.
*
* @author Rossen Stoyanchev
*/
public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTests {
@Override
@Before
public void setUp() {
super.setUp();
}
@Test
public void readMessagesXhr() throws Exception {
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8"));
handleRequest(new XhrTransportHandler());
assertEquals(204, this.servletResponse.getStatus());
}
@Test
public void readMessagesJsonp() throws Exception {
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8"));
handleRequest(new JsonpTransportHandler());
assertEquals(200, this.servletResponse.getStatus());
assertEquals("ok", this.servletResponse.getContentAsString());
}
@Test
public void readMessagesJsonpFormEncoded() throws Exception {
this.servletRequest.setContent("d=[\"x\"]".getBytes("UTF-8"));
this.servletRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
handleRequest(new JsonpTransportHandler());
assertEquals(200, this.servletResponse.getStatus());
assertEquals("ok", this.servletResponse.getContentAsString());
}
@Test
public void readMessagesBadContent() throws Exception {
this.servletRequest.setContent("".getBytes("UTF-8"));
handleRequestAndExpectFailure();
this.servletRequest.setContent("[\"x]".getBytes("UTF-8"));
handleRequestAndExpectFailure();
}
@Test
public void readMessagesNoSession() throws Exception {
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, null);
assertEquals(404, this.servletResponse.getStatus());
}
@Test
public void delegateMessageException() throws Exception {
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8"));
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
TestSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler);
session.delegateConnectionEstablished();
doThrow(new Exception()).when(webSocketHandler).handleMessage(session, new TextMessage("x"));
try {
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, session);
fail("Expected exception");
}
catch (TransportErrorException ex) {
assertEquals(CloseStatus.SERVER_ERROR, session.getStatus());
}
}
private void handleRequest(AbstractHttpReceivingTransportHandler transportHandler)
throws Exception {
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler);
transportHandler.handleRequest(this.request, this.response, webSocketHandler, session);
assertEquals("text/plain;charset=UTF-8", this.response.getHeaders().getContentType().toString());
verify(webSocketHandler).handleMessage(session, new TextMessage("x"));
}
private void handleRequestAndExpectFailure() throws Exception {
resetResponse();
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler);
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, session);
assertEquals(500, this.servletResponse.getStatus());
verifyNoMoreInteractions(webSocketHandler);
}
}

186
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/HttpSendingTransportHandlerTests.java

@ -0,0 +1,186 @@ @@ -0,0 +1,186 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
import java.sql.Date;
import org.junit.Before;
import org.junit.Test;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.StubSockJsConfig;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link AbstractHttpSendingTransportHandler} and sub-classes.
*
* @author Rossen Stoyanchev
*/
public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests {
private WebSocketHandler webSocketHandler;
private StubSockJsConfig sockJsConfig;
private TaskScheduler taskScheduler;
@Override
@Before
public void setUp() {
super.setUp();
this.webSocketHandler = mock(WebSocketHandler.class);
this.taskScheduler = mock(TaskScheduler.class);
this.sockJsConfig = new StubSockJsConfig();
this.sockJsConfig.setTaskScheduler(this.taskScheduler);
}
@Test
public void handleRequestXhr() throws Exception {
XhrPollingTransportHandler transportHandler = new XhrPollingTransportHandler();
transportHandler.setSockJsConfiguration(sockJsConfig);
AbstractSockJsSession session = transportHandler.createSession("1", webSocketHandler);
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString());
assertEquals("o\n", this.servletResponse.getContentAsString());
assertFalse("Polling request should complete after open frame", this.servletRequest.isAsyncStarted());
verify(webSocketHandler).afterConnectionEstablished(session);
resetResponse();
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertTrue("Polling request should remain open", this.servletRequest.isAsyncStarted());
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class));
resetRequestAndResponse();
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertFalse("Request should have been rejected", this.servletRequest.isAsyncStarted());
assertEquals("c[2010,\"Another connection still open\"]\n", this.servletResponse.getContentAsString());
}
@Test
public void jsonpTransport() throws Exception {
JsonpPollingTransportHandler transportHandler = new JsonpPollingTransportHandler();
transportHandler.setSockJsConfiguration(sockJsConfig);
PollingSockJsSession session = transportHandler.createSession("1", webSocketHandler);
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals(500, this.servletResponse.getStatus());
assertEquals("\"callback\" parameter required", this.servletResponse.getContentAsString());
resetRequestAndResponse();
this.servletRequest.addParameter("c", "callback");
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString());
assertFalse("Polling request should complete after open frame", this.servletRequest.isAsyncStarted());
verify(webSocketHandler).afterConnectionEstablished(session);
}
@Test
public void handleRequestXhrStreaming() throws Exception {
XhrStreamingTransportHandler transportHandler = new XhrStreamingTransportHandler();
transportHandler.setSockJsConfiguration(sockJsConfig);
AbstractSockJsSession session = transportHandler.createSession("1", webSocketHandler);
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString());
assertTrue("Streaming request not started", this.servletRequest.isAsyncStarted());
verify(webSocketHandler).afterConnectionEstablished(session);
}
@Test
public void htmlFileTransport() throws Exception {
HtmlFileTransportHandler transportHandler = new HtmlFileTransportHandler();
transportHandler.setSockJsConfiguration(sockJsConfig);
StreamingSockJsSession session = transportHandler.createSession("1", webSocketHandler);
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals(500, this.servletResponse.getStatus());
assertEquals("\"callback\" parameter required", this.servletResponse.getContentAsString());
resetRequestAndResponse();
this.servletRequest.addParameter("c", "callback");
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals("text/html;charset=UTF-8", this.response.getHeaders().getContentType().toString());
assertTrue("Streaming request not started", this.servletRequest.isAsyncStarted());
verify(webSocketHandler).afterConnectionEstablished(session);
}
@Test
public void eventSourceTransport() throws Exception {
EventSourceTransportHandler transportHandler = new EventSourceTransportHandler();
transportHandler.setSockJsConfiguration(sockJsConfig);
StreamingSockJsSession session = transportHandler.createSession("1", webSocketHandler);
transportHandler.handleRequest(request, response, webSocketHandler, session);
assertEquals("text/event-stream;charset=UTF-8", this.response.getHeaders().getContentType().toString());
assertTrue("Streaming request not started", this.servletRequest.isAsyncStarted());
verify(webSocketHandler).afterConnectionEstablished(session);
}
@Test
public void frameFormats() throws Exception {
this.servletRequest.addParameter("c", "callback");
SockJsFrame frame = SockJsFrame.openFrame();
FrameFormat format = new XhrPollingTransportHandler().getFrameFormat(request);
SockJsFrame formatted = format.format(frame);
assertEquals(frame.getContent() + "\n", formatted.getContent());
format = new XhrStreamingTransportHandler().getFrameFormat(request);
formatted = format.format(frame);
assertEquals(frame.getContent() + "\n", formatted.getContent());
format = new HtmlFileTransportHandler().getFrameFormat(request);
formatted = format.format(frame);
assertEquals("<script>\np(\"" + frame.getContent() + "\");\n</script>\r\n", formatted.getContent());
format = new EventSourceTransportHandler().getFrameFormat(request);
formatted = format.format(frame);
assertEquals("data: " + frame.getContent() + "\r\n\r\n", formatted.getContent());
format = new JsonpPollingTransportHandler().getFrameFormat(request);
formatted = format.format(frame);
assertEquals("callback(\"" + frame.getContent() + "\");\r\n", formatted.getContent());
}
}

153
spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/WebSocketServerSockJsSessionTests.java

@ -0,0 +1,153 @@ @@ -0,0 +1,153 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.BaseAbstractSockJsSessionTests;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.transport.WebSocketServerSockJsSessionTests.TestWebSocketServerSockJsSession;
import org.springframework.web.socket.support.TestWebSocketSession;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link WebSocketServerSockJsSession}.
*
* @author Rossen Stoyanchev
*/
public class WebSocketServerSockJsSessionTests extends BaseAbstractSockJsSessionTests<TestWebSocketServerSockJsSession> {
private TestWebSocketSession webSocketSession;
@Before
public void setup() {
super.setUp();
this.webSocketSession = new TestWebSocketSession();
this.webSocketSession.setOpen(true);
}
@Override
protected TestWebSocketServerSockJsSession initSockJsSession() {
return new TestWebSocketServerSockJsSession(this.sockJsConfig, this.webSocketHandler);
}
@Test
public void isActive() throws Exception {
assertFalse(this.session.isActive());
this.session.initWebSocketSession(this.webSocketSession);
assertTrue(this.session.isActive());
this.webSocketSession.setOpen(false);
assertFalse(this.session.isActive());
}
@Test
public void initWebSocketSession() throws Exception {
this.session.initWebSocketSession(this.webSocketSession);
assertEquals("Open frame not sent",
Collections.singletonList(new TextMessage("o")), this.webSocketSession.getSentMessages());
assertEquals(Arrays.asList("schedule"), this.session.heartbeatSchedulingEvents);
verify(this.webSocketHandler).afterConnectionEstablished(this.session);
verifyNoMoreInteractions(this.taskScheduler, this.webSocketHandler);
}
@Test
public void handleMessageEmptyPayload() throws Exception {
this.session.handleMessage(new TextMessage(""), this.webSocketSession);
verifyNoMoreInteractions(this.webSocketHandler);
}
@Test
public void handleMessage() throws Exception {
TextMessage message = new TextMessage("[\"x\"]");
this.session.handleMessage(message, this.webSocketSession);
verify(this.webSocketHandler).handleMessage(this.session, new TextMessage("x"));
verifyNoMoreInteractions(this.webSocketHandler);
}
@Test
public void handleMessageBadData() throws Exception {
TextMessage message = new TextMessage("[\"x]");
this.session.handleMessage(message, this.webSocketSession);
this.session.isClosed();
verify(this.webSocketHandler).handleTransportError(same(this.session), any(IOException.class));
verifyNoMoreInteractions(this.webSocketHandler);
}
@Test
public void sendMessageInternal() throws Exception {
this.session.initWebSocketSession(this.webSocketSession);
this.session.sendMessageInternal("x");
assertEquals(Arrays.asList(new TextMessage("o"), new TextMessage("a[\"x\"]")),
this.webSocketSession.getSentMessages());
assertEquals(Arrays.asList("schedule", "cancel", "schedule"), this.session.heartbeatSchedulingEvents);
}
@Test
public void disconnect() throws Exception {
this.session.initWebSocketSession(this.webSocketSession);
this.session.close(CloseStatus.NOT_ACCEPTABLE);
assertEquals(CloseStatus.NOT_ACCEPTABLE, this.webSocketSession.getCloseStatus());
}
static class TestWebSocketServerSockJsSession extends WebSocketServerSockJsSession {
private final List<String> heartbeatSchedulingEvents = new ArrayList<>();
public TestWebSocketServerSockJsSession(SockJsConfiguration config, WebSocketHandler handler) {
super("1", config, handler);
}
@Override
protected void scheduleHeartbeat() {
this.heartbeatSchedulingEvents.add("schedule");
}
@Override
protected void cancelHeartbeat() {
this.heartbeatSchedulingEvents.add("cancel");
}
}
}

96
spring-websocket/src/test/java/org/springframework/web/socket/support/BeanCreatingHandlerProviderTests.java

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.support;
import org.junit.Test;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.*;
/**
* Test fixture for {@link BeanCreatingHandlerProvider}.
*
* @author Rossen Stoyanchev
*/
public class BeanCreatingHandlerProviderTests {
@Test
public void getHandlerSimpleInstantiation() {
BeanCreatingHandlerProvider<SimpleEchoHandler> provider =
new BeanCreatingHandlerProvider<SimpleEchoHandler>(SimpleEchoHandler.class);
assertNotNull(provider.getHandler());
}
@Test
public void getHandlerWithBeanFactory() {
@SuppressWarnings("resource")
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
BeanCreatingHandlerProvider<EchoHandler> provider =
new BeanCreatingHandlerProvider<EchoHandler>(EchoHandler.class);
provider.setBeanFactory(context.getBeanFactory());
assertNotNull(provider.getHandler());
}
@Test(expected=BeanInstantiationException.class)
public void getHandlerNoBeanFactory() {
BeanCreatingHandlerProvider<EchoHandler> provider =
new BeanCreatingHandlerProvider<EchoHandler>(EchoHandler.class);
provider.getHandler();
}
@Configuration
static class Config {
@Bean
public EchoService echoService() {
return new EchoService();
}
}
public static class SimpleEchoHandler {
}
private static class EchoHandler {
@SuppressWarnings("unused")
private final EchoService service;
@Autowired
public EchoHandler(EchoService service) {
this.service = service;
}
}
private static class EchoService { }
}

102
spring-websocket/src/test/java/org/springframework/web/socket/support/ExceptionWebSocketHandlerDecoratorTests.java

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.support;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link ExceptionWebSocketHandlerDecorator}.
*
* @author Rossen Stoyanchev
*/
public class ExceptionWebSocketHandlerDecoratorTests {
private TestWebSocketSession session;
private ExceptionWebSocketHandlerDecorator decorator;
private WebSocketHandler delegate;
@Before
public void setup() {
this.delegate = mock(WebSocketHandler.class);
this.decorator = new ExceptionWebSocketHandlerDecorator(this.delegate);
this.session = new TestWebSocketSession();
this.session.setOpen(true);
}
@Test
public void afterConnectionEstablished() throws Exception {
doThrow(new IllegalStateException("error"))
.when(this.delegate).afterConnectionEstablished(this.session);
this.decorator.afterConnectionEstablished(this.session);
assertEquals(CloseStatus.SERVER_ERROR, this.session.getCloseStatus());
}
@Test
public void handleMessage() throws Exception {
TextMessage message = new TextMessage("payload");
doThrow(new IllegalStateException("error"))
.when(this.delegate).handleMessage(this.session, message);
this.decorator.handleMessage(this.session, message);
assertEquals(CloseStatus.SERVER_ERROR, this.session.getCloseStatus());
}
@Test
public void handleTransportError() throws Exception {
Exception exception = new Exception("transport error");
doThrow(new IllegalStateException("error"))
.when(this.delegate).handleTransportError(this.session, exception);
this.decorator.handleTransportError(this.session, exception);
assertEquals(CloseStatus.SERVER_ERROR, this.session.getCloseStatus());
}
@Test
public void afterConnectionClosed() throws Exception {
CloseStatus closeStatus = CloseStatus.NORMAL;
doThrow(new IllegalStateException("error"))
.when(this.delegate).afterConnectionClosed(this.session, closeStatus);
this.decorator.afterConnectionClosed(this.session, closeStatus);
assertNull(this.session.getCloseStatus());
}
}

82
spring-websocket/src/test/java/org/springframework/web/socket/support/PerConnectionWebSocketHandlerTests.java

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.support;
import org.junit.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.WebSocketHandlerAdapter;
import static org.junit.Assert.*;
/**
* Test fixture for {@link PerConnectionWebSocketHandler}.
*
* @author Rossen Stoyanchev
*/
public class PerConnectionWebSocketHandlerTests {
@Test
public void afterConnectionEstablished() throws Exception {
@SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
EchoHandler.reset();
PerConnectionWebSocketHandler handler = new PerConnectionWebSocketHandler(EchoHandler.class);
handler.setBeanFactory(context.getBeanFactory());
WebSocketSession session = new TestWebSocketSession();
handler.afterConnectionEstablished(session);
assertEquals(1, EchoHandler.initCount);
assertEquals(0, EchoHandler.destroyCount);
handler.afterConnectionClosed(session, CloseStatus.NORMAL);
assertEquals(1, EchoHandler.initCount);
assertEquals(1, EchoHandler.destroyCount);
}
public static class EchoHandler extends WebSocketHandlerAdapter implements DisposableBean {
private static int initCount;
private static int destroyCount;
public EchoHandler() {
initCount++;
}
@Override
public void destroy() throws Exception {
destroyCount++;
}
public static void reset() {
initCount = 0;
destroyCount = 0;
}
}
}

185
spring-websocket/src/test/java/org/springframework/web/socket/support/TestWebSocketSession.java

@ -0,0 +1,185 @@ @@ -0,0 +1,185 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.support;
import java.io.IOException;
import java.net.URI;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
* A {@link WebSocketSession} for use in tests.
*
* @author Rossen Stoyanchev
*/
public class TestWebSocketSession implements WebSocketSession {
private String id;
private URI uri;
private boolean secure;
private Principal principal;
private String remoteHostName;
private String remoteAddress;
private boolean open;
private final List<WebSocketMessage<?>> messages = new ArrayList<>();
private CloseStatus status;
/**
* @return the id
*/
@Override
public String getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* @return the uri
*/
@Override
public URI getUri() {
return uri;
}
/**
* @param uri the uri to set
*/
public void setUri(URI uri) {
this.uri = uri;
}
/**
* @return the secure
*/
@Override
public boolean isSecure() {
return secure;
}
/**
* @param secure the secure to set
*/
public void setSecure(boolean secure) {
this.secure = secure;
}
/**
* @return the principal
*/
@Override
public Principal getPrincipal() {
return principal;
}
/**
* @param principal the principal to set
*/
public void setPrincipal(Principal principal) {
this.principal = principal;
}
/**
* @return the remoteHostName
*/
@Override
public String getRemoteHostName() {
return remoteHostName;
}
/**
* @param remoteHostName the remoteHostName to set
*/
public void setRemoteHostName(String remoteHostName) {
this.remoteHostName = remoteHostName;
}
/**
* @return the remoteAddress
*/
@Override
public String getRemoteAddress() {
return remoteAddress;
}
/**
* @param remoteAddress the remoteAddress to set
*/
public void setRemoteAddress(String remoteAddress) {
this.remoteAddress = remoteAddress;
}
/**
* @return the open
*/
@Override
public boolean isOpen() {
return open;
}
/**
* @param open the open to set
*/
public void setOpen(boolean open) {
this.open = open;
}
public List<WebSocketMessage<?>> getSentMessages() {
return this.messages;
}
public CloseStatus getCloseStatus() {
return this.status;
}
@Override
public void sendMessage(WebSocketMessage<?> message) throws IOException {
this.messages.add(message);
}
@Override
public void close() throws IOException {
this.open = false;
}
@Override
public void close(CloseStatus status) throws IOException {
this.open = false;
this.status = status;
}
}
Loading…
Cancel
Save