Support encoded query string in reactive requests

In addition to the updates from PR#1297 this commit adds an integration
test and fixes for the resulting failures with RxNetty, Reactor Netty,
and Undertow.

Also replaced use of URLDecoder which is not for decoding URIs with use
of UriUtils for decoding query parameters.

Issue: SPR-15140
This commit is contained in:
Rossen Stoyanchev
2017-01-17 12:41:54 -05:00
parent 613e65f043
commit 9819558cba
7 changed files with 49 additions and 57 deletions

View File

@@ -16,9 +16,7 @@
package org.springframework.http.server.reactive;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -29,6 +27,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriUtils;
/**
* Common base class for {@link ServerHttpRequest} implementations.
@@ -79,22 +78,6 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
return this.queryParams;
}
/**
* A method for decoding name and value string in a name-value pair.
* <p>Note that the plus sign "+" is converted into a space character " ".</p>
* @param encodedString the string to be decoded
* @return the decoded string
* @see java.net.URLDecoder#decode(String, String)
*/
private static String decodeQueryParam(final String encodedString) {
try {
return URLDecoder.decode(encodedString, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
// StandardCharsets are guaranteed to be available on every implementation of the Java platform, so this should never happen.
throw new IllegalStateException(e);
}
}
/**
* A method for parsing of the query into name-value pairs. The return
* value is turned into an immutable map and cached.
@@ -113,13 +96,16 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
String eq = matcher.group(2);
String value = matcher.group(3);
value = (value != null ? value : (StringUtils.hasLength(eq) ? "" : null));
queryParams.add(decodeQueryParam(name),
value != null ? decodeQueryParam(value) : null);
queryParams.add(decodeQueryParam(name), decodeQueryParam(value));
}
}
return queryParams;
}
private static String decodeQueryParam(String value) {
return (value != null ? UriUtils.decode(value, StandardCharsets.UTF_8) : null);
}
@Override
public MultiValueMap<String, HttpCookie> getCookies() {
if (this.cookies == null) {

View File

@@ -56,20 +56,17 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest {
private static URI initUri(HttpServerRequest channel) {
Assert.notNull("'channel' must not be null");
InetSocketAddress address = channel.remoteAddress();
return (address == null ? URI.create(channel.uri()) : getBaseUrl(address).resolve(channel.uri()));
}
private static URI getBaseUrl(InetSocketAddress address) {
try {
URI uri = new URI(channel.uri());
InetSocketAddress remoteAddress = channel.remoteAddress();
return new URI(
uri.getScheme(),
uri.getUserInfo(),
(remoteAddress != null ? remoteAddress.getHostString() : null),
(remoteAddress != null ? remoteAddress.getPort() : -1),
uri.getPath(),
uri.getQuery(),
uri.getFragment());
return new URI(null, null, address.getHostString(), address.getPort(), null, null, null);
}
catch (URISyntaxException ex) {
throw new IllegalStateException("Could not get URI: " + ex.getMessage(), ex);
// Should not happen...
throw new IllegalStateException(ex);
}
}

View File

@@ -63,25 +63,20 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest {
private static URI initUri(HttpServerRequest<ByteBuf> request) {
Assert.notNull("'request', request must not be null");
return StringUtils.isEmpty(request.getHostHeader()) ?
URI.create(request.getUri()) : getBaseUrl(request).resolve(request.getUri());
}
private static URI getBaseUrl(HttpServerRequest<ByteBuf> request) {
HttpHeaders headers = new HttpHeaders();
headers.add("Host", request.getHostHeader());
InetSocketAddress address = headers.getHost();
try {
URI uri = new URI(request.getUri());
InetSocketAddress remoteAddress = null;
if (!StringUtils.isEmpty(request.getHostHeader())) {
HttpHeaders headers = new HttpHeaders();
headers.add("Host", request.getHostHeader());
remoteAddress = headers.getHost();
}
return new URI(
uri.getScheme(),
uri.getUserInfo(),
(remoteAddress != null ? remoteAddress.getHostString() : null),
(remoteAddress != null ? remoteAddress.getPort() : -1),
uri.getPath(),
uri.getQuery(),
uri.getFragment());
return new URI(null, null, address.getHostString(), address.getPort(), null, null, null);
}
catch (URISyntaxException ex) {
throw new IllegalStateException("Could not get URI: " + ex.getMessage(), ex);
// Should not happen...
throw new IllegalStateException(ex);
}
}

View File

@@ -63,11 +63,19 @@ public class UndertowServerHttpRequest extends AbstractServerHttpRequest {
private static URI initUri(HttpServerExchange exchange) {
Assert.notNull(exchange, "HttpServerExchange is required.");
String requestURI = exchange.getRequestURI();
String query = exchange.getQueryString();
String requestUriAndQuery = StringUtils.isEmpty(query) ? requestURI : requestURI + "?" + query;
return (exchange.isHostIncludedInRequestURI()) ?
URI.create(requestUriAndQuery) : getBaseUri(exchange).resolve(requestUriAndQuery);
}
private static URI getBaseUri(HttpServerExchange exchange) {
try {
String query = exchange.getQueryString();
return new URI(exchange.getRequestScheme(), null,
exchange.getHostName(), exchange.getHostPort(),
exchange.getRequestURI(), StringUtils.hasText(query) ? query : null, null);
String scheme = exchange.getRequestScheme();
String host = exchange.getHostName();
int port = exchange.getHostPort();
return new URI(scheme, null, host, port, null, null, null);
}
catch (URISyntaxException ex) {
throw new IllegalStateException("Could not get URI: " + ex.getMessage(), ex);