From 49d3ec58fcfa4dd48f74c76f42ad111840edf612 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 10 Jan 2024 09:53:02 +0100 Subject: [PATCH] Use timeout for JdkClientHttpRequest response retrieval This commit ensures that, in JdkClientHttpRequest, the response is obtained using a timeout, instead of blocking indefinitely. Closes gh-31911 --- .../http/client/JdkClientHttpRequest.java | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) 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 886b921aaf4b..3237f32adc6c 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.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 @@ public URI getURI() { 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 @@ protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body } 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); } }