Use undecoded pathWithinApplication in WebFlux
Introduce pathWithinApplication() in ServerHttpRequest and use it for request mapping purposes instead of LookupPath. In turn this means that for request mapping purposes: 1) the path is not decoded 2) suffix pattern matching is not supported Issue: SPR-15640
This commit is contained in:
@@ -4,11 +4,10 @@ package org.springframework.http.server.reactive;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* {@code HttpHandler} delegating requests to one of several {@code HttpHandler}'s
|
||||
@@ -49,7 +48,8 @@ public class ContextPathCompositeHandler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
String path = getPathWithinApplication(request);
|
||||
// Remove underlying context path first (e.g. Servlet container)
|
||||
String path = request.getPathWithinApplication();
|
||||
return this.handlerMap.entrySet().stream()
|
||||
.filter(entry -> path.startsWith(entry.getKey()))
|
||||
.findFirst()
|
||||
@@ -65,18 +65,4 @@ public class ContextPathCompositeHandler implements HttpHandler {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path within the "native" context path of the underlying server,
|
||||
* for example when running on a Servlet container.
|
||||
*/
|
||||
private String getPathWithinApplication(ServerHttpRequest request) {
|
||||
String path = request.getURI().getRawPath();
|
||||
String contextPath = request.getContextPath();
|
||||
if (!StringUtils.hasText(contextPath)) {
|
||||
return path;
|
||||
}
|
||||
int length = contextPath.length();
|
||||
return (path.length() > length ? path.substring(length) : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents a reactive server-side HTTP request
|
||||
@@ -35,16 +36,36 @@ import org.springframework.util.MultiValueMap;
|
||||
public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage {
|
||||
|
||||
/**
|
||||
* Returns the portion of the URL path that represents the context path for the
|
||||
* current {@link HttpHandler}. The context path is always at the beginning of
|
||||
* the request path. It starts with "/" but but does not end with "/".
|
||||
* <p>This method may return an empty string if no context path is configured.
|
||||
* Returns the portion of the URL path that represents the application.
|
||||
* The context path is always at the beginning of the path and starts but
|
||||
* does not end with "/". It is shared for URLs of the same application.
|
||||
* <p>The context path may come from the underlying runtime API such as
|
||||
* when deploying as a WAR to a Servlet container or it may also be assigned
|
||||
* through the use of {@link ContextPathCompositeHandler} or both.
|
||||
* <p>The context path is not decoded.
|
||||
* @return the context path (not decoded) or an empty string
|
||||
*/
|
||||
default String getContextPath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of the URL path after the {@link #getContextPath()
|
||||
* contextPath}. The returned path is not decoded.
|
||||
* @return the path under the contextPath
|
||||
*/
|
||||
default String getPathWithinApplication() {
|
||||
String path = getURI().getRawPath();
|
||||
String contextPath = getContextPath();
|
||||
if (StringUtils.hasText(contextPath)) {
|
||||
int length = contextPath.length();
|
||||
return (path.length() > length ? path.substring(length) : "");
|
||||
}
|
||||
else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a read-only map with parsed and decoded query parameter values.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.support.LookupPath;
|
||||
import org.springframework.web.util.pattern.ParsingPathMatcher;
|
||||
|
||||
/**
|
||||
@@ -81,7 +80,7 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource
|
||||
|
||||
@Override
|
||||
public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) {
|
||||
String lookupPath = LookupPath.getCurrent(exchange).getPath();
|
||||
String lookupPath = exchange.getRequest().getPathWithinApplication();
|
||||
for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
|
||||
if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
|
||||
return entry.getValue();
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.web.util.UriUtils;
|
||||
*/
|
||||
public class HttpRequestPathHelper {
|
||||
|
||||
private boolean urlDecode = true;
|
||||
private boolean urlDecode = false;
|
||||
|
||||
|
||||
// TODO: sanitize path, default/request encoding?, remove path params?
|
||||
@@ -58,26 +58,6 @@ public class HttpRequestPathHelper {
|
||||
}
|
||||
|
||||
|
||||
public LookupPath getLookupPathForRequest(ServerWebExchange exchange) {
|
||||
String path = getPathWithinApplication(exchange.getRequest());
|
||||
path = (shouldUrlDecode() ? decode(exchange, path) : path);
|
||||
int begin = path.lastIndexOf('/') + 1;
|
||||
int end = path.length();
|
||||
int paramIndex = path.indexOf(';', begin);
|
||||
int extIndex = path.lastIndexOf('.', paramIndex != -1 ? paramIndex : end);
|
||||
return new LookupPath(path, extIndex, paramIndex);
|
||||
}
|
||||
|
||||
private String getPathWithinApplication(ServerHttpRequest request) {
|
||||
String contextPath = request.getContextPath();
|
||||
String path = request.getURI().getRawPath();
|
||||
if (!StringUtils.hasText(contextPath)) {
|
||||
return path;
|
||||
}
|
||||
int contextLength = contextPath.length();
|
||||
return (path.length() > contextLength ? path.substring(contextLength) : "");
|
||||
}
|
||||
|
||||
private String decode(ServerWebExchange exchange, String path) {
|
||||
// TODO: look up request encoding?
|
||||
try {
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.web.server.support;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Lookup path information of an incoming HTTP request.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
* @see HttpRequestPathHelper
|
||||
*/
|
||||
public final class LookupPath {
|
||||
|
||||
/**
|
||||
* Name of request attribute under which the LookupPath is stored via
|
||||
* {@link #getOrCreate} and accessed via {@link #getCurrent}.
|
||||
*/
|
||||
public static final String LOOKUP_PATH_ATTRIBUTE_NAME = LookupPath.class.getName();
|
||||
|
||||
|
||||
private final String path;
|
||||
|
||||
private final int fileExtStartIndex;
|
||||
|
||||
private final int fileExtEndIndex;
|
||||
|
||||
|
||||
public LookupPath(String path, int fileExtStartIndex, int fileExtEndIndex) {
|
||||
this.path = path;
|
||||
this.fileExtStartIndex = fileExtStartIndex;
|
||||
this.fileExtEndIndex = fileExtEndIndex;
|
||||
}
|
||||
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public String getPathWithoutExtension() {
|
||||
if (this.fileExtStartIndex != -1) {
|
||||
return this.path.substring(0, this.fileExtStartIndex);
|
||||
}
|
||||
else {
|
||||
return this.path;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFileExtension() {
|
||||
if (this.fileExtStartIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
else if (this.fileExtEndIndex == -1) {
|
||||
return this.path.substring(this.fileExtStartIndex);
|
||||
}
|
||||
else {
|
||||
return this.path.substring(this.fileExtStartIndex, this.fileExtEndIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the LookupPath for the current request from the request attribute
|
||||
* {@link #LOOKUP_PATH_ATTRIBUTE_NAME} or otherwise create and stored it
|
||||
* under that attribute for subsequent use.
|
||||
* @param exchange the current exchange
|
||||
* @param pathHelper the pathHelper to create the LookupPath with
|
||||
* @return the LookupPath for the current request
|
||||
*/
|
||||
public static LookupPath getOrCreate(ServerWebExchange exchange, HttpRequestPathHelper pathHelper) {
|
||||
return exchange.<LookupPath>getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE_NAME)
|
||||
.orElseGet(() -> {
|
||||
LookupPath lookupPath = pathHelper.getLookupPathForRequest(exchange);
|
||||
exchange.getAttributes().put(LookupPath.LOOKUP_PATH_ATTRIBUTE_NAME, lookupPath);
|
||||
return lookupPath;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LookupPath for the current request from the request attribute
|
||||
* {@link #LOOKUP_PATH_ATTRIBUTE_NAME} or raise an {@link IllegalStateException}
|
||||
* if not found.
|
||||
* @param exchange the current exchange
|
||||
* @return the LookupPath, never {@code null}
|
||||
*/
|
||||
public static LookupPath getCurrent(ServerWebExchange exchange) {
|
||||
return exchange.<LookupPath>getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE_NAME)
|
||||
.orElseThrow(() -> new IllegalStateException("No LookupPath attribute."));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user