HandlerMappingIntrospector handles attribute changes properly

Closes gh-26833
This commit is contained in:
Rossen Stoyanchev
2021-04-22 17:21:03 +01:00
parent 7c3a18490b
commit 86123de883
3 changed files with 114 additions and 58 deletions

View File

@@ -19,6 +19,8 @@ package org.springframework.web.servlet.handler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -138,16 +140,17 @@ public class HandlerMappingIntrospector
*/
@Nullable
public MatchableHandlerMapping getMatchableHandlerMapping(HttpServletRequest request) throws Exception {
HttpServletRequest wrappedRequest = new RequestAttributeChangeIgnoringWrapper(request);
HttpServletRequest wrappedRequest = new AttributesPreservingRequest(request);
return doWithMatchingMapping(wrappedRequest, false, (matchedMapping, executionChain) -> {
if (matchedMapping instanceof MatchableHandlerMapping) {
PathPatternMatchableHandlerMapping mapping = this.pathPatternHandlerMappings.get(matchedMapping);
if (mapping != null) {
RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
return mapping.decorate(requestPath);
RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(wrappedRequest);
return new PathSettingHandlerMapping(mapping, requestPath);
}
else {
return (MatchableHandlerMapping) matchedMapping;
String lookupPath = (String) wrappedRequest.getAttribute(UrlPathHelper.PATH_ATTRIBUTE);
return new PathSettingHandlerMapping((MatchableHandlerMapping) matchedMapping, lookupPath);
}
}
throw new IllegalStateException("HandlerMapping is not a MatchableHandlerMapping");
@@ -157,7 +160,7 @@ public class HandlerMappingIntrospector
@Override
@Nullable
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
RequestAttributeChangeIgnoringWrapper wrappedRequest = new RequestAttributeChangeIgnoringWrapper(request);
AttributesPreservingRequest wrappedRequest = new AttributesPreservingRequest(request);
return doWithMatchingMappingIgnoringException(wrappedRequest, (handlerMapping, executionChain) -> {
for (HandlerInterceptor interceptor : executionChain.getInterceptorList()) {
if (interceptor instanceof CorsConfigurationSource) {
@@ -272,19 +275,83 @@ public class HandlerMappingIntrospector
/**
* Request wrapper that ignores request attribute changes.
* Request wrapper that buffers request attributes in order protect the
* underlying request from attribute changes.
*/
private static class RequestAttributeChangeIgnoringWrapper extends HttpServletRequestWrapper {
private static class AttributesPreservingRequest extends HttpServletRequestWrapper {
RequestAttributeChangeIgnoringWrapper(HttpServletRequest request) {
private final Map<String, Object> attributes;
AttributesPreservingRequest(HttpServletRequest request) {
super(request);
this.attributes = initAttributes(request);
}
private Map<String, Object> initAttributes(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
Enumeration<String> names = request.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
map.put(name, request.getAttribute(name));
}
return map;
}
@Override
public void setAttribute(String name, Object value) {
if (name.equals(ServletRequestPathUtils.PATH_ATTRIBUTE) || name.equals(UrlPathHelper.PATH_ATTRIBUTE)) {
super.setAttribute(name, value);
}
this.attributes.put(name, value);
}
@Override
public Object getAttribute(String name) {
return this.attributes.get(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(this.attributes.keySet());
}
@Override
public void removeAttribute(String name) {
this.attributes.remove(name);
}
}
private static class PathSettingHandlerMapping implements MatchableHandlerMapping {
private final MatchableHandlerMapping delegate;
private final Object path;
private final String pathAttributeName;
PathSettingHandlerMapping(MatchableHandlerMapping delegate, Object path) {
this.delegate = delegate;
this.path = path;
this.pathAttributeName = (path instanceof RequestPath ?
ServletRequestPathUtils.PATH_ATTRIBUTE : UrlPathHelper.PATH_ATTRIBUTE);
}
@Nullable
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
Object previousPath = request.getAttribute(this.pathAttributeName);
request.setAttribute(this.pathAttributeName, this.path);
try {
return this.delegate.match(request, pattern);
}
finally {
request.setAttribute(this.pathAttributeName, previousPath);
}
}
@Nullable
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
return this.delegate.getHandler(request);
}
}
}

View File

@@ -21,7 +21,6 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.RequestPath;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerExecutionChain;
@@ -72,38 +71,4 @@ class PathPatternMatchableHandlerMapping implements MatchableHandlerMapping {
return this.delegate.getHandler(request);
}
MatchableHandlerMapping decorate(RequestPath requestPath) {
return new MatchableHandlerMapping() {
@Nullable
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
RequestPath previousPath = setRequestPathAttribute(request);
try {
return PathPatternMatchableHandlerMapping.this.match(request, pattern);
}
finally {
ServletRequestPathUtils.setParsedRequestPath(previousPath, request);
}
}
@Nullable
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
RequestPath previousPath = setRequestPathAttribute(request);
try {
return PathPatternMatchableHandlerMapping.this.getHandler(request);
}
finally {
ServletRequestPathUtils.setParsedRequestPath(previousPath, request);
}
}
private RequestPath setRequestPathAttribute(HttpServletRequest request) {
RequestPath previous = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.setParsedRequestPath(requestPath, request);
return previous;
}
};
}
}