diff --git a/docs/src/main/asciidoc/spring-cloud-gateway.adoc b/docs/src/main/asciidoc/spring-cloud-gateway.adoc index c3935e9c8f..d6b4217b35 100644 --- a/docs/src/main/asciidoc/spring-cloud-gateway.adoc +++ b/docs/src/main/asciidoc/spring-cloud-gateway.adoc @@ -2396,6 +2396,31 @@ In the preceding example, CORS requests are allowed from requests that originate To provide the same CORS configuration to requests that are not handled by some gateway route predicate, set the `spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping` property to `true`. This is useful when you try to support CORS preflight requests and your route predicate does not evalute to `true` because the HTTP method is `options`. +== Handle CORS Preflight Request By Upstream + +You can configure the gateway to pass CORS preflight request to upstream per route configuration. +The following example shows how to do this: + +.application.yml +==== +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: handle_pre_flight_request_by_upstream + uri: ${upstream-uri} + handle-preflight-request-by-upstream: true + predicates: + - Path=/some-path/** +---- +==== + +In the preceding example, CORS preflight request will be handled by upstream for this route configuration, no matter what `globalcors` is configured. + +Route configuration without `handle-preflight-request-by-upstream` or explicitly setting to `false` will obey gateway CORS configuration. + == Actuator API The `/gateway` actuator endpoint lets you monitor and interact with a Spring Cloud Gateway application. diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java index 16b472b0bc..eec4a29e58 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java @@ -52,4 +52,9 @@ public int getOrder() { return 0; } + @Override + public boolean handlePreFlightRequest() { + return true; + } + } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java index 771587846e..3c1177c018 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java @@ -31,7 +31,7 @@ * @author Rossen Stoyanchev * @since 5.0 */ -public interface GatewayFilter extends ShortcutConfigurable { +public interface GatewayFilter extends PreFlightRequestFilter, ShortcutConfigurable { /** * Name key. @@ -52,4 +52,9 @@ public interface GatewayFilter extends ShortcutConfigurable { */ Mono filter(ServerWebExchange exchange, GatewayFilterChain chain); + @Override + default boolean handlePreFlightRequest() { + return true; + } + } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java index 96f87091f2..836fa1eb7e 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java @@ -28,7 +28,7 @@ * @author Rossen Stoyanchev * @since 5.0 */ -public interface GlobalFilter { +public interface GlobalFilter extends PreFlightRequestFilter { /** * Process the Web request and (optionally) delegate to the next {@code WebFilter} @@ -39,4 +39,9 @@ public interface GlobalFilter { */ Mono filter(ServerWebExchange exchange, GatewayFilterChain chain); + @Override + default boolean handlePreFlightRequest() { + return false; + } + } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java index 511893e7f5..60f3da354e 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java @@ -69,7 +69,7 @@ * @author Spencer Gibb * @author Biju Kunjummen */ -public class NettyRoutingFilter implements GlobalFilter, Ordered { +public class NettyRoutingFilter implements GlobalFilter, PreFlightRequestFilter, Ordered { /** * The order of the NettyRoutingFilter. See {@link Ordered#LOWEST_PRECEDENCE}. @@ -198,6 +198,11 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return responseFlux.then(chain.filter(exchange)); } + @Override + public boolean handlePreFlightRequest() { + return true; + } + protected ByteBuf getByteBuf(DataBuffer dataBuffer) { if (dataBuffer instanceof NettyDataBuffer) { NettyDataBuffer buffer = (NettyDataBuffer) dataBuffer; diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java index 0c7972722d..3437714c75 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java @@ -44,6 +44,11 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return this.delegate.filter(exchange, chain); } + @Override + public boolean handlePreFlightRequest() { + return delegate.handlePreFlightRequest(); + } + @Override public int getOrder() { return this.order; diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/PreFlightRequestFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/PreFlightRequestFilter.java new file mode 100644 index 0000000000..c7acd5537d --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/PreFlightRequestFilter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.filter; + +/** + * Indicate a filter should handle pre-flight request and pass the request to upstream. + * Only filters that affect route process should return true with handlePreFlightRequest method. + * + * @author Tommas Yuan + */ +public interface PreFlightRequestFilter { + + /** + * @return {@code true} to indicate this filter will handle pre-flight request + */ + boolean handlePreFlightRequest(); + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ReactiveLoadBalancerClientFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ReactiveLoadBalancerClientFilter.java index 3de2276981..fb27c51b1d 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ReactiveLoadBalancerClientFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ReactiveLoadBalancerClientFilter.java @@ -180,4 +180,9 @@ private String getHint(String serviceId) { return hintPropertyValue != null ? hintPropertyValue : defaultHint; } + @Override + public boolean handlePreFlightRequest() { + return true; + } + } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java index 35c1a77c6e..98629b5ea8 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java @@ -92,4 +92,9 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange); } + @Override + public boolean handlePreFlightRequest() { + return true; + } + } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java index 8be3308bde..d745a4f5d5 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java @@ -51,8 +51,12 @@ public class FilteringWebHandler implements WebHandler { private final List globalFilters; + private final List preflightRequestFilters; + public FilteringWebHandler(List globalFilters) { this.globalFilters = loadFilters(globalFilters); + this.preflightRequestFilters = this.globalFilters.stream().filter(filter -> filter.handlePreFlightRequest()) + .toList(); } private static List loadFilters(List filters) { @@ -88,6 +92,23 @@ public Mono handle(ServerWebExchange exchange) { return new DefaultGatewayFilterChain(combined).filter(exchange); } + public Mono handlePreFlight(ServerWebExchange exchange) { + Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); + List preflightRequestFilters = route.getFilters().stream() + .filter(filter -> filter.handlePreFlightRequest()).toList(); + + List combined = new ArrayList<>(this.preflightRequestFilters); + combined.addAll(preflightRequestFilters); + + AnnotationAwareOrderComparator.sort(combined); + + if (logger.isDebugEnabled()) { + logger.debug("Sorted preflight request filters: " + combined); + } + + return new DefaultGatewayFilterChain(combined).filter(exchange); + } + private static class DefaultGatewayFilterChain implements GatewayFilterChain { private final int index; diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java index 39b8919767..2ee0045d19 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java @@ -25,7 +25,10 @@ import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.reactive.CorsUtils; import org.springframework.web.reactive.handler.AbstractHandlerMapping; import org.springframework.web.server.ServerWebExchange; @@ -73,6 +76,30 @@ private static Integer getPortProperty(Environment environment, String prefix) { return environment.getProperty(prefix + "port", Integer.class); } + @Override + public Mono getHandler(ServerWebExchange exchange) { + Mono mono = super.getHandler(exchange); + + if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { + return mono.flatMap(handler -> { + Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); + // pass pre-flight request to upstream, if the request is permitted + ServerHttpResponse response = exchange.getResponse(); + if (route != null && route.handlePreflightRequest()) { + // remove cors response headers generated by gateway + HttpHeaders headers = response.getHeaders(); + headers.remove(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN); + headers.remove(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS); + headers.remove(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS); + return webHandler.handlePreFlight(exchange).then(Mono.just(handler)); + } + return Mono.just(handler); + }); + } + + return mono; + } + @Override protected Mono getHandlerInternal(ServerWebExchange exchange) { // don't handle requests on management port if set and different than server port diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/Route.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/Route.java index b4a30a5ec5..04a9362881 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/Route.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/Route.java @@ -55,14 +55,17 @@ public class Route implements Ordered { private final Map metadata; + private boolean handlePreflightRequest; + private Route(String id, URI uri, int order, AsyncPredicate predicate, - List gatewayFilters, Map metadata) { + List gatewayFilters, Map metadata, boolean handlePreflightRequest) { this.id = id; this.uri = uri; this.order = order; this.predicate = predicate; this.gatewayFilters = gatewayFilters; this.metadata = metadata; + this.handlePreflightRequest = handlePreflightRequest; } public static Builder builder() { @@ -74,7 +77,8 @@ public static Builder builder(RouteDefinition routeDefinition) { return new Builder().id(routeDefinition.getId()) .uri(routeDefinition.getUri()) .order(routeDefinition.getOrder()) - .metadata(routeDefinition.getMetadata()); + .metadata(routeDefinition.getMetadata()) + .handlePreflightRequestByUpstream(routeDefinition.isHandlePreflightRequestByUpstream()); // @formatter:on } @@ -87,7 +91,8 @@ public static AsyncBuilder async(RouteDefinition routeDefinition) { return new AsyncBuilder().id(routeDefinition.getId()) .uri(routeDefinition.getUri()) .order(routeDefinition.getOrder()) - .metadata(routeDefinition.getMetadata()); + .metadata(routeDefinition.getMetadata()) + .handlePreflightRequestByUpstream(routeDefinition.isHandlePreflightRequestByUpstream()); // @formatter:on } @@ -115,6 +120,10 @@ public Map getMetadata() { return Collections.unmodifiableMap(metadata); } + public boolean handlePreflightRequest() { + return handlePreflightRequest; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -127,12 +136,14 @@ public boolean equals(Object o) { return this.order == route.order && Objects.equals(this.id, route.id) && Objects.equals(this.uri, route.uri) && Objects.equals(this.predicate, route.predicate) && Objects.equals(this.gatewayFilters, route.gatewayFilters) - && Objects.equals(this.metadata, route.metadata); + && Objects.equals(this.metadata, route.metadata) + && this.handlePreflightRequest == route.handlePreflightRequest; } @Override public int hashCode() { - return Objects.hash(this.id, this.uri, this.order, this.predicate, this.gatewayFilters, this.metadata); + return Objects.hash(this.id, this.uri, this.order, this.predicate, this.gatewayFilters, this.metadata, + this.handlePreflightRequest); } @Override @@ -144,6 +155,7 @@ public String toString() { sb.append(", predicate=").append(predicate); sb.append(", gatewayFilters=").append(gatewayFilters); sb.append(", metadata=").append(metadata); + sb.append(", handlePreflightRequest=").append(handlePreflightRequest); sb.append('}'); return sb.toString(); } @@ -160,6 +172,8 @@ public abstract static class AbstractBuilder> imple protected Map metadata = new HashMap<>(); + protected boolean handlePreflightRequestByUpstream; + protected AbstractBuilder() { } @@ -210,6 +224,11 @@ public B metadata(String key, Object value) { return getThis(); } + public B handlePreflightRequestByUpstream(boolean handlePreflightRequestByUpstream) { + this.handlePreflightRequestByUpstream = handlePreflightRequestByUpstream; + return getThis(); + } + public abstract AsyncPredicate getPredicate(); public B replaceFilters(List gatewayFilters) { @@ -237,7 +256,8 @@ public Route build() { AsyncPredicate predicate = getPredicate(); Assert.notNull(predicate, "predicate can not be null"); - return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters, this.metadata); + return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters, this.metadata, + this.handlePreflightRequestByUpstream); } } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java index 8c35e138de..48498bfc27 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java @@ -56,6 +56,8 @@ public class RouteDefinition { private int order = 0; + private boolean handlePreflightRequestByUpstream; + public RouteDefinition() { } @@ -125,6 +127,14 @@ public void setMetadata(Map metadata) { this.metadata = metadata; } + public boolean isHandlePreflightRequestByUpstream() { + return handlePreflightRequestByUpstream; + } + + public void setHandlePreflightRequestByUpstream(boolean handlePreflightRequestByUpstream) { + this.handlePreflightRequestByUpstream = handlePreflightRequestByUpstream; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -136,18 +146,21 @@ public boolean equals(Object o) { RouteDefinition that = (RouteDefinition) o; return this.order == that.order && Objects.equals(this.id, that.id) && Objects.equals(this.predicates, that.predicates) && Objects.equals(this.filters, that.filters) - && Objects.equals(this.uri, that.uri) && Objects.equals(this.metadata, that.metadata); + && Objects.equals(this.uri, that.uri) && Objects.equals(this.metadata, that.metadata) + && this.handlePreflightRequestByUpstream == that.handlePreflightRequestByUpstream; } @Override public int hashCode() { - return Objects.hash(this.id, this.predicates, this.filters, this.uri, this.metadata, this.order); + return Objects.hash(this.id, this.predicates, this.filters, this.uri, this.metadata, this.order, + this.handlePreflightRequestByUpstream); } @Override public String toString() { return "RouteDefinition{" + "id='" + id + '\'' + ", predicates=" + predicates + ", filters=" + filters - + ", uri=" + uri + ", order=" + order + ", metadata=" + metadata + '}'; + + ", uri=" + uri + ", order=" + order + ", metadata=" + metadata + "handlePreFlightRequest=" + + handlePreflightRequestByUpstream + '}'; } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingPreFlightRequestTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingPreFlightRequestTests.java new file mode 100644 index 0000000000..c336839dbd --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingPreFlightRequestTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.handler; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +@ActiveProfiles("pre-flight-request") +public class RoutePredicateHandlerMappingPreFlightRequestTests extends BaseWebClientTests { + + @Test + public void allowedByUpStreamTest() { + testClient.mutate().build().options().uri("/allowedbyupstream") + .header("Origin", "http://www.passthroughpreflight.org").header("Access-Control-Request-Method", "PUT") + .exchange().expectStatus().is2xxSuccessful().expectHeader() + .valueEquals("Access-Control-Allow-Methods", "PUT"); + } + + @Test + public void rejectedByUpStreamTest() { + testClient.mutate().build().options().uri("/rejectedbyupstream") + .header("Origin", "http://www.passthroughpreflight.org").header("Access-Control-Request-Method", "GET") + .exchange().expectStatus().isForbidden(); + } + + @Test + public void rejectedByNotFoundUpstreamTest() { + testClient.mutate().build().options().uri("/rejectedbynotfoundupstream") + .header("Origin", "http://www.passthroughpreflight.org").header("Access-Control-Request-Method", "POST") + .exchange().expectStatus().isForbidden(); + } + + @Test + public void allowedByGatewayTest() { + testClient.mutate().build().options().uri("/allowedbygateway") + .header("Origin", "http://www.passthroughpreflight.org").header("Access-Control-Request-Method", "POST") + .exchange().expectStatus().is2xxSuccessful().expectHeader() + .valueEquals("Access-Control-Allow-Methods", "GET,POST"); + } + + @Test + public void rejectedByGatewayTest() { + testClient.mutate().build().options().uri("/rejectedbygateway") + .header("Origin", "http://www.passthroughpreflight.org").header("Access-Control-Request-Method", "PUT") + .exchange().expectStatus().isForbidden(); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + @RestController + public static class TestConfig { + + @RequestMapping("/httpbin/allowedbyupstream") + @CrossOrigin(methods = RequestMethod.PUT) + String allowedByUpstream() { + return "preflight request"; + } + + @RequestMapping("/httpbin/rejectedbyupstream") + @CrossOrigin(methods = RequestMethod.POST) + String rejectedByUpstream() { + return "preflight request"; + } + + @RequestMapping("/httpbin/rejectedbygateway") + @CrossOrigin(methods = RequestMethod.PUT) + String rejectedByGateway() { + return "preflight request"; + } + } + +} diff --git a/spring-cloud-gateway-server/src/test/resources/application-pre-flight-request.yml b/spring-cloud-gateway-server/src/test/resources/application-pre-flight-request.yml new file mode 100644 index 0000000000..301026d06f --- /dev/null +++ b/spring-cloud-gateway-server/src/test/resources/application-pre-flight-request.yml @@ -0,0 +1,45 @@ +spring: + cloud: + gateway: + globalcors: + cors-configurations: + '[/**]': + maxAge: 10 + allowedOrigins: "*" + allowedMethods: + - GET + - POST + + routes: + # ===================================== + - id: pre_flight_request_allowed_by_upstream + uri: ${test.uri} + handle-preflight-request-by-upstream: true + predicates: + - Path=/allowedbyupstream/** + + # ===================================== + - id: pre_flight_request_rejected_by_upstream + uri: ${test.uri} + handle-preflight-request-by-upstream: true + predicates: + - Path=/rejectedbyupstream/** + + # ===================================== + - id: pre_flight_request_rejected_by_not_found_upstream + uri: ${test.uri} + handle-preflight-request-by-upstream: true + predicates: + - Path=/rejectedbynotfoundupstream/** + + # ===================================== + - id: pre_flight_request_allowed_by_gateway + uri: ${test.uri} + predicates: + - Path=/allowedbygateway/** + + # ===================================== + - id: pre_flight_request_rejected_by_gateway + uri: ${test.uri} + predicates: + - Path=/rejectedbygateway/** \ No newline at end of file