RFR: 8371903: HttpClient: improve handling of HTTP/2 GOAWAY frames with error code [v8]
Daniel Jeliński
djelinski at openjdk.org
Fri Dec 19 08:28:58 UTC 2025
On Thu, 18 Dec 2025 18:16:50 GMT, EunHyunsu <duke at openjdk.org> wrote:
>> Occasionally more than one request fails with the GOAWAY exception. I suppose this might be because of a race between the thread that iterates over the list of streams, and another thread adding new streams to the list; the newly added streams may be missed by the iterator, and then are subsequently closed with the connection's termination cause. I only observed the failure 3 times in 2500 repeats.
>>
>> The exception itself is not visible in the test output; printStackTrace helps a lot in diagnosing test failures, could you add it?
>
> Thank you @djelinski for finding this. I've added `e.printStackTrace()` to make the exception visible in test output.
>
> If I understand correctly, the issue might be a race between `streams.forEach()` in `handleGoAwayWithError()` and other threads adding new streams. If streams 3 and 5 get added while forEach is running (or just after it finishes), they would miss the `closeAsUnprocessed()` call and get closed with the GOAWAY cause instead. Is that the scenario you're thinking of?
>
> The printStackTrace should help confirm what's actually happening. Let me know if there's anything else I should add.
Thanks @ehs208 for making the change. That's exactly the scenario I had in mind.
I observed 2 cases where the second failure had the same stack trace as the original one, and one case where the second failure had this stack trace:
java.util.concurrent.CompletionException: java.net.ProtocolException: Received GOAWAY with error code Protocol error (0x1): Test GOAWAY error from server
at java.base/java.util.concurrent.CompletableFuture.wrapInCompletionException(CompletableFuture.java:323)
at java.base/java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:412)
at java.base/java.util.concurrent.CompletableFuture.completeRelay(CompletableFuture.java:421)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1173)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:531)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1794)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.base/java.lang.Thread.run(Thread.java:1516)
Caused by: java.net.ProtocolException: Received GOAWAY with error code Protocol error (0x1): Test GOAWAY error from server
at java.net.http/jdk.internal.net.http.Http2TerminationCause$ProtocolError.<init>(Http2TerminationCause.java:247)
at java.net.http/jdk.internal.net.http.Http2TerminationCause$ProtocolError.<init>(Http2TerminationCause.java:241)
at java.net.http/jdk.internal.net.http.Http2TerminationCause.forH2Error(Http2TerminationCause.java:159)
at java.net.http/jdk.internal.net.http.Http2Connection.handleGoAwayWithError(Http2Connection.java:1463)
<the remainder of the stack trace is the same as the original exception>
It would be good to avoid these exceptions, and mark the relevant exchanges as unprocessed too.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/28632#issuecomment-3674074748
More information about the net-dev
mailing list