Bug 58657 - Spring DeferredResult errorResult response not returned in async servlet 3.1 mode
Summary: Spring DeferredResult errorResult response not returned in async servlet 3.1 ...
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 8
Classification: Unclassified
Component: Util (show other bugs)
Version: 8.0.28
Hardware: Macintosh Mac OS X 10.1
: P2 normal (vote)
Target Milestone: ----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-11-26 15:18 UTC by Jonathan
Modified: 2015-12-02 10:17 UTC (History)
0 users



Attachments
Main class of demo (3.31 KB, text/plain)
2015-11-26 15:22 UTC, Jonathan
Details
Application config file for demo (1.05 KB, text/plain)
2015-11-26 15:23 UTC, Jonathan
Details
Maven project of demo (97.72 KB, application/zip)
2015-11-30 11:55 UTC, Jonathan
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan 2015-11-26 15:18:24 UTC

    
Comment 1 Jonathan 2015-11-26 15:22:49 UTC
Created attachment 33301 [details]
Main class of demo

Spring boot java class showing demonstration of error
Comment 2 Jonathan 2015-11-26 15:23:26 UTC
Created attachment 33302 [details]
Application config file for demo

App config file for Spring boot demo
Comment 3 Jonathan 2015-11-26 15:39:33 UTC
I have attached two classes as part of a spring boot demo to show an issue we are running into. (Would have attached whole war but for file size limit, please contact if you would like it.)

With a simple Spring MVC controller using Springs DeferredResult object and Servlet 3.1 asynchronous requests, there is an issue whereby if an error occurs in the onDataAvailable() method and the onError() method is called, the error response is never returned to the user.

With Jetty the exact same code returns the error response message and a HTTP status code.

I have tried to look through both the Jetty code and Tomcat code to see where the difference may lie and unfortunately it is unclear to me. The actual dispatching of the async request seems to happen similarly in both containers in that the Async state is set to DISPATCHING etc. but when the responses are returned to the WebAsyncManager that Spring is using to setConcurrentResultAndDispatch() that the problem appears to occur. I tried to follow the code path but once the tomcat demo goes into NioEndPoint.class, its here that the response is completely severed.

This issue does not occur if returning normal responses from a Spring Controller, only when using DeferredResults.
Comment 4 Mark Thomas 2015-11-27 23:24:42 UTC
Your test case, while welcome, assumes the Tomcat developers are familiar with Spring Boot.

A zip or tar.gz of the source tree, a pointer to a github project or similar to build the WAR would be better. Assuming, of course, the build uses a standard build tool like Ant, Maven, Gradle etc.

While the offer of the WAR is appreciated, cynical souls that we are, we aren't too keen on running some random binary we know little about.
Comment 5 Jonathan 2015-11-30 11:54:24 UTC
Hi Mark,

Sorry about that. I've attached a zip there of the project. Its a maven project and can be assembled with the usual mvn clean install to get the WAR
Comment 6 Jonathan 2015-11-30 11:55:08 UTC
Created attachment 33312 [details]
Maven project of demo
Comment 7 Mark Thomas 2015-11-30 17:12:42 UTC
Daft question. What URL do I need to request to see the issue?
Comment 8 Jonathan 2015-11-30 17:18:17 UTC
(In reply to Mark Thomas from comment #7)
> Daft question. What URL do I need to request to see the issue?

Not daft at all, terrible naming convention on my part...

http://localhost:8080/demo-0.0.1-SNAPSHOT/demo/hello/test
Comment 9 Mark Thomas 2015-11-30 22:48:22 UTC
OK. I have this working now. Some additional details for other people who want to try this test case:
- URL expects POST requests. I used JMeter to send one.
- The POST body needs to be big enough that it isn't all read at the same time. I added an ~8MB text file to the POST

Tomcat is handling the error and immediately closing the connection. This is by design. I need to dig further to understand what use case led to this approach and if we can take a more relaxed approach and close the connection cleanly rather than immediately.
Comment 10 Mark Thomas 2015-12-01 15:16:45 UTC
A little more information for anyone researching this in the future.

Once Tomcat encounters an error, it normally closes the connection. How quickly it does this depends on the severity of the error. If the error appears to be mild enough to allow a response to be written, Tomcat will:
- swallow any remaining input
- write the error response
- close the connection

There is a limit to how much input Tomcat will swallow (connector attribute maxSwallowSize). If more data than this needs to be read, Tomcat will close the connection immediately without writing an error response. This is because many clients don't read the response until the request has been fully written.
Comment 11 Mark Thomas 2015-12-01 20:47:50 UTC
Fixed in 9.0.x for 9.0.0.M2 and in 8.0.x for 8.0.30.
Comment 12 Jonathan 2015-12-02 10:17:33 UTC
Thank you Mark.