SockJsServiceTests.java 8.2 KB
Newer Older
R
Rossen Stoyanchev 已提交
1
/*
2
 * Copyright 2002-2014 the original author or authors.
R
Rossen Stoyanchev 已提交
3 4 5 6 7
 *
 * 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
 *
S
Sam Brannen 已提交
8
 *      http://www.apache.org/licenses/LICENSE-2.0
R
Rossen Stoyanchev 已提交
9 10 11 12 13 14 15 16
 *
 * 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.
 */

17
package org.springframework.web.socket.sockjs.support;
R
Rossen Stoyanchev 已提交
18 19 20

import java.io.IOException;

P
Phillip Webb 已提交
21 22 23
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

R
Rossen Stoyanchev 已提交
24 25 26 27 28
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
29
import org.springframework.http.server.ServletServerHttpResponse;
R
Rossen Stoyanchev 已提交
30 31 32 33
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
34
import org.springframework.web.socket.sockjs.SockJsException;
R
Rossen Stoyanchev 已提交
35 36

import static org.junit.Assert.*;
P
Phillip Webb 已提交
37
import static org.mockito.BDDMockito.*;
R
Rossen Stoyanchev 已提交
38 39

/**
40 41
 * Test fixture for {@link AbstractSockJsService}.
 *
R
Rossen Stoyanchev 已提交
42 43
 * @author Rossen Stoyanchev
 */
S
Sam Brannen 已提交
44
public class SockJsServiceTests extends AbstractHttpRequestTests {
R
Rossen Stoyanchev 已提交
45 46 47 48 49 50

	private TestSockJsService service;

	private WebSocketHandler handler;


51
	@Override
R
Rossen Stoyanchev 已提交
52 53 54 55 56 57
	@Before
	public void setUp() {
		super.setUp();
		this.service = new TestSockJsService(new ThreadPoolTaskScheduler());
	}

58 59
	@Test
	public void validateRequest() throws Exception {
R
Rossen Stoyanchev 已提交
60

61
		this.service.setWebSocketEnabled(false);
62
		resetResponseAndHandleRequest("GET", "/echo/server/session/websocket", HttpStatus.NOT_FOUND);
63

64
		this.service.setWebSocketEnabled(true);
65 66 67 68 69 70 71 72 73 74
		resetResponseAndHandleRequest("GET", "/echo/server/session/websocket", HttpStatus.OK);

		resetResponseAndHandleRequest("GET", "/echo//", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo///", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo/other", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo//service/websocket", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo/server//websocket", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo/server/session/", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo/s.erver/session/websocket", HttpStatus.NOT_FOUND);
		resetResponseAndHandleRequest("GET", "/echo/server/s.ession/websocket", HttpStatus.NOT_FOUND);
R
Rossen Stoyanchev 已提交
75 76
	}

77 78 79
	@Test
	public void handleInfoGet() throws Exception {

80
		resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
81 82 83 84 85 86 87 88

		assertEquals("application/json;charset=UTF-8", this.servletResponse.getContentType());
		assertEquals("*", this.servletResponse.getHeader("Access-Control-Allow-Origin"));
		assertEquals("true", this.servletResponse.getHeader("Access-Control-Allow-Credentials"));
		assertEquals("no-store, no-cache, must-revalidate, max-age=0", this.servletResponse.getHeader("Cache-Control"));

		String body = this.servletResponse.getContentAsString();
		assertEquals("{\"entropy\"", body.substring(0, body.indexOf(':')));
89
		assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":true,\"websocket\":true}",
90 91
				body.substring(body.indexOf(',')));

92
		this.service.setSessionCookieNeeded(false);
93
		this.service.setWebSocketEnabled(false);
94
		resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
95 96 97 98 99

		body = this.servletResponse.getContentAsString();
		assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":false,\"websocket\":false}",
				body.substring(body.indexOf(',')));
	}
R
Rossen Stoyanchev 已提交
100

101 102 103 104 105 106 107 108 109 110 111 112 113
	// SPR-11443

	@Test
	public void handleInfoGetCorsFilter() throws Exception {

		// Simulate scenario where Filter would have already set CORS headers
		this.servletResponse.setHeader("Access-Control-Allow-Origin", "foobar:123");

		handleRequest("GET", "/echo/info", HttpStatus.OK);

		assertEquals("foobar:123", this.servletResponse.getHeader("Access-Control-Allow-Origin"));
	}

114 115 116 117 118 119
	// SPR-11919

	@Test
	public void handleInfoGetWildflyNPE() throws Exception {
		HttpServletResponse mockResponse = mock(HttpServletResponse.class);
		ServletOutputStream ous = mock(ServletOutputStream.class);
P
Phillip Webb 已提交
120 121
		given(mockResponse.getHeaders("Access-Control-Allow-Origin")).willThrow(NullPointerException.class);
		given(mockResponse.getOutputStream()).willReturn(ous);
122 123 124 125
		this.response = new ServletServerHttpResponse(mockResponse);

		handleRequest("GET", "/echo/info", HttpStatus.OK);

B
Brian Clozel 已提交
126
		verify(mockResponse, times(1)).getOutputStream();
127 128
	}

R
Rossen Stoyanchev 已提交
129
	@Test
130 131 132 133
	public void handleInfoOptions() throws Exception {

		this.servletRequest.addHeader("Access-Control-Request-Headers", "Last-Modified");

134
		resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
135
		this.response.flush();
136 137 138 139 140 141

		assertEquals("*", this.servletResponse.getHeader("Access-Control-Allow-Origin"));
		assertEquals("true", this.servletResponse.getHeader("Access-Control-Allow-Credentials"));
		assertEquals("Last-Modified", this.servletResponse.getHeader("Access-Control-Allow-Headers"));
		assertEquals("OPTIONS, GET", this.servletResponse.getHeader("Access-Control-Allow-Methods"));
		assertEquals("31536000", this.servletResponse.getHeader("Access-Control-Max-Age"));
R
Rossen Stoyanchev 已提交
142 143 144
	}

	@Test
145 146
	public void handleIframeRequest() throws Exception {

147
		resetResponseAndHandleRequest("GET", "/echo/iframe.html", HttpStatus.OK);
148 149 150

		assertEquals("text/html;charset=UTF-8", this.servletResponse.getContentType());
		assertTrue(this.servletResponse.getContentAsString().startsWith("<!DOCTYPE html>\n"));
151
		assertEquals(490, this.servletResponse.getContentLength());
152
		assertEquals("public, max-age=31536000", this.response.getHeaders().getCacheControl());
153
		assertEquals("\"06b486b3208b085d9e3220f456a6caca4\"", this.response.getHeaders().getETag());
R
Rossen Stoyanchev 已提交
154 155 156
	}

	@Test
157 158
	public void handleIframeRequestNotModified() throws Exception {

159
		this.servletRequest.addHeader("If-None-Match", "\"06b486b3208b085d9e3220f456a6caca4\"");
160

161
		resetResponseAndHandleRequest("GET", "/echo/iframe.html", HttpStatus.NOT_MODIFIED);
R
Rossen Stoyanchev 已提交
162 163 164
	}

	@Test
165
	public void handleRawWebSocketRequest() throws Exception {
R
Rossen Stoyanchev 已提交
166

167
		resetResponseAndHandleRequest("GET", "/echo", HttpStatus.OK);
168 169
		assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());

170
		resetResponseAndHandleRequest("GET", "/echo/websocket", HttpStatus.OK);
171 172
		assertNull("Raw WebSocket should not open a SockJS session", this.service.sessionId);
		assertSame(this.handler, this.service.handler);
R
Rossen Stoyanchev 已提交
173 174
	}

175 176
	@Test
	public void handleEmptyContentType() throws Exception {
R
Rossen Stoyanchev 已提交
177

178 179 180 181 182 183 184
		servletRequest.setContentType("");
		resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);

		assertEquals("Invalid/empty content should have been ignored", 200, this.servletResponse.getStatus());
	}

	private void resetResponseAndHandleRequest(String httpMethod, String uri, HttpStatus httpStatus) throws IOException {
R
Rossen Stoyanchev 已提交
185
		resetResponse();
186 187 188 189
		handleRequest(httpMethod, uri, httpStatus);
	}

	private void handleRequest(String httpMethod, String uri, HttpStatus httpStatus) throws IOException {
190
		setRequest(httpMethod, uri);
191 192
		String sockJsPath = uri.substring("/echo".length());
		this.service.handleRequest(this.request, this.response, sockJsPath, this.handler);
R
Rossen Stoyanchev 已提交
193 194 195 196

		assertEquals(httpStatus.value(), this.servletResponse.getStatus());
	}

197

R
Rossen Stoyanchev 已提交
198 199 200 201
	private static class TestSockJsService extends AbstractSockJsService {

		private String sessionId;

202
		@SuppressWarnings("unused")
203
		private String transport;
R
Rossen Stoyanchev 已提交
204 205 206 207 208 209 210 211

		private WebSocketHandler handler;

		public TestSockJsService(TaskScheduler scheduler) {
			super(scheduler);
		}

		@Override
212 213
		protected void handleRawWebSocketRequest(ServerHttpRequest req, ServerHttpResponse res,
				WebSocketHandler handler) throws IOException {
R
Rossen Stoyanchev 已提交
214 215 216 217 218

			this.handler = handler;
		}

		@Override
219
		protected void handleTransportRequest(ServerHttpRequest req, ServerHttpResponse res, WebSocketHandler handler,
220
				String sessionId, String transport) throws SockJsException {
R
Rossen Stoyanchev 已提交
221 222

			this.sessionId = sessionId;
223
			this.transport = transport;
R
Rossen Stoyanchev 已提交
224 225 226 227 228
			this.handler = handler;
		}
	}

}