emarichal
emarichal

Reputation: 71

IllegalStateException: async support must be enabled on a servlet and for all filters

I'm attempting to use the return type Callable in a controller in order to release the servlet thread while I process the request, but i'm getting the following error when the spring boot application is deployed on development environment:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding \"true\" to servlet and filter declarations in web.xml.

NOTE: I already read several posts about this error but none of them solved my problem.

The particular behavior i'm having is that locally everything works as expected: the servlet thread is released and the request is processed out of band to finally return to the client the desired response.

But when the application is deployed in development environment as mentioned before, things doesn't work as expected.

When testing locally, I checked that the servlet/filters were async supported; to do when debugging i just put a breakpoint in a given filter of ApplicationFilterChain and then inspect the whole chain. There I can inspect the servlet properties (where I see the asyncSupported on true) along with each filter included in the chain (one by one i checked them; all of them had the asyncSupported setted on true).

I also have a custom JwtAuthenticationFilter which extends from AbstractAuthenticationProcessingFilter as part of the authentication phase so I also put a breakpoint in such filter and inspect the chain. There I see the originalChain (the ApplicationFilterChain commented before) and "additionalFilters" collection in which appears my JwtAuthenticationFilter along with the spring security filters. But none of them have asyncSupported property so i'm assuming they aren't part of the error thrown by the development server. Note that as I mentioned locally everything works fine; the error just appear when deploying to development server.

Controller method:

  @GetMapping(path = "/export")
  public Callable<ResponseEntity> exportData() throws IOException {
    return new CustomContextAwareCallable(() -> handleResponse());
  }

So my question is: even if each filter on ApplicationFilterChain along with the servlet have asyncSupported on true, why could I receive the error shown above from the server after deploying?

The application isn't deployed as a .WAR on the server, it's just using embedded tomcat (the same I doing locally).

UPDATE: another difference is that in development environment client requests pass through nginx proxy (just wondering if for some reason the request attribute org.apache.catalina.ASYNC_SUPPORTED could be modified there to be false (locally it's coming on true).

Any ideas?

Upvotes: 1

Views: 4308

Answers (2)

flyingAssistant
flyingAssistant

Reputation: 920

For those having a Filter implementation. Instead of setting request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true) to each request in your Controller, you can do the same in doFilter() callback. This is how I did it in Kotlin:

import org.apache.catalina.Globals
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.annotation.WebFilter
import javax.servlet.http.HttpServletResponse

@Suppress("unused")
@WebFilter(urlPatterns = ["/v1/*"])
class RequestResponseFilter : Filter {

    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)
        val httpServletResponse = response as HttpServletResponse
        httpServletResponse.setHeader("Cache-Control", "max-age=60")
        chain.doFilter(request, response)
    }

}

Upvotes: 2

emarichal
emarichal

Reputation: 71

I found a workaround by adding the async support attribute to the current request like:

  @GetMapping(path = "/export")
  public Callable<ResponseEntity> exportData(HttpServletRequest request) throws IOException {
    request.setAttribute(org.apache.catalina.Globals.ASYNC_SUPPORTED_ATTR, true);
    return new CustomContextAwareCallable(() -> handleResponse());
  }

Since all filters are being async supported along with dispatchServlet i'm using (as commented at the original question) seems that when deploying in the development environment some of the following tomcat valves is setting such attribute on false for the request:

  • StandardEngineValve
  • StandardHostValve
  • StandardContextValve

I verified the embedded tomcat version i'm using locally and it's the same as the development environment: Apache Tomcat/8.5.23

Seems also that the error I showed before:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding \"true\" to servlet and filter declarations in web.xml.

isn't exclusively tied to servlet & filters as it describes, because any of listed tomcat valves could eventually disallow the request to be async supported.

At the moment I don't have enough details to know why that's happening (it would be great to have the chance of remote-debugging this against the development environment to see how each tomcat valve is handling the request).

Upvotes: 6

Related Questions