Merge branch '6.1.x'
This commit is contained in:
commit
1ffffef85e
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -95,6 +95,7 @@ public class HandlerFunctionAdapter implements HandlerAdapter, Ordered {
|
||||||
Object handler) throws Exception {
|
Object handler) throws Exception {
|
||||||
|
|
||||||
WebAsyncManager asyncManager = getWebAsyncManager(servletRequest, servletResponse);
|
WebAsyncManager asyncManager = getWebAsyncManager(servletRequest, servletResponse);
|
||||||
|
servletResponse = getWrappedResponse(asyncManager);
|
||||||
|
|
||||||
ServerRequest serverRequest = getServerRequest(servletRequest);
|
ServerRequest serverRequest = getServerRequest(servletRequest);
|
||||||
ServerResponse serverResponse;
|
ServerResponse serverResponse;
|
||||||
|
@ -124,6 +125,22 @@ public class HandlerFunctionAdapter implements HandlerAdapter, Ordered {
|
||||||
return asyncManager;
|
return asyncManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain response wrapped by
|
||||||
|
* {@link org.springframework.web.context.request.async.StandardServletAsyncWebRequest}
|
||||||
|
* to enforce lifecycle rules from Servlet spec (section 2.3.3.4)
|
||||||
|
* in case of async handling.
|
||||||
|
*/
|
||||||
|
private static HttpServletResponse getWrappedResponse(WebAsyncManager asyncManager) {
|
||||||
|
AsyncWebRequest asyncRequest = asyncManager.getAsyncWebRequest();
|
||||||
|
Assert.notNull(asyncRequest, "No AsyncWebRequest");
|
||||||
|
|
||||||
|
HttpServletResponse servletResponse = asyncRequest.getNativeResponse(HttpServletResponse.class);
|
||||||
|
Assert.notNull(servletResponse, "No HttpServletResponse");
|
||||||
|
|
||||||
|
return servletResponse;
|
||||||
|
}
|
||||||
|
|
||||||
private ServerRequest getServerRequest(HttpServletRequest servletRequest) {
|
private ServerRequest getServerRequest(HttpServletRequest servletRequest) {
|
||||||
ServerRequest serverRequest =
|
ServerRequest serverRequest =
|
||||||
(ServerRequest) servletRequest.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
|
(ServerRequest) servletRequest.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2024 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.web.servlet.function.support;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.servlet.AsyncEvent;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
|
import org.springframework.web.context.request.async.AsyncRequestNotUsableException;
|
||||||
|
import org.springframework.web.context.request.async.StandardServletAsyncWebRequest;
|
||||||
|
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||||
|
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||||
|
import org.springframework.web.servlet.function.HandlerFunction;
|
||||||
|
import org.springframework.web.servlet.function.RouterFunctions;
|
||||||
|
import org.springframework.web.servlet.function.ServerRequest;
|
||||||
|
import org.springframework.web.servlet.function.ServerResponse;
|
||||||
|
import org.springframework.web.testfixture.servlet.MockAsyncContext;
|
||||||
|
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||||
|
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.BDDMockito.doThrow;
|
||||||
|
import static org.mockito.BDDMockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link HandlerFunctionAdapter}.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class HandlerFunctionAdapterTests {
|
||||||
|
|
||||||
|
private final MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
|
||||||
|
|
||||||
|
private final MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
private final HandlerFunctionAdapter adapter = new HandlerFunctionAdapter();
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
this.servletRequest.setAttribute(RouterFunctions.REQUEST_ATTRIBUTE,
|
||||||
|
ServerRequest.create(this.servletRequest, List.of(new StringHttpMessageConverter())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asyncRequestNotUsable() throws Exception {
|
||||||
|
|
||||||
|
HandlerFunction<?> handler = request -> ServerResponse.sse(sseBuilder -> {
|
||||||
|
try {
|
||||||
|
sseBuilder.data("data 1");
|
||||||
|
sseBuilder.data("data 2");
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.servletRequest.setAsyncSupported(true);
|
||||||
|
|
||||||
|
HttpServletResponse mockServletResponse = mock(HttpServletResponse.class);
|
||||||
|
doThrow(new IOException("Broken pipe")).when(mockServletResponse).getOutputStream();
|
||||||
|
|
||||||
|
// Use of response should be rejected
|
||||||
|
assertThatThrownBy(() -> adapter.handle(servletRequest, mockServletResponse, handler))
|
||||||
|
.hasRootCauseInstanceOf(IOException.class)
|
||||||
|
.hasRootCauseMessage("Broken pipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asyncRequestNotUsableOnAsyncDispatch() throws Exception {
|
||||||
|
|
||||||
|
HandlerFunction<?> handler = request -> ServerResponse.ok().body("body");
|
||||||
|
|
||||||
|
// Put AsyncWebRequest in ERROR state
|
||||||
|
StandardServletAsyncWebRequest asyncRequest = new StandardServletAsyncWebRequest(servletRequest, servletResponse);
|
||||||
|
asyncRequest.onError(new AsyncEvent(new MockAsyncContext(servletRequest, servletResponse), new Exception()));
|
||||||
|
|
||||||
|
// Set it as the current AsyncWebRequest, from the initial REQUEST dispatch
|
||||||
|
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(servletRequest);
|
||||||
|
asyncManager.setAsyncWebRequest(asyncRequest);
|
||||||
|
|
||||||
|
// Use of response should be rejected
|
||||||
|
assertThatThrownBy(() -> adapter.handle(servletRequest, servletResponse, handler))
|
||||||
|
.isInstanceOf(AsyncRequestNotUsableException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue