Bug 60076 - ClientAbortException / IOException occurs only when using HttpServletResponseWrapper
Summary: ClientAbortException / IOException occurs only when using HttpServletResponse...
Status: CLOSED INVALID
Alias: None
Product: Tomcat 6
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 6.0.45
Hardware: PC All
: P2 normal (vote)
Target Milestone: default
Assignee: Tomcat Developers Mailing List
URL: http://mail-archives.apache.org/mod_m...
Keywords:
Depends on:
Blocks:
 
Reported: 2016-09-01 08:58 UTC by Olivier Jaquemet
Modified: 2016-09-09 12:30 UTC (History)
0 users



Attachments
Test webapp compile with JDK 1.6 for Tomcat 6 (204.43 KB, application/zip)
2016-09-01 08:58 UTC, Olivier Jaquemet
Details
Test webapp compilee with JDK 1.8 for Tomcat 7+ (204.72 KB, application/zip)
2016-09-01 09:00 UTC, Olivier Jaquemet
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Olivier Jaquemet 2016-09-01 08:58:13 UTC
Created attachment 34189 [details]
Test webapp compile with JDK 1.6 for Tomcat 6

The following exception occurs when users try to  access AVI video playback on IE11.
My analysis is that it's a bad behavior of Tomcat when using  HttpServletResponseWrapper.

ClientAbortException:  java.io.IOException
        at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:370)
        at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:368)
        at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:393)
        at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:382)
        at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89)
        at org.apache.catalina.servlets.DefaultServlet.copy(DefaultServlet.java:1994)
        at org.apache.catalina.servlets.DefaultServlet.serveResource(DefaultServlet.java:954)
        at org.apache.catalina.servlets.DefaultServlet.doGet(DefaultServlet.java:420)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at com.example.BasicFilter.doFilter(BasicFilter.java:35)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
        at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:879)
        at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:610)
        at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1777)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.io.IOException
        at org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer(InternalAprOutputBuffer.java:717)
        at org.apache.coyote.http11.InternalAprOutputBuffer$SocketOutputBuffer.doWrite(InternalAprOutputBuffer.java:747)
        at org.apache.coyote.http11.filters.IdentityOutputFilter.doWrite(IdentityOutputFilter.java:118)
        at org.apache.coyote.http11.InternalAprOutputBuffer.doWrite(InternalAprOutputBuffer.java:557)
        at org.apache.coyote.Response.doWrite(Response.java:533)
        at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:365)
        ... 24 more

I have setup a very simple reproduction webapp (2 nearly empty class, one html, one video) attach to this issue.
(test-t6.war for Tomcat 6, test.war for Tomcat 7+)

This webapp is configured with a simple servlet filter, doing nothing  special but wrapping the response using a simple HttpServletResponseWrapper (not doing anything at all) and logging request information on exception.
To help demonstrate the cause of the problem, the filter expose an init parameter "wrap-response" in web.xml which quickly allows the webapp to be tested with 2 behavior : response wrapped (to exhibit the bug) or not (to behave properly).
The home page contains a video player with a very small AVI file.

The reproduction steps :
  * Start Tomcat (any version) with the webapp above
  * Access the index page using IE 11 on Windows 7 (very important : clear browser cache between each test)
  --> The exception above occurs.
  * Edit web.xml, set "wrap-response" init parameter to false
  * Repeat the test with IE (don't forget to clear cache!)
  --> No exception.

The problem may be workaround by modifying Tomcat's web.xml by disabling Accept-Ranges support.

    <init-param>
        <param-name>useAcceptRanges</param-name>
        <param-value>false</param-value>
    </init-param>

Because the ClientAbortException/IOException does not occurs if the response is not wrapped, and does not occurs either if acceptRange is disabled, it really make me think that is a bug in Tomcat (and not a behavior of the client contrary to what ClientAbortException implies).

Bug verified to exist in all following version of tomcat (with a default install) 
(using JDK 1.6.0_45-b06 on Win7 SP1):
  Apache Tomcat/7.0.45
(using JDK 1.8.0_102-b14 on Win7 SP1):
  Apache Tomcat/7.0.70
  Apache Tomcat/8.0.36
  Apache Tomcat/8.5.4
  Apache Tomcat/9.0.0.M9


Bug initially discussed on tomcat users ML
http://mail-archives.apache.org/mod_mbox/tomcat-users/201608.mbox/%3C6be06ed8-64da-4f3d-4204-0e89b40617de%40jalios.com%3E
Comment 1 Olivier Jaquemet 2016-09-01 09:00:31 UTC
Created attachment 34190 [details]
Test webapp compilee with JDK 1.8 for Tomcat 7+
Comment 2 Mark Thomas 2016-09-01 21:04:22 UTC
I've looked at a WireShark trace with and without the wrapper. There is no Tomcat bug here. The client drops the connection for no obvious reason on multiple occasions without and without the wrapper. It just so happens that with the wrapper the timing is such that the Exception gets caught by the Filter.

I'll add that the client makes multiple requests for the same parts of the avi file. Ignoring the requests it makes and then aborts, it still ends up downloading over 500K of data.
Comment 3 Olivier Jaquemet 2016-09-02 07:03:16 UTC
Thank you Mark for this analysis.

I am completely ok to accept that IE behaves like sh%t, this would not be the first time.

However, how do you explain the fact that without using the HttpServletResponseWrapper the exception does not occur ? 
It really seems to me that there is a change in Tomcat behavior somewhere, otherwise exception would occur everytime, don't you think ?
Comment 4 Mark Thomas 2016-09-02 16:57:57 UTC
(In reply to Olivier Jaquemet from comment #3)
> It really seems to me that there is a change in Tomcat behavior somewhere,
> otherwise exception would occur everytime, don't you think ?

There is a change. I dug into it a little more. With the wrapper, the Default Servlet writes the content and you see the exception when the client aborts the connection. Without the wrapper, Tomcat uses sendFile so the write is handled by a separate thread so the app never sees the exception.

This is all entirely as expected.
Comment 5 Olivier Jaquemet 2016-09-09 12:30:15 UTC
OKay, everything is now much clearer to me.

Thank you Mark for the taking the time to analyze this issue.