1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.socket.sockjs.transport.handler;
18
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.Map;
22
23 import org.hamcrest.Matchers;
24 import org.junit.Before;
25 import org.junit.Test;
26 import org.mockito.Mock;
27 import org.mockito.MockitoAnnotations;
28
29 import org.springframework.scheduling.TaskScheduler;
30 import org.springframework.web.socket.AbstractHttpRequestTests;
31 import org.springframework.web.socket.WebSocketHandler;
32 import org.springframework.web.socket.handler.TestPrincipal;
33 import org.springframework.web.socket.server.HandshakeHandler;
34 import org.springframework.web.socket.server.support.OriginHandshakeInterceptor;
35 import org.springframework.web.socket.sockjs.transport.SockJsSessionFactory;
36 import org.springframework.web.socket.sockjs.transport.TransportHandler;
37 import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService;
38 import org.springframework.web.socket.sockjs.transport.TransportType;
39 import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig;
40 import org.springframework.web.socket.sockjs.transport.session.TestSockJsSession;
41
42 import static org.junit.Assert.*;
43 import static org.mockito.BDDMockito.*;
44
45
46
47
48
49
50
51 public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
52
53 private static final String sockJsPrefix = "/mysockjs";
54
55 private static final String sessionId = "session1";
56
57 private static final String sessionUrlPrefix = "/server1/" + sessionId + "/";
58
59
60 @Mock private SessionCreatingTransportHandler xhrHandler;
61
62 @Mock private TransportHandler xhrSendHandler;
63
64 @Mock private SessionCreatingTransportHandler jsonpHandler;
65
66 @Mock private TransportHandler jsonpSendHandler;
67
68 @Mock private HandshakeTransportHandler wsTransportHandler;
69
70 @Mock private WebSocketHandler wsHandler;
71
72 @Mock private TaskScheduler taskScheduler;
73
74 private TestSockJsSession session;
75
76 private TransportHandlingSockJsService service;
77
78
79 @Before
80 public void setup() {
81 super.setUp();
82 MockitoAnnotations.initMocks(this);
83
84 Map<String, Object> attributes = Collections.emptyMap();
85 this.session = new TestSockJsSession(sessionId, new StubSockJsServiceConfig(), this.wsHandler, attributes);
86
87 given(this.xhrHandler.getTransportType()).willReturn(TransportType.XHR);
88 given(this.xhrHandler.createSession(sessionId, this.wsHandler, attributes)).willReturn(this.session);
89 given(this.xhrSendHandler.getTransportType()).willReturn(TransportType.XHR_SEND);
90 given(this.jsonpHandler.getTransportType()).willReturn(TransportType.JSONP);
91 given(this.jsonpHandler.createSession(sessionId, this.wsHandler, attributes)).willReturn(this.session);
92 given(this.jsonpSendHandler.getTransportType()).willReturn(TransportType.JSONP_SEND);
93 given(this.wsTransportHandler.getTransportType()).willReturn(TransportType.WEBSOCKET);
94
95 this.service = new TransportHandlingSockJsService(this.taskScheduler, this.xhrHandler, this.xhrSendHandler);
96 }
97
98 @Test
99 public void defaultTransportHandlers() {
100 DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class));
101 Map<TransportType, TransportHandler> handlers = service.getTransportHandlers();
102
103 assertEquals(8, handlers.size());
104 assertNotNull(handlers.get(TransportType.WEBSOCKET));
105 assertNotNull(handlers.get(TransportType.XHR));
106 assertNotNull(handlers.get(TransportType.XHR_SEND));
107 assertNotNull(handlers.get(TransportType.XHR_STREAMING));
108 assertNotNull(handlers.get(TransportType.JSONP));
109 assertNotNull(handlers.get(TransportType.JSONP_SEND));
110 assertNotNull(handlers.get(TransportType.HTML_FILE));
111 assertNotNull(handlers.get(TransportType.EVENT_SOURCE));
112 }
113
114 @Test
115 public void defaultTransportHandlersWithOverride() {
116 XhrReceivingTransportHandler xhrHandler = new XhrReceivingTransportHandler();
117
118 DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), xhrHandler);
119 Map<TransportType, TransportHandler> handlers = service.getTransportHandlers();
120
121 assertEquals(8, handlers.size());
122 assertSame(xhrHandler, handlers.get(xhrHandler.getTransportType()));
123 }
124
125 @Test(expected = IllegalArgumentException.class)
126 public void nullAllowedOriginList() {
127 this.service.setAllowedOrigins(null);
128 }
129
130 @Test
131 public void emptyAllowedOriginList() {
132 this.service.setAllowedOrigins(Arrays.asList());
133 assertThat(this.service.getAllowedOrigins(), Matchers.empty());
134 }
135
136 @Test(expected = IllegalArgumentException.class)
137 public void invalidAllowedOrigin() {
138 this.service.setAllowedOrigins(Arrays.asList("domain.com"));
139 }
140
141 @Test
142 public void validAllowedOrigins() {
143 this.service.setAllowedOrigins(Arrays.asList("http://domain.com", "https://domain.com", "*"));
144 }
145
146 @Test
147 public void customizedTransportHandlerList() {
148 TransportHandlingSockJsService service = new TransportHandlingSockJsService(
149 mock(TaskScheduler.class), new XhrPollingTransportHandler(), new XhrReceivingTransportHandler());
150 Map<TransportType, TransportHandler> actualHandlers = service.getTransportHandlers();
151
152 assertEquals(2, actualHandlers.size());
153 }
154
155 @Test
156 public void handleTransportRequestXhr() throws Exception {
157 String sockJsPath = sessionUrlPrefix + "xhr";
158 setRequest("POST", sockJsPrefix + sockJsPath);
159 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
160
161 assertEquals(200, this.servletResponse.getStatus());
162 verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
163 verify(taskScheduler).scheduleAtFixedRate(any(Runnable.class), eq(service.getDisconnectDelay()));
164
165 assertEquals("no-store, no-cache, must-revalidate, max-age=0", this.response.getHeaders().getCacheControl());
166 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
167 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
168 }
169
170 @Test
171 public void handleTransportRequestXhrAllowedOriginsMatch() throws Exception {
172 String sockJsPath = sessionUrlPrefix + "xhr";
173 setRequest("POST", sockJsPrefix + sockJsPath);
174 this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com", "http://mydomain2.com"));
175 setOrigin("http://mydomain1.com");
176 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
177
178 assertEquals(200, this.servletResponse.getStatus());
179 assertEquals("http://mydomain1.com", this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
180 assertEquals("true", this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
181 }
182
183 @Test
184 public void handleTransportRequestXhrAllowedOriginsNoMatch() throws Exception {
185 String sockJsPath = sessionUrlPrefix + "xhr";
186 setRequest("POST", sockJsPrefix + sockJsPath);
187 this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com", "http://mydomain2.com"));
188 setOrigin("http://mydomain3.com");
189 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
190
191 assertEquals(403, this.servletResponse.getStatus());
192 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
193 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
194 }
195
196 @Test
197 public void handleTransportRequestXhrOptions() throws Exception {
198 String sockJsPath = sessionUrlPrefix + "xhr";
199 setRequest("OPTIONS", sockJsPrefix + sockJsPath);
200 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
201
202 assertEquals(204, this.servletResponse.getStatus());
203 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
204 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
205 assertNull(this.response.getHeaders().getFirst("Access-Control-Allow-Methods"));
206 }
207
208 @Test
209 public void handleTransportRequestNoSuitableHandler() throws Exception {
210 String sockJsPath = sessionUrlPrefix + "eventsource";
211 setRequest("POST", sockJsPrefix + sockJsPath);
212 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
213
214 assertEquals(404, this.servletResponse.getStatus());
215 }
216
217 @Test
218 public void handleTransportRequestXhrSend() throws Exception {
219 String sockJsPath = sessionUrlPrefix + "xhr_send";
220 setRequest("POST", sockJsPrefix + sockJsPath);
221 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
222
223 assertEquals(404, this.servletResponse.getStatus());
224
225 resetResponse();
226 sockJsPath = sessionUrlPrefix + "xhr";
227 setRequest("POST", sockJsPrefix + sockJsPath);
228 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
229
230 assertEquals(200, this.servletResponse.getStatus());
231 verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
232
233 resetResponse();
234 sockJsPath = sessionUrlPrefix + "xhr_send";
235 setRequest("POST", sockJsPrefix + sockJsPath);
236 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
237
238 assertEquals(200, this.servletResponse.getStatus());
239 verify(this.xhrSendHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
240 }
241
242 @Test
243 public void handleTransportRequestXhrSendWithDifferentUser() throws Exception {
244 String sockJsPath = sessionUrlPrefix + "xhr";
245 setRequest("POST", sockJsPrefix + sockJsPath);
246 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
247
248 assertEquals(200, this.servletResponse.getStatus());
249 verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
250
251 this.session.setPrincipal(new TestPrincipal("little red riding hood"));
252 this.servletRequest.setUserPrincipal(new TestPrincipal("wolf"));
253
254 resetResponse();
255 reset(this.xhrSendHandler);
256 sockJsPath = sessionUrlPrefix + "xhr_send";
257 setRequest("POST", sockJsPrefix + sockJsPath);
258 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
259
260 assertEquals(404, this.servletResponse.getStatus());
261 verifyNoMoreInteractions(this.xhrSendHandler);
262 }
263
264 @Test
265 public void handleTransportRequestJsonp() throws Exception {
266 TransportHandlingSockJsService jsonpService = new TransportHandlingSockJsService(this.taskScheduler, this.jsonpHandler, this.jsonpSendHandler);
267 String sockJsPath = sessionUrlPrefix+ "jsonp";
268 setRequest("GET", sockJsPrefix + sockJsPath);
269 jsonpService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
270 assertEquals(404, this.servletResponse.getStatus());
271
272 resetRequestAndResponse();
273 jsonpService.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
274 setRequest("GET", sockJsPrefix + sockJsPath);
275 jsonpService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
276 assertEquals(404, this.servletResponse.getStatus());
277
278 resetRequestAndResponse();
279 jsonpService.setAllowedOrigins(Arrays.asList("*"));
280 setRequest("GET", sockJsPrefix + sockJsPath);
281 jsonpService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
282 assertNotEquals(404, this.servletResponse.getStatus());
283 }
284
285 @Test
286 public void handleTransportRequestWebsocket() throws Exception {
287 TransportHandlingSockJsService wsService = new TransportHandlingSockJsService(this.taskScheduler, this.wsTransportHandler);
288 String sockJsPath = "/websocket";
289 setRequest("GET", sockJsPrefix + sockJsPath);
290 wsService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
291 assertNotEquals(403, this.servletResponse.getStatus());
292
293 resetRequestAndResponse();
294 OriginHandshakeInterceptor interceptor = new OriginHandshakeInterceptor(Arrays.asList("http://mydomain1.com"));
295 wsService.setHandshakeInterceptors(Arrays.asList(interceptor));
296 setRequest("GET", sockJsPrefix + sockJsPath);
297 setOrigin("http://mydomain1.com");
298 wsService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
299 assertNotEquals(403, this.servletResponse.getStatus());
300
301 resetRequestAndResponse();
302 setRequest("GET", sockJsPrefix + sockJsPath);
303 setOrigin("http://mydomain2.com");
304 wsService.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
305 assertEquals(403, this.servletResponse.getStatus());
306 }
307
308 @Test
309 public void handleTransportRequestIframe() throws Exception {
310 String sockJsPath = "/iframe.html";
311 setRequest("GET", sockJsPrefix + sockJsPath);
312 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
313 assertNotEquals(404, this.servletResponse.getStatus());
314 assertEquals("SAMEORIGIN", this.servletResponse.getHeader("X-Frame-Options"));
315
316 resetRequestAndResponse();
317 setRequest("GET", sockJsPrefix + sockJsPath);
318 this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
319 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
320 assertEquals(404, this.servletResponse.getStatus());
321 assertNull(this.servletResponse.getHeader("X-Frame-Options"));
322
323 resetRequestAndResponse();
324 setRequest("GET", sockJsPrefix + sockJsPath);
325 this.service.setAllowedOrigins(Arrays.asList("*"));
326 this.service.handleRequest(this.request, this.response, sockJsPath, this.wsHandler);
327 assertNotEquals(404, this.servletResponse.getStatus());
328 assertNull(this.servletResponse.getHeader("X-Frame-Options"));
329 }
330
331
332 interface SessionCreatingTransportHandler extends TransportHandler, SockJsSessionFactory {
333 }
334
335 interface HandshakeTransportHandler extends TransportHandler, HandshakeHandler {
336 }
337
338 }