RFR: 8368249: HttpClient: Translate exceptions thrown by sendAsync [v3]
Daniel Fuchs
dfuchs at openjdk.org
Wed Oct 22 12:59:04 UTC 2025
On Wed, 22 Oct 2025 12:01:43 GMT, Jaikiran Pai <jpai at openjdk.org> wrote:
>> OK - I think what you have is fine. Let's peel of UncheckedIOException. In most cases I could see the UncheckedIOException is only used to smugle an IOException, and in most case it's created at the point where the IOException is created too. That let me think that the extra stack trace will not be needed in most of the cases. We could revisit possibly later in a followup if we have reason to...
>
> I think, not propagating the original `UncheckedIOException` would be a bug. Imagine this application code:
>
>
> final Future<HttpResponse<String>> future = client.sendAsync(req, new BodyHandler<String>() {
> @Override
> public BodySubscriber<String> apply(ResponseInfo ri) {
> throw new UncheckedIOException("application specific message", new IOException("some other message"));
> }
> });
> final HttpResponse<String> resp;
> try {
> resp = future.get();
> } catch (ExecutionException e) {
> final Throwable cause = e.getCause();
> // cause must be UncheckedIOException and its message must be "application specific message"
> assert ...
> }
>
> I think the application should receive the `UncheckedIOException` as the top level cause of the `ExecutionException` and that `UncheckedIOException` must contain the original cause and the original message that was there when the application raised it. Not passing along that to the application, I think, would be wrong.
Then we should wrap - but that could be strange. Especially in those cases where the application does:
throw new UncheckedIOException(new IOException(msg));
But yes - maybe you're right. Since send() does rewrap UncheckedIOException into IOException then maybe sendAsync should do so too. Utils.toIOException could be overriden with a method that takes a lambda to convert UncheckedIOException to IOException. Something like:
public static IOException toIOException(Throwable cause) {
+ return toIOException(cause, UncheckedIOException::getCause);
+ }
+
+ public static IOException toIOException(Throwable cause, Function<UncheckedIOException, IOException> uncheckedIOConverter) {
if (cause == null) return null;
if (cause instanceof CompletionException ce) {
cause = ce.getCause();
@@ -522,7 +526,7 @@ public static IOException toIOException(Throwable cause) {
if (cause instanceof IOException io) {
return io;
} else if (cause instanceof UncheckedIOException uio) {
- return uio.getCause();
+ return uncheckedIOConverter.apply(uio);
}
return new IOException(cause.getMessage(), cause);
}
Then sendAsync could call `Utils.toIOException(throwable, IOException::new)`
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/27787#discussion_r2452001406
More information about the net-dev
mailing list