Rossen Stoyanchev
12 years ago
52 changed files with 2927 additions and 387 deletions
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
package org.springframework.web.socket.server.endpoint; |
||||
|
||||
|
||||
public class Test { |
||||
|
||||
} |
@ -1,162 +0,0 @@
@@ -1,162 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs; |
||||
|
||||
import java.io.EOFException; |
||||
import java.io.IOException; |
||||
import java.net.SocketException; |
||||
import java.util.Date; |
||||
import java.util.concurrent.ScheduledFuture; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.TextMessage; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.WebSocketMessage; |
||||
|
||||
/** |
||||
* Provides partial implementations of {@link SockJsSession} methods to send messages, |
||||
* including heartbeat messages and to manage session state. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.0 |
||||
*/ |
||||
public abstract class AbstractServerSockJsSession extends AbstractSockJsSession { |
||||
|
||||
private final SockJsConfiguration sockJsConfig; |
||||
|
||||
private ScheduledFuture<?> heartbeatTask; |
||||
|
||||
|
||||
public AbstractServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) { |
||||
super(sessionId, handler); |
||||
this.sockJsConfig = config; |
||||
} |
||||
|
||||
protected SockJsConfiguration getSockJsConfig() { |
||||
return this.sockJsConfig; |
||||
} |
||||
|
||||
@Override |
||||
public final synchronized void sendMessage(WebSocketMessage message) throws IOException { |
||||
Assert.isTrue(!isClosed(), "Cannot send a message when session is closed"); |
||||
Assert.isInstanceOf(TextMessage.class, message, "Expected text message: " + message); |
||||
sendMessageInternal(((TextMessage) message).getPayload()); |
||||
} |
||||
|
||||
protected abstract void sendMessageInternal(String message) throws IOException; |
||||
|
||||
|
||||
@Override |
||||
public void connectionClosedInternal(CloseStatus status) { |
||||
updateLastActiveTime(); |
||||
cancelHeartbeat(); |
||||
} |
||||
|
||||
@Override |
||||
public final synchronized void closeInternal(CloseStatus status) throws IOException { |
||||
if (isActive()) { |
||||
// TODO: deliver messages "in flight" before sending close frame
|
||||
try { |
||||
// bypass writeFrame
|
||||
writeFrameInternal(SockJsFrame.closeFrame(status.getCode(), status.getReason())); |
||||
} |
||||
catch (Throwable ex) { |
||||
logger.warn("Failed to send SockJS close frame: " + ex.getMessage()); |
||||
} |
||||
} |
||||
updateLastActiveTime(); |
||||
cancelHeartbeat(); |
||||
disconnect(status); |
||||
} |
||||
|
||||
protected abstract void disconnect(CloseStatus status) throws IOException; |
||||
|
||||
/** |
||||
* For internal use within a TransportHandler and the (TransportHandler-specific) |
||||
* session sub-class. |
||||
*/ |
||||
protected void writeFrame(SockJsFrame frame) throws IOException { |
||||
if (logger.isTraceEnabled()) { |
||||
logger.trace("Preparing to write " + frame); |
||||
} |
||||
try { |
||||
writeFrameInternal(frame); |
||||
} |
||||
catch (IOException ex) { |
||||
if (ex instanceof EOFException || ex instanceof SocketException) { |
||||
logger.warn("Client went away. Terminating connection"); |
||||
} |
||||
else { |
||||
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage()); |
||||
} |
||||
disconnect(CloseStatus.SERVER_ERROR); |
||||
close(CloseStatus.SERVER_ERROR); |
||||
throw ex; |
||||
} |
||||
catch (Throwable ex) { |
||||
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage()); |
||||
disconnect(CloseStatus.SERVER_ERROR); |
||||
close(CloseStatus.SERVER_ERROR); |
||||
throw new SockJsRuntimeException("Failed to write " + frame, ex); |
||||
} |
||||
} |
||||
|
||||
protected abstract void writeFrameInternal(SockJsFrame frame) throws Exception; |
||||
|
||||
public synchronized void sendHeartbeat() throws Exception { |
||||
if (isActive()) { |
||||
writeFrame(SockJsFrame.heartbeatFrame()); |
||||
scheduleHeartbeat(); |
||||
} |
||||
} |
||||
|
||||
protected void scheduleHeartbeat() { |
||||
Assert.notNull(getSockJsConfig().getTaskScheduler(), "heartbeatScheduler not configured"); |
||||
cancelHeartbeat(); |
||||
if (!isActive()) { |
||||
return; |
||||
} |
||||
Date time = new Date(System.currentTimeMillis() + getSockJsConfig().getHeartbeatTime()); |
||||
this.heartbeatTask = getSockJsConfig().getTaskScheduler().schedule(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
try { |
||||
sendHeartbeat(); |
||||
} |
||||
catch (Throwable t) { |
||||
// ignore
|
||||
} |
||||
} |
||||
}, time); |
||||
if (logger.isTraceEnabled()) { |
||||
logger.trace("Scheduled heartbeat after " + getSockJsConfig().getHeartbeatTime()/1000 + " seconds"); |
||||
} |
||||
} |
||||
|
||||
protected void cancelHeartbeat() { |
||||
if ((this.heartbeatTask != null) && !this.heartbeatTask.isDone()) { |
||||
if (logger.isTraceEnabled()) { |
||||
logger.trace("Cancelling heartbeat"); |
||||
} |
||||
this.heartbeatTask.cancel(false); |
||||
} |
||||
this.heartbeatTask = null; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.adapter; |
||||
|
||||
import org.eclipse.jetty.websocket.api.Session; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
|
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link JettyWebSocketListenerAdapter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class JettyWebSocketListenerAdapterTests { |
||||
|
||||
private JettyWebSocketListenerAdapter adapter; |
||||
|
||||
private WebSocketHandler webSocketHandler; |
||||
|
||||
private JettyWebSocketSessionAdapter webSocketSession; |
||||
|
||||
private Session session; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.session = mock(Session.class); |
||||
this.webSocketHandler = mock(WebSocketHandler.class); |
||||
this.webSocketSession = new JettyWebSocketSessionAdapter(); |
||||
this.adapter = new JettyWebSocketListenerAdapter(this.webSocketHandler, this.webSocketSession); |
||||
} |
||||
|
||||
@Test |
||||
public void onOpen() throws Throwable { |
||||
this.adapter.onWebSocketConnect(this.session); |
||||
verify(this.webSocketHandler).afterConnectionEstablished(this.webSocketSession); |
||||
} |
||||
|
||||
@Test |
||||
public void onClose() throws Throwable { |
||||
this.adapter.onWebSocketClose(1000, "reason"); |
||||
verify(this.webSocketHandler).afterConnectionClosed(this.webSocketSession, CloseStatus.NORMAL.withReason("reason")); |
||||
} |
||||
|
||||
@Test |
||||
public void onError() throws Throwable { |
||||
Exception exception = new Exception(); |
||||
this.adapter.onWebSocketError(exception); |
||||
verify(this.webSocketHandler).handleTransportError(this.webSocketSession, exception); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.adapter; |
||||
|
||||
import javax.websocket.CloseReason; |
||||
import javax.websocket.CloseReason.CloseCodes; |
||||
import javax.websocket.MessageHandler; |
||||
import javax.websocket.Session; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Matchers.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link StandardEndpointAdapter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class StandardEndpointAdapterTests { |
||||
|
||||
private StandardEndpointAdapter adapter; |
||||
|
||||
private WebSocketHandler webSocketHandler; |
||||
|
||||
private StandardWebSocketSessionAdapter webSocketSession; |
||||
|
||||
private Session session; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.session = mock(Session.class); |
||||
this.webSocketHandler = mock(WebSocketHandler.class); |
||||
this.webSocketSession = new StandardWebSocketSessionAdapter(); |
||||
this.adapter = new StandardEndpointAdapter(webSocketHandler, webSocketSession); |
||||
} |
||||
|
||||
@Test |
||||
public void onOpen() throws Throwable { |
||||
this.adapter.onOpen(session, null); |
||||
|
||||
verify(this.webSocketHandler).afterConnectionEstablished(this.webSocketSession); |
||||
verify(session, atLeast(2)).addMessageHandler(any(MessageHandler.Whole.class)); |
||||
|
||||
when(session.getId()).thenReturn("123"); |
||||
assertEquals("123", this.webSocketSession.getId()); |
||||
} |
||||
|
||||
@Test |
||||
public void onClose() throws Throwable { |
||||
this.adapter.onClose(session, new CloseReason(CloseCodes.NORMAL_CLOSURE, "reason")); |
||||
verify(this.webSocketHandler).afterConnectionClosed(this.webSocketSession, CloseStatus.NORMAL.withReason("reason")); |
||||
} |
||||
|
||||
@Test |
||||
public void onError() throws Throwable { |
||||
Exception exception = new Exception(); |
||||
this.adapter.onError(session, exception); |
||||
verify(this.webSocketHandler).handleTransportError(this.webSocketSession, exception); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.client; |
||||
|
||||
import java.net.URI; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
import org.mockito.ArgumentCaptor; |
||||
import org.springframework.context.SmartLifecycle; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.WebSocketSession; |
||||
import org.springframework.web.socket.adapter.WebSocketHandlerAdapter; |
||||
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator; |
||||
import org.springframework.web.socket.support.LoggingWebSocketHandlerDecorator; |
||||
import org.springframework.web.socket.support.WebSocketHandlerDecorator; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link WebSocketConnectionManager}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class WebSocketConnectionManagerTests { |
||||
|
||||
|
||||
@Test |
||||
public void openConnection() throws Exception { |
||||
|
||||
List<String> subprotocols = Arrays.asList("abc"); |
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.setSecWebSocketProtocol(subprotocols); |
||||
|
||||
WebSocketClient client = mock(WebSocketClient.class); |
||||
WebSocketHandler handler = new WebSocketHandlerAdapter(); |
||||
|
||||
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler , "/path/{id}", "123"); |
||||
manager.setSubProtocols(subprotocols); |
||||
manager.openConnection(); |
||||
|
||||
ArgumentCaptor<WebSocketHandlerDecorator> captor = ArgumentCaptor.forClass(WebSocketHandlerDecorator.class); |
||||
ArgumentCaptor<HttpHeaders> headersCaptor = ArgumentCaptor.forClass(HttpHeaders.class); |
||||
ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class); |
||||
|
||||
verify(client).doHandshake(captor.capture(), headersCaptor.capture(), uriCaptor.capture()); |
||||
|
||||
assertEquals(headers, headersCaptor.getValue()); |
||||
assertEquals(new URI("/path/123"), uriCaptor.getValue()); |
||||
|
||||
WebSocketHandlerDecorator loggingHandler = captor.getValue(); |
||||
assertEquals(LoggingWebSocketHandlerDecorator.class, loggingHandler.getClass()); |
||||
|
||||
WebSocketHandlerDecorator exceptionHandler = (WebSocketHandlerDecorator) loggingHandler.getDelegate(); |
||||
assertNotNull(exceptionHandler); |
||||
assertEquals(ExceptionWebSocketHandlerDecorator.class, exceptionHandler.getClass()); |
||||
|
||||
assertSame(handler, exceptionHandler.getDelegate()); |
||||
} |
||||
|
||||
@Test |
||||
public void syncClientLifecycle() throws Exception { |
||||
|
||||
TestLifecycleWebSocketClient client = new TestLifecycleWebSocketClient(false); |
||||
WebSocketHandler handler = new WebSocketHandlerAdapter(); |
||||
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler , "/a"); |
||||
|
||||
manager.startInternal(); |
||||
assertTrue(client.isRunning()); |
||||
|
||||
manager.stopInternal(); |
||||
assertFalse(client.isRunning()); |
||||
} |
||||
|
||||
@Test |
||||
public void dontSyncClientLifecycle() throws Exception { |
||||
|
||||
TestLifecycleWebSocketClient client = new TestLifecycleWebSocketClient(true); |
||||
WebSocketHandler handler = new WebSocketHandlerAdapter(); |
||||
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler , "/a"); |
||||
|
||||
manager.startInternal(); |
||||
assertTrue(client.isRunning()); |
||||
|
||||
manager.stopInternal(); |
||||
assertTrue(client.isRunning()); |
||||
} |
||||
|
||||
|
||||
private static class TestLifecycleWebSocketClient implements WebSocketClient, SmartLifecycle { |
||||
|
||||
private boolean running; |
||||
|
||||
public TestLifecycleWebSocketClient(boolean running) { |
||||
this.running = running; |
||||
} |
||||
|
||||
@Override |
||||
public void start() { |
||||
this.running = true; |
||||
} |
||||
|
||||
@Override |
||||
public void stop() { |
||||
this.running = false; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isRunning() { |
||||
return this.running; |
||||
} |
||||
|
||||
@Override |
||||
public int getPhase() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAutoStartup() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public void stop(Runnable callback) { |
||||
this.running = false; |
||||
} |
||||
|
||||
@Override |
||||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables) |
||||
throws WebSocketConnectFailureException { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, URI uri) |
||||
throws WebSocketConnectFailureException { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.client.endpoint; |
||||
|
||||
import java.net.URI; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import javax.websocket.ClientEndpointConfig; |
||||
import javax.websocket.Endpoint; |
||||
import javax.websocket.WebSocketContainer; |
||||
|
||||
import org.junit.Test; |
||||
import org.mockito.ArgumentCaptor; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.WebSocketSession; |
||||
import org.springframework.web.socket.adapter.StandardEndpointAdapter; |
||||
import org.springframework.web.socket.adapter.WebSocketHandlerAdapter; |
||||
import org.springframework.web.socket.client.endpoint.StandardWebSocketClient; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link StandardWebSocketClient}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class StandardWebSocketClientTests { |
||||
|
||||
|
||||
@Test |
||||
public void doHandshake() throws Exception { |
||||
|
||||
URI uri = new URI("ws://example.com/abc"); |
||||
List<String> subprotocols = Arrays.asList("abc"); |
||||
|
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.setSecWebSocketProtocol(subprotocols); |
||||
headers.add("foo", "bar"); |
||||
|
||||
WebSocketHandler handler = new WebSocketHandlerAdapter(); |
||||
WebSocketContainer webSocketContainer = mock(WebSocketContainer.class); |
||||
StandardWebSocketClient client = new StandardWebSocketClient(); |
||||
client.setWebSocketContainer(webSocketContainer); |
||||
WebSocketSession session = client.doHandshake(handler, headers, uri); |
||||
|
||||
ArgumentCaptor<Endpoint> endpointArg = ArgumentCaptor.forClass(Endpoint.class); |
||||
ArgumentCaptor<ClientEndpointConfig> configArg = ArgumentCaptor.forClass(ClientEndpointConfig.class); |
||||
ArgumentCaptor<URI> uriArg = ArgumentCaptor.forClass(URI.class); |
||||
|
||||
|
||||
verify(webSocketContainer).connectToServer(endpointArg.capture(), configArg.capture(), uriArg.capture()); |
||||
|
||||
|
||||
assertNotNull(endpointArg.getValue()); |
||||
assertEquals(StandardEndpointAdapter.class, endpointArg.getValue().getClass()); |
||||
|
||||
ClientEndpointConfig config = configArg.getValue(); |
||||
assertEquals(subprotocols, config.getPreferredSubprotocols()); |
||||
|
||||
Map<String, List<String>> map = new HashMap<>(); |
||||
config.getConfigurator().beforeRequest(map); |
||||
assertEquals(Collections.singletonMap("foo", Arrays.asList("bar")), map); |
||||
|
||||
assertEquals(uri, uriArg.getValue()); |
||||
assertEquals(uri, session.getUri()); |
||||
assertEquals("example.com", session.getRemoteHostName()); |
||||
} |
||||
} |
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.server.endpoint; |
||||
|
||||
import javax.websocket.Endpoint; |
||||
import javax.websocket.EndpointConfig; |
||||
import javax.websocket.Session; |
||||
import javax.websocket.server.ServerContainer; |
||||
import javax.websocket.server.ServerEndpoint; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.mock.web.test.MockServletContext; |
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; |
||||
|
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link ServerEndpointExporter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ServerEndpointExporterTests { |
||||
|
||||
private ServerContainer serverContainer; |
||||
|
||||
private ServerEndpointExporter exporter; |
||||
|
||||
private AnnotationConfigWebApplicationContext webAppContext; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.serverContainer = mock(ServerContainer.class); |
||||
|
||||
MockServletContext servletContext = new MockServletContext(); |
||||
servletContext.setAttribute("javax.websocket.server.ServerContainer", serverContainer); |
||||
|
||||
this.webAppContext = new AnnotationConfigWebApplicationContext(); |
||||
this.webAppContext.register(Config.class); |
||||
this.webAppContext.setServletContext(servletContext); |
||||
this.webAppContext.refresh(); |
||||
|
||||
this.exporter = new ServerEndpointExporter(); |
||||
this.exporter.setApplicationContext(this.webAppContext); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void addAnnotatedEndpointBean() throws Exception { |
||||
|
||||
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class); |
||||
this.exporter.afterPropertiesSet(); |
||||
|
||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class); |
||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class); |
||||
} |
||||
|
||||
@Test |
||||
public void addServerEndpointConfigBean() throws Exception { |
||||
|
||||
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint()); |
||||
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint"); |
||||
|
||||
verify(this.serverContainer).addEndpoint(endpointRegistration); |
||||
} |
||||
|
||||
|
||||
private static class DummyEndpoint extends Endpoint { |
||||
|
||||
@Override |
||||
public void onOpen(Session session, EndpointConfig config) { |
||||
} |
||||
} |
||||
|
||||
@ServerEndpoint("/path") |
||||
private static class AnnotatedDummyEndpoint { |
||||
} |
||||
|
||||
@ServerEndpoint("/path") |
||||
private static class AnnotatedDummyEndpointBean { |
||||
} |
||||
|
||||
@Configuration |
||||
static class Config { |
||||
|
||||
@Bean |
||||
public AnnotatedDummyEndpointBean annotatedEndpoint1() { |
||||
return new AnnotatedDummyEndpointBean(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.server.endpoint; |
||||
|
||||
import javax.websocket.Endpoint; |
||||
import javax.websocket.EndpointConfig; |
||||
import javax.websocket.Session; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link ServerEndpointRegistration}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ServerEndpointRegistrationTests { |
||||
|
||||
|
||||
@Test |
||||
public void endpointPerConnection() throws Exception { |
||||
|
||||
@SuppressWarnings("resource") |
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Config.class); |
||||
|
||||
ServerEndpointRegistration registration = new ServerEndpointRegistration("/path", EchoEndpoint.class); |
||||
registration.setBeanFactory(context.getBeanFactory()); |
||||
|
||||
EchoEndpoint endpoint = registration.getConfigurator().getEndpointInstance(EchoEndpoint.class); |
||||
|
||||
assertNotNull(endpoint); |
||||
} |
||||
|
||||
@Test |
||||
public void endpointSingleton() throws Exception { |
||||
|
||||
EchoEndpoint endpoint = new EchoEndpoint(new EchoService()); |
||||
ServerEndpointRegistration registration = new ServerEndpointRegistration("/path", endpoint); |
||||
|
||||
EchoEndpoint actual = registration.getConfigurator().getEndpointInstance(EchoEndpoint.class); |
||||
|
||||
assertSame(endpoint, actual); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
static class Config { |
||||
|
||||
@Bean |
||||
public EchoService echoService() { |
||||
return new EchoService(); |
||||
} |
||||
} |
||||
|
||||
private static class EchoEndpoint extends Endpoint { |
||||
|
||||
@SuppressWarnings("unused") |
||||
private final EchoService service; |
||||
|
||||
@Autowired |
||||
public EchoEndpoint(EchoService service) { |
||||
this.service = service; |
||||
} |
||||
|
||||
@Override |
||||
public void onOpen(Session session, EndpointConfig config) { |
||||
} |
||||
} |
||||
|
||||
private static class EchoService { } |
||||
|
||||
} |
@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.server.endpoint; |
||||
|
||||
import javax.websocket.Endpoint; |
||||
import javax.websocket.EndpointConfig; |
||||
import javax.websocket.Session; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.mock.web.test.MockServletContext; |
||||
import org.springframework.web.context.ContextLoader; |
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
public class SpringConfiguratorTests { |
||||
|
||||
|
||||
private MockServletContext servletContext; |
||||
|
||||
private ContextLoader contextLoader; |
||||
|
||||
private AnnotationConfigWebApplicationContext webAppContext; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.servletContext = new MockServletContext(); |
||||
|
||||
this.webAppContext = new AnnotationConfigWebApplicationContext(); |
||||
this.webAppContext.register(Config.class); |
||||
|
||||
this.contextLoader = new ContextLoader(webAppContext); |
||||
this.contextLoader.initWebApplicationContext(this.servletContext); |
||||
} |
||||
|
||||
@After |
||||
public void destroy() { |
||||
this.contextLoader.closeWebApplicationContext(this.servletContext); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void getEndpointInstanceCreateBean() throws Exception { |
||||
|
||||
PerConnectionEchoEndpoint endpoint = new SpringConfigurator().getEndpointInstance(PerConnectionEchoEndpoint.class); |
||||
|
||||
assertNotNull(endpoint); |
||||
} |
||||
|
||||
@Test |
||||
public void getEndpointInstanceUseBean() throws Exception { |
||||
|
||||
EchoEndpointBean expected = this.webAppContext.getBean(EchoEndpointBean.class); |
||||
EchoEndpointBean actual = new SpringConfigurator().getEndpointInstance(EchoEndpointBean.class); |
||||
|
||||
assertSame(expected, actual); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
static class Config { |
||||
|
||||
@Bean |
||||
public EchoEndpointBean echoEndpointBean() { |
||||
return new EchoEndpointBean(echoService()); |
||||
} |
||||
|
||||
@Bean |
||||
public EchoService echoService() { |
||||
return new EchoService(); |
||||
} |
||||
} |
||||
|
||||
private static class EchoEndpointBean extends Endpoint { |
||||
|
||||
@SuppressWarnings("unused") |
||||
private final EchoService service; |
||||
|
||||
@Autowired |
||||
public EchoEndpointBean(EchoService service) { |
||||
this.service = service; |
||||
} |
||||
|
||||
@Override |
||||
public void onOpen(Session session, EndpointConfig config) { |
||||
} |
||||
} |
||||
|
||||
private static class PerConnectionEchoEndpoint extends Endpoint { |
||||
|
||||
@SuppressWarnings("unused") |
||||
private final EchoService service; |
||||
|
||||
@Autowired |
||||
public PerConnectionEchoEndpoint(EchoService service) { |
||||
this.service = service; |
||||
} |
||||
|
||||
@Override |
||||
public void onOpen(Session session, EndpointConfig config) { |
||||
} |
||||
} |
||||
|
||||
private static class EchoService { } |
||||
|
||||
} |
@ -0,0 +1,269 @@
@@ -0,0 +1,269 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs; |
||||
|
||||
import java.io.IOException; |
||||
import java.sql.Date; |
||||
import java.util.Collections; |
||||
import java.util.concurrent.ScheduledFuture; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.TextMessage; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Matchers.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link AbstractSockJsSession}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class AbstractSockJsSessionTests extends BaseAbstractSockJsSessionTests<TestSockJsSession> { |
||||
|
||||
|
||||
@Override |
||||
protected TestSockJsSession initSockJsSession() { |
||||
return new TestSockJsSession("1", this.sockJsConfig, this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void getTimeSinceLastActive() throws Exception { |
||||
|
||||
Thread.sleep(1); |
||||
|
||||
long time1 = this.session.getTimeSinceLastActive(); |
||||
assertTrue(time1 > 0); |
||||
|
||||
Thread.sleep(1); |
||||
|
||||
long time2 = this.session.getTimeSinceLastActive(); |
||||
assertTrue(time2 > time1); |
||||
|
||||
this.session.delegateConnectionEstablished(); |
||||
|
||||
Thread.sleep(1); |
||||
|
||||
this.session.setActive(false); |
||||
assertTrue(this.session.getTimeSinceLastActive() > 0); |
||||
|
||||
this.session.setActive(true); |
||||
assertEquals(0, this.session.getTimeSinceLastActive()); |
||||
} |
||||
|
||||
@Test |
||||
public void delegateConnectionEstablished() throws Exception { |
||||
assertNew(); |
||||
this.session.delegateConnectionEstablished(); |
||||
assertOpen(); |
||||
verify(this.webSocketHandler).afterConnectionEstablished(this.session); |
||||
} |
||||
|
||||
@Test |
||||
public void delegateError() throws Exception { |
||||
Exception ex = new Exception(); |
||||
this.session.delegateError(ex); |
||||
verify(this.webSocketHandler).handleTransportError(this.session, ex); |
||||
} |
||||
|
||||
@Test |
||||
public void delegateMessages() throws Exception { |
||||
String msg1 = "message 1"; |
||||
String msg2 = "message 2"; |
||||
this.session.delegateMessages(new String[] { msg1, msg2 }); |
||||
|
||||
verify(this.webSocketHandler).handleMessage(this.session, new TextMessage(msg1)); |
||||
verify(this.webSocketHandler).handleMessage(this.session, new TextMessage(msg2)); |
||||
verifyNoMoreInteractions(this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void delegateConnectionClosed() throws Exception { |
||||
this.session.delegateConnectionEstablished(); |
||||
this.session.delegateConnectionClosed(CloseStatus.GOING_AWAY); |
||||
|
||||
assertClosed(); |
||||
assertEquals(1, this.session.getNumberOfLastActiveTimeUpdates()); |
||||
assertTrue(this.session.didCancelHeartbeat()); |
||||
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.GOING_AWAY); |
||||
} |
||||
|
||||
@Test |
||||
public void closeWhenNotOpen() throws Exception { |
||||
|
||||
assertNew(); |
||||
|
||||
this.session.close(); |
||||
assertNull("Close not ignored for a new session", this.session.getStatus()); |
||||
|
||||
this.session.delegateConnectionEstablished(); |
||||
assertOpen(); |
||||
|
||||
this.session.close(); |
||||
assertClosed(); |
||||
assertEquals(3000, this.session.getStatus().getCode()); |
||||
|
||||
this.session.close(CloseStatus.SERVER_ERROR); |
||||
assertEquals("Close should be ignored if already closed", 3000, this.session.getStatus().getCode()); |
||||
} |
||||
|
||||
@Test |
||||
public void closeWhenNotActive() throws Exception { |
||||
|
||||
this.session.delegateConnectionEstablished(); |
||||
assertOpen(); |
||||
|
||||
this.session.setActive(false); |
||||
this.session.close(); |
||||
|
||||
assertEquals(Collections.emptyList(), this.session.getSockJsFramesWritten()); |
||||
} |
||||
|
||||
@Test |
||||
public void close() throws Exception { |
||||
|
||||
this.session.delegateConnectionEstablished(); |
||||
assertOpen(); |
||||
|
||||
this.session.setActive(true); |
||||
this.session.close(); |
||||
|
||||
assertEquals(1, this.session.getSockJsFramesWritten().size()); |
||||
assertEquals(SockJsFrame.closeFrameGoAway(), this.session.getSockJsFramesWritten().get(0)); |
||||
|
||||
assertEquals(1, this.session.getNumberOfLastActiveTimeUpdates()); |
||||
assertTrue(this.session.didCancelHeartbeat()); |
||||
|
||||
assertEquals(new CloseStatus(3000, "Go away!"), this.session.getStatus()); |
||||
assertClosed(); |
||||
verify(this.webSocketHandler).afterConnectionClosed(this.session, new CloseStatus(3000, "Go away!")); |
||||
} |
||||
|
||||
@Test |
||||
public void closeWithWriteFrameExceptions() throws Exception { |
||||
|
||||
this.session.setExceptionOnWriteFrame(new IOException()); |
||||
|
||||
this.session.delegateConnectionEstablished(); |
||||
this.session.setActive(true); |
||||
this.session.close(); |
||||
|
||||
assertEquals(new CloseStatus(3000, "Go away!"), this.session.getStatus()); |
||||
assertClosed(); |
||||
} |
||||
|
||||
@Test |
||||
public void closeWithWebSocketHandlerExceptions() throws Exception { |
||||
|
||||
doThrow(new Exception()).when(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.NORMAL); |
||||
|
||||
this.session.delegateConnectionEstablished(); |
||||
this.session.setActive(true); |
||||
this.session.close(CloseStatus.NORMAL); |
||||
|
||||
assertEquals(CloseStatus.NORMAL, this.session.getStatus()); |
||||
assertClosed(); |
||||
} |
||||
|
||||
@Test |
||||
public void writeFrame() throws Exception { |
||||
this.session.writeFrame(SockJsFrame.openFrame()); |
||||
|
||||
assertEquals(1, this.session.getSockJsFramesWritten().size()); |
||||
assertEquals(SockJsFrame.openFrame(), this.session.getSockJsFramesWritten().get(0)); |
||||
} |
||||
|
||||
@Test |
||||
public void writeFrameIoException() throws Exception { |
||||
this.session.setExceptionOnWriteFrame(new IOException()); |
||||
this.session.delegateConnectionEstablished(); |
||||
try { |
||||
this.session.writeFrame(SockJsFrame.openFrame()); |
||||
fail("expected exception"); |
||||
} |
||||
catch (IOException ex) { |
||||
assertEquals(CloseStatus.SERVER_ERROR, this.session.getStatus()); |
||||
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.SERVER_ERROR); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void writeFrameThrowable() throws Exception { |
||||
this.session.setExceptionOnWriteFrame(new NullPointerException()); |
||||
this.session.delegateConnectionEstablished(); |
||||
try { |
||||
this.session.writeFrame(SockJsFrame.openFrame()); |
||||
fail("expected exception"); |
||||
} |
||||
catch (SockJsRuntimeException ex) { |
||||
assertEquals(CloseStatus.SERVER_ERROR, this.session.getStatus()); |
||||
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.SERVER_ERROR); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void sendHeartbeatWhenNotActive() throws Exception { |
||||
this.session.setActive(false); |
||||
this.session.sendHeartbeat(); |
||||
|
||||
assertEquals(Collections.emptyList(), this.session.getSockJsFramesWritten()); |
||||
} |
||||
|
||||
@Test |
||||
public void sendHeartbeat() throws Exception { |
||||
this.session.setActive(true); |
||||
this.session.sendHeartbeat(); |
||||
|
||||
assertEquals(1, this.session.getSockJsFramesWritten().size()); |
||||
assertEquals(SockJsFrame.heartbeatFrame(), this.session.getSockJsFramesWritten().get(0)); |
||||
|
||||
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); |
||||
verifyNoMoreInteractions(this.taskScheduler); |
||||
} |
||||
|
||||
@Test |
||||
public void scheduleHeartbeatNotActive() throws Exception { |
||||
this.session.setActive(false); |
||||
this.session.scheduleHeartbeat(); |
||||
|
||||
verifyNoMoreInteractions(this.taskScheduler); |
||||
} |
||||
|
||||
@Test |
||||
public void scheduleAndCancelHeartbeat() throws Exception { |
||||
|
||||
ScheduledFuture<?> task = mock(ScheduledFuture.class); |
||||
doReturn(task).when(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); |
||||
|
||||
this.session.setActive(true); |
||||
this.session.scheduleHeartbeat(); |
||||
|
||||
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); |
||||
verifyNoMoreInteractions(this.taskScheduler); |
||||
|
||||
doReturn(false).when(task).isDone(); |
||||
|
||||
this.session.cancelHeartbeat(); |
||||
|
||||
verify(task).isDone(); |
||||
verify(task).cancel(false); |
||||
verifyNoMoreInteractions(task); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs; |
||||
|
||||
import org.junit.Before; |
||||
import org.springframework.scheduling.TaskScheduler; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Base class for {@link AbstractSockJsSession} classes. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public abstract class BaseAbstractSockJsSessionTests<S extends AbstractSockJsSession> { |
||||
|
||||
protected WebSocketHandler webSocketHandler; |
||||
|
||||
protected StubSockJsConfig sockJsConfig; |
||||
|
||||
protected TaskScheduler taskScheduler; |
||||
|
||||
protected S session; |
||||
|
||||
|
||||
@Before |
||||
public void setUp() { |
||||
this.webSocketHandler = mock(WebSocketHandler.class); |
||||
this.taskScheduler = mock(TaskScheduler.class); |
||||
|
||||
this.sockJsConfig = new StubSockJsConfig(); |
||||
this.sockJsConfig.setTaskScheduler(this.taskScheduler); |
||||
|
||||
this.session = initSockJsSession(); |
||||
} |
||||
|
||||
protected abstract S initSockJsSession(); |
||||
|
||||
protected void assertNew() { |
||||
assertState(true, false, false); |
||||
} |
||||
|
||||
protected void assertOpen() { |
||||
assertState(false, true, false); |
||||
} |
||||
|
||||
protected void assertClosed() { |
||||
assertState(false, false, true); |
||||
} |
||||
|
||||
private void assertState(boolean isNew, boolean isOpen, boolean isClosed) { |
||||
assertEquals(isNew, this.session.isNew()); |
||||
assertEquals(isOpen, this.session.isOpen()); |
||||
assertEquals(isClosed, this.session.isClosed()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
|
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class TestSockJsSession extends AbstractSockJsSession { |
||||
|
||||
private boolean active; |
||||
|
||||
private final List<SockJsFrame> sockJsFramesWritten = new ArrayList<>(); |
||||
|
||||
private CloseStatus status; |
||||
|
||||
private Exception exceptionOnWriteFrame; |
||||
|
||||
private int numberOfLastActiveTimeUpdates; |
||||
|
||||
private boolean cancelledHeartbeat; |
||||
|
||||
|
||||
public TestSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) { |
||||
super(sessionId, config, handler); |
||||
} |
||||
|
||||
public CloseStatus getStatus() { |
||||
return this.status; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isActive() { |
||||
return this.active; |
||||
} |
||||
|
||||
public void setActive(boolean active) { |
||||
this.active = active; |
||||
} |
||||
|
||||
public List<SockJsFrame> getSockJsFramesWritten() { |
||||
return this.sockJsFramesWritten; |
||||
} |
||||
|
||||
public void setExceptionOnWriteFrame(Exception exceptionOnWriteFrame) { |
||||
this.exceptionOnWriteFrame = exceptionOnWriteFrame; |
||||
} |
||||
|
||||
public int getNumberOfLastActiveTimeUpdates() { |
||||
return this.numberOfLastActiveTimeUpdates; |
||||
} |
||||
|
||||
public boolean didCancelHeartbeat() { |
||||
return this.cancelledHeartbeat; |
||||
} |
||||
|
||||
@Override |
||||
protected void updateLastActiveTime() { |
||||
this.numberOfLastActiveTimeUpdates++; |
||||
super.updateLastActiveTime(); |
||||
} |
||||
|
||||
@Override |
||||
protected void cancelHeartbeat() { |
||||
this.cancelledHeartbeat = true; |
||||
super.cancelHeartbeat(); |
||||
} |
||||
|
||||
@Override |
||||
protected void sendMessageInternal(String message) throws IOException { |
||||
} |
||||
|
||||
@Override |
||||
protected void writeFrameInternal(SockJsFrame frame) throws Exception { |
||||
this.sockJsFramesWritten.add(frame); |
||||
if (this.exceptionOnWriteFrame != null) { |
||||
throw exceptionOnWriteFrame; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void disconnect(CloseStatus status) throws IOException { |
||||
this.status = status; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs.transport; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.http.server.AsyncServletServerHttpRequest; |
||||
import org.springframework.http.server.ServerHttpRequest; |
||||
import org.springframework.http.server.ServerHttpResponse; |
||||
import org.springframework.http.server.ServletServerHttpResponse; |
||||
import org.springframework.mock.web.test.MockHttpServletRequest; |
||||
import org.springframework.mock.web.test.MockHttpServletResponse; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.sockjs.BaseAbstractSockJsSessionTests; |
||||
import org.springframework.web.socket.sockjs.SockJsConfiguration; |
||||
import org.springframework.web.socket.sockjs.SockJsFrame; |
||||
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat; |
||||
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat; |
||||
import org.springframework.web.socket.sockjs.transport.AbstractHttpSockJsSessionTests.TestAbstractHttpSockJsSession; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
/** |
||||
* Test fixture for {@link AbstractHttpSockJsSession}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class AbstractHttpSockJsSessionTests extends BaseAbstractSockJsSessionTests<TestAbstractHttpSockJsSession> { |
||||
|
||||
protected ServerHttpRequest request; |
||||
|
||||
protected ServerHttpResponse response; |
||||
|
||||
protected MockHttpServletRequest servletRequest; |
||||
|
||||
protected MockHttpServletResponse servletResponse; |
||||
|
||||
private FrameFormat frameFormat; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
|
||||
super.setUp(); |
||||
|
||||
this.frameFormat = new DefaultFrameFormat("%s"); |
||||
|
||||
this.servletResponse = new MockHttpServletResponse(); |
||||
this.response = new ServletServerHttpResponse(this.servletResponse); |
||||
|
||||
this.servletRequest = new MockHttpServletRequest(); |
||||
this.servletRequest.setAsyncSupported(true); |
||||
this.request = new AsyncServletServerHttpRequest(this.servletRequest, this.servletResponse); |
||||
} |
||||
|
||||
@Override |
||||
protected TestAbstractHttpSockJsSession initSockJsSession() { |
||||
return new TestAbstractHttpSockJsSession(this.sockJsConfig, this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void setInitialRequest() throws Exception { |
||||
|
||||
this.session.setInitialRequest(this.request, this.response, this.frameFormat); |
||||
|
||||
assertTrue(this.session.hasRequest()); |
||||
assertTrue(this.session.hasResponse()); |
||||
|
||||
assertEquals("o", this.servletResponse.getContentAsString()); |
||||
assertFalse(this.servletRequest.isAsyncStarted()); |
||||
|
||||
verify(this.webSocketHandler).afterConnectionEstablished(this.session); |
||||
} |
||||
|
||||
@Test |
||||
public void setLongPollingRequest() throws Exception { |
||||
|
||||
this.session.getMessageCache().add("x"); |
||||
this.session.setLongPollingRequest(this.request, this.response, this.frameFormat); |
||||
|
||||
assertTrue(this.session.hasRequest()); |
||||
assertTrue(this.session.hasResponse()); |
||||
assertTrue(this.servletRequest.isAsyncStarted()); |
||||
|
||||
assertTrue(this.session.wasHeartbeatScheduled()); |
||||
assertTrue(this.session.wasCacheFlushed()); |
||||
|
||||
verifyNoMoreInteractions(this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void setLongPollingRequestWhenClosed() throws Exception { |
||||
|
||||
this.session.delegateConnectionClosed(CloseStatus.NORMAL); |
||||
assertClosed(); |
||||
|
||||
this.session.setLongPollingRequest(this.request, this.response, this.frameFormat); |
||||
|
||||
assertEquals("c[3000,\"Go away!\"]", this.servletResponse.getContentAsString()); |
||||
assertFalse(this.servletRequest.isAsyncStarted()); |
||||
} |
||||
|
||||
|
||||
static class TestAbstractHttpSockJsSession extends AbstractHttpSockJsSession { |
||||
|
||||
private IOException exceptionOnWriteFrame; |
||||
|
||||
private boolean cacheFlushed; |
||||
|
||||
private boolean heartbeatScheduled; |
||||
|
||||
|
||||
public TestAbstractHttpSockJsSession(SockJsConfiguration config, WebSocketHandler handler) { |
||||
super("1", config, handler); |
||||
} |
||||
|
||||
public boolean wasCacheFlushed() { |
||||
return this.cacheFlushed; |
||||
} |
||||
|
||||
public boolean wasHeartbeatScheduled() { |
||||
return this.heartbeatScheduled; |
||||
} |
||||
|
||||
public boolean hasRequest() { |
||||
return getRequest() != null; |
||||
} |
||||
|
||||
public boolean hasResponse() { |
||||
return getResponse() != null; |
||||
} |
||||
|
||||
public void setExceptionOnWriteFrame(IOException exceptionOnWriteFrame) { |
||||
this.exceptionOnWriteFrame = exceptionOnWriteFrame; |
||||
} |
||||
|
||||
@Override |
||||
protected void flushCache() throws IOException { |
||||
this.cacheFlushed = true; |
||||
} |
||||
|
||||
@Override |
||||
protected void scheduleHeartbeat() { |
||||
this.heartbeatScheduled = true; |
||||
} |
||||
|
||||
@Override |
||||
protected synchronized void writeFrameInternal(SockJsFrame frame) throws IOException { |
||||
if (this.exceptionOnWriteFrame != null) { |
||||
throw this.exceptionOnWriteFrame; |
||||
} |
||||
else { |
||||
super.writeFrameInternal(frame); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs.transport; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.web.socket.AbstractHttpRequestTests; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.TextMessage; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.sockjs.AbstractSockJsSession; |
||||
import org.springframework.web.socket.sockjs.StubSockJsConfig; |
||||
import org.springframework.web.socket.sockjs.TestSockJsSession; |
||||
import org.springframework.web.socket.sockjs.TransportErrorException; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
/** |
||||
* Test fixture for {@link AbstractHttpReceivingTransportHandler} and sub-classes |
||||
* {@link XhrTransportHandler} and {@link JsonpTransportHandler}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTests { |
||||
|
||||
|
||||
@Override |
||||
@Before |
||||
public void setUp() { |
||||
super.setUp(); |
||||
} |
||||
|
||||
@Test |
||||
public void readMessagesXhr() throws Exception { |
||||
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8")); |
||||
handleRequest(new XhrTransportHandler()); |
||||
|
||||
assertEquals(204, this.servletResponse.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void readMessagesJsonp() throws Exception { |
||||
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8")); |
||||
handleRequest(new JsonpTransportHandler()); |
||||
|
||||
assertEquals(200, this.servletResponse.getStatus()); |
||||
assertEquals("ok", this.servletResponse.getContentAsString()); |
||||
} |
||||
|
||||
@Test |
||||
public void readMessagesJsonpFormEncoded() throws Exception { |
||||
this.servletRequest.setContent("d=[\"x\"]".getBytes("UTF-8")); |
||||
this.servletRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); |
||||
handleRequest(new JsonpTransportHandler()); |
||||
|
||||
assertEquals(200, this.servletResponse.getStatus()); |
||||
assertEquals("ok", this.servletResponse.getContentAsString()); |
||||
} |
||||
|
||||
@Test |
||||
public void readMessagesBadContent() throws Exception { |
||||
this.servletRequest.setContent("".getBytes("UTF-8")); |
||||
handleRequestAndExpectFailure(); |
||||
|
||||
this.servletRequest.setContent("[\"x]".getBytes("UTF-8")); |
||||
handleRequestAndExpectFailure(); |
||||
} |
||||
|
||||
@Test |
||||
public void readMessagesNoSession() throws Exception { |
||||
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class); |
||||
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, null); |
||||
|
||||
assertEquals(404, this.servletResponse.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void delegateMessageException() throws Exception { |
||||
|
||||
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8")); |
||||
|
||||
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class); |
||||
TestSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler); |
||||
session.delegateConnectionEstablished(); |
||||
|
||||
doThrow(new Exception()).when(webSocketHandler).handleMessage(session, new TextMessage("x")); |
||||
|
||||
try { |
||||
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, session); |
||||
fail("Expected exception"); |
||||
} |
||||
catch (TransportErrorException ex) { |
||||
assertEquals(CloseStatus.SERVER_ERROR, session.getStatus()); |
||||
} |
||||
} |
||||
|
||||
|
||||
private void handleRequest(AbstractHttpReceivingTransportHandler transportHandler) |
||||
throws Exception { |
||||
|
||||
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class); |
||||
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler); |
||||
|
||||
transportHandler.handleRequest(this.request, this.response, webSocketHandler, session); |
||||
|
||||
assertEquals("text/plain;charset=UTF-8", this.response.getHeaders().getContentType().toString()); |
||||
verify(webSocketHandler).handleMessage(session, new TextMessage("x")); |
||||
} |
||||
|
||||
private void handleRequestAndExpectFailure() throws Exception { |
||||
|
||||
resetResponse(); |
||||
|
||||
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class); |
||||
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler); |
||||
|
||||
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, session); |
||||
|
||||
assertEquals(500, this.servletResponse.getStatus()); |
||||
verifyNoMoreInteractions(webSocketHandler); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,186 @@
@@ -0,0 +1,186 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs.transport; |
||||
|
||||
import java.sql.Date; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.scheduling.TaskScheduler; |
||||
import org.springframework.web.socket.AbstractHttpRequestTests; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.sockjs.AbstractSockJsSession; |
||||
import org.springframework.web.socket.sockjs.SockJsFrame; |
||||
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat; |
||||
import org.springframework.web.socket.sockjs.StubSockJsConfig; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Matchers.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
/** |
||||
* Test fixture for {@link AbstractHttpSendingTransportHandler} and sub-classes. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests { |
||||
|
||||
private WebSocketHandler webSocketHandler; |
||||
|
||||
private StubSockJsConfig sockJsConfig; |
||||
|
||||
private TaskScheduler taskScheduler; |
||||
|
||||
|
||||
@Override |
||||
@Before |
||||
public void setUp() { |
||||
super.setUp(); |
||||
|
||||
this.webSocketHandler = mock(WebSocketHandler.class); |
||||
this.taskScheduler = mock(TaskScheduler.class); |
||||
|
||||
this.sockJsConfig = new StubSockJsConfig(); |
||||
this.sockJsConfig.setTaskScheduler(this.taskScheduler); |
||||
} |
||||
|
||||
@Test |
||||
public void handleRequestXhr() throws Exception { |
||||
|
||||
XhrPollingTransportHandler transportHandler = new XhrPollingTransportHandler(); |
||||
transportHandler.setSockJsConfiguration(sockJsConfig); |
||||
|
||||
AbstractSockJsSession session = transportHandler.createSession("1", webSocketHandler); |
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString()); |
||||
assertEquals("o\n", this.servletResponse.getContentAsString()); |
||||
assertFalse("Polling request should complete after open frame", this.servletRequest.isAsyncStarted()); |
||||
verify(webSocketHandler).afterConnectionEstablished(session); |
||||
|
||||
resetResponse(); |
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertTrue("Polling request should remain open", this.servletRequest.isAsyncStarted()); |
||||
verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); |
||||
|
||||
resetRequestAndResponse(); |
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertFalse("Request should have been rejected", this.servletRequest.isAsyncStarted()); |
||||
assertEquals("c[2010,\"Another connection still open\"]\n", this.servletResponse.getContentAsString()); |
||||
} |
||||
|
||||
@Test |
||||
public void jsonpTransport() throws Exception { |
||||
|
||||
JsonpPollingTransportHandler transportHandler = new JsonpPollingTransportHandler(); |
||||
transportHandler.setSockJsConfiguration(sockJsConfig); |
||||
PollingSockJsSession session = transportHandler.createSession("1", webSocketHandler); |
||||
|
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals(500, this.servletResponse.getStatus()); |
||||
assertEquals("\"callback\" parameter required", this.servletResponse.getContentAsString()); |
||||
|
||||
resetRequestAndResponse(); |
||||
this.servletRequest.addParameter("c", "callback"); |
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString()); |
||||
assertFalse("Polling request should complete after open frame", this.servletRequest.isAsyncStarted()); |
||||
verify(webSocketHandler).afterConnectionEstablished(session); |
||||
} |
||||
|
||||
@Test |
||||
public void handleRequestXhrStreaming() throws Exception { |
||||
|
||||
XhrStreamingTransportHandler transportHandler = new XhrStreamingTransportHandler(); |
||||
transportHandler.setSockJsConfiguration(sockJsConfig); |
||||
AbstractSockJsSession session = transportHandler.createSession("1", webSocketHandler); |
||||
|
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals("application/javascript;charset=UTF-8", this.response.getHeaders().getContentType().toString()); |
||||
assertTrue("Streaming request not started", this.servletRequest.isAsyncStarted()); |
||||
verify(webSocketHandler).afterConnectionEstablished(session); |
||||
} |
||||
|
||||
@Test |
||||
public void htmlFileTransport() throws Exception { |
||||
|
||||
HtmlFileTransportHandler transportHandler = new HtmlFileTransportHandler(); |
||||
transportHandler.setSockJsConfiguration(sockJsConfig); |
||||
StreamingSockJsSession session = transportHandler.createSession("1", webSocketHandler); |
||||
|
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals(500, this.servletResponse.getStatus()); |
||||
assertEquals("\"callback\" parameter required", this.servletResponse.getContentAsString()); |
||||
|
||||
resetRequestAndResponse(); |
||||
this.servletRequest.addParameter("c", "callback"); |
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals("text/html;charset=UTF-8", this.response.getHeaders().getContentType().toString()); |
||||
assertTrue("Streaming request not started", this.servletRequest.isAsyncStarted()); |
||||
verify(webSocketHandler).afterConnectionEstablished(session); |
||||
} |
||||
|
||||
@Test |
||||
public void eventSourceTransport() throws Exception { |
||||
|
||||
EventSourceTransportHandler transportHandler = new EventSourceTransportHandler(); |
||||
transportHandler.setSockJsConfiguration(sockJsConfig); |
||||
StreamingSockJsSession session = transportHandler.createSession("1", webSocketHandler); |
||||
|
||||
transportHandler.handleRequest(request, response, webSocketHandler, session); |
||||
|
||||
assertEquals("text/event-stream;charset=UTF-8", this.response.getHeaders().getContentType().toString()); |
||||
assertTrue("Streaming request not started", this.servletRequest.isAsyncStarted()); |
||||
verify(webSocketHandler).afterConnectionEstablished(session); |
||||
} |
||||
|
||||
@Test |
||||
public void frameFormats() throws Exception { |
||||
|
||||
this.servletRequest.addParameter("c", "callback"); |
||||
|
||||
SockJsFrame frame = SockJsFrame.openFrame(); |
||||
|
||||
FrameFormat format = new XhrPollingTransportHandler().getFrameFormat(request); |
||||
SockJsFrame formatted = format.format(frame); |
||||
assertEquals(frame.getContent() + "\n", formatted.getContent()); |
||||
|
||||
format = new XhrStreamingTransportHandler().getFrameFormat(request); |
||||
formatted = format.format(frame); |
||||
assertEquals(frame.getContent() + "\n", formatted.getContent()); |
||||
|
||||
format = new HtmlFileTransportHandler().getFrameFormat(request); |
||||
formatted = format.format(frame); |
||||
assertEquals("<script>\np(\"" + frame.getContent() + "\");\n</script>\r\n", formatted.getContent()); |
||||
|
||||
format = new EventSourceTransportHandler().getFrameFormat(request); |
||||
formatted = format.format(frame); |
||||
assertEquals("data: " + frame.getContent() + "\r\n\r\n", formatted.getContent()); |
||||
|
||||
format = new JsonpPollingTransportHandler().getFrameFormat(request); |
||||
formatted = format.format(frame); |
||||
assertEquals("callback(\"" + frame.getContent() + "\");\r\n", formatted.getContent()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,153 @@
@@ -0,0 +1,153 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.sockjs.transport; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.TextMessage; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.sockjs.BaseAbstractSockJsSessionTests; |
||||
import org.springframework.web.socket.sockjs.SockJsConfiguration; |
||||
import org.springframework.web.socket.sockjs.transport.WebSocketServerSockJsSessionTests.TestWebSocketServerSockJsSession; |
||||
import org.springframework.web.socket.support.TestWebSocketSession; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Matchers.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link WebSocketServerSockJsSession}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class WebSocketServerSockJsSessionTests extends BaseAbstractSockJsSessionTests<TestWebSocketServerSockJsSession> { |
||||
|
||||
private TestWebSocketSession webSocketSession; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
super.setUp(); |
||||
this.webSocketSession = new TestWebSocketSession(); |
||||
this.webSocketSession.setOpen(true); |
||||
} |
||||
|
||||
@Override |
||||
protected TestWebSocketServerSockJsSession initSockJsSession() { |
||||
return new TestWebSocketServerSockJsSession(this.sockJsConfig, this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void isActive() throws Exception { |
||||
assertFalse(this.session.isActive()); |
||||
|
||||
this.session.initWebSocketSession(this.webSocketSession); |
||||
assertTrue(this.session.isActive()); |
||||
|
||||
this.webSocketSession.setOpen(false); |
||||
assertFalse(this.session.isActive()); |
||||
} |
||||
|
||||
@Test |
||||
public void initWebSocketSession() throws Exception { |
||||
|
||||
this.session.initWebSocketSession(this.webSocketSession); |
||||
|
||||
assertEquals("Open frame not sent", |
||||
Collections.singletonList(new TextMessage("o")), this.webSocketSession.getSentMessages()); |
||||
|
||||
assertEquals(Arrays.asList("schedule"), this.session.heartbeatSchedulingEvents); |
||||
verify(this.webSocketHandler).afterConnectionEstablished(this.session); |
||||
verifyNoMoreInteractions(this.taskScheduler, this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void handleMessageEmptyPayload() throws Exception { |
||||
this.session.handleMessage(new TextMessage(""), this.webSocketSession); |
||||
verifyNoMoreInteractions(this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void handleMessage() throws Exception { |
||||
|
||||
TextMessage message = new TextMessage("[\"x\"]"); |
||||
this.session.handleMessage(message, this.webSocketSession); |
||||
|
||||
verify(this.webSocketHandler).handleMessage(this.session, new TextMessage("x")); |
||||
verifyNoMoreInteractions(this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void handleMessageBadData() throws Exception { |
||||
TextMessage message = new TextMessage("[\"x]"); |
||||
this.session.handleMessage(message, this.webSocketSession); |
||||
|
||||
this.session.isClosed(); |
||||
verify(this.webSocketHandler).handleTransportError(same(this.session), any(IOException.class)); |
||||
verifyNoMoreInteractions(this.webSocketHandler); |
||||
} |
||||
|
||||
@Test |
||||
public void sendMessageInternal() throws Exception { |
||||
|
||||
this.session.initWebSocketSession(this.webSocketSession); |
||||
this.session.sendMessageInternal("x"); |
||||
|
||||
assertEquals(Arrays.asList(new TextMessage("o"), new TextMessage("a[\"x\"]")), |
||||
this.webSocketSession.getSentMessages()); |
||||
|
||||
assertEquals(Arrays.asList("schedule", "cancel", "schedule"), this.session.heartbeatSchedulingEvents); |
||||
} |
||||
|
||||
@Test |
||||
public void disconnect() throws Exception { |
||||
|
||||
this.session.initWebSocketSession(this.webSocketSession); |
||||
this.session.close(CloseStatus.NOT_ACCEPTABLE); |
||||
|
||||
assertEquals(CloseStatus.NOT_ACCEPTABLE, this.webSocketSession.getCloseStatus()); |
||||
} |
||||
|
||||
|
||||
static class TestWebSocketServerSockJsSession extends WebSocketServerSockJsSession { |
||||
|
||||
private final List<String> heartbeatSchedulingEvents = new ArrayList<>(); |
||||
|
||||
public TestWebSocketServerSockJsSession(SockJsConfiguration config, WebSocketHandler handler) { |
||||
super("1", config, handler); |
||||
} |
||||
|
||||
@Override |
||||
protected void scheduleHeartbeat() { |
||||
this.heartbeatSchedulingEvents.add("schedule"); |
||||
} |
||||
|
||||
@Override |
||||
protected void cancelHeartbeat() { |
||||
this.heartbeatSchedulingEvents.add("cancel"); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.support; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.beans.BeanInstantiationException; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
|
||||
/** |
||||
* Test fixture for {@link BeanCreatingHandlerProvider}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class BeanCreatingHandlerProviderTests { |
||||
|
||||
|
||||
@Test |
||||
public void getHandlerSimpleInstantiation() { |
||||
|
||||
BeanCreatingHandlerProvider<SimpleEchoHandler> provider = |
||||
new BeanCreatingHandlerProvider<SimpleEchoHandler>(SimpleEchoHandler.class); |
||||
|
||||
assertNotNull(provider.getHandler()); |
||||
} |
||||
|
||||
@Test |
||||
public void getHandlerWithBeanFactory() { |
||||
|
||||
@SuppressWarnings("resource") |
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Config.class); |
||||
|
||||
BeanCreatingHandlerProvider<EchoHandler> provider = |
||||
new BeanCreatingHandlerProvider<EchoHandler>(EchoHandler.class); |
||||
provider.setBeanFactory(context.getBeanFactory()); |
||||
|
||||
assertNotNull(provider.getHandler()); |
||||
} |
||||
|
||||
@Test(expected=BeanInstantiationException.class) |
||||
public void getHandlerNoBeanFactory() { |
||||
|
||||
BeanCreatingHandlerProvider<EchoHandler> provider = |
||||
new BeanCreatingHandlerProvider<EchoHandler>(EchoHandler.class); |
||||
|
||||
provider.getHandler(); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
static class Config { |
||||
|
||||
@Bean |
||||
public EchoService echoService() { |
||||
return new EchoService(); |
||||
} |
||||
} |
||||
|
||||
public static class SimpleEchoHandler { |
||||
} |
||||
|
||||
private static class EchoHandler { |
||||
|
||||
@SuppressWarnings("unused") |
||||
private final EchoService service; |
||||
|
||||
@Autowired |
||||
public EchoHandler(EchoService service) { |
||||
this.service = service; |
||||
} |
||||
} |
||||
|
||||
private static class EchoService { } |
||||
|
||||
} |
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.support; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.TextMessage; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
/** |
||||
* Test fixture for {@link ExceptionWebSocketHandlerDecorator}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ExceptionWebSocketHandlerDecoratorTests { |
||||
|
||||
private TestWebSocketSession session; |
||||
|
||||
private ExceptionWebSocketHandlerDecorator decorator; |
||||
|
||||
private WebSocketHandler delegate; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
|
||||
this.delegate = mock(WebSocketHandler.class); |
||||
this.decorator = new ExceptionWebSocketHandlerDecorator(this.delegate); |
||||
|
||||
this.session = new TestWebSocketSession(); |
||||
this.session.setOpen(true); |
||||
} |
||||
|
||||
@Test |
||||
public void afterConnectionEstablished() throws Exception { |
||||
|
||||
doThrow(new IllegalStateException("error")) |
||||
.when(this.delegate).afterConnectionEstablished(this.session); |
||||
|
||||
this.decorator.afterConnectionEstablished(this.session); |
||||
|
||||
assertEquals(CloseStatus.SERVER_ERROR, this.session.getCloseStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void handleMessage() throws Exception { |
||||
|
||||
TextMessage message = new TextMessage("payload"); |
||||
|
||||
doThrow(new IllegalStateException("error")) |
||||
.when(this.delegate).handleMessage(this.session, message); |
||||
|
||||
this.decorator.handleMessage(this.session, message); |
||||
|
||||
assertEquals(CloseStatus.SERVER_ERROR, this.session.getCloseStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void handleTransportError() throws Exception { |
||||
|
||||
Exception exception = new Exception("transport error"); |
||||
|
||||
doThrow(new IllegalStateException("error")) |
||||
.when(this.delegate).handleTransportError(this.session, exception); |
||||
|
||||
this.decorator.handleTransportError(this.session, exception); |
||||
|
||||
assertEquals(CloseStatus.SERVER_ERROR, this.session.getCloseStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void afterConnectionClosed() throws Exception { |
||||
|
||||
CloseStatus closeStatus = CloseStatus.NORMAL; |
||||
|
||||
doThrow(new IllegalStateException("error")) |
||||
.when(this.delegate).afterConnectionClosed(this.session, closeStatus); |
||||
|
||||
this.decorator.afterConnectionClosed(this.session, closeStatus); |
||||
|
||||
assertNull(this.session.getCloseStatus()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.support; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.DisposableBean; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.WebSocketSession; |
||||
import org.springframework.web.socket.adapter.WebSocketHandlerAdapter; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* Test fixture for {@link PerConnectionWebSocketHandler}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class PerConnectionWebSocketHandlerTests { |
||||
|
||||
|
||||
@Test |
||||
public void afterConnectionEstablished() throws Exception { |
||||
|
||||
@SuppressWarnings("resource") |
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); |
||||
context.refresh(); |
||||
|
||||
EchoHandler.reset(); |
||||
PerConnectionWebSocketHandler handler = new PerConnectionWebSocketHandler(EchoHandler.class); |
||||
handler.setBeanFactory(context.getBeanFactory()); |
||||
|
||||
WebSocketSession session = new TestWebSocketSession(); |
||||
handler.afterConnectionEstablished(session); |
||||
|
||||
assertEquals(1, EchoHandler.initCount); |
||||
assertEquals(0, EchoHandler.destroyCount); |
||||
|
||||
handler.afterConnectionClosed(session, CloseStatus.NORMAL); |
||||
|
||||
assertEquals(1, EchoHandler.initCount); |
||||
assertEquals(1, EchoHandler.destroyCount); |
||||
} |
||||
|
||||
|
||||
public static class EchoHandler extends WebSocketHandlerAdapter implements DisposableBean { |
||||
|
||||
private static int initCount; |
||||
|
||||
private static int destroyCount; |
||||
|
||||
|
||||
public EchoHandler() { |
||||
initCount++; |
||||
} |
||||
|
||||
@Override |
||||
public void destroy() throws Exception { |
||||
destroyCount++; |
||||
} |
||||
|
||||
public static void reset() { |
||||
initCount = 0; |
||||
destroyCount = 0; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,185 @@
@@ -0,0 +1,185 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.support; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.URI; |
||||
import java.security.Principal; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.WebSocketMessage; |
||||
import org.springframework.web.socket.WebSocketSession; |
||||
|
||||
|
||||
/** |
||||
* A {@link WebSocketSession} for use in tests. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class TestWebSocketSession implements WebSocketSession { |
||||
|
||||
private String id; |
||||
|
||||
private URI uri; |
||||
|
||||
private boolean secure; |
||||
|
||||
private Principal principal; |
||||
|
||||
private String remoteHostName; |
||||
|
||||
private String remoteAddress; |
||||
|
||||
private boolean open; |
||||
|
||||
private final List<WebSocketMessage<?>> messages = new ArrayList<>(); |
||||
|
||||
private CloseStatus status; |
||||
|
||||
|
||||
/** |
||||
* @return the id |
||||
*/ |
||||
@Override |
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
/** |
||||
* @param id the id to set |
||||
*/ |
||||
public void setId(String id) { |
||||
this.id = id; |
||||
} |
||||
|
||||
/** |
||||
* @return the uri |
||||
*/ |
||||
@Override |
||||
public URI getUri() { |
||||
return uri; |
||||
} |
||||
|
||||
/** |
||||
* @param uri the uri to set |
||||
*/ |
||||
public void setUri(URI uri) { |
||||
this.uri = uri; |
||||
} |
||||
|
||||
/** |
||||
* @return the secure |
||||
*/ |
||||
@Override |
||||
public boolean isSecure() { |
||||
return secure; |
||||
} |
||||
|
||||
/** |
||||
* @param secure the secure to set |
||||
*/ |
||||
public void setSecure(boolean secure) { |
||||
this.secure = secure; |
||||
} |
||||
|
||||
/** |
||||
* @return the principal |
||||
*/ |
||||
@Override |
||||
public Principal getPrincipal() { |
||||
return principal; |
||||
} |
||||
|
||||
/** |
||||
* @param principal the principal to set |
||||
*/ |
||||
public void setPrincipal(Principal principal) { |
||||
this.principal = principal; |
||||
} |
||||
|
||||
/** |
||||
* @return the remoteHostName |
||||
*/ |
||||
@Override |
||||
public String getRemoteHostName() { |
||||
return remoteHostName; |
||||
} |
||||
|
||||
/** |
||||
* @param remoteHostName the remoteHostName to set |
||||
*/ |
||||
public void setRemoteHostName(String remoteHostName) { |
||||
this.remoteHostName = remoteHostName; |
||||
} |
||||
|
||||
/** |
||||
* @return the remoteAddress |
||||
*/ |
||||
@Override |
||||
public String getRemoteAddress() { |
||||
return remoteAddress; |
||||
} |
||||
|
||||
/** |
||||
* @param remoteAddress the remoteAddress to set |
||||
*/ |
||||
public void setRemoteAddress(String remoteAddress) { |
||||
this.remoteAddress = remoteAddress; |
||||
} |
||||
|
||||
/** |
||||
* @return the open |
||||
*/ |
||||
@Override |
||||
public boolean isOpen() { |
||||
return open; |
||||
} |
||||
|
||||
/** |
||||
* @param open the open to set |
||||
*/ |
||||
public void setOpen(boolean open) { |
||||
this.open = open; |
||||
} |
||||
|
||||
public List<WebSocketMessage<?>> getSentMessages() { |
||||
return this.messages; |
||||
} |
||||
|
||||
public CloseStatus getCloseStatus() { |
||||
return this.status; |
||||
} |
||||
|
||||
@Override |
||||
public void sendMessage(WebSocketMessage<?> message) throws IOException { |
||||
this.messages.add(message); |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
this.open = false; |
||||
} |
||||
|
||||
@Override |
||||
public void close(CloseStatus status) throws IOException { |
||||
this.open = false; |
||||
this.status = status; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue