Leverage PathPatternParser in CORS configuration source
Previously `UrlBasedCorsConfigurationSource` was relying on `PathMatcher` implementations for matching incoming request lookup paths with the configured path patterns for CORS configuration. This commit replaces the use of `PathMatcher` with a `PathPatternParser` that parses the string patterns into `PathPattenr` instances and allows for faster matching against lookup paths. Issue: SPR-15688
This commit is contained in:
parent
fd1859c34c
commit
9c93521512
|
|
@ -16,16 +16,14 @@
|
|||
|
||||
package org.springframework.web.cors.reactive;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
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.util.pattern.ParsingPathMatcher;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Provide a per reactive request {@link CorsConfiguration} instance based on a
|
||||
|
|
@ -35,23 +33,18 @@ import org.springframework.web.util.pattern.ParsingPathMatcher;
|
|||
* as well as Ant-style path patterns (such as {@code "/admin/**"}).
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
*/
|
||||
public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource {
|
||||
|
||||
private final Map<String, CorsConfiguration> corsConfigurations = new LinkedHashMap<>();
|
||||
private final Map<PathPattern, CorsConfiguration> corsConfigurations;
|
||||
|
||||
private PathMatcher pathMatcher = new ParsingPathMatcher();
|
||||
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths
|
||||
* against registered URL patterns. Default is ParsingPathMatcher.
|
||||
* @see ParsingPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
this.pathMatcher = pathMatcher;
|
||||
private final PathPatternParser patternParser;
|
||||
|
||||
public UrlBasedCorsConfigurationSource(PathPatternParser patternParser) {
|
||||
this.corsConfigurations = new LinkedHashMap<>();
|
||||
this.patternParser = patternParser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,33 +53,25 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource
|
|||
public void setCorsConfigurations(@Nullable Map<String, CorsConfiguration> corsConfigurations) {
|
||||
this.corsConfigurations.clear();
|
||||
if (corsConfigurations != null) {
|
||||
this.corsConfigurations.putAll(corsConfigurations);
|
||||
corsConfigurations.forEach((path, config) -> registerCorsConfiguration(path, config));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CORS configuration.
|
||||
*/
|
||||
public Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||
return Collections.unmodifiableMap(this.corsConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link CorsConfiguration} for the specified path pattern.
|
||||
*/
|
||||
public void registerCorsConfiguration(String path, CorsConfiguration config) {
|
||||
this.corsConfigurations.put(path, config);
|
||||
this.corsConfigurations.put(this.patternParser.parse(path), config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) {
|
||||
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
|
||||
for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
|
||||
if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return this.corsConfigurations.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().matches(lookupPath))
|
||||
.map(entry -> entry.getValue())
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.junit.Test;
|
|||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
|
@ -33,7 +34,8 @@ import static org.junit.Assert.assertNull;
|
|||
*/
|
||||
public class UrlBasedCorsConfigurationSourceTests {
|
||||
|
||||
private final UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
|
||||
private final UrlBasedCorsConfigurationSource configSource
|
||||
= new UrlBasedCorsConfigurationSource(new PathPatternParser());
|
||||
|
||||
|
||||
@Test
|
||||
|
|
@ -54,9 +56,4 @@ public class UrlBasedCorsConfigurationSourceTests {
|
|||
assertEquals(config, this.configSource.getCorsConfiguration(exchange));
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void unmodifiableConfigurationsMap() {
|
||||
this.configSource.getCorsConfigurations().put("/**", new CorsConfiguration());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,13 +51,18 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im
|
|||
|
||||
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||
|
||||
private PathPatternParser patternParser = new PathPatternParser();
|
||||
private final PathPatternParser patternParser;
|
||||
|
||||
private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();
|
||||
private final UrlBasedCorsConfigurationSource globalCorsConfigSource;
|
||||
|
||||
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
||||
|
||||
|
||||
public AbstractHandlerMapping() {
|
||||
this.patternParser = new PathPatternParser();
|
||||
this.globalCorsConfigSource = new UrlBasedCorsConfigurationSource(this.patternParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order value for this HandlerMapping bean.
|
||||
* <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered.
|
||||
|
|
@ -106,13 +111,6 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im
|
|||
this.globalCorsConfigSource.setCorsConfigurations(corsConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "global" CORS configuration.
|
||||
*/
|
||||
public Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||
return this.globalCorsConfigSource.getCorsConfigurations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a custom {@link CorsProcessor} to use to apply the matched
|
||||
* {@link CorsConfiguration} for a request.
|
||||
|
|
|
|||
Loading…
Reference in New Issue