diff --git a/spring-web/src/main/java/org/springframework/http/client/JdkClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/JdkClientHttpRequest.java index 886b921aaf..3237f32adc 100644 --- a/spring-web/src/main/java/org/springframework/http/client/JdkClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/JdkClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,11 @@ import java.time.Duration; import java.util.Collections; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -90,8 +93,14 @@ class JdkClientHttpRequest extends AbstractStreamingClientHttpRequest { protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException { try { HttpRequest request = buildRequest(headers, body); - HttpResponse response = - this.httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); + HttpResponse response; + if (this.timeout != null) { + response = this.httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) + .get(this.timeout.toMillis(), TimeUnit.MILLISECONDS); + } + else { + response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); + } return new JdkClientHttpResponse(response); } catch (UncheckedIOException ex) { @@ -99,7 +108,26 @@ class JdkClientHttpRequest extends AbstractStreamingClientHttpRequest { } catch (InterruptedException ex) { Thread.currentThread().interrupt(); - throw new IOException("Could not send request: " + ex.getMessage(), ex); + throw new IOException("Request was interrupted: " + ex.getMessage(), ex); + } + catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + + if (cause instanceof UncheckedIOException uioEx) { + throw uioEx.getCause(); + } + if (cause instanceof RuntimeException rtEx) { + throw rtEx; + } + else if (cause instanceof IOException ioEx) { + throw ioEx; + } + else { + throw new IOException(cause.getMessage(), cause); + } + } + catch (TimeoutException ex) { + throw new IOException("Request timed out: " + ex.getMessage(), ex); } }