1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
73
74
75
76
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 }