Skip to content

Commit 983b5a4

Browse files
committed
Pass preflight request to upstream. Fixes gh-2472
1 parent fc31ccb commit 983b5a4

File tree

15 files changed

+332
-12
lines changed

15 files changed

+332
-12
lines changed

docs/src/main/asciidoc/spring-cloud-gateway.adoc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2396,6 +2396,31 @@ In the preceding example, CORS requests are allowed from requests that originate
23962396
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`.
23972397
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`.
23982398

2399+
== Handle CORS Preflight Request By Upstream
2400+
2401+
You can configure the gateway to pass CORS preflight request to upstream per route configuration.
2402+
The following example shows how to do this:
2403+
2404+
.application.yml
2405+
====
2406+
[source,yaml]
2407+
----
2408+
spring:
2409+
cloud:
2410+
gateway:
2411+
routes:
2412+
- id: handle_pre_flight_request_by_upstream
2413+
uri: ${upstream-uri}
2414+
handle-preflight-request-by-upstream: true
2415+
predicates:
2416+
- Path=/some-path/**
2417+
----
2418+
====
2419+
2420+
In the preceding example, CORS preflight request will be handled by upstream for this route configuration, no matter what `globalcors` is configured.
2421+
2422+
Route configuration without `handle-preflight-request-by-upstream` or explicitly setting to `false` will obey gateway CORS configuration.
2423+
23992424
== Actuator API
24002425

24012426
The `/gateway` actuator endpoint lets you monitor and interact with a Spring Cloud Gateway application.

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,9 @@ public int getOrder() {
5252
return 0;
5353
}
5454

55+
@Override
56+
public boolean handlePreFlightRequest() {
57+
return true;
58+
}
59+
5560
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* @author Rossen Stoyanchev
3232
* @since 5.0
3333
*/
34-
public interface GatewayFilter extends ShortcutConfigurable {
34+
public interface GatewayFilter extends PreFlightRequestFilter, ShortcutConfigurable {
3535

3636
/**
3737
* Name key.
@@ -52,4 +52,9 @@ public interface GatewayFilter extends ShortcutConfigurable {
5252
*/
5353
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
5454

55+
@Override
56+
default boolean handlePreFlightRequest() {
57+
return true;
58+
}
59+
5560
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* @author Rossen Stoyanchev
2929
* @since 5.0
3030
*/
31-
public interface GlobalFilter {
31+
public interface GlobalFilter extends PreFlightRequestFilter {
3232

3333
/**
3434
* Process the Web request and (optionally) delegate to the next {@code WebFilter}
@@ -39,4 +39,9 @@ public interface GlobalFilter {
3939
*/
4040
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
4141

42+
@Override
43+
default boolean handlePreFlightRequest() {
44+
return false;
45+
}
46+
4247
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
* @author Spencer Gibb
7070
* @author Biju Kunjummen
7171
*/
72-
public class NettyRoutingFilter implements GlobalFilter, Ordered {
72+
public class NettyRoutingFilter implements GlobalFilter, PreFlightRequestFilter, Ordered {
7373

7474
/**
7575
* The order of the NettyRoutingFilter. See {@link Ordered#LOWEST_PRECEDENCE}.
@@ -198,6 +198,11 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
198198
return responseFlux.then(chain.filter(exchange));
199199
}
200200

201+
@Override
202+
public boolean handlePreFlightRequest() {
203+
return true;
204+
}
205+
201206
protected ByteBuf getByteBuf(DataBuffer dataBuffer) {
202207
if (dataBuffer instanceof NettyDataBuffer) {
203208
NettyDataBuffer buffer = (NettyDataBuffer) dataBuffer;

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
4444
return this.delegate.filter(exchange, chain);
4545
}
4646

47+
@Override
48+
public boolean handlePreFlightRequest() {
49+
return delegate.handlePreFlightRequest();
50+
}
51+
4752
@Override
4853
public int getOrder() {
4954
return this.order;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gateway.filter;
18+
19+
/**
20+
* Indicate a filter should handle pre-flight request and pass the request to upstream.
21+
* Only filters that affect route process should return true with handlePreFlightRequest method.
22+
*
23+
* @author Tommas Yuan
24+
*/
25+
public interface PreFlightRequestFilter {
26+
27+
/**
28+
* @return {@code true} to indicate this filter will handle pre-flight request
29+
*/
30+
boolean handlePreFlightRequest();
31+
32+
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ReactiveLoadBalancerClientFilter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,9 @@ private String getHint(String serviceId) {
180180
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
181181
}
182182

183+
@Override
184+
public boolean handlePreFlightRequest() {
185+
return true;
186+
}
187+
183188
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,9 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
9292
return chain.filter(exchange);
9393
}
9494

95+
@Override
96+
public boolean handlePreFlightRequest() {
97+
return true;
98+
}
99+
95100
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,12 @@ public class FilteringWebHandler implements WebHandler {
5151

5252
private final List<GatewayFilter> globalFilters;
5353

54+
private final List<GatewayFilter> preflightRequestFilters;
55+
5456
public FilteringWebHandler(List<GlobalFilter> globalFilters) {
5557
this.globalFilters = loadFilters(globalFilters);
58+
this.preflightRequestFilters = this.globalFilters.stream().filter(filter -> filter.handlePreFlightRequest())
59+
.toList();
5660
}
5761

5862
private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
@@ -88,6 +92,23 @@ public Mono<Void> handle(ServerWebExchange exchange) {
8892
return new DefaultGatewayFilterChain(combined).filter(exchange);
8993
}
9094

95+
public Mono<Void> handlePreFlight(ServerWebExchange exchange) {
96+
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
97+
List<GatewayFilter> preflightRequestFilters = route.getFilters().stream()
98+
.filter(filter -> filter.handlePreFlightRequest()).toList();
99+
100+
List<GatewayFilter> combined = new ArrayList<>(this.preflightRequestFilters);
101+
combined.addAll(preflightRequestFilters);
102+
103+
AnnotationAwareOrderComparator.sort(combined);
104+
105+
if (logger.isDebugEnabled()) {
106+
logger.debug("Sorted preflight request filters: " + combined);
107+
}
108+
109+
return new DefaultGatewayFilterChain(combined).filter(exchange);
110+
}
111+
91112
private static class DefaultGatewayFilterChain implements GatewayFilterChain {
92113

93114
private final int index;

0 commit comments

Comments
 (0)