Polish reactive CORS support
This commit is contained in:
parent
e31a2f778b
commit
33c48e7a17
|
|
@ -24,20 +24,16 @@ import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code CorsRegistration} assists with the creation of a
|
* Assists with the creation of a {@link CorsConfiguration} instance mapped to
|
||||||
* {@link CorsConfiguration} instance mapped to a path pattern.
|
* a path pattern.
|
||||||
*
|
*
|
||||||
* <p>If no path pattern is specified, cross-origin request handling is
|
* <p>If no path pattern is specified, by default cross-origin request handling
|
||||||
* mapped to {@code "/**"}.
|
* is mapped to {@code "/**"}. Also by default, all origins, headers,
|
||||||
*
|
* credentials and {@code GET}, {@code HEAD}, and {@code POST} methods are
|
||||||
* <p>By default, all origins, all headers, credentials and {@code GET},
|
* allowed, while the max age is set to 30 minutes.
|
||||||
* {@code HEAD}, and {@code POST} methods are allowed, and the max age is
|
|
||||||
* set to 30 minutes.
|
|
||||||
*
|
*
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
* @author Sam Brannen
|
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see CorsConfiguration
|
|
||||||
* @see CorsRegistry
|
* @see CorsRegistry
|
||||||
*/
|
*/
|
||||||
public class CorsRegistration {
|
public class CorsRegistration {
|
||||||
|
|
|
||||||
|
|
@ -58,4 +58,5 @@ public class CorsRegistry {
|
||||||
}
|
}
|
||||||
return configs;
|
return configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||||
import org.springframework.http.codec.HttpMessageReader;
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
import org.springframework.http.codec.HttpMessageWriter;
|
import org.springframework.http.codec.HttpMessageWriter;
|
||||||
|
import org.springframework.http.codec.Jackson2ServerHttpMessageReader;
|
||||||
|
import org.springframework.http.codec.Jackson2ServerHttpMessageWriter;
|
||||||
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
import org.springframework.http.codec.ResourceHttpMessageWriter;
|
||||||
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
|
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
|
||||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||||
|
|
@ -61,8 +63,6 @@ import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuild
|
||||||
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
||||||
import org.springframework.web.reactive.result.SimpleHandlerAdapter;
|
import org.springframework.web.reactive.result.SimpleHandlerAdapter;
|
||||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.http.codec.Jackson2ServerHttpMessageReader;
|
|
||||||
import org.springframework.http.codec.Jackson2ServerHttpMessageWriter;
|
|
||||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
||||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||||
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
||||||
|
|
@ -90,14 +90,14 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||||
ClassUtils.isPresent("javax.xml.bind.Binder", WebReactiveConfiguration.class.getClassLoader());
|
ClassUtils.isPresent("javax.xml.bind.Binder", WebReactiveConfiguration.class.getClassLoader());
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, CorsConfiguration> corsConfigurations;
|
||||||
|
|
||||||
private PathMatchConfigurer pathMatchConfigurer;
|
private PathMatchConfigurer pathMatchConfigurer;
|
||||||
|
|
||||||
private List<HttpMessageReader<?>> messageReaders;
|
private List<HttpMessageReader<?>> messageReaders;
|
||||||
|
|
||||||
private List<HttpMessageWriter<?>> messageWriters;
|
private List<HttpMessageWriter<?>> messageWriters;
|
||||||
|
|
||||||
private Map<String, CorsConfiguration> corsConfigurations;
|
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -171,6 +171,26 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||||
protected void configureRequestedContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
|
protected void configureRequestedContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for building the global CORS configuration. This method is final.
|
||||||
|
* Use {@link #addCorsMappings(CorsRegistry)} to customize the CORS conifg.
|
||||||
|
*/
|
||||||
|
protected final Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||||
|
if (this.corsConfigurations == null) {
|
||||||
|
CorsRegistry registry = new CorsRegistry();
|
||||||
|
addCorsMappings(registry);
|
||||||
|
this.corsConfigurations = registry.getCorsConfigurations();
|
||||||
|
}
|
||||||
|
return this.corsConfigurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to configure cross origin requests processing.
|
||||||
|
* @see CorsRegistry
|
||||||
|
*/
|
||||||
|
protected void addCorsMappings(CorsRegistry registry) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for building the {@link PathMatchConfigurer}. This method is
|
* Callback for building the {@link PathMatchConfigurer}. This method is
|
||||||
* final, use {@link #configurePathMatching} to customize path matching.
|
* final, use {@link #configurePathMatching} to customize path matching.
|
||||||
|
|
@ -209,6 +229,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||||
if (pathMatchConfigurer.getPathHelper() != null) {
|
if (pathMatchConfigurer.getPathHelper() != null) {
|
||||||
handlerMapping.setPathHelper(pathMatchConfigurer.getPathHelper());
|
handlerMapping.setPathHelper(pathMatchConfigurer.getPathHelper());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handlerMapping = new EmptyHandlerMapping();
|
handlerMapping = new EmptyHandlerMapping();
|
||||||
|
|
@ -444,22 +465,6 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||||
protected void configureViewResolvers(ViewResolverRegistry registry) {
|
protected void configureViewResolvers(ViewResolverRegistry registry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Map<String, CorsConfiguration> getCorsConfigurations() {
|
|
||||||
if (this.corsConfigurations == null) {
|
|
||||||
CorsRegistry registry = new CorsRegistry();
|
|
||||||
addCorsMappings(registry);
|
|
||||||
this.corsConfigurations = registry.getCorsConfigurations();
|
|
||||||
}
|
|
||||||
return this.corsConfigurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override this method to configure cross origin requests processing.
|
|
||||||
* @see CorsRegistry
|
|
||||||
*/
|
|
||||||
protected void addCorsMappings(CorsRegistry registry) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static final class EmptyHandlerMapping extends AbstractHandlerMapping {
|
private static final class EmptyHandlerMapping extends AbstractHandlerMapping {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,10 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||||
|
|
||||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
protected CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();
|
||||||
|
|
||||||
|
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
||||||
|
|
||||||
protected final UrlBasedCorsConfigurationSource corsConfigSource = new UrlBasedCorsConfigurationSource();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the order value for this HandlerMapping bean.
|
* Specify the order value for this HandlerMapping bean.
|
||||||
|
|
@ -104,7 +105,7 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||||
this.pathMatcher = pathMatcher;
|
this.pathMatcher = pathMatcher;
|
||||||
this.corsConfigSource.setPathMatcher(pathMatcher);
|
this.globalCorsConfigSource.setPathMatcher(pathMatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -115,9 +116,26 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||||
return this.pathMatcher;
|
return this.pathMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set "global" CORS configuration based on URL patterns. By default the
|
||||||
|
* first matching URL pattern is combined with handler-level CORS
|
||||||
|
* configuration if any.
|
||||||
|
*/
|
||||||
|
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
||||||
|
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
|
* Configure a custom {@link CorsProcessor} to use to apply the matched
|
||||||
* {@link CorsConfiguration} for a request. By default {@link DefaultCorsProcessor} is used.
|
* {@link CorsConfiguration} for a request.
|
||||||
|
* <p>By default an instance of {@link DefaultCorsProcessor} is used.
|
||||||
*/
|
*/
|
||||||
public void setCorsProcessor(CorsProcessor corsProcessor) {
|
public void setCorsProcessor(CorsProcessor corsProcessor) {
|
||||||
Assert.notNull(corsProcessor, "CorsProcessor must not be null");
|
Assert.notNull(corsProcessor, "CorsProcessor must not be null");
|
||||||
|
|
@ -131,22 +149,27 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||||
return this.corsProcessor;
|
return this.corsProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set "global" CORS configuration based on URL patterns. By default the first
|
protected Object processCorsRequest(ServerWebExchange exchange, Object handler) {
|
||||||
* matching URL pattern is combined with the CORS configuration for the
|
if (CorsUtils.isCorsRequest(exchange.getRequest())) {
|
||||||
* handler, if any.
|
CorsConfiguration configA = this.globalCorsConfigSource.getCorsConfiguration(exchange);
|
||||||
*/
|
CorsConfiguration configB = getCorsConfiguration(handler, exchange);
|
||||||
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
CorsConfiguration config = (configA != null ? configA.combine(configB) : configB);
|
||||||
this.corsConfigSource.setCorsConfigurations(corsConfigurations);
|
|
||||||
|
if (!getCorsProcessor().processRequest(config, exchange) ||
|
||||||
|
CorsUtils.isPreFlightRequest(exchange.getRequest())) {
|
||||||
|
return REQUEST_HANDLED_HANDLER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the CORS configuration.
|
* Retrieve the CORS configuration for the given handler.
|
||||||
|
* @param handler the handler to check (never {@code null}).
|
||||||
|
* @param exchange the current exchange
|
||||||
|
* @return the CORS configuration for the handler or {@code null}.
|
||||||
*/
|
*/
|
||||||
public Map<String, CorsConfiguration> getCorsConfigurations() {
|
|
||||||
return this.corsConfigSource.getCorsConfigurations();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
|
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
|
||||||
if (handler != null && handler instanceof CorsConfigurationSource) {
|
if (handler != null && handler instanceof CorsConfigurationSource) {
|
||||||
return ((CorsConfigurationSource) handler).getCorsConfiguration(exchange);
|
return ((CorsConfigurationSource) handler).getCorsConfiguration(exchange);
|
||||||
|
|
@ -154,23 +177,7 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object processCorsRequest(ServerWebExchange exchange, Object handler) {
|
|
||||||
if (CorsUtils.isCorsRequest(exchange.getRequest())) {
|
|
||||||
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(exchange);
|
|
||||||
CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
|
|
||||||
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
|
|
||||||
if (!corsProcessor.processRequest(config, exchange) || CorsUtils.isPreFlightRequest(exchange.getRequest())) {
|
|
||||||
return new NoOpHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NoOpHandler implements WebHandler {
|
private static final WebHandler REQUEST_HANDLED_HANDLER = exchange -> Mono.empty();
|
||||||
@Override
|
|
||||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||||
@Override
|
@Override
|
||||||
public Mono<Object> getHandler(ServerWebExchange exchange) {
|
public Mono<Object> getHandler(ServerWebExchange exchange) {
|
||||||
String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
|
String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
|
||||||
Object handler = null;
|
Object handler;
|
||||||
try {
|
try {
|
||||||
handler = lookupHandler(lookupPath, exchange);
|
handler = lookupHandler(lookupPath, exchange);
|
||||||
handler = processCorsRequest(exchange, handler);
|
handler = processCorsRequest(exchange, handler);
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,11 @@ import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.core.MethodIntrospector;
|
import org.springframework.core.MethodIntrospector;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
|
@ -75,8 +72,13 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||||
*/
|
*/
|
||||||
private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
|
private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HandlerMethod to return on a pre-flight request match when the request
|
||||||
|
* mappings are more nuanced than the access control headers.
|
||||||
|
*/
|
||||||
private static final HandlerMethod PREFLIGHT_AMBIGUOUS_MATCH =
|
private static final HandlerMethod PREFLIGHT_AMBIGUOUS_MATCH =
|
||||||
new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle"));
|
new HandlerMethod(new PreFlightAmbiguousMatchHandler(),
|
||||||
|
ClassUtils.getMethod(PreFlightAmbiguousMatchHandler.class, "handle"));
|
||||||
|
|
||||||
private static final CorsConfiguration ALLOW_CORS_CONFIG = new CorsConfiguration();
|
private static final CorsConfiguration ALLOW_CORS_CONFIG = new CorsConfiguration();
|
||||||
|
|
||||||
|
|
@ -372,12 +374,10 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||||
if (handler instanceof HandlerMethod) {
|
if (handler instanceof HandlerMethod) {
|
||||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||||
if (handlerMethod.equals(PREFLIGHT_AMBIGUOUS_MATCH)) {
|
if (handlerMethod.equals(PREFLIGHT_AMBIGUOUS_MATCH)) {
|
||||||
return AbstractHandlerMethodMapping.ALLOW_CORS_CONFIG;
|
return ALLOW_CORS_CONFIG;
|
||||||
}
|
|
||||||
else {
|
|
||||||
CorsConfiguration corsConfigFromMethod = this.mappingRegistry.getCorsConfiguration(handlerMethod);
|
|
||||||
corsConfig = (corsConfig != null ? corsConfig.combine(corsConfigFromMethod) : corsConfigFromMethod);
|
|
||||||
}
|
}
|
||||||
|
CorsConfiguration methodConfig = this.mappingRegistry.getCorsConfiguration(handlerMethod);
|
||||||
|
corsConfig = (corsConfig != null ? corsConfig.combine(methodConfig) : methodConfig);
|
||||||
}
|
}
|
||||||
return corsConfig;
|
return corsConfig;
|
||||||
}
|
}
|
||||||
|
|
@ -625,7 +625,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EmptyHandler {
|
private static class PreFlightAmbiguousMatchHandler {
|
||||||
|
|
||||||
public void handle() {
|
public void handle() {
|
||||||
throw new UnsupportedOperationException("not implemented");
|
throw new UnsupportedOperationException("not implemented");
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||||
@Override
|
@Override
|
||||||
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
|
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
|
||||||
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
|
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
|
||||||
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), CrossOrigin.class);
|
Class<?> beanType = handlerMethod.getBeanType();
|
||||||
|
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
|
||||||
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
|
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
|
||||||
|
|
||||||
if (typeAnnotation == null && methodAnnotation == null) {
|
if (typeAnnotation == null && methodAnnotation == null) {
|
||||||
|
|
@ -310,6 +311,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||||
if (config.getMaxAge() == null) {
|
if (config.getMaxAge() == null) {
|
||||||
config.setMaxAge(CrossOrigin.DEFAULT_MAX_AGE);
|
config.setMaxAge(CrossOrigin.DEFAULT_MAX_AGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.web.reactive.handler;
|
package org.springframework.web.reactive.handler;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
@ -23,9 +22,6 @@ import static org.junit.Assert.assertSame;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
|
@ -44,132 +40,111 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class CorsAbstractUrlHandlerMappingTests {
|
public class CorsUrlHandlerMappingTests {
|
||||||
|
|
||||||
private AnnotationConfigApplicationContext wac;
|
private AbstractUrlHandlerMapping handlerMapping;
|
||||||
|
|
||||||
private TestUrlHandlerMapping handlerMapping;
|
private Object welcomeController = new Object();
|
||||||
|
|
||||||
private Object mainController;
|
private CorsAwareHandler corsController = new CorsAwareHandler();
|
||||||
|
|
||||||
private CorsAwareHandler corsConfigurationSourceController;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
wac = new AnnotationConfigApplicationContext();
|
this.handlerMapping = new AbstractUrlHandlerMapping() {};
|
||||||
wac.register(WebConfig.class);
|
this.handlerMapping.setUseTrailingSlashMatch(true);
|
||||||
wac.refresh();
|
this.handlerMapping.registerHandler("/welcome.html", this.welcomeController);
|
||||||
|
this.handlerMapping.registerHandler("/cors.html", this.corsController);
|
||||||
handlerMapping = (TestUrlHandlerMapping) wac.getBean("handlerMapping");
|
|
||||||
mainController = wac.getBean("mainController");
|
|
||||||
corsConfigurationSourceController = (CorsAwareHandler) wac.getBean("corsConfigurationSourceController");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithoutCorsConfigurationProvider() throws Exception {
|
public void actualRequestWithoutCorsConfigurationProvider() throws Exception {
|
||||||
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", "http://domain2.com", "GET");
|
String origin = "http://domain2.com";
|
||||||
Object actual = handlerMapping.getHandler(exchange).block();
|
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", origin, "GET");
|
||||||
|
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||||
|
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertSame(mainController, actual);
|
assertSame(this.welcomeController, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithoutCorsConfigurationProvider() throws Exception {
|
public void preflightRequestWithoutCorsConfigurationProvider() throws Exception {
|
||||||
ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", "http://domain2.com", "GET");
|
String origin = "http://domain2.com";
|
||||||
Object actual = handlerMapping.getHandler(exchange).block();
|
ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", origin, "GET");
|
||||||
|
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||||
|
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertEquals("NoOpHandler", actual.getClass().getSimpleName());
|
assertNotSame(this.welcomeController, actual);
|
||||||
assertNull(exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertNull(exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithCorsConfigurationProvider() throws Exception {
|
public void actualRequestWithCorsAwareHandler() throws Exception {
|
||||||
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/cors.html", "http://domain2.com", "GET");
|
String origin = "http://domain2.com";
|
||||||
Object actual = handlerMapping.getHandler(exchange).block();
|
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/cors.html", origin, "GET");
|
||||||
|
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||||
|
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertSame(corsConfigurationSourceController, actual);
|
assertSame(this.corsController, actual);
|
||||||
CorsConfiguration config = ((CorsConfigurationSource)actual).getCorsConfiguration(createExchange(HttpMethod.GET, "", "",""));
|
|
||||||
assertNotNull(config);
|
|
||||||
assertArrayEquals(config.getAllowedOrigins().toArray(), new String[]{"*"});
|
|
||||||
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithCorsConfigurationProvider() throws Exception {
|
public void preFlightWithCorsAwareHandler() throws Exception {
|
||||||
ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/cors.html", "http://domain2.com", "GET");
|
String origin = "http://domain2.com";
|
||||||
Object actual = handlerMapping.getHandler(exchange).block();
|
ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/cors.html", origin, "GET");
|
||||||
|
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||||
|
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertEquals("NoOpHandler", actual.getClass().getSimpleName());
|
assertNotSame(this.corsController, actual);
|
||||||
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithMappedCorsConfiguration() throws Exception {
|
public void actualRequestWithGlobalCorsConfig() throws Exception {
|
||||||
CorsConfiguration mappedConfig = new CorsConfiguration();
|
CorsConfiguration mappedConfig = new CorsConfiguration();
|
||||||
mappedConfig.addAllowedOrigin("*");
|
mappedConfig.addAllowedOrigin("*");
|
||||||
this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/welcome.html", mappedConfig));
|
this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/welcome.html", mappedConfig));
|
||||||
|
|
||||||
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", "http://domain2.com", "GET");
|
String origin = "http://domain2.com";
|
||||||
Object actual = handlerMapping.getHandler(exchange).block();
|
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", origin, "GET");
|
||||||
|
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||||
|
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertSame(mainController, actual);
|
assertSame(this.welcomeController, actual);
|
||||||
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithMappedCorsConfiguration() throws Exception {
|
public void preFlightRequestWithGlobalCorsConfig() throws Exception {
|
||||||
CorsConfiguration mappedConfig = new CorsConfiguration();
|
CorsConfiguration mappedConfig = new CorsConfiguration();
|
||||||
mappedConfig.addAllowedOrigin("*");
|
mappedConfig.addAllowedOrigin("*");
|
||||||
this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/welcome.html", mappedConfig));
|
this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/welcome.html", mappedConfig));
|
||||||
|
|
||||||
ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", "http://domain2.com", "GET");
|
String origin = "http://domain2.com";
|
||||||
Object actual = handlerMapping.getHandler(exchange).block();
|
ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", origin, "GET");
|
||||||
|
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||||
|
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertEquals("NoOpHandler", actual.getClass().getSimpleName());
|
assertNotSame(this.welcomeController, actual);
|
||||||
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ServerWebExchange createExchange(HttpMethod method, String path, String origin,
|
private ServerWebExchange createExchange(HttpMethod method, String path, String origin,
|
||||||
String accessControlRequestMethod) throws URISyntaxException {
|
String accessControlRequestMethod) {
|
||||||
|
|
||||||
ServerHttpRequest request = new MockServerHttpRequest(method, "http://localhost" + path);
|
ServerHttpRequest request = new MockServerHttpRequest(method, "http://localhost" + path);
|
||||||
request.getHeaders().add(HttpHeaders.ORIGIN, origin);
|
request.getHeaders().add(HttpHeaders.ORIGIN, origin);
|
||||||
request.getHeaders().add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, accessControlRequestMethod);
|
request.getHeaders().add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, accessControlRequestMethod);
|
||||||
|
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||||
WebSessionManager sessionManager = new MockWebSessionManager();
|
WebSessionManager sessionManager = new MockWebSessionManager();
|
||||||
return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
return new DefaultServerWebExchange(request, response, sessionManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
private class CorsAwareHandler implements CorsConfigurationSource {
|
||||||
static class WebConfig {
|
|
||||||
|
|
||||||
@Bean @SuppressWarnings("unused")
|
|
||||||
public TestUrlHandlerMapping handlerMapping() {
|
|
||||||
TestUrlHandlerMapping hm = new TestUrlHandlerMapping();
|
|
||||||
hm.setUseTrailingSlashMatch(true);
|
|
||||||
hm.registerHandler("/welcome.html", mainController());
|
|
||||||
hm.registerHandler("/cors.html", corsConfigurationSourceController());
|
|
||||||
return hm;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Object mainController() {
|
|
||||||
return new Object();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public CorsAwareHandler corsConfigurationSourceController() {
|
|
||||||
return new CorsAwareHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TestUrlHandlerMapping extends AbstractUrlHandlerMapping {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class CorsAwareHandler implements CorsConfigurationSource {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) {
|
public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) {
|
||||||
|
|
@ -16,9 +16,13 @@
|
||||||
package org.springframework.web.reactive.result.method.annotation;
|
package org.springframework.web.reactive.result.method.annotation;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
@ -30,20 +34,24 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||||
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
||||||
|
|
||||||
import static org.springframework.http.RequestEntity.get;
|
import static org.springframework.http.RequestEntity.get;
|
||||||
|
import static org.springframework.http.RequestEntity.options;
|
||||||
|
import static org.springframework.http.RequestEntity.post;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Base class for integration tests with {@code @RequestMapping methods}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRequestMappingIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
public abstract class AbstractRequestMappingIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
|
||||||
|
|
||||||
private RestTemplate restTemplate = new RestTemplate();
|
private RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHandler createHttpHandler() {
|
protected HttpHandler createHttpHandler() {
|
||||||
|
this.restTemplate = initRestTemplate();
|
||||||
this.applicationContext = initApplicationContext();
|
this.applicationContext = initApplicationContext();
|
||||||
return WebHttpHandlerBuilder
|
return WebHttpHandlerBuilder
|
||||||
.webHandler(new DispatcherHandler(this.applicationContext))
|
.webHandler(new DispatcherHandler(this.applicationContext))
|
||||||
|
|
@ -53,49 +61,98 @@ public abstract class AbstractRequestMappingIntegrationTests extends AbstractHtt
|
||||||
|
|
||||||
protected abstract ApplicationContext initApplicationContext();
|
protected abstract ApplicationContext initApplicationContext();
|
||||||
|
|
||||||
|
protected RestTemplate initRestTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
ApplicationContext getApplicationContext() {
|
protected ApplicationContext getApplicationContext() {
|
||||||
return this.applicationContext;
|
return this.applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
RestTemplate getRestTemplate() {
|
protected RestTemplate getRestTemplate() {
|
||||||
return this.restTemplate;
|
return this.restTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<T> ResponseEntity<T> performGet(String url, MediaType out,
|
<T> ResponseEntity<T> performGet(String url, MediaType out, Class<T> type) throws Exception {
|
||||||
Class<T> type) throws Exception {
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setAccept(Collections.singletonList(out));
|
||||||
return this.restTemplate.exchange(prepareGet(url, out), type);
|
return getRestTemplate().exchange(prepareGet(url, headers), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> ResponseEntity<T> performGet(String url, MediaType out,
|
<T> ResponseEntity<T> performGet(String url, HttpHeaders headers, Class<T> type) throws Exception {
|
||||||
ParameterizedTypeReference<T> type) throws Exception {
|
return getRestTemplate().exchange(prepareGet(url, headers), type);
|
||||||
|
}
|
||||||
|
|
||||||
return this.restTemplate.exchange(prepareGet(url, out), type);
|
<T> ResponseEntity<T> performGet(String url, MediaType out, ParameterizedTypeReference<T> type)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setAccept(Collections.singletonList(out));
|
||||||
|
return this.restTemplate.exchange(prepareGet(url, headers), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> ResponseEntity<T> performOptions(String url, HttpHeaders headers, Class<T> type)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return getRestTemplate().exchange(prepareOptions(url, headers), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> ResponseEntity<T> performPost(String url, MediaType in, Object body, MediaType out, Class<T> type)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(in);
|
||||||
|
if (out != null) {
|
||||||
|
headers.setAccept(Collections.singletonList(out));
|
||||||
|
}
|
||||||
|
return getRestTemplate().exchange(preparePost(url, headers, body), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
<T> ResponseEntity<T> performPost(String url, HttpHeaders headers, Object body,
|
||||||
|
Class<T> type) throws Exception {
|
||||||
|
|
||||||
|
return getRestTemplate().exchange(preparePost(url, headers, body), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> ResponseEntity<T> performPost(String url, MediaType in, Object body, MediaType out,
|
<T> ResponseEntity<T> performPost(String url, MediaType in, Object body, MediaType out,
|
||||||
Class<T> type) throws Exception {
|
ParameterizedTypeReference<T> type) throws Exception {
|
||||||
|
|
||||||
return this.restTemplate.exchange(preparePost(url, in, body, out), type);
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(in);
|
||||||
|
if (out != null) {
|
||||||
|
headers.setAccept(Collections.singletonList(out));
|
||||||
|
}
|
||||||
|
return getRestTemplate().exchange(preparePost(url, headers, body), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> ResponseEntity<T> performPost(String url, MediaType in, Object body,
|
private RequestEntity<Void> prepareGet(String url, HttpHeaders headers) throws Exception {
|
||||||
MediaType out, ParameterizedTypeReference<T> type) throws Exception {
|
|
||||||
|
|
||||||
return this.restTemplate.exchange(preparePost(url, in, body, out), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RequestEntity<Void> prepareGet(String url, MediaType accept) throws Exception {
|
|
||||||
URI uri = new URI("http://localhost:" + this.port + url);
|
URI uri = new URI("http://localhost:" + this.port + url);
|
||||||
return (accept != null ? get(uri).accept(accept).build() : get(uri).build());
|
RequestEntity.HeadersBuilder<?> builder = get(uri);
|
||||||
|
addHeaders(builder, headers);
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> preparePost(String url, MediaType in, Object body, MediaType out) throws Exception {
|
private RequestEntity<Void> prepareOptions(String url, HttpHeaders headers) throws Exception {
|
||||||
URI uri = new URI("http://localhost:" + this.port + url);
|
URI uri = new URI("http://localhost:" + this.port + url);
|
||||||
return (out != null ?
|
RequestEntity.HeadersBuilder<?> builder = options(uri);
|
||||||
RequestEntity.post(uri).contentType(in).accept(out).body(body) :
|
addHeaders(builder, headers);
|
||||||
RequestEntity.post(uri).contentType(in).body(body));
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHeaders(RequestEntity.HeadersBuilder<?> builder, HttpHeaders headers) {
|
||||||
|
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
builder.header(entry.getKey(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestEntity<?> preparePost(String url, HttpHeaders headers, Object body) throws Exception {
|
||||||
|
URI uri = new URI("http://localhost:" + this.port + url);
|
||||||
|
RequestEntity.BodyBuilder builder = post(uri);
|
||||||
|
addHeaders(builder, headers);
|
||||||
|
return builder.body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,7 @@ package org.springframework.web.reactive.result.method.annotation;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.Before;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
@ -28,76 +27,82 @@ import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.core.env.PropertiesPropertySource;
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
import org.springframework.http.HttpEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Integration tests with {@code @CrossOrigin} and {@code @RequestMapping}
|
||||||
|
* annotated handler methods.
|
||||||
|
*
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappingIntegrationTests {
|
public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappingIntegrationTests {
|
||||||
|
|
||||||
// JDK default HTTP client blacklist headers like Origin
|
private HttpHeaders headers;
|
||||||
private RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
super.setup();
|
||||||
|
this.headers = new HttpHeaders();
|
||||||
|
this.headers.setOrigin("http://site1.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ApplicationContext initApplicationContext() {
|
protected ApplicationContext initApplicationContext() {
|
||||||
AnnotationConfigApplicationContext wac = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
wac.register(WebConfig.class);
|
context.register(WebConfig.class);
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.setProperty("myOrigin", "http://site1.com");
|
props.setProperty("myOrigin", "http://site1.com");
|
||||||
wac.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("ps", props));
|
context.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("ps", props));
|
||||||
wac.register(PropertySourcesPlaceholderConfigurer.class);
|
context.register(PropertySourcesPlaceholderConfigurer.class);
|
||||||
wac.refresh();
|
context.refresh();
|
||||||
return wac;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
RestTemplate getRestTemplate() {
|
protected RestTemplate initRestTemplate() {
|
||||||
return this.restTemplate;
|
// JDK default HTTP client blacklist headers like Origin
|
||||||
|
return new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualGetRequestWithoutAnnotation() {
|
public void actualGetRequestWithoutAnnotation() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/no", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/no"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("no", entity.getBody());
|
assertEquals("no", entity.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualPostRequestWithoutAnnotation() {
|
public void actualPostRequestWithoutAnnotation() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performPost("/no", this.headers, null, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/no"),
|
|
||||||
HttpMethod.POST, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("no-post", entity.getBody());
|
assertEquals("no-post", entity.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithDefaultAnnotation() {
|
public void actualRequestWithDefaultAnnotation() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/default", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/default"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
|
|
@ -105,13 +110,9 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithDefaultAnnotation() {
|
public void preflightRequestWithDefaultAnnotation() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
ResponseEntity<Void> entity = performOptions("/default", this.headers, Void.class);
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<Void> entity = this.restTemplate.exchange(getUrl("/default"),
|
|
||||||
HttpMethod.OPTIONS, requestEntity, Void.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals(1800, entity.getHeaders().getAccessControlMaxAge());
|
assertEquals(1800, entity.getHeaders().getAccessControlMaxAge());
|
||||||
|
|
@ -119,23 +120,17 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithDefaultAnnotationAndNoOrigin() {
|
public void actualRequestWithDefaultAnnotationAndNoOrigin() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
ResponseEntity<String> entity = performGet("/default", headers, String.class);
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/default"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("default", entity.getBody());
|
assertEquals("default", entity.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithCustomizedAnnotation() {
|
public void actualRequestWithCustomizedAnnotation() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/customized", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/customized"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
|
|
@ -144,67 +139,54 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithCustomizedAnnotation() {
|
public void preflightRequestWithCustomizedAnnotation() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "header1, header2");
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
ResponseEntity<String> entity = performOptions("/customized", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "header1, header2");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/customized"),
|
|
||||||
HttpMethod.OPTIONS, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertArrayEquals(new HttpMethod[] {HttpMethod.GET}, entity.getHeaders().getAccessControlAllowMethods().toArray());
|
assertArrayEquals(new HttpMethod[] {HttpMethod.GET},
|
||||||
|
entity.getHeaders().getAccessControlAllowMethods().toArray());
|
||||||
|
assertArrayEquals(new String[] {"header1", "header2"},
|
||||||
|
entity.getHeaders().getAccessControlAllowHeaders().toArray());
|
||||||
|
assertArrayEquals(new String[] {"header3", "header4"},
|
||||||
|
entity.getHeaders().getAccessControlExposeHeaders().toArray());
|
||||||
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
assertArrayEquals(new String[] {"header1", "header2"}, entity.getHeaders().getAccessControlAllowHeaders().toArray());
|
|
||||||
assertArrayEquals(new String[] {"header3", "header4"}, entity.getHeaders().getAccessControlExposeHeaders().toArray());
|
|
||||||
assertEquals(123, entity.getHeaders().getAccessControlMaxAge());
|
assertEquals(123, entity.getHeaders().getAccessControlMaxAge());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customOriginDefinedViaValueAttribute() {
|
public void customOriginDefinedViaValueAttribute() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/origin-value-attribute", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/origin-value-attribute"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("value-attribute", entity.getBody());
|
assertEquals("value-attribute", entity.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customOriginDefinedViaPlaceholder() {
|
public void customOriginDefinedViaPlaceholder() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/origin-placeholder", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/origin-placeholder"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("placeholder", entity.getBody());
|
assertEquals("placeholder", entity.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void classLevel() {
|
public void classLevel() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/foo", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/foo"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
assertEquals("foo", entity.getBody());
|
assertEquals("foo", entity.getBody());
|
||||||
|
|
||||||
entity = this.restTemplate.exchange(getUrl("/bar"), HttpMethod.GET, requestEntity, String.class);
|
entity = performGet("/bar", this.headers, String.class);
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
assertEquals("bar", entity.getBody());
|
assertEquals("bar", entity.getBody());
|
||||||
|
|
||||||
entity = this.restTemplate.exchange(getUrl("/baz"), HttpMethod.GET, requestEntity, String.class);
|
entity = performGet("/baz", this.headers, String.class);
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
|
|
@ -213,107 +195,104 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ambiguousHeaderPreflightRequest() throws Exception {
|
public void ambiguousHeaderPreflightRequest() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "header1");
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
ResponseEntity<String> entity = performOptions("/ambiguous-header", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "header1");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/ambiguous-header"),
|
|
||||||
HttpMethod.OPTIONS, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertArrayEquals(new HttpMethod[] {HttpMethod.GET}, entity.getHeaders().getAccessControlAllowMethods().toArray());
|
assertArrayEquals(new HttpMethod[] {HttpMethod.GET},
|
||||||
|
entity.getHeaders().getAccessControlAllowMethods().toArray());
|
||||||
|
assertArrayEquals(new String[] {"header1"},
|
||||||
|
entity.getHeaders().getAccessControlAllowHeaders().toArray());
|
||||||
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
assertArrayEquals(new String[] {"header1"}, entity.getHeaders().getAccessControlAllowHeaders().toArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ambiguousProducesPreflightRequest() throws Exception {
|
public void ambiguousProducesPreflightRequest() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://site1.com");
|
ResponseEntity<String> entity = performOptions("/ambiguous-produces", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/ambiguous-produces"),
|
|
||||||
HttpMethod.OPTIONS, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertArrayEquals(new HttpMethod[] {HttpMethod.GET}, entity.getHeaders().getAccessControlAllowMethods().toArray());
|
assertArrayEquals(new HttpMethod[] {HttpMethod.GET},
|
||||||
|
entity.getHeaders().getAccessControlAllowMethods().toArray());
|
||||||
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUrl(String path) {
|
|
||||||
return "http://localhost:" + this.port + path;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(resourcePattern = "**/CrossOriginAnnotationIntegrationTests*")
|
@ComponentScan(resourcePattern = "**/CrossOriginAnnotationIntegrationTests*")
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
static class WebConfig extends WebReactiveConfiguration {
|
static class WebConfig extends WebReactiveConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController @SuppressWarnings("unused")
|
||||||
private static class MethodLevelController {
|
private static class MethodLevelController {
|
||||||
|
|
||||||
@RequestMapping(path = "/no", method = RequestMethod.GET)
|
@GetMapping("/no")
|
||||||
public String noAnnotation() {
|
public String noAnnotation() {
|
||||||
return "no";
|
return "no";
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/no", method = RequestMethod.POST)
|
@PostMapping("/no")
|
||||||
public String noAnnotationPost() {
|
public String noAnnotationPost() {
|
||||||
return "no-post";
|
return "no-post";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/default", method = RequestMethod.GET)
|
@GetMapping("/default")
|
||||||
public String defaultAnnotation() {
|
public String defaultAnnotation() {
|
||||||
return "default";
|
return "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/default", method = RequestMethod.GET, params = "q")
|
@GetMapping(path = "/default", params = "q")
|
||||||
public void defaultAnnotationWithParams() {
|
public void defaultAnnotationWithParams() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/ambiguous-header", method = RequestMethod.GET, headers = "header1=a")
|
@GetMapping(path = "/ambiguous-header", headers = "header1=a")
|
||||||
public void ambigousHeader1a() {
|
public void ambigousHeader1a() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/ambiguous-header", method = RequestMethod.GET, headers = "header1=b")
|
@GetMapping(path = "/ambiguous-header", headers = "header1=b")
|
||||||
public void ambigousHeader1b() {
|
public void ambigousHeader1b() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/ambiguous-produces", method = RequestMethod.GET, produces = "application/xml")
|
@GetMapping(path = "/ambiguous-produces", produces = "application/xml")
|
||||||
public String ambigousProducesXml() {
|
public String ambigousProducesXml() {
|
||||||
return "<a></a>";
|
return "<a></a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/ambiguous-produces", method = RequestMethod.GET, produces = "application/json")
|
@GetMapping(path = "/ambiguous-produces", produces = "application/json")
|
||||||
public String ambigousProducesJson() {
|
public String ambigousProducesJson() {
|
||||||
return "{}";
|
return "{}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin(origins = { "http://site1.com", "http://site2.com" }, allowedHeaders = { "header1", "header2" },
|
@CrossOrigin(
|
||||||
exposedHeaders = { "header3", "header4" }, methods = RequestMethod.GET, maxAge = 123, allowCredentials = "false")
|
origins = { "http://site1.com", "http://site2.com" },
|
||||||
|
allowedHeaders = { "header1", "header2" },
|
||||||
|
exposedHeaders = { "header3", "header4" },
|
||||||
|
methods = RequestMethod.GET,
|
||||||
|
maxAge = 123,
|
||||||
|
allowCredentials = "false")
|
||||||
@RequestMapping(path = "/customized", method = { RequestMethod.GET, RequestMethod.POST })
|
@RequestMapping(path = "/customized", method = { RequestMethod.GET, RequestMethod.POST })
|
||||||
public String customized() {
|
public String customized() {
|
||||||
return "customized";
|
return "customized";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin("http://site1.com")
|
@CrossOrigin("http://site1.com")
|
||||||
@RequestMapping("/origin-value-attribute")
|
@GetMapping("/origin-value-attribute")
|
||||||
public String customOriginDefinedViaValueAttribute() {
|
public String customOriginDefinedViaValueAttribute() {
|
||||||
return "value-attribute";
|
return "value-attribute";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin("${myOrigin}")
|
@CrossOrigin("${myOrigin}")
|
||||||
@RequestMapping("/origin-placeholder")
|
@GetMapping("/origin-placeholder")
|
||||||
public String customOriginDefinedViaPlaceholder() {
|
public String customOriginDefinedViaPlaceholder() {
|
||||||
return "placeholder";
|
return "placeholder";
|
||||||
}
|
}
|
||||||
|
|
@ -321,25 +300,25 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin(allowCredentials = "false")
|
@CrossOrigin(allowCredentials = "false")
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class ClassLevelController {
|
private static class ClassLevelController {
|
||||||
|
|
||||||
@RequestMapping(path = "/foo", method = RequestMethod.GET)
|
@GetMapping("/foo")
|
||||||
public String foo() {
|
public String foo() {
|
||||||
return "foo";
|
return "foo";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/bar", method = RequestMethod.GET)
|
@GetMapping("/bar")
|
||||||
public String bar() {
|
public String bar() {
|
||||||
return "bar";
|
return "bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
@CrossOrigin(allowCredentials = "true")
|
@CrossOrigin(allowCredentials = "true")
|
||||||
@RequestMapping(path = "/baz", method = RequestMethod.GET)
|
@GetMapping("/baz")
|
||||||
public String baz() {
|
public String baz() {
|
||||||
return "baz";
|
return "baz";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,14 @@
|
||||||
|
|
||||||
package org.springframework.web.reactive.result.method.annotation;
|
package org.springframework.web.reactive.result.method.annotation;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
|
|
@ -36,34 +34,49 @@ import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.reactive.config.CorsRegistry;
|
import org.springframework.web.reactive.config.CorsRegistry;
|
||||||
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
||||||
|
|
||||||
/**
|
import static org.junit.Assert.assertEquals;
|
||||||
* @author Sebastien Deleuze
|
import static org.junit.Assert.assertNull;
|
||||||
*/
|
import static org.junit.Assert.fail;
|
||||||
public class CorsConfigurationIntegrationTests extends AbstractRequestMappingIntegrationTests {
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Integration tests with {@code @RequestMapping} handler methods and global
|
||||||
|
* CORS configuration.
|
||||||
|
*
|
||||||
|
* @author Sebastien Deleuze
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class GlobalCorsConfigIntegrationTests extends AbstractRequestMappingIntegrationTests {
|
||||||
|
|
||||||
|
private HttpHeaders headers;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
super.setup();
|
||||||
|
this.headers = new HttpHeaders();
|
||||||
|
this.headers.setOrigin("http://localhost:9000");
|
||||||
|
}
|
||||||
|
|
||||||
// JDK default HTTP client blacklist headers like Origin
|
|
||||||
private RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ApplicationContext initApplicationContext() {
|
protected ApplicationContext initApplicationContext() {
|
||||||
AnnotationConfigApplicationContext wac = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
wac.register(WebConfig.class);
|
context.register(WebConfig.class);
|
||||||
wac.refresh();
|
context.refresh();
|
||||||
return wac;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
RestTemplate getRestTemplate() {
|
protected RestTemplate initRestTemplate() {
|
||||||
return this.restTemplate;
|
// JDK default HTTP client blacklists headers like Origin
|
||||||
|
return new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithCorsEnabled() throws Exception {
|
public void actualRequestWithCorsEnabled() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/cors", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://localhost:9000");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/cors"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("cors", entity.getBody());
|
assertEquals("cors", entity.getBody());
|
||||||
|
|
@ -71,85 +84,58 @@ public class CorsConfigurationIntegrationTests extends AbstractRequestMappingInt
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithCorsRejected() throws Exception {
|
public void actualRequestWithCorsRejected() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://localhost:9000");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
try {
|
try {
|
||||||
this.restTemplate.exchange(getUrl("/cors-restricted"), HttpMethod.GET,
|
performGet("/cors-restricted", this.headers, String.class);
|
||||||
requestEntity, String.class);
|
fail();
|
||||||
}
|
}
|
||||||
catch (HttpClientErrorException e) {
|
catch (HttpClientErrorException e) {
|
||||||
assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
fail();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void actualRequestWithoutCorsEnabled() throws Exception {
|
public void actualRequestWithoutCorsEnabled() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
ResponseEntity<String> entity = performGet("/welcome", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://localhost:9000");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/welcome"),
|
|
||||||
HttpMethod.GET, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
assertNull(entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
assertEquals("welcome", entity.getBody());
|
assertEquals("welcome", entity.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithCorsEnabled() throws Exception {
|
public void preFlightRequestWithCorsEnabled() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://localhost:9000");
|
ResponseEntity<String> entity = performOptions("/cors", this.headers, String.class);
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
ResponseEntity<String> entity = this.restTemplate.exchange(getUrl("/cors"),
|
|
||||||
HttpMethod.OPTIONS, requestEntity, String.class);
|
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin());
|
assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithCorsRejected() throws Exception {
|
public void preFlightRequestWithCorsRejected() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://localhost:9000");
|
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
try {
|
try {
|
||||||
this.restTemplate.exchange(getUrl("/cors-restricted"), HttpMethod.OPTIONS,
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
requestEntity, String.class);
|
performOptions("/cors-restricted", this.headers, String.class);
|
||||||
|
fail();
|
||||||
}
|
}
|
||||||
catch (HttpClientErrorException e) {
|
catch (HttpClientErrorException e) {
|
||||||
assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
fail();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestWithoutCorsEnabled() throws Exception {
|
public void preFlightRequestWithoutCorsEnabled() throws Exception {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.add(HttpHeaders.ORIGIN, "http://localhost:9000");
|
|
||||||
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
|
||||||
HttpEntity<?> requestEntity = new HttpEntity(headers);
|
|
||||||
try {
|
try {
|
||||||
this.restTemplate.exchange(getUrl("/welcome"), HttpMethod.OPTIONS,
|
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
requestEntity, String.class);
|
performOptions("/welcome", this.headers, String.class);
|
||||||
|
fail();
|
||||||
}
|
}
|
||||||
catch (HttpClientErrorException e) {
|
catch (HttpClientErrorException e) {
|
||||||
assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, e.getStatusCode());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUrl(String path) {
|
|
||||||
return "http://localhost:" + this.port + path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(resourcePattern = "**/CorsConfigurationIntegrationTests*.class")
|
@ComponentScan(resourcePattern = "**/GlobalCorsConfigIntegrationTests*.class")
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
static class WebConfig extends WebReactiveConfiguration {
|
static class WebConfig extends WebReactiveConfiguration {
|
||||||
|
|
||||||
|
|
@ -160,7 +146,7 @@ public class CorsConfigurationIntegrationTests extends AbstractRequestMappingInt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController @SuppressWarnings("unused")
|
||||||
static class TestController {
|
static class TestController {
|
||||||
|
|
||||||
@GetMapping("/welcome")
|
@GetMapping("/welcome")
|
||||||
|
|
@ -177,7 +163,6 @@ public class CorsConfigurationIntegrationTests extends AbstractRequestMappingInt
|
||||||
public String corsRestricted() {
|
public String corsRestricted() {
|
||||||
return "corsRestricted";
|
return "corsRestricted";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
@ -52,13 +53,13 @@ public class RequestMappingExceptionHandlingIntegrationTests extends AbstractReq
|
||||||
@Test
|
@Test
|
||||||
public void controllerThrowingException() throws Exception {
|
public void controllerThrowingException() throws Exception {
|
||||||
String expected = "Recovered from error: Boo";
|
String expected = "Recovered from error: Boo";
|
||||||
assertEquals(expected, performGet("/thrown-exception", null, String.class).getBody());
|
assertEquals(expected, performGet("/thrown-exception", new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void controllerReturnsMonoError() throws Exception {
|
public void controllerReturnsMonoError() throws Exception {
|
||||||
String expected = "Recovered from error: Boo";
|
String expected = "Recovered from error: Boo";
|
||||||
assertEquals(expected, performGet("/mono-error", null, String.class).getBody());
|
assertEquals(expected, performGet("/mono-error", new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
@ -56,13 +57,13 @@ public class RequestMappingIntegrationTests extends AbstractRequestMappingIntegr
|
||||||
@Test
|
@Test
|
||||||
public void handleWithParam() throws Exception {
|
public void handleWithParam() throws Exception {
|
||||||
String expected = "Hello George!";
|
String expected = "Hello George!";
|
||||||
assertEquals(expected, performGet("/param?name=George", null, String.class).getBody());
|
assertEquals(expected, performGet("/param?name=George", new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void longStreamResult() throws Exception {
|
public void longStreamResult() throws Exception {
|
||||||
String[] expected = {"0", "1", "2", "3", "4"};
|
String[] expected = {"0", "1", "2", "3", "4"};
|
||||||
assertArrayEquals(expected, performGet("/long-stream-result", null, String[].class).getBody());
|
assertArrayEquals(expected, performGet("/long-stream-result", new HttpHeaders(), String[].class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
@ -94,25 +95,26 @@ public class RequestMappingMessageConversionIntegrationTests extends AbstractReq
|
||||||
@Test
|
@Test
|
||||||
public void byteBufferResponseBodyWithFlux() throws Exception {
|
public void byteBufferResponseBodyWithFlux() throws Exception {
|
||||||
String expected = "Hello!";
|
String expected = "Hello!";
|
||||||
assertEquals(expected, performGet("/raw-response/flux", null, String.class).getBody());
|
assertEquals(expected, performGet("/raw-response/flux", new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void byteBufferResponseBodyWithObservable() throws Exception {
|
public void byteBufferResponseBodyWithObservable() throws Exception {
|
||||||
String expected = "Hello!";
|
String expected = "Hello!";
|
||||||
assertEquals(expected, performGet("/raw-response/observable", null, String.class).getBody());
|
assertEquals(expected, performGet("/raw-response/observable", new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void byteBufferResponseBodyWithRxJava2Observable() throws Exception {
|
public void byteBufferResponseBodyWithRxJava2Observable() throws Exception {
|
||||||
String expected = "Hello!";
|
String expected = "Hello!";
|
||||||
assertEquals(expected, performGet("/raw-response/rxjava2-observable", null, String.class).getBody());
|
assertEquals(expected, performGet("/raw-response/rxjava2-observable",
|
||||||
|
new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void byteBufferResponseBodyWithFlowable() throws Exception {
|
public void byteBufferResponseBodyWithFlowable() throws Exception {
|
||||||
String expected = "Hello!";
|
String expected = "Hello!";
|
||||||
assertEquals(expected, performGet("/raw-response/flowable", null, String.class).getBody());
|
assertEquals(expected, performGet("/raw-response/flowable", new HttpHeaders(), String.class).getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -171,7 +173,7 @@ public class RequestMappingMessageConversionIntegrationTests extends AbstractReq
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resource() throws Exception {
|
public void resource() throws Exception {
|
||||||
ResponseEntity<byte[]> response = performGet("/resource", null, byte[].class);
|
ResponseEntity<byte[]> response = performGet("/resource", new HttpHeaders(), byte[].class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertTrue(response.hasBody());
|
assertTrue(response.hasBody());
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,12 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseHasCors(response)) {
|
if (responseHasCors(response)) {
|
||||||
logger.debug("Skip CORS processing: response already contains \"Access-Control-Allow-Origin\" header");
|
logger.debug("Skip CORS: response already contains \"Access-Control-Allow-Origin\" header");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CorsUtils.isSameOrigin(request)) {
|
if (CorsUtils.isSameOrigin(request)) {
|
||||||
logger.debug("Skip CORS processing: request is from same origin");
|
logger.debug("Skip CORS: request is from same origin");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.web.cors.reactive;
|
package org.springframework.web.cors.reactive;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
@ -26,17 +25,22 @@ import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
|
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||||
import org.springframework.web.server.session.MockWebSessionManager;
|
import org.springframework.web.server.session.MockWebSessionManager;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.springframework.http.HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS;
|
||||||
|
import static org.springframework.http.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||||
|
import static org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS;
|
||||||
|
import static org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test reactive {@link DefaultCorsProcessor} with simple or preflight CORS request.
|
* {@link DefaultCorsProcessor} tests with simple or pre-flight CORS request.
|
||||||
*
|
*
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
* @author Rossen Stoyanchev
|
|
||||||
* @author Juergen Hoeller
|
|
||||||
*/
|
*/
|
||||||
public class DefaultCorsProcessorTests {
|
public class DefaultCorsProcessorTests {
|
||||||
|
|
||||||
|
|
@ -69,7 +73,7 @@ public class DefaultCorsProcessorTests {
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +83,7 @@ public class DefaultCorsProcessorTests {
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(null, this.exchange);
|
this.processor.processRequest(null, this.exchange);
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,8 +94,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("*", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("*", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_MAX_AGE));
|
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_MAX_AGE));
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
|
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
|
|
@ -107,8 +111,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.setAllowCredentials(true);
|
this.conf.setAllowCredentials(true);
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
assertEquals("true", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
assertEquals("true", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
|
|
@ -122,8 +126,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.setAllowCredentials(true);
|
this.conf.setAllowCredentials(true);
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
assertEquals("true", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
assertEquals("true", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
|
|
@ -136,7 +140,7 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.addAllowedOrigin("http://DOMAIN2.com");
|
this.conf.addAllowedOrigin("http://DOMAIN2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,8 +153,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.addAllowedOrigin("http://domain2.com");
|
this.conf.addAllowedOrigin("http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
|
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
|
||||||
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header1"));
|
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header1"));
|
||||||
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header2"));
|
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header2"));
|
||||||
|
|
@ -161,7 +165,7 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestAllOriginsAllowed() throws Exception {
|
public void preflightRequestAllOriginsAllowed() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
|
|
@ -172,7 +176,7 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestWrongAllowedMethod() throws Exception {
|
public void preflightRequestWrongAllowedMethod() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "DELETE");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "DELETE");
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
|
|
@ -183,7 +187,7 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestMatchedAllowedMethod() throws Exception {
|
public void preflightRequestMatchedAllowedMethod() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
|
|
@ -197,7 +201,7 @@ public class DefaultCorsProcessorTests {
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,10 +209,10 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestWithoutRequestMethod() throws Exception {
|
public void preflightRequestWithoutRequestMethod() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,11 +220,11 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestWithRequestAndMethodHeaderButNoConfig() throws Exception {
|
public void preflightRequestWithRequestAndMethodHeaderButNoConfig() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,8 +232,8 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestValidRequestAndConfig() throws Exception {
|
public void preflightRequestValidRequestAndConfig() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
this.conf.addAllowedMethod("GET");
|
this.conf.addAllowedMethod("GET");
|
||||||
this.conf.addAllowedMethod("PUT");
|
this.conf.addAllowedMethod("PUT");
|
||||||
|
|
@ -237,8 +241,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.addAllowedHeader("header2");
|
this.conf.addAllowedHeader("header2");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("*", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("*", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
|
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
|
||||||
assertEquals("GET,PUT", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
|
assertEquals("GET,PUT", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_MAX_AGE));
|
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_MAX_AGE));
|
||||||
|
|
@ -249,8 +253,8 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestCredentials() throws Exception {
|
public void preflightRequestCredentials() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
||||||
this.conf.addAllowedOrigin("http://domain1.com");
|
this.conf.addAllowedOrigin("http://domain1.com");
|
||||||
this.conf.addAllowedOrigin("http://domain2.com");
|
this.conf.addAllowedOrigin("http://domain2.com");
|
||||||
this.conf.addAllowedOrigin("http://domain3.com");
|
this.conf.addAllowedOrigin("http://domain3.com");
|
||||||
|
|
@ -258,8 +262,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.setAllowCredentials(true);
|
this.conf.setAllowCredentials(true);
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
assertEquals("true", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
assertEquals("true", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
|
|
@ -269,8 +273,8 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestCredentialsWithOriginWildcard() throws Exception {
|
public void preflightRequestCredentialsWithOriginWildcard() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
||||||
this.conf.addAllowedOrigin("http://domain1.com");
|
this.conf.addAllowedOrigin("http://domain1.com");
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
this.conf.addAllowedOrigin("http://domain3.com");
|
this.conf.addAllowedOrigin("http://domain3.com");
|
||||||
|
|
@ -278,8 +282,8 @@ public class DefaultCorsProcessorTests {
|
||||||
this.conf.setAllowCredentials(true);
|
this.conf.setAllowCredentials(true);
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertEquals("http://domain2.com", this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,19 +291,19 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestAllowedHeaders() throws Exception {
|
public void preflightRequestAllowedHeaders() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
|
||||||
this.conf.addAllowedHeader("Header1");
|
this.conf.addAllowedHeader("Header1");
|
||||||
this.conf.addAllowedHeader("Header2");
|
this.conf.addAllowedHeader("Header2");
|
||||||
this.conf.addAllowedHeader("Header3");
|
this.conf.addAllowedHeader("Header3");
|
||||||
this.conf.addAllowedOrigin("http://domain2.com");
|
this.conf.addAllowedOrigin("http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS));
|
||||||
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1"));
|
assertTrue(this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1"));
|
||||||
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2"));
|
assertTrue(this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2"));
|
||||||
assertFalse(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header3"));
|
assertFalse(this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS).contains("Header3"));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,17 +311,17 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestAllowsAllHeaders() throws Exception {
|
public void preflightRequestAllowsAllHeaders() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
|
||||||
this.conf.addAllowedHeader("*");
|
this.conf.addAllowedHeader("*");
|
||||||
this.conf.addAllowedOrigin("http://domain2.com");
|
this.conf.addAllowedOrigin("http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS));
|
||||||
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1"));
|
assertTrue(this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1"));
|
||||||
assertTrue(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2"));
|
assertTrue(this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2"));
|
||||||
assertFalse(this.response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("*"));
|
assertFalse(this.response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS).contains("*"));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,14 +329,14 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestWithEmptyHeaders() throws Exception {
|
public void preflightRequestWithEmptyHeaders() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_HEADERS, "");
|
||||||
this.conf.addAllowedHeader("*");
|
this.conf.addAllowedHeader("*");
|
||||||
this.conf.addAllowedOrigin("http://domain2.com");
|
this.conf.addAllowedOrigin("http://domain2.com");
|
||||||
|
|
||||||
this.processor.processRequest(this.conf, this.exchange);
|
this.processor.processRequest(this.conf, this.exchange);
|
||||||
assertTrue(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertTrue(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS));
|
||||||
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
assertEquals(HttpStatus.OK, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,11 +344,11 @@ public class DefaultCorsProcessorTests {
|
||||||
public void preflightRequestWithNullConfig() throws Exception {
|
public void preflightRequestWithNullConfig() throws Exception {
|
||||||
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
this.request.setHttpMethod(HttpMethod.OPTIONS);
|
||||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
|
||||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
this.request.addHeader(ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
this.conf.addAllowedOrigin("*");
|
this.conf.addAllowedOrigin("*");
|
||||||
|
|
||||||
this.processor.processRequest(null, this.exchange);
|
this.processor.processRequest(null, this.exchange);
|
||||||
assertFalse(this.response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
assertFalse(this.response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
assertEquals(HttpStatus.FORBIDDEN, this.response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.web.cors.reactive;
|
package org.springframework.web.cors.reactive;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
@ -29,19 +27,23 @@ import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||||
import org.springframework.web.server.session.MockWebSessionManager;
|
import org.springframework.web.server.session.MockWebSessionManager;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for reactive {@link UrlBasedCorsConfigurationSource}.
|
* Unit tests for {@link UrlBasedCorsConfigurationSource}.
|
||||||
|
*
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class UrlBasedCorsConfigurationSourceTests {
|
public class UrlBasedCorsConfigurationSourceTests {
|
||||||
|
|
||||||
private final UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
|
private final UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void empty() {
|
public void empty() {
|
||||||
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/bar/test.html");
|
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/bar/test.html");
|
||||||
ServerWebExchange exchange = new DefaultServerWebExchange(request,
|
|
||||||
new MockServerHttpResponse(), new MockWebSessionManager());
|
|
||||||
assertNull(this.configSource.getCorsConfiguration(exchange));
|
assertNull(this.configSource.getCorsConfiguration(exchange));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,15 +51,12 @@ public class UrlBasedCorsConfigurationSourceTests {
|
||||||
public void registerAndMatch() {
|
public void registerAndMatch() {
|
||||||
CorsConfiguration config = new CorsConfiguration();
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
this.configSource.registerCorsConfiguration("/bar/**", config);
|
this.configSource.registerCorsConfiguration("/bar/**", config);
|
||||||
assertNull(this.configSource.getCorsConfiguration(
|
|
||||||
new DefaultServerWebExchange(
|
ServerWebExchange exchange = createExchange(HttpMethod.GET, "/foo/test.html");
|
||||||
new MockServerHttpRequest(HttpMethod.GET, "/foo/test.html"),
|
assertNull(this.configSource.getCorsConfiguration(exchange));
|
||||||
new MockServerHttpResponse(),
|
|
||||||
new MockWebSessionManager())));
|
exchange = createExchange(HttpMethod.GET, "/bar/test.html");
|
||||||
assertEquals(config, this.configSource.getCorsConfiguration(new DefaultServerWebExchange(
|
assertEquals(config, this.configSource.getCorsConfiguration(exchange));
|
||||||
new MockServerHttpRequest(HttpMethod.GET, "/bar/test.html"),
|
|
||||||
new MockServerHttpResponse(),
|
|
||||||
new MockWebSessionManager())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = UnsupportedOperationException.class)
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
|
|
@ -65,4 +64,12 @@ public class UrlBasedCorsConfigurationSourceTests {
|
||||||
this.configSource.getCorsConfigurations().put("/**", new CorsConfiguration());
|
this.configSource.getCorsConfigurations().put("/**", new CorsConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ServerWebExchange createExchange(HttpMethod httpMethod, String url) {
|
||||||
|
ServerHttpRequest request = new MockServerHttpRequest(httpMethod, url);
|
||||||
|
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||||
|
MockWebSessionManager sessionManager = new MockWebSessionManager();
|
||||||
|
return new DefaultServerWebExchange(request, response, sessionManager);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ public class CorsRegistration {
|
||||||
|
|
||||||
private final CorsConfiguration config;
|
private final CorsConfiguration config;
|
||||||
|
|
||||||
|
|
||||||
public CorsRegistration(String pathPattern) {
|
public CorsRegistration(String pathPattern) {
|
||||||
this.pathPattern = pathPattern;
|
this.pathPattern = pathPattern;
|
||||||
// Same implicit default values as the @CrossOrigin annotation + allows simple methods
|
// Same implicit default values as the @CrossOrigin annotation + allows simple methods
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,9 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
|
|
||||||
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
|
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
|
||||||
|
|
||||||
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();
|
||||||
|
|
||||||
private final UrlBasedCorsConfigurationSource corsConfigSource = new UrlBasedCorsConfigurationSource();
|
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -123,7 +123,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
*/
|
*/
|
||||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||||
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
|
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||||
this.corsConfigSource.setAlwaysUseFullPath(alwaysUseFullPath);
|
this.globalCorsConfigSource.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -135,7 +135,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
*/
|
*/
|
||||||
public void setUrlDecode(boolean urlDecode) {
|
public void setUrlDecode(boolean urlDecode) {
|
||||||
this.urlPathHelper.setUrlDecode(urlDecode);
|
this.urlPathHelper.setUrlDecode(urlDecode);
|
||||||
this.corsConfigSource.setUrlDecode(urlDecode);
|
this.globalCorsConfigSource.setUrlDecode(urlDecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -145,7 +145,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
*/
|
*/
|
||||||
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
|
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
|
||||||
this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
|
this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
|
||||||
this.corsConfigSource.setRemoveSemicolonContent(removeSemicolonContent);
|
this.globalCorsConfigSource.setRemoveSemicolonContent(removeSemicolonContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -157,7 +157,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||||
this.urlPathHelper = urlPathHelper;
|
this.urlPathHelper = urlPathHelper;
|
||||||
this.corsConfigSource.setUrlPathHelper(urlPathHelper);
|
this.globalCorsConfigSource.setUrlPathHelper(urlPathHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,7 +175,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||||
this.pathMatcher = pathMatcher;
|
this.pathMatcher = pathMatcher;
|
||||||
this.corsConfigSource.setPathMatcher(pathMatcher);
|
this.globalCorsConfigSource.setPathMatcher(pathMatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,6 +200,23 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
this.interceptors.addAll(Arrays.asList(interceptors));
|
this.interceptors.addAll(Arrays.asList(interceptors));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set "global" CORS configuration based on URL patterns. By default the first
|
||||||
|
* matching URL pattern is combined with the CORS configuration for the
|
||||||
|
* handler, if any.
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
||||||
|
this.globalCorsConfigSource.setCorsConfigurations(corsConfigurations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the "global" CORS configuration.
|
||||||
|
*/
|
||||||
|
public Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||||
|
return this.globalCorsConfigSource.getCorsConfigurations();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a custom {@link CorsProcessor} to use to apply the matched
|
* Configure a custom {@link CorsProcessor} to use to apply the matched
|
||||||
* {@link CorsConfiguration} for a request.
|
* {@link CorsConfiguration} for a request.
|
||||||
|
|
@ -218,23 +235,6 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
return this.corsProcessor;
|
return this.corsProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set "global" CORS configuration based on URL patterns. By default the first
|
|
||||||
* matching URL pattern is combined with the CORS configuration for the
|
|
||||||
* handler, if any.
|
|
||||||
* @since 4.2
|
|
||||||
*/
|
|
||||||
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
|
||||||
this.corsConfigSource.setCorsConfigurations(corsConfigurations);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the CORS configuration.
|
|
||||||
*/
|
|
||||||
public Map<String, CorsConfiguration> getCorsConfigurations() {
|
|
||||||
return this.corsConfigSource.getCorsConfigurations();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the interceptors.
|
* Initializes the interceptors.
|
||||||
|
|
@ -364,7 +364,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||||
|
|
||||||
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
|
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
|
||||||
if (CorsUtils.isCorsRequest(request)) {
|
if (CorsUtils.isCorsRequest(request)) {
|
||||||
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
|
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
|
||||||
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
|
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
|
||||||
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
|
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
|
||||||
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
|
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||||
@Override
|
@Override
|
||||||
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
|
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
|
||||||
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
|
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
|
||||||
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), CrossOrigin.class);
|
Class<?> beanType = handlerMethod.getBeanType();
|
||||||
|
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
|
||||||
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
|
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
|
||||||
|
|
||||||
if (typeAnnotation == null && methodAnnotation == null) {
|
if (typeAnnotation == null && methodAnnotation == null) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue