mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Add preflight handling in RouterFunctionWebHandler
Request predicates support preflight request matching based on the "would be" request (e.g. target HTTP method) so the actual handler is not meant to be invoked. That's the case only with a DispatcherHandler setup. Closes gh-37024
This commit is contained in:
committed by
Brian Clozel
parent
dadd474d21
commit
07cbd482a0
+15
-3
@@ -38,6 +38,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.cors.reactive.CorsUtils;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -1475,9 +1476,14 @@ public abstract class RouterFunctions {
|
||||
addAttributes(exchange, request);
|
||||
return this.routerFunction.route(request)
|
||||
.switchIfEmpty(createNotFoundError())
|
||||
.flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request)))
|
||||
.flatMap(response -> wrapException(() -> response.writeTo(exchange,
|
||||
new HandlerStrategiesResponseContext(this.strategies))));
|
||||
.flatMap(handlerFunction -> wrapException(() -> {
|
||||
if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
|
||||
return handlePreFlightRequest();
|
||||
}
|
||||
return handlerFunction.handle(request);
|
||||
}))
|
||||
.flatMap(response -> wrapException(() ->
|
||||
response.writeTo(exchange, new HandlerStrategiesResponseContext(this.strategies))));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1498,5 +1504,11 @@ public abstract class RouterFunctions {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends ServerResponse> Mono<T> handlePreFlightRequest() {
|
||||
return (Mono<T>) ServerResponse.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+55
@@ -28,11 +28,14 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
@@ -282,6 +285,58 @@ class RouterFunctionsTests {
|
||||
assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toHttpHandlerPreFlightRequestDefaultHandling() {
|
||||
RouterFunction<ServerResponse> routerFunction =
|
||||
RouterFunctions.route(RequestPredicates.all(), request -> ServerResponse.accepted().build());
|
||||
|
||||
HttpHandler handler = RouterFunctions.toHttpHandler(routerFunction);
|
||||
assertThat(handler).isNotNull();
|
||||
|
||||
MockServerHttpRequest httpRequest = MockServerHttpRequest.options("https://localhost")
|
||||
.header("Origin", "https://example.com")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT")
|
||||
.build();
|
||||
|
||||
MockServerHttpResponse httpResponse = new MockServerHttpResponse();
|
||||
handler.handle(httpRequest, httpResponse).block();
|
||||
|
||||
assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toHttpHandlerPreFlightRequestHandled() {
|
||||
|
||||
CorsWebFilter corsFilter = new CorsWebFilter(exchange -> {
|
||||
if (exchange.getRequest().getPath().value().equals("/path")) {
|
||||
CorsConfiguration corsConfig = new CorsConfiguration();
|
||||
corsConfig.addAllowedOrigin("https://example.com");
|
||||
corsConfig.addAllowedMethod(HttpMethod.PUT);
|
||||
return corsConfig;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
RouterFunction<ServerResponse> routerFunction = RouterFunctions.route()
|
||||
.PUT("/path", request -> {
|
||||
throw new IllegalStateException("Not expected");
|
||||
})
|
||||
.build();
|
||||
|
||||
HttpHandler handler = RouterFunctions.toHttpHandler(
|
||||
routerFunction, HandlerStrategies.builder().webFilter(corsFilter).build());
|
||||
|
||||
MockServerHttpRequest httpRequest = MockServerHttpRequest.options("https://localhost/path")
|
||||
.header("Origin", "https://example.com")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT")
|
||||
.build();
|
||||
|
||||
MockServerHttpResponse httpResponse = new MockServerHttpResponse();
|
||||
handler.handle(httpRequest, httpResponse).block();
|
||||
|
||||
assertThat(httpResponse.getStatusCode()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void toHttpHandlerWebFilter() {
|
||||
AtomicBoolean filterInvoked = new AtomicBoolean();
|
||||
|
||||
Reference in New Issue
Block a user