ServletServerHttpRequest.getURI() ignores malformed query string

The resolved URI instance is also being cached now. This should not make a difference in a real Servlet environment but does affect tests which assumed they could modify an HttpServletRequest path behind a pre-created ServletServerHttpRequest instance. Our WebSocket test base class has been revised accordingly, re-creating the ServletServerHttpRequest in such a case.

Issue: SPR-16414
This commit is contained in:
Juergen Hoeller
2018-01-24 22:13:40 +01:00
parent 6db1b692b8
commit 0e6f8df0a8
9 changed files with 151 additions and 97 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2018 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.
@@ -45,13 +45,14 @@ public abstract class AbstractHttpRequestTests {
@Before
public void setUp() {
public void setup() {
resetRequestAndResponse();
}
protected void setRequest(String method, String requestUri) {
this.servletRequest.setMethod(method);
this.servletRequest.setRequestURI(requestUri);
this.request = new ServletServerHttpRequest(this.servletRequest);
}
protected void resetRequestAndResponse() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2018 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.
@@ -50,19 +50,18 @@ public class DefaultHandshakeHandlerTests extends AbstractHttpRequestTests {
@Before
public void setup() throws Exception {
public void setup() {
super.setup();
MockitoAnnotations.initMocks(this);
this.handshakeHandler = new DefaultHandshakeHandler(this.upgradeStrategy);
}
@Test
public void supportedSubProtocols() throws Exception {
public void supportedSubProtocols() {
this.handshakeHandler.setSupportedProtocols("stomp", "mqtt");
given(this.upgradeStrategy.getSupportedVersions()).willReturn(new String[] {"13"});
this.servletRequest.setMethod("GET");
WebSocketHttpHeaders headers = new WebSocketHttpHeaders(this.request.getHeaders());
@@ -73,22 +72,20 @@ public class DefaultHandshakeHandlerTests extends AbstractHttpRequestTests {
headers.setSecWebSocketProtocol("STOMP");
WebSocketHandler handler = new TextWebSocketHandler();
Map<String, Object> attributes = Collections.<String, Object>emptyMap();
Map<String, Object> attributes = Collections.emptyMap();
this.handshakeHandler.doHandshake(this.request, this.response, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response,
"STOMP", Collections.<WebSocketExtension>emptyList(), null, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response, "STOMP",
Collections.emptyList(), null, handler, attributes);
}
@Test
public void supportedExtensions() throws Exception {
public void supportedExtensions() {
WebSocketExtension extension1 = new WebSocketExtension("ext1");
WebSocketExtension extension2 = new WebSocketExtension("ext2");
given(this.upgradeStrategy.getSupportedVersions()).willReturn(new String[] {"13"});
given(this.upgradeStrategy.getSupportedExtensions(this.request)).willReturn(Arrays.asList(extension1));
given(this.upgradeStrategy.getSupportedExtensions(this.request)).willReturn(Collections.singletonList(extension1));
this.servletRequest.setMethod("GET");
@@ -103,14 +100,13 @@ public class DefaultHandshakeHandlerTests extends AbstractHttpRequestTests {
Map<String, Object> attributes = Collections.<String, Object>emptyMap();
this.handshakeHandler.doHandshake(this.request, this.response, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response, null, Arrays.asList(extension1),
null, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response, null,
Collections.singletonList(extension1), null, handler, attributes);
}
@Test
public void subProtocolCapableHandler() throws Exception {
given(this.upgradeStrategy.getSupportedVersions()).willReturn(new String[]{"13"});
public void subProtocolCapableHandler() {
given(this.upgradeStrategy.getSupportedVersions()).willReturn(new String[] {"13"});
this.servletRequest.setMethod("GET");
@@ -125,14 +121,13 @@ public class DefaultHandshakeHandlerTests extends AbstractHttpRequestTests {
Map<String, Object> attributes = Collections.<String, Object>emptyMap();
this.handshakeHandler.doHandshake(this.request, this.response, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response,
"v11.stomp", Collections.<WebSocketExtension>emptyList(), null, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response, "v11.stomp",
Collections.emptyList(), null, handler, attributes);
}
@Test
public void subProtocolCapableHandlerNoMatch() throws Exception {
given(this.upgradeStrategy.getSupportedVersions()).willReturn(new String[]{"13"});
public void subProtocolCapableHandlerNoMatch() {
given(this.upgradeStrategy.getSupportedVersions()).willReturn(new String[] {"13"});
this.servletRequest.setMethod("GET");
@@ -147,8 +142,8 @@ public class DefaultHandshakeHandlerTests extends AbstractHttpRequestTests {
Map<String, Object> attributes = Collections.<String, Object>emptyMap();
this.handshakeHandler.doHandshake(this.request, this.response, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response,
null, Collections.<WebSocketExtension>emptyList(), null, handler, attributes);
verify(this.upgradeStrategy).upgrade(this.request, this.response, null,
Collections.emptyList(), null, handler, attributes);
}
@@ -156,8 +151,7 @@ public class DefaultHandshakeHandlerTests extends AbstractHttpRequestTests {
private final List<String> subProtocols;
private SubProtocolCapableHandler(String... subProtocols) {
public SubProtocolCapableHandler(String... subProtocols) {
this.subProtocols = Arrays.asList(subProtocols);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -52,6 +52,8 @@ public class HandshakeInterceptorChainTests extends AbstractHttpRequestTests {
@Before
public void setup() {
super.setup();
i1 = mock(HandshakeInterceptor.class);
i2 = mock(HandshakeInterceptor.class);
i3 = mock(HandshakeInterceptor.class);

View File

@@ -18,13 +18,13 @@ package org.springframework.web.socket.sockjs.support;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Collections;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest;
@@ -55,15 +55,14 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
@Override
@Before
public void setUp() {
super.setUp();
public void setup() {
super.setup();
this.service = new TestSockJsService(new ThreadPoolTaskScheduler());
}
@Test
public void validateRequest() throws Exception {
public void validateRequest() {
this.service.setWebSocketEnabled(false);
resetResponseAndHandleRequest("GET", "/echo/server/session/websocket", HttpStatus.NOT_FOUND);
@@ -82,7 +81,7 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
@Test
public void handleInfoGet() throws Exception {
public void handleInfoGet() throws IOException {
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
assertEquals("application/json;charset=UTF-8", this.servletResponse.getContentType());
@@ -105,7 +104,7 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":false,\"websocket\":false}",
body.substring(body.indexOf(',')));
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
assertNull(this.servletResponse.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertNull(this.servletResponse.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
@@ -113,7 +112,7 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
@Test // SPR-12226 and SPR-12660
public void handleInfoGetWithOrigin() throws Exception {
public void handleInfoGetWithOrigin() throws IOException {
this.servletRequest.setServerName("mydomain2.com");
this.servletRequest.addHeader(HttpHeaders.ORIGIN, "http://mydomain2.com");
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
@@ -126,24 +125,22 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":true,\"websocket\":true}",
body.substring(body.indexOf(',')));
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
List<String> origins = Arrays.asList("http://mydomain1.com", "http://mydomain2.com", "http://mydomain3.com");
this.service.setAllowedOrigins(origins);
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com", "http://mydomain2.com", "http://mydomain3.com"));
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
this.service.setAllowedOrigins(Arrays.asList("*"));
this.service.setAllowedOrigins(Collections.singletonList("*"));
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
this.servletRequest.setServerName("mydomain3.com");
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.FORBIDDEN);
}
@Test // SPR-11443
public void handleInfoGetCorsFilter() throws Exception {
public void handleInfoGetCorsFilter() {
// Simulate scenario where Filter would have already set CORS headers
this.servletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "foobar:123");
@@ -153,7 +150,7 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
@Test // SPR-11919
public void handleInfoGetWildflyNPE() throws Exception {
public void handleInfoGetWildflyNPE() throws IOException {
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream ous = mock(ServletOutputStream.class);
given(mockResponse.getHeaders(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).willThrow(NullPointerException.class);
@@ -166,18 +163,18 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
@Test // SPR-12660
public void handleInfoOptions() throws Exception {
public void handleInfoOptions() {
this.servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Last-Modified");
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNull(this.service.getCorsConfiguration(this.servletRequest));
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNull(this.service.getCorsConfiguration(this.servletRequest));
}
@Test // SPR-12226 and SPR-12660
public void handleInfoOptionsWithAllowedOrigin() throws Exception {
public void handleInfoOptionsWithAllowedOrigin() {
this.servletRequest.setServerName("mydomain2.com");
this.servletRequest.addHeader(HttpHeaders.ORIGIN, "http://mydomain2.com");
this.servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
@@ -185,22 +182,21 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNotNull(this.service.getCorsConfiguration(this.servletRequest));
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNotNull(this.service.getCorsConfiguration(this.servletRequest));
List<String> origins = Arrays.asList("http://mydomain1.com", "http://mydomain2.com", "http://mydomain3.com");
this.service.setAllowedOrigins(origins);
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com", "http://mydomain2.com", "http://mydomain3.com"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNotNull(this.service.getCorsConfiguration(this.servletRequest));
this.service.setAllowedOrigins(Arrays.asList("*"));
this.service.setAllowedOrigins(Collections.singletonList("*"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNotNull(this.service.getCorsConfiguration(this.servletRequest));
}
@Test // SPR-16304
public void handleInfoOptionsWithForbiddenOrigin() throws Exception {
public void handleInfoOptionsWithForbiddenOrigin() {
this.servletRequest.setServerName("mydomain3.com");
this.servletRequest.addHeader(HttpHeaders.ORIGIN, "http://mydomain2.com");
this.servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
@@ -209,34 +205,33 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
CorsConfiguration corsConfiguration = this.service.getCorsConfiguration(this.servletRequest);
assertTrue(corsConfiguration.getAllowedOrigins().isEmpty());
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.FORBIDDEN);
corsConfiguration = this.service.getCorsConfiguration(this.servletRequest);
assertEquals(Arrays.asList("http://mydomain1.com"), corsConfiguration.getAllowedOrigins());
assertEquals(Collections.singletonList("http://mydomain1.com"), corsConfiguration.getAllowedOrigins());
}
@Test // SPR-12283
public void handleInfoOptionsWithOriginAndCorsHeadersDisabled() throws Exception {
public void handleInfoOptionsWithOriginAndCorsHeadersDisabled() {
this.servletRequest.addHeader(HttpHeaders.ORIGIN, "http://mydomain2.com");
this.service.setAllowedOrigins(Arrays.asList("*"));
this.service.setAllowedOrigins(Collections.singletonList("*"));
this.service.setSuppressCors(true);
this.servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Last-Modified");
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNull(this.service.getCorsConfiguration(this.servletRequest));
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com"));
this.service.setAllowedOrigins(Collections.singletonList("http://mydomain1.com"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.FORBIDDEN);
assertNull(this.service.getCorsConfiguration(this.servletRequest));
List<String> origins = Arrays.asList("http://mydomain1.com", "http://mydomain2.com", "http://mydomain3.com");
this.service.setAllowedOrigins(origins);
this.service.setAllowedOrigins(Arrays.asList("http://mydomain1.com", "http://mydomain2.com", "http://mydomain3.com"));
resetResponseAndHandleRequest("OPTIONS", "/echo/info", HttpStatus.NO_CONTENT);
assertNull(this.service.getCorsConfiguration(this.servletRequest));
}
@Test
public void handleIframeRequest() throws Exception {
public void handleIframeRequest() throws IOException {
resetResponseAndHandleRequest("GET", "/echo/iframe.html", HttpStatus.OK);
assertEquals("text/html;charset=UTF-8", this.servletResponse.getContentType());
@@ -247,13 +242,13 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
@Test
public void handleIframeRequestNotModified() throws Exception {
public void handleIframeRequestNotModified() {
this.servletRequest.addHeader("If-None-Match", "\"0096cbd37f2a5218c33bb0826a7c74cbf\"");
resetResponseAndHandleRequest("GET", "/echo/iframe.html", HttpStatus.NOT_MODIFIED);
}
@Test
public void handleRawWebSocketRequest() throws Exception {
public void handleRawWebSocketRequest() throws IOException {
resetResponseAndHandleRequest("GET", "/echo", HttpStatus.OK);
assertEquals("Welcome to SockJS!\n", this.servletResponse.getContentAsString());
@@ -263,7 +258,7 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
@Test
public void handleEmptyContentType() throws Exception {
public void handleEmptyContentType() {
this.servletRequest.setContentType("");
resetResponseAndHandleRequest("GET", "/echo/info", HttpStatus.OK);
@@ -271,12 +266,12 @@ public class SockJsServiceTests extends AbstractHttpRequestTests {
}
private void resetResponseAndHandleRequest(String httpMethod, String uri, HttpStatus httpStatus) throws IOException {
private void resetResponseAndHandleRequest(String httpMethod, String uri, HttpStatus httpStatus) {
resetResponse();
handleRequest(httpMethod, uri, httpStatus);
}
private void handleRequest(String httpMethod, String uri, HttpStatus httpStatus) throws IOException {
private void handleRequest(String httpMethod, String uri, HttpStatus httpStatus) {
setRequest(httpMethod, uri);
String sockJsPath = uri.substring("/echo".length());
this.service.handleRequest(this.request, this.response, sockJsPath, this.handler);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -80,7 +80,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
@Before
public void setup() {
super.setUp();
super.setup();
MockitoAnnotations.initMocks(this);
Map<String, Object> attributes = Collections.emptyMap();
@@ -97,6 +97,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
this.service = new TransportHandlingSockJsService(this.taskScheduler, this.xhrHandler, this.xhrSendHandler);
}
@Test
public void defaultTransportHandlers() {
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class));

View File

@@ -52,8 +52,8 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
@Override
@Before
public void setUp() {
super.setUp();
public void setup() {
super.setup();
this.webSocketHandler = mock(WebSocketHandler.class);
this.taskScheduler = mock(TaskScheduler.class);