View Javadoc
1   /*
2    * Copyright 2002-2015 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.socket.config;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Arrays;
22  import java.util.Date;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.ScheduledFuture;
26  
27  import static org.junit.Assert.assertEquals;
28  import org.junit.Before;
29  import org.junit.Test;
30  
31  import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
32  import org.springframework.core.io.ClassPathResource;
33  import org.springframework.http.server.ServerHttpRequest;
34  import org.springframework.http.server.ServerHttpResponse;
35  import org.springframework.messaging.support.ChannelInterceptorAdapter;
36  import org.springframework.scheduling.TaskScheduler;
37  import org.springframework.scheduling.Trigger;
38  import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
39  import org.springframework.web.context.support.GenericWebApplicationContext;
40  import org.springframework.web.servlet.HandlerMapping;
41  import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
42  import org.springframework.web.socket.CloseStatus;
43  import org.springframework.web.socket.WebSocketHandler;
44  import org.springframework.web.socket.WebSocketMessage;
45  import org.springframework.web.socket.WebSocketSession;
46  import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
47  import org.springframework.web.socket.server.HandshakeHandler;
48  import org.springframework.web.socket.server.HandshakeInterceptor;
49  import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
50  import org.springframework.web.socket.server.support.OriginHandshakeInterceptor;
51  import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
52  import org.springframework.web.socket.sockjs.SockJsService;
53  import org.springframework.web.socket.sockjs.frame.SockJsMessageCodec;
54  import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
55  import org.springframework.web.socket.sockjs.transport.TransportHandler;
56  import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService;
57  import org.springframework.web.socket.sockjs.transport.TransportType;
58  import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
59  import org.springframework.web.socket.sockjs.transport.handler.EventSourceTransportHandler;
60  import org.springframework.web.socket.sockjs.transport.handler.HtmlFileTransportHandler;
61  import org.springframework.web.socket.sockjs.transport.handler.JsonpPollingTransportHandler;
62  import org.springframework.web.socket.sockjs.transport.handler.JsonpReceivingTransportHandler;
63  import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;
64  import org.springframework.web.socket.sockjs.transport.handler.XhrPollingTransportHandler;
65  import org.springframework.web.socket.sockjs.transport.handler.XhrReceivingTransportHandler;
66  import org.springframework.web.socket.sockjs.transport.handler.XhrStreamingTransportHandler;
67  
68  import static org.hamcrest.Matchers.*;
69  import static org.junit.Assert.*;
70  
71  /**
72   * Test fixture for HandlersBeanDefinitionParser.
73   * See test configuration files websocket-config-handlers-*.xml.
74   *
75   * @author Brian Clozel
76   * @author Rossen Stoyanchev
77   */
78  public class HandlersBeanDefinitionParserTests {
79  
80  	private GenericWebApplicationContext appContext;
81  
82  
83  	@Before
84  	public void setup() {
85  		this.appContext = new GenericWebApplicationContext();
86  	}
87  
88  
89  	@Test
90  	public void webSocketHandlers() {
91  		loadBeanDefinitions("websocket-config-handlers.xml");
92  
93  		Map<String, HandlerMapping> handlersMap = this.appContext.getBeansOfType(HandlerMapping.class);
94  		assertNotNull(handlersMap);
95  		assertThat(handlersMap.values(), hasSize(2));
96  
97  		for (HandlerMapping hm : handlersMap.values()) {
98  			assertTrue(hm instanceof SimpleUrlHandlerMapping);
99  			SimpleUrlHandlerMapping shm = (SimpleUrlHandlerMapping) hm;
100 
101 			if (shm.getUrlMap().keySet().contains("/foo")) {
102 				assertThat(shm.getUrlMap().keySet(), contains("/foo", "/bar"));
103 				WebSocketHttpRequestHandler handler = (WebSocketHttpRequestHandler) shm.getUrlMap().get("/foo");
104 				assertNotNull(handler);
105 				unwrapAndCheckDecoratedHandlerType(handler.getWebSocketHandler(), FooWebSocketHandler.class);
106 				HandshakeHandler handshakeHandler = handler.getHandshakeHandler();
107 				assertNotNull(handshakeHandler);
108 				assertTrue(handshakeHandler instanceof DefaultHandshakeHandler);
109 				assertFalse(handler.getHandshakeInterceptors().isEmpty());
110 				assertTrue(handler.getHandshakeInterceptors().get(0) instanceof OriginHandshakeInterceptor);
111 			}
112 			else {
113 				assertThat(shm.getUrlMap().keySet(), contains("/test"));
114 				WebSocketHttpRequestHandler handler = (WebSocketHttpRequestHandler) shm.getUrlMap().get("/test");
115 				assertNotNull(handler);
116 				unwrapAndCheckDecoratedHandlerType(handler.getWebSocketHandler(), TestWebSocketHandler.class);
117 				HandshakeHandler handshakeHandler = handler.getHandshakeHandler();
118 				assertNotNull(handshakeHandler);
119 				assertTrue(handshakeHandler instanceof DefaultHandshakeHandler);
120 				assertFalse(handler.getHandshakeInterceptors().isEmpty());
121 				assertTrue(handler.getHandshakeInterceptors().get(0) instanceof OriginHandshakeInterceptor);
122 			}
123 		}
124 	}
125 
126 	@Test
127 	@SuppressWarnings("unchecked")
128 	public void webSocketHandlersAttributes() {
129 		loadBeanDefinitions("websocket-config-handlers-attributes.xml");
130 
131 		HandlerMapping handlerMapping = this.appContext.getBean(HandlerMapping.class);
132 		assertNotNull(handlerMapping);
133 		assertTrue(handlerMapping instanceof SimpleUrlHandlerMapping);
134 
135 		SimpleUrlHandlerMapping urlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
136 		assertEquals(2, urlHandlerMapping.getOrder());
137 
138 		WebSocketHttpRequestHandler handler = (WebSocketHttpRequestHandler) urlHandlerMapping.getUrlMap().get("/foo");
139 		assertNotNull(handler);
140 		unwrapAndCheckDecoratedHandlerType(handler.getWebSocketHandler(), FooWebSocketHandler.class);
141 		HandshakeHandler handshakeHandler = handler.getHandshakeHandler();
142 		assertNotNull(handshakeHandler);
143 		assertTrue(handshakeHandler instanceof TestHandshakeHandler);
144 		List<HandshakeInterceptor> interceptors = handler.getHandshakeInterceptors();
145 		assertThat(interceptors, contains(instanceOf(FooTestInterceptor.class),
146 				instanceOf(BarTestInterceptor.class), instanceOf(OriginHandshakeInterceptor.class)));
147 
148 		handler = (WebSocketHttpRequestHandler) urlHandlerMapping.getUrlMap().get("/test");
149 		assertNotNull(handler);
150 		unwrapAndCheckDecoratedHandlerType(handler.getWebSocketHandler(), TestWebSocketHandler.class);
151 		handshakeHandler = handler.getHandshakeHandler();
152 		assertNotNull(handshakeHandler);
153 		assertTrue(handshakeHandler instanceof TestHandshakeHandler);
154 		interceptors = handler.getHandshakeInterceptors();
155 		assertThat(interceptors, contains(instanceOf(FooTestInterceptor.class),
156 				instanceOf(BarTestInterceptor.class), instanceOf(OriginHandshakeInterceptor.class)));
157 	}
158 
159 	@Test
160 	@SuppressWarnings("unchecked")
161 	public void sockJs() {
162 		loadBeanDefinitions("websocket-config-handlers-sockjs.xml");
163 
164 		SimpleUrlHandlerMapping handlerMapping = this.appContext.getBean(SimpleUrlHandlerMapping.class);
165 		assertNotNull(handlerMapping);
166 
167 		SockJsHttpRequestHandler testHandler = (SockJsHttpRequestHandler) handlerMapping.getUrlMap().get("/test/**");
168 		assertNotNull(testHandler);
169 		unwrapAndCheckDecoratedHandlerType(testHandler.getWebSocketHandler(), TestWebSocketHandler.class);
170 		SockJsService testSockJsService = testHandler.getSockJsService();
171 
172 		SockJsHttpRequestHandler fooHandler = (SockJsHttpRequestHandler) handlerMapping.getUrlMap().get("/foo/**");
173 		assertNotNull(fooHandler);
174 		unwrapAndCheckDecoratedHandlerType(fooHandler.getWebSocketHandler(), FooWebSocketHandler.class);
175 		SockJsService sockJsService = fooHandler.getSockJsService();
176 		assertNotNull(sockJsService);
177 
178 		assertSame(testSockJsService, sockJsService);
179 
180 		assertThat(sockJsService, instanceOf(DefaultSockJsService.class));
181 		DefaultSockJsService defaultSockJsService = (DefaultSockJsService) sockJsService;
182 		assertThat(defaultSockJsService.getTaskScheduler(), instanceOf(ThreadPoolTaskScheduler.class));
183 		assertFalse(defaultSockJsService.shouldSuppressCors());
184 
185 		Map<TransportType, TransportHandler> transportHandlers = defaultSockJsService.getTransportHandlers();
186 		assertThat(transportHandlers.values(),
187 				containsInAnyOrder(
188 						instanceOf(XhrPollingTransportHandler.class),
189 						instanceOf(XhrReceivingTransportHandler.class),
190 						instanceOf(JsonpPollingTransportHandler.class),
191 						instanceOf(JsonpReceivingTransportHandler.class),
192 						instanceOf(XhrStreamingTransportHandler.class),
193 						instanceOf(EventSourceTransportHandler.class),
194 						instanceOf(HtmlFileTransportHandler.class),
195 						instanceOf(WebSocketTransportHandler.class)));
196 
197 		WebSocketTransportHandler handler = (WebSocketTransportHandler) transportHandlers.get(TransportType.WEBSOCKET);
198 		assertEquals(TestHandshakeHandler.class, handler.getHandshakeHandler().getClass());
199 
200 		List<HandshakeInterceptor> interceptors = defaultSockJsService.getHandshakeInterceptors();
201 		assertThat(interceptors, contains(instanceOf(FooTestInterceptor.class), instanceOf(BarTestInterceptor.class), instanceOf(OriginHandshakeInterceptor.class)));
202 	}
203 
204 	@Test
205 	@SuppressWarnings("unchecked")
206 	public void sockJsAttributes() {
207 		loadBeanDefinitions("websocket-config-handlers-sockjs-attributes.xml");
208 
209 		SimpleUrlHandlerMapping handlerMapping = appContext.getBean(SimpleUrlHandlerMapping.class);
210 		assertNotNull(handlerMapping);
211 
212 		SockJsHttpRequestHandler handler = (SockJsHttpRequestHandler) handlerMapping.getUrlMap().get("/test/**");
213 		assertNotNull(handler);
214 		unwrapAndCheckDecoratedHandlerType(handler.getWebSocketHandler(), TestWebSocketHandler.class);
215 
216 		SockJsService sockJsService = handler.getSockJsService();
217 		assertNotNull(sockJsService);
218 		assertThat(sockJsService, instanceOf(TransportHandlingSockJsService.class));
219 		TransportHandlingSockJsService transportService = (TransportHandlingSockJsService) sockJsService;
220 		assertThat(transportService.getTaskScheduler(), instanceOf(TestTaskScheduler.class));
221 		assertThat(transportService.getTransportHandlers().values(),
222 				containsInAnyOrder(
223 						instanceOf(XhrPollingTransportHandler.class),
224 						instanceOf(XhrStreamingTransportHandler.class)));
225 
226 		assertEquals("testSockJsService", transportService.getName());
227 		assertFalse(transportService.isWebSocketEnabled());
228 		assertFalse(transportService.isSessionCookieNeeded());
229 		assertEquals(2048, transportService.getStreamBytesLimit());
230 		assertEquals(256, transportService.getDisconnectDelay());
231 		assertEquals(1024, transportService.getHttpMessageCacheSize());
232 		assertEquals(20, transportService.getHeartbeatTime());
233 		assertEquals(TestMessageCodec.class, transportService.getMessageCodec().getClass());
234 
235 		List<HandshakeInterceptor> interceptors = transportService.getHandshakeInterceptors();
236 		assertThat(interceptors, contains(instanceOf(OriginHandshakeInterceptor.class)));
237 		assertEquals(Arrays.asList("http://mydomain1.com", "http://mydomain2.com"), transportService.getAllowedOrigins());
238 		assertTrue(transportService.shouldSuppressCors());
239 	}
240 
241 	private void loadBeanDefinitions(String fileName) {
242 		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.appContext);
243 		ClassPathResource resource = new ClassPathResource(fileName, HandlersBeanDefinitionParserTests.class);
244 		reader.loadBeanDefinitions(resource);
245 		this.appContext.refresh();
246 	}
247 
248 	private static void unwrapAndCheckDecoratedHandlerType(WebSocketHandler handler, Class<?> handlerClass) {
249 		if (handler instanceof WebSocketHandlerDecorator) {
250 			handler = ((WebSocketHandlerDecorator) handler).getLastHandler();
251 		}
252 		assertTrue(handlerClass.isInstance(handler));
253 	}
254 }
255 
256 
257 class TestWebSocketHandler implements WebSocketHandler {
258 
259 	@Override
260 	public void afterConnectionEstablished(WebSocketSession session) {
261 	}
262 
263 	@Override
264 	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
265 	}
266 
267 	@Override
268 	public void handleTransportError(WebSocketSession session, Throwable exception) {
269 	}
270 
271 	@Override
272 	public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {
273 	}
274 
275 	@Override
276 	public boolean supportsPartialMessages() {
277 		return false;
278 	}
279 }
280 
281 class FooWebSocketHandler extends TestWebSocketHandler {
282 }
283 
284 class TestHandshakeHandler implements HandshakeHandler {
285 
286 	@Override
287 	public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
288 			WebSocketHandler wsHandler, Map<String, Object> attributes) {
289 
290 		return false;
291 	}
292 }
293 
294 class TestChannelInterceptor extends ChannelInterceptorAdapter {
295 }
296 
297 class FooTestInterceptor implements HandshakeInterceptor {
298 
299 	@Override
300 	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
301 			WebSocketHandler wsHandler, Map<String, Object> attributes) {
302 
303 		return false;
304 	}
305 
306 	@Override
307 	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
308 			WebSocketHandler wsHandler, Exception exception) {
309 	}
310 }
311 
312 class BarTestInterceptor extends FooTestInterceptor {
313 }
314 
315 @SuppressWarnings({ "unchecked", "rawtypes" })
316 class TestTaskScheduler implements TaskScheduler {
317 
318 	@Override
319 	public ScheduledFuture schedule(Runnable task, Trigger trigger) {
320 		return null;
321 	}
322 
323 	@Override
324 	public ScheduledFuture schedule(Runnable task, Date startTime) {
325 		return null;
326 	}
327 
328 	@Override
329 	public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) {
330 		return null;
331 	}
332 
333 	@Override
334 	public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) {
335 		return null;
336 	}
337 
338 	@Override
339 	public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {
340 		return null;
341 	}
342 
343 	@Override
344 	public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) {
345 		return null;
346 	}
347 
348 }
349 
350 class TestMessageCodec implements SockJsMessageCodec {
351 
352 	@Override
353 	public String encode(String... messages) {
354 		return null;
355 	}
356 
357 	@Override
358 	public String[] decode(String content) throws IOException {
359 		return new String[0];
360 	}
361 
362 	@Override
363 	public String[] decodeInputStream(InputStream content) throws IOException {
364 		return new String[0];
365 	}
366 }