Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MockAsyncContext not found. Did request wrapper not delegate startAsync? [SPR-17353] #21887

Closed
spring-projects-issues opened this issue Oct 8, 2018 · 4 comments
Assignees
Labels
in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Oct 8, 2018

Réda Housni Alaoui opened SPR-17353 and commented

I am using Spring Security 3.2.4.
I am migrating from Spring framework 5.0.1 to 5.1.0.

My old test:

Map<String, String> authRequest = new HashMap<>();
authRequest.put("login", "tom");
authRequest.put("password", "titi");

MockHttpServletResponse response =
    mockMvc()
        .perform(
            post("/api/authentication/")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsBytes(authRequest)))
        .andExpect(status().isOk())
        .andReturn()
        .getResponse();

Cookie authCookie = response.getCookie(AUTH_TOKEN_KEY);
assertThat(authCookie).isNotNull();
assertThat(authCookie.getValue()).isNotNull();

response =
    mockMvc()
        .perform(get("/fake/hello-world").cookie(authCookie))
        .andExpect(status().isOk())
        .andReturn()
        .getResponse();

assertThat(
        Stream.of(response.getCookies())
            .filter(cookie -> AUTH_TOKEN_KEY.equals(cookie.getName())))
    .hasSize(1); 

Fails with:

java.lang.IllegalArgumentException: MockAsyncContext not found. Did request wrapper not delegate startAsync?java.lang.IllegalArgumentException: MockAsyncContext not found. Did request wrapper not delegate startAsync?
 at org.springframework.util.Assert.notNull(Assert.java:198) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:77) at javax.servlet.http.HttpServlet.service(HttpServlet.java:770) at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133) at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182) at com.cos.framework.http.security.security.AuthTokenSecurityContextHandlerTest.testCookie(AuthTokenSecurityContextHandlerTest.java:97) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) 

Maybe TestDispatcherServlet is more restrictive?

Anyway my application is not meant to support async.
But MockHttpServletRequestBuilder creates async supported requests every time:

 /**
 * Build a {@link MockHttpServletRequest}.
 */
@Override
public final MockHttpServletRequest buildRequest(ServletContext servletContext) {
   MockHttpServletRequest request = createServletRequest(servletContext);

   request.setAsyncSupported(true);
   request.setMethod(this.method);

   String requestUri = this.url.getRawPath();
   request.setRequestURI(requestUri);

Without setting an async context.


Affects: 5.1 GA

Issue Links:

Referenced from: commits 29ff8a8, fc2f3ec, f61f6f2

Backported to: 5.0.10, 4.3.20

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Oct 8, 2018

Juergen Hoeller commented

Rossen Stoyanchev, this seems to be caused by the changes behind #21236, with the outer request.getAsyncContext() returning a non-null reference... but the inner MockHttpServletRequest.getAsyncContext() returning null after unwrapping.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

request.setAsyncSupported(true) by itself is not an issue and has no impact unless a controller returns an asynchronous value such as DeferredResult, CompletableFuture, and others, or calls request.startAsync() explicitly. The fact that the outer request returns an AsyncContext means that something did cause asynchronous request processing to start but maybe a request wrapper did not delegate the startAsync call all the way to the MockHttpServletRequest, or perhaps the outer wrapper is returning an AsyncContext even though an async request wasn't really started?

Both of these options are odd to say the least. I can improve the checks to make them more defensive, but before I do that I would very much like to try and find out what's causing this? Can you put a breakpoint right here and step inside to find out which request wrapper returns the AsyncContext, and checks what its startAsync logic looks like, does it delegate it for example at any point, and maybe even break in the startAsync implementation of the wrapper to confirm if it is getting called and by who since you said the application doesn't start async requests?

One thing to point out is that version 3.2.4 of Spring Security is very old, and not supported. I understand your app was already working with 5.0 but nevertheless using an unsupported version of Spring Security is a security issue in its own right.

@spring-projects-issues
Copy link
Collaborator Author

Réda Housni Alaoui commented

Upgrading to Spring Security 5.0.8 fixed the issue.
I think it should be closed as invalid.

Thank you very much.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

I've added a more defensive check even if the underlying issue is due to a misbehaving outer request wrapper, possibly an old bug in Spring Security that was later fixed.

@spring-projects-issues spring-projects-issues added type: regression A bug that is also a regression in: test Issues in the test module status: backported An issue that has been backported to maintenance branches in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.1.1 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

2 participants