Browse Source

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
pull/1468/merge
Brian Clozel 8 years ago
parent
commit
9c93521512
  1. 47
      spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
  2. 9
      spring-web/src/test/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSourceTests.java
  3. 16
      spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java

47
spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java

@ -16,16 +16,14 @@ @@ -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; @@ -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 @@ -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);
}
}

9
spring-web/src/test/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSourceTests.java

@ -21,6 +21,7 @@ import org.junit.Test; @@ -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; @@ -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 { @@ -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());
}
}

16
spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java

@ -51,13 +51,18 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im @@ -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 @@ -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…
Cancel
Save