Skip to content

Commit 7dbc9e7

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

14 files changed

+300
-12
lines changed

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;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2013-2020 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+
* @author Tommas Yuan
21+
*/
22+
public interface PreFlightRequestFilter {
23+
24+
/**
25+
* @return {@code true} to indicate this filter will handle pre-flight request
26+
*/
27+
boolean handlePreFlightRequest();
28+
29+
}

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;

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
import org.springframework.cloud.gateway.route.Route;
2626
import org.springframework.cloud.gateway.route.RouteLocator;
2727
import org.springframework.core.env.Environment;
28+
import org.springframework.http.HttpHeaders;
29+
import org.springframework.http.server.reactive.ServerHttpResponse;
2830
import org.springframework.web.cors.CorsConfiguration;
31+
import org.springframework.web.cors.reactive.CorsUtils;
2932
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
3033
import org.springframework.web.server.ServerWebExchange;
3134

@@ -73,6 +76,30 @@ private static Integer getPortProperty(Environment environment, String prefix) {
7376
return environment.getProperty(prefix + "port", Integer.class);
7477
}
7578

79+
@Override
80+
public Mono<Object> getHandler(ServerWebExchange exchange) {
81+
Mono<Object> mono = super.getHandler(exchange);
82+
83+
if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
84+
return mono.flatMap(handler -> {
85+
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
86+
// pass pre-flight request to upstream, if the request is permitted
87+
ServerHttpResponse response = exchange.getResponse();
88+
if (route != null && route.handlePreflightRequest() && response.getStatusCode().is2xxSuccessful()) {
89+
// remove cors response headers generated by gateway
90+
HttpHeaders headers = response.getHeaders();
91+
headers.remove(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN);
92+
headers.remove(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS);
93+
headers.remove(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS);
94+
return webHandler.handlePreFlight(exchange).then(Mono.just(handler));
95+
}
96+
return Mono.just(handler);
97+
});
98+
}
99+
100+
return mono;
101+
}
102+
76103
@Override
77104
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
78105
// don't handle requests on management port if set and different than server port

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/Route.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,17 @@ public class Route implements Ordered {
5555

5656
private final Map<String, Object> metadata;
5757

58+
private boolean handlePreflightRequest;
59+
5860
private Route(String id, URI uri, int order, AsyncPredicate<ServerWebExchange> predicate,
59-
List<GatewayFilter> gatewayFilters, Map<String, Object> metadata) {
61+
List<GatewayFilter> gatewayFilters, Map<String, Object> metadata, boolean preflightRequestHandler) {
6062
this.id = id;
6163
this.uri = uri;
6264
this.order = order;
6365
this.predicate = predicate;
6466
this.gatewayFilters = gatewayFilters;
6567
this.metadata = metadata;
68+
this.handlePreflightRequest = preflightRequestHandler;
6669
}
6770

6871
public static Builder builder() {
@@ -74,7 +77,8 @@ public static Builder builder(RouteDefinition routeDefinition) {
7477
return new Builder().id(routeDefinition.getId())
7578
.uri(routeDefinition.getUri())
7679
.order(routeDefinition.getOrder())
77-
.metadata(routeDefinition.getMetadata());
80+
.metadata(routeDefinition.getMetadata())
81+
.handlePreflightRequest(routeDefinition.isHandlePreflightRequest());
7882
// @formatter:on
7983
}
8084

@@ -87,7 +91,8 @@ public static AsyncBuilder async(RouteDefinition routeDefinition) {
8791
return new AsyncBuilder().id(routeDefinition.getId())
8892
.uri(routeDefinition.getUri())
8993
.order(routeDefinition.getOrder())
90-
.metadata(routeDefinition.getMetadata());
94+
.metadata(routeDefinition.getMetadata())
95+
.handlePreflightRequest(routeDefinition.isHandlePreflightRequest());
9196
// @formatter:on
9297
}
9398

@@ -115,6 +120,10 @@ public Map<String, Object> getMetadata() {
115120
return Collections.unmodifiableMap(metadata);
116121
}
117122

123+
public boolean handlePreflightRequest() {
124+
return handlePreflightRequest;
125+
}
126+
118127
@Override
119128
public boolean equals(Object o) {
120129
if (this == o) {
@@ -127,12 +136,14 @@ public boolean equals(Object o) {
127136
return this.order == route.order && Objects.equals(this.id, route.id) && Objects.equals(this.uri, route.uri)
128137
&& Objects.equals(this.predicate, route.predicate)
129138
&& Objects.equals(this.gatewayFilters, route.gatewayFilters)
130-
&& Objects.equals(this.metadata, route.metadata);
139+
&& Objects.equals(this.metadata, route.metadata)
140+
&& this.handlePreflightRequest == route.handlePreflightRequest;
131141
}
132142

133143
@Override
134144
public int hashCode() {
135-
return Objects.hash(this.id, this.uri, this.order, this.predicate, this.gatewayFilters, this.metadata);
145+
return Objects.hash(this.id, this.uri, this.order, this.predicate, this.gatewayFilters, this.metadata,
146+
this.handlePreflightRequest);
136147
}
137148

138149
@Override
@@ -144,6 +155,7 @@ public String toString() {
144155
sb.append(", predicate=").append(predicate);
145156
sb.append(", gatewayFilters=").append(gatewayFilters);
146157
sb.append(", metadata=").append(metadata);
158+
sb.append(", handlePreflightRequest=").append(handlePreflightRequest);
147159
sb.append('}');
148160
return sb.toString();
149161
}
@@ -160,6 +172,8 @@ public abstract static class AbstractBuilder<B extends AbstractBuilder<B>> imple
160172

161173
protected Map<String, Object> metadata = new HashMap<>();
162174

175+
protected boolean handlePreflightRequest;
176+
163177
protected AbstractBuilder() {
164178
}
165179

@@ -210,6 +224,11 @@ public B metadata(String key, Object value) {
210224
return getThis();
211225
}
212226

227+
public B handlePreflightRequest(boolean handlePreflightRequest) {
228+
this.handlePreflightRequest = handlePreflightRequest;
229+
return getThis();
230+
}
231+
213232
public abstract AsyncPredicate<ServerWebExchange> getPredicate();
214233

215234
public B replaceFilters(List<GatewayFilter> gatewayFilters) {
@@ -237,7 +256,8 @@ public Route build() {
237256
AsyncPredicate<ServerWebExchange> predicate = getPredicate();
238257
Assert.notNull(predicate, "predicate can not be null");
239258

240-
return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters, this.metadata);
259+
return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters, this.metadata,
260+
this.handlePreflightRequest);
241261
}
242262

243263
}

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public class RouteDefinition {
5656

5757
private int order = 0;
5858

59+
private boolean handlePreflightRequest;
60+
5961
public RouteDefinition() {
6062
}
6163

@@ -125,6 +127,14 @@ public void setMetadata(Map<String, Object> metadata) {
125127
this.metadata = metadata;
126128
}
127129

130+
public boolean isHandlePreflightRequest() {
131+
return handlePreflightRequest;
132+
}
133+
134+
public void setHandlePreflightRequest(boolean handlePreflightRequest) {
135+
this.handlePreflightRequest = handlePreflightRequest;
136+
}
137+
128138
@Override
129139
public boolean equals(Object o) {
130140
if (this == o) {
@@ -136,18 +146,21 @@ public boolean equals(Object o) {
136146
RouteDefinition that = (RouteDefinition) o;
137147
return this.order == that.order && Objects.equals(this.id, that.id)
138148
&& Objects.equals(this.predicates, that.predicates) && Objects.equals(this.filters, that.filters)
139-
&& Objects.equals(this.uri, that.uri) && Objects.equals(this.metadata, that.metadata);
149+
&& Objects.equals(this.uri, that.uri) && Objects.equals(this.metadata, that.metadata)
150+
&& this.handlePreflightRequest == that.handlePreflightRequest;
140151
}
141152

142153
@Override
143154
public int hashCode() {
144-
return Objects.hash(this.id, this.predicates, this.filters, this.uri, this.metadata, this.order);
155+
return Objects.hash(this.id, this.predicates, this.filters, this.uri, this.metadata, this.order,
156+
this.handlePreflightRequest);
145157
}
146158

147159
@Override
148160
public String toString() {
149161
return "RouteDefinition{" + "id='" + id + '\'' + ", predicates=" + predicates + ", filters=" + filters
150-
+ ", uri=" + uri + ", order=" + order + ", metadata=" + metadata + '}';
162+
+ ", uri=" + uri + ", order=" + order + ", metadata=" + metadata + "handlePreFlightRequest="
163+
+ handlePreflightRequest + '}';
151164
}
152165

153166
}

0 commit comments

Comments
 (0)