jetty.project
jetty.project copied to clipboard
OutputStreamContentSource.AsyncOutputStream rethrows already thrown exception
Jetty version(s) 12.0.11
Jetty Environment core
Java version/vendor (use: java -version)
OpenJDK 17.0.10
OS type/version Windows
Description
Executing a JettyClientHttpRequest throws an IllegalArgumentException with message Self-suppression not permitted if a SocketTimeoutException occurs when writing the response.
This is similar to #11736.
Reason
- In
JettyClientHttpRequest.executeInternalanOutputStreamis constructed fromrequestContent.getOutputStream()and thebodyis written to it - That stream is of type
OutputStreamContentSource.AsyncOutputStream - During writing of the body, a flush might be performed triggering
AsyncOutputStream.write - The write delegates to
AsyncOutput.writeand theSocketTimeoutExceptionmay arise (let's assume that) - That exception is caught and written to
persistentFailureand thrown (and rethrown) inAsyncOutputStream.write. JettyClientHttpRequest.executeInternal's try-with-resources-block notices the exception and tries to close the output stream- Closing the stream again causes a
flushwhich repeats thepersistentFailure - Java notices that the try-with-resources block's finally-block throws and wants to suppress the original exception (from the write) - but it's exactly the same exception again, which leads to the
IllegalArgumentException
How to reproduce? Try to send a large enough request to an unresponsive host causing an intermediate flush.
This happens in my Spring Boot application using a RestTemplate.
To temporarily avoid the error, I've created a ClientHttpRequestInterceptor which catches IllegalArgumentException if it contains the message Self-suppression not permitted and in this case rethrows its cause (wrapped in an IOException if that's not the case already) - otherwise rethrows the exception unmodified.
See also https://github.com/spring-projects/spring-framework/issues/33375 where another popular use case is reported with a repro.
This issue has been automatically marked as stale because it has been a full year without activity. It will be closed if no further activity occurs. Thank you for your contributions.
Similar issue to #11736, with a similar solution.
For the record, here is how try-with-resources is specified: https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-14.20.3.1
Fixed in #13514.