Add spring-websocket module tests
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 { }
|
||||
|
||||
}
|
||||
@@ -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 { }
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
@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);
|
||||
handleRequest("GET", "/a/", HttpStatus.OK);
|
||||
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
|
||||
|
||||
this.service.setValidSockJsPrefixes("/b");
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSockJsPathForInfoRequest() throws Exception {
|
||||
|
||||
handleRequest("GET", "/a/info", HttpStatus.OK);
|
||||
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
|
||||
|
||||
handleRequest("/echo/", HttpStatus.OK);
|
||||
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
|
||||
this.service.setValidSockJsPrefixes("/b");
|
||||
|
||||
handleRequest("/echo/iframe.html", HttpStatus.OK);
|
||||
assertTrue(this.servletResponse.getContentAsString().startsWith("<!DOCTYPE html>\n"));
|
||||
handleRequest("GET", "/a/info", HttpStatus.NOT_FOUND);
|
||||
|
||||
handleRequest("/echo/websocket", HttpStatus.OK);
|
||||
assertNull(this.service.sessionId);
|
||||
assertSame(this.handler, this.service.handler);
|
||||
handleRequest("GET", "/b/info", HttpStatus.OK);
|
||||
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
|
||||
}
|
||||
|
||||
handleRequest("/echo/server1/session2/xhr", HttpStatus.OK);
|
||||
assertEquals("session2", this.service.sessionId);
|
||||
@Test
|
||||
public void getSockJsPathForTransportRequest() throws Exception {
|
||||
|
||||
// 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/other", HttpStatus.NOT_FOUND);
|
||||
handleRequest("/echo//", HttpStatus.NOT_FOUND);
|
||||
handleRequest("/echo///", HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getSockJsPathGreetingRequest() throws Exception {
|
||||
handleRequest("/echo", HttpStatus.OK);
|
||||
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSockJsPathInfoRequest() throws Exception {
|
||||
handleRequest("/echo/info", HttpStatus.OK);
|
||||
assertTrue(this.servletResponse.getContentAsString().startsWith("{\"entropy\":"));
|
||||
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 getSockJsPathWithConfiguredPrefix() throws Exception {
|
||||
public void validateRequest() throws Exception {
|
||||
|
||||
this.service.setValidSockJsPrefixes("/echo");
|
||||
handleRequest("/echo/s1/s2/xhr", HttpStatus.OK);
|
||||
|
||||
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 getInfoOptions() throws Exception {
|
||||
setRequest("OPTIONS", "/echo/info");
|
||||
this.service.handleRequest(this.request, this.response, this.handler);
|
||||
public void handleInfoGet() throws Exception {
|
||||
|
||||
assertEquals(204, servletResponse.getStatus());
|
||||
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 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 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 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 handleRawWebSocketRequest() throws Exception {
|
||||
|
||||
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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.*;
|
||||
*/
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 { }
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user