Skip to content

Commit 0fc8bf6

Browse files
committed
Expose DispatcherHandler as PreFlightRequestHandler
Closes gh-26257
1 parent 729535d commit 0fc8bf6

File tree

2 files changed

+60
-10
lines changed

2 files changed

+60
-10
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2021 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+
package org.springframework.web.cors.reactive;
17+
18+
import reactor.core.publisher.Mono;
19+
20+
import org.springframework.web.server.ServerWebExchange;
21+
22+
/**
23+
* Handler for CORS pre-flight requests.
24+
*
25+
* @author Rossen Stoyanchev
26+
* @since 5.3.4
27+
*/
28+
public interface PreFlightRequestHandler {
29+
30+
/**
31+
* Handle a pre-flight request by finding and applying the CORS configuration
32+
* that matches the expected actual request. As a result of handling, the
33+
* response should be updated with CORS headers or rejected with
34+
* {@link org.springframework.http.HttpStatus#FORBIDDEN}.
35+
* @param exchange the exchange for the request
36+
* @return a completion handle
37+
*/
38+
Mono<Void> handlePreFlight(ServerWebExchange exchange);
39+
40+
}

spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
import org.springframework.context.ApplicationContextAware;
3030
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3131
import org.springframework.http.HttpStatus;
32-
import org.springframework.http.server.reactive.ServerHttpRequest;
3332
import org.springframework.lang.Nullable;
3433
import org.springframework.util.ObjectUtils;
3534
import org.springframework.web.cors.reactive.CorsUtils;
35+
import org.springframework.web.cors.reactive.PreFlightRequestHandler;
3636
import org.springframework.web.server.ResponseStatusException;
3737
import org.springframework.web.server.ServerWebExchange;
3838
import org.springframework.web.server.WebHandler;
@@ -53,9 +53,10 @@
5353
*
5454
* <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
5555
* implements {@link ApplicationContextAware} for access to the context it runs
56-
* in. If {@code DispatcherHandler} is declared with the bean name "webHandler"
57-
* it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which
58-
* creates a processing chain together with {@code WebFilter},
56+
* in. If {@code DispatcherHandler} is declared as a bean with the name
57+
* "webHandler", it is discovered by
58+
* {@link WebHttpHandlerBuilder#applicationContext(ApplicationContext)} which
59+
* puts together a processing chain together with {@code WebFilter},
5960
* {@code WebExceptionHandler} and others.
6061
*
6162
* <p>A {@code DispatcherHandler} bean declaration is included in
@@ -68,7 +69,7 @@
6869
* @since 5.0
6970
* @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
7071
*/
71-
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
72+
public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {
7273

7374
@Nullable
7475
private List<HandlerMapping> handlerMappings;
@@ -142,6 +143,9 @@ public Mono<Void> handle(ServerWebExchange exchange) {
142143
if (this.handlerMappings == null) {
143144
return createNotFoundError();
144145
}
146+
if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
147+
return handlePreFlight(exchange);
148+
}
145149
return Flux.fromIterable(this.handlerMappings)
146150
.concatMap(mapping -> mapping.getHandler(exchange))
147151
.next()
@@ -158,11 +162,8 @@ private <R> Mono<R> createNotFoundError() {
158162
}
159163

160164
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
161-
// No handling for CORS rejected requests and pre-flight requests
162-
ServerHttpRequest request = exchange.getRequest();
163-
HttpStatus status = exchange.getResponse().getStatusCode();
164-
if (ObjectUtils.nullSafeEquals(status, HttpStatus.FORBIDDEN) || CorsUtils.isPreFlightRequest(request)) {
165-
return Mono.empty();
165+
if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) {
166+
return Mono.empty(); // CORS rejection
166167
}
167168
if (this.handlerAdapters != null) {
168169
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
@@ -196,4 +197,13 @@ private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
196197
throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
197198
}
198199

200+
@Override
201+
public Mono<Void> handlePreFlight(ServerWebExchange exchange) {
202+
return Flux.fromIterable(this.handlerMappings != null ? this.handlerMappings : Collections.emptyList())
203+
.concatMap(mapping -> mapping.getHandler(exchange))
204+
.switchIfEmpty(Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN)))
205+
.next()
206+
.then();
207+
}
208+
199209
}

0 commit comments

Comments
 (0)