Bug 65001 - HTTPNIO non-blocking IO servlet 3.1 API may fail to alert webapp that a request is complete
Summary: HTTPNIO non-blocking IO servlet 3.1 API may fail to alert webapp that a reque...
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 9.0.41
Hardware: All All
: P2 normal (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
Depends on:
Reported: 2020-12-17 15:38 UTC by frrajott
Modified: 2021-01-07 16:43 UTC (History)
0 users

Test servlet (6.02 KB, application/zip)
2020-12-17 15:38 UTC, frrajott

Note You need to log in before you can comment on or make changes to this bug.
Description frrajott 2020-12-17 15:38:53 UTC
Created attachment 37624 [details]
Test servlet

Under certain IO error conditions, tomcat will fail to alert that an asynchronous request is completed and destroyed.
Both the AsyncListener's onComplete method and ServletRequestListener's requestDestroyed method are not called.
This may cause a webapp to believe that a request is still valid while tomcat has already recycled the objects.

I was able to reproduce this issue using a WriteListener (non-blocking IO servlet API 3.1) with the httpnio connector on the latest tomcat release (9.0.41).
This appears to be caused when an IOException is raised while attempting to write to the ServletOutputChannel during the WriteListener's onWritePossible callback.

It also seems possible to trigger this issue by manually throwing an exception in the onWritePossible callback.

The following exception can also be seen in the logs:
java.lang.IllegalStateException: Calling [asyncPostProcess()] is not valid for a request with Async state [ERROR]

I've attached a test servlet that demonstrates this issue.
To simulate IO errors, an http request is aborted before the full response body is read. The servlet then records various async callbacks it receives and it can be seen that some callbacks are not always called, i.e. the AsyncListener's onComplete callback and ServletRequestListener's requestDestroyed callback.
The servlet compares synchronous requests, asynchronous requests, and asynchronous requests with non-blocking IO.
Comment 1 Mark Thomas 2021-01-07 14:19:26 UTC
Thanks for the really good test case. I've found a (hopefully the) bug. I'm currently working on a fix.
Comment 2 Mark Thomas 2021-01-07 16:43:27 UTC
Thanks again for the test case. A good test case really does make a huge difference to how easily we can fix a bug.

Fixed in:
- 10.0.x for 10.0.1 onwards
- 9.0.x for 9.0.42 onwards
- 8.5.x for 8.5.62 onwards