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-2017 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.
@@ -33,6 +33,8 @@ public interface HttpRequest extends HttpMessage {
* Return the HTTP method of the request.
* @return the HTTP method as an HttpMethod enum value, or {@code null}
* if not resolvable (e.g. in case of a non-standard HTTP method)
* @see #getMethodValue()
* @see HttpMethod#resolve(String)
*/
@Nullable
default HttpMethod getMethod() {
@@ -40,13 +42,16 @@ public interface HttpRequest extends HttpMessage {
}
/**
* Return the HTTP method of the request as a String
* @return the HTTP method as a String
* Return the HTTP method of the request as a String value.
* @return the HTTP method as a plain String
* @since 5.0
* @see #getMethod()
*/
String getMethodValue();
/**
* Return the URI of the request.
* Return the URI of the request (including a query string if any,
* but only if it is well-formed for a URI representation).
* @return the URI of the request (never {@code null})
*/
URI getURI();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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,6 +50,7 @@ import org.springframework.util.StringUtils;
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.0
*/
public class ServletServerHttpRequest implements ServerHttpRequest {
@@ -61,6 +62,9 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
private final HttpServletRequest servletRequest;
@Nullable
private URI uri;
@Nullable
private HttpHeaders headers;
@@ -85,6 +89,12 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
return this.servletRequest;
}
@Override
@Nullable
public HttpMethod getMethod() {
return HttpMethod.resolve(this.servletRequest.getMethod());
}
@Override
public String getMethodValue() {
return this.servletRequest.getMethod();
@@ -92,17 +102,34 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
@Override
public URI getURI() {
try {
StringBuffer url = this.servletRequest.getRequestURL();
String query = this.servletRequest.getQueryString();
if (StringUtils.hasText(query)) {
url.append('?').append(query);
if (this.uri == null) {
String urlString = null;
boolean hasQuery = false;
try {
StringBuffer url = this.servletRequest.getRequestURL();
String query = this.servletRequest.getQueryString();
hasQuery = StringUtils.hasText(query);
if (hasQuery) {
url.append('?').append(query);
}
urlString = url.toString();
this.uri = new URI(urlString);
}
catch (URISyntaxException ex) {
if (true || !hasQuery) {
throw new IllegalStateException("Could not resolve HttpServletRequest as URI: " + urlString, ex);
}
// Maybe a malformed query string... try plain request URL
try {
urlString = this.servletRequest.getRequestURL().toString();
this.uri = new URI(urlString);
}
catch (URISyntaxException ex2) {
throw new IllegalStateException("Could not resolve HttpServletRequest as URI: " + urlString, ex2);
}
}
return new URI(url.toString());
}
catch (URISyntaxException ex) {
throw new IllegalStateException("Could not get HttpServletRequest URI: " + ex.getMessage(), ex);
}
return this.uri;
}
@Override

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.
@@ -16,7 +16,9 @@
package org.springframework.http.server;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.List;
@@ -33,6 +35,7 @@ import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
* @author Juergen Hoeller
*/
public class ServletServerHttpRequestTests {
@@ -42,30 +45,56 @@ public class ServletServerHttpRequestTests {
@Before
public void create() throws Exception {
public void create() {
mockRequest = new MockHttpServletRequest();
request = new ServletServerHttpRequest(mockRequest);
}
@Test
public void getMethod() throws Exception {
public void getMethod() {
mockRequest.setMethod("POST");
assertEquals("Invalid method", HttpMethod.POST, request.getMethod());
}
@Test
public void getURI() throws Exception {
public void getUriForSimplePath() throws URISyntaxException {
URI uri = new URI("http://example.com/path");
mockRequest.setServerName(uri.getHost());
mockRequest.setServerPort(uri.getPort());
mockRequest.setRequestURI(uri.getPath());
mockRequest.setQueryString(uri.getQuery());
assertEquals(uri, request.getURI());
}
@Test
public void getUriWithQueryString() throws URISyntaxException {
URI uri = new URI("http://example.com/path?query");
mockRequest.setServerName(uri.getHost());
mockRequest.setServerPort(uri.getPort());
mockRequest.setRequestURI(uri.getPath());
mockRequest.setQueryString(uri.getQuery());
assertEquals("Invalid uri", uri, request.getURI());
assertEquals(uri, request.getURI());
}
@Test // SPR-16414
public void getUriWithQueryParam() throws URISyntaxException {
mockRequest.setServerName("example.com");
mockRequest.setRequestURI("/path");
mockRequest.setQueryString("query=foo");
assertEquals(new URI("http://example.com/path?query=foo"), request.getURI());
}
@Test // SPR-16414
public void getUriWithMalformedQueryParam() throws URISyntaxException {
mockRequest.setServerName("example.com");
mockRequest.setRequestURI("/path");
mockRequest.setQueryString("query=foo%%x");
assertEquals(new URI("http://example.com/path"), request.getURI());
}
@Test // SPR-13876
public void getUriWithEncoding() throws Exception {
public void getUriWithEncoding() throws URISyntaxException {
URI uri = new URI("https://example.com/%E4%B8%AD%E6%96%87" +
"?redirect=https%3A%2F%2Fgithub.com%2Fspring-projects%2Fspring-framework");
mockRequest.setScheme(uri.getScheme());
@@ -73,11 +102,11 @@ public class ServletServerHttpRequestTests {
mockRequest.setServerPort(uri.getPort());
mockRequest.setRequestURI(uri.getRawPath());
mockRequest.setQueryString(uri.getRawQuery());
assertEquals("Invalid uri", uri, request.getURI());
assertEquals(uri, request.getURI());
}
@Test
public void getHeaders() throws Exception {
public void getHeaders() {
String headerName = "MyHeader";
String headerValue1 = "value1";
String headerValue2 = "value2";
@@ -98,7 +127,7 @@ public class ServletServerHttpRequestTests {
}
@Test
public void getHeadersWithEmptyContentTypeAndEncoding() throws Exception {
public void getHeadersWithEmptyContentTypeAndEncoding() {
String headerName = "MyHeader";
String headerValue1 = "value1";
String headerValue2 = "value2";
@@ -118,7 +147,7 @@ public class ServletServerHttpRequestTests {
}
@Test
public void getBody() throws Exception {
public void getBody() throws IOException {
byte[] content = "Hello World".getBytes("UTF-8");
mockRequest.setContent(content);
@@ -127,7 +156,7 @@ public class ServletServerHttpRequestTests {
}
@Test
public void getFormBody() throws Exception {
public void getFormBody() throws IOException {
// Charset (SPR-8676)
mockRequest.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
mockRequest.setMethod("POST");