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

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?

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).

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)
}
}

Related

HttpServletRequest throws error when used within Aspect

I have a method which has an aspect. When I try to #Autowire HttpServletRequest, and use request.getHeader(something), I get this error -
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
How do I fix this? I tried using RequestContextHolder, but upon debugging I still see null. How do I use the RequestContextListener when my project has no web.xml.
Request Header can be accessed using HttpServletRequest below way.
private static HttpServletRequest getRequest() {
return((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getApiTraceId() {
return getRequest().getHeader(something);
}
Aspect annotations spins a new thread which is different from the one httpservlet is available in. This is why request was not available within the #ASpect. To resolve it, call the request object BEFORE the aspect method, cache it and call the same method as before.

Configuring Spring WebSecurityConfigurerAdapter to use exception handler

Spring Boot here. I just read this excellent Baeldung article on Spring Security and implementing basic auth with it. I'm interested in implementing it for a simple REST service (so no UI/webapp) that I need to build.
I'm particularly interested in the BasicAuthenticationEntryPoint impl. In this impl's commence override, the author:
Adds a WWW-Authenticate header to the response; and
Sets the HTTP status code on the response; and
Writes the actual response entity directly to the response; and
Sets the name of the realm
I want to follow this author's example to implement basic auth for my app, but I already have a perfectly functioning ResponseEntityExceptionHandler working for my app:
#ControllerAdvice
public class MyAppExceptionMapper extends ResponseEntityExceptionHandler {
#ExceptionHandler(IllegalArgumentException.class)
#ResponseBody
public ResponseEntity<ErrorResponse> handleIllegalArgumentExeption(IllegalArgumentException iaEx) {
return new ResponseEntity<ErrorResponse>(buildErrorResponse(iaEx,
iaEx.message,
"Please check your request and make sure it contains a valid entity/body."),
HttpStatus.BAD_REQUEST);
}
// other exceptions handled down here, etc.
// TODO: Handle Spring Security-related auth exceptions as well!
}
Is there any way to tie Spring Security and Basic Auth fails into my existing/working ResponseEntityExceptionHandler?
Ideally there's a way to tie my WebSecurityConfigurerAdapter impl into the exception handler such that failed authentication or authorization attempts throw exceptions that are then caught by my exception handler.
My motivation for doing this would be so that my exception handler is the central location for managing and configuring the HTTP response when any exception occurs, whether its auth-related or not.
Is this possible to do, if so, how? And if it is possible, would I need to still add that WWW-Authenticate to the response in my exception handler (why/why not)? Thanks in advance!
I don't think that this is possible. Spring security is applied as a ServletFilter, way before the request ever reaches any #Controller annotated class - thus exceptions thrown by Spring Security cannot be caught by an exception handler (annotated with #ControllerAdvice).
Having had a similar problem, I ended up using a custom org.springframework.security.web.AuthenticationEntryPoint which sends an error, which in turn is forwarded to a custom org.springframework.boot.autoconfigure.web.ErrorController

How to get request info on session created in Spring MVC?

I'm hoping to save some client info (IP address, etc) to a database on session created in Spring MVC.
I created a class implementing HttpSessionListener and configured it in web.xml. However, I'm not sure where to go after that.
Would like to be able to inject a bean as well (Spring Data JPA repository).
I've seen How to get the IP address when a session is created? , however if I try to access RequestContextHolder.currentRequestAttributes() I get the following exception:
SEVERE: Session event listener threw exception
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
You can create a bean this way with Spring java config:
#Bean
#Named ("IP")
#Scope ("session")
public String ip (HttpServletRequest request) {
return request.getRemoteAddr ();
}
If all you want to do is log stuff then you should use the HttpSessionListener, please provide your source and full stack trace. Use pastebin.com if necessary.

How to get source address / ip from inside ContainerResponseFilter

I'm writing a logging filter that logs all HTTP requests / responses for a web app running in Jersey. ContainerResponseFilter seems to a straight forward solution and I've managed to get it to work.
Next step is to log the IP of the requests. Is there a way to do that from inside the ContainerResponseFilter ?
Short answer:
#Provider
public class YourContextFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest sr;
#Override
public synchronized void filter(ContainerRequestContext request) throws IOException {
/*
* Returns the Internet Protocol (IP) address of the client or
* last proxy that sent the request. For HTTP servlets, same as
* the value of the CGI variable REMOTE_ADDR.
*/
String ip = sr.getRemoteAddr();
// ... log it ...
}
}
EDIT
(regarding the wish for a more detailed answer)
Afaig:
The #Context annotation allows to inject JAX-RS–specific components (one might say you are able to inject contextual information objects). JAX-RS itself is a Java based specification for RESTful Web Services over HTTP protocol. So we are able to inject stuff like:
javax.ws.rs.core.UriInfo
javax.ws.rs.core.Request
javax.ws.rs.core.SecurityContext
and also
javax.servlet.http.HttpServletRequest
In the IOC Chapter of the Jersey docs, you will find these notes:
[...] Jersey implementation allows you to directly inject HttpServletRequest instance into your JAX-RS components [...] - https://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e2401
[...] The exception exists for specific request objects which can injected even into constructor or class fields. For these objects the runtime will inject proxies which are able to simultaneously server more request. These request objects are HttpHeaders, Request, UriInfo, SecurityContext. These proxies can be injected using the #Context annotation. [...]
[...] When deploying a JAX-RS application using servlet then ServletConfig, ServletContext, HttpServletRequest and HttpServletResponse are available using #Context. [...]
And if you do so, you inject in fact a Proxy named org.apache.catalina.connector.RequestFacade (link). This proxy functioned as your direct hotline to your Coyote (HTTP Connector) and thereby to the Coyote request object (link).
Hope this was helpful somehow :) - Have a nice day.

#ExceptionHandler + #ResponseStatus

I am handling my controlled exceptions using the following code:
#ExceptionHandler(MyException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleMyException(MyException e) {
ModelAndView mav = new ModelAndView(ERROR_PAGE);
(...)
return mav;
}
That is, I want to both use custom views for different errors AND use response status code for the HTTP response.
At the same time, for pure 404 I have the following config in web.xml
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/400</location>
</error-page>
Which takes to a 404 specific view.
The problem is that when a NOT_FOUND is thrown from my #ExceptionHandled method, it is not showing my custom view, debugging shows that execution actually goes through the handleMyException method, but after it's done it also goes through the method that maps the /404 in web.xml, and that is the view that gets shown.
Also if I throw a different Response Code, I get the default behavior on Exceptions, instead of my custom view.
I can't reproduce your problem with Tomcat 6 ans Spring 2.3.4. That is correct, because accroding to Servlet specification 2.5, the deployment descriptor defines a list of error
page descriptions. The syntax allows the configuration of resources to be returned
by the container either when a servlet or filter calls sendError
on the response for specific status codes (...)
I tracked where Spring sets response code basing on #ResponseStatus(HttpStatus.NOT_FOUND)
It is here:
public class ServletInvocableHandlerMethod (...)
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
if (this.responseStatus == null) {
return;
}
if (StringUtils.hasText(this.responseReason)) {
webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
}
else {
webRequest.getResponse().setStatus(this.responseStatus.value());
}
// to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
}
In my case if error handler method is annotated
#ResponseStatus(HttpStatus.NOT_FOUND)
the following branch is selected:
else {
webRequest.getResponse().setStatus(this.responseStatus.value());
}
Because HttpServletResponse.setStatus is called and NOT HttpServletResponse.sendError, web container ignores error page defined in <error-code>404</error-code>
I hope my explanation will be useful to track the problem yourself. I suspect somewhere HttpServletResponse.sendError is called and it triggers web container to return default error page
It sounds like the problem is probably that the web container is trying to handle the 400/404's its seeing from the web application (because it doesn't understand the context of those errors). You probably need to get rid of the web.xml error page definitions and add more configuration to the Spring controllers to handle the generic 400/404 errors as well.
This guide helped me a lot when I was setting up exception handling in my app: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
The web.xml tells the app container how to handle various response codes that are generated by the application. When you get an exception out of a controller method, it gets handled by the Spring #ExceptionHandler annotated method. At this point, the app container isn't involved so it has no idea what's going on yet.
My best understanding is that when you generate a 404 Http status from the exception handler method and return, Spring's basically done at that point, and the app container steps back in and says "I got a 404, what do I do with a 404? ah, redirect to /404". And then, control goes back to the web app itself to handle the /404 request.

Resources