This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 86196af675 fix(sdk): preserve retryable http client failures (#7072)
86196af675 is described below
commit 86196af6755a50c67f2b581b81e56be62ec1de24
Author: Liming Deng <[email protected]>
AuthorDate: Wed Sep 30 09:56:47 2026 +0800
fix(sdk): preserve retryable http client failures (#7072)
* fix(sdk): preserve retryable http client failures
* test(sdk): cover cancelled http requests
---
.../shenyu/sdk/httpclient/HttpShenyuSdkClient.java | 35 +++++++++---
.../sdk/httpclient/HttpShenyuSdkClientTest.java | 64 ++++++++++++++++++++++
2 files changed, 92 insertions(+), 7 deletions(-)
diff --git
a/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClient.java
b/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClient.java
index 17125d305e..32dfbc29fc 100644
---
a/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClient.java
+++
b/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClient.java
@@ -52,6 +52,8 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
@@ -162,14 +164,33 @@ public class HttpShenyuSdkClient extends
AbstractShenyuSdkClient {
LOG.debug("HttpResponse cancelled.");
}
});
+ HttpResponse response = waitForResponse(execute);
+ return new ShenyuResponse(response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase(),
+
Arrays.stream(response.getAllHeaders()).collect(Collectors.groupingBy(Header::getName,
HashMap::new,
+ Collectors.mapping(Header::getValue,
Collectors.toCollection(LinkedList::new)))),
+ EntityUtils.toString(response.getEntity(),
StandardCharsets.UTF_8), request);
+ }
+
+ private HttpResponse waitForResponse(final Future<HttpResponse>
responseFuture) throws IOException {
try {
- HttpResponse response = execute.get();
- return new
ShenyuResponse(response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase(),
-
Arrays.stream(response.getAllHeaders()).collect(Collectors.groupingBy(Header::getName,
HashMap::new,
- Collectors.mapping(Header::getValue,
Collectors.toCollection(LinkedList::new)))),
- EntityUtils.toString(response.getEntity(),
StandardCharsets.UTF_8), request);
- } catch (Exception e) {
- throw new ShenyuException(e);
+ return responseFuture.get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Interrupted while waiting for the HTTP
response", e);
+ } catch (ExecutionException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ }
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+ if (cause instanceof Error) {
+ throw (Error) cause;
+ }
+ throw new IOException("HTTP request failed", cause);
+ } catch (CancellationException e) {
+ throw new IOException("HTTP request was cancelled", e);
}
}
diff --git
a/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClientTest.java
b/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClientTest.java
index a77e194adf..07d116109f 100644
---
a/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClientTest.java
+++
b/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpShenyuSdkClientTest.java
@@ -17,16 +17,25 @@
package org.apache.shenyu.sdk.httpclient;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.shenyu.sdk.core.ShenyuRequest;
+import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -44,4 +53,59 @@ public class HttpShenyuSdkClientTest {
when(shenyuHttpClient.doRequest(shenyuRequest)).thenCallRealMethod();
}
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testTransportFailureThrowsIOException() throws Exception {
+ HttpShenyuSdkClient shenyuHttpClient = new HttpShenyuSdkClient();
+ HttpAsyncClient httpAsyncClient = mock(HttpAsyncClient.class);
+ Future<HttpResponse> future = mock(Future.class);
+ when(httpAsyncClient.execute(Mockito.any(HttpUriRequest.class),
Mockito.<FutureCallback<HttpResponse>>any())).thenReturn(future);
+ when(future.get()).thenThrow(new ExecutionException(new
IOException("connection reset")));
+ setHttpAsyncClient(shenyuHttpClient, httpAsyncClient);
+
+ Assert.assertThrows(IOException.class, () ->
shenyuHttpClient.doRequest(createRequest()));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testInterruptedFailureRestoresInterruptStatus() throws
Exception {
+ HttpShenyuSdkClient shenyuHttpClient = new HttpShenyuSdkClient();
+ HttpAsyncClient httpAsyncClient = mock(HttpAsyncClient.class);
+ Future<HttpResponse> future = mock(Future.class);
+ when(httpAsyncClient.execute(Mockito.any(HttpUriRequest.class),
Mockito.<FutureCallback<HttpResponse>>any())).thenReturn(future);
+ when(future.get()).thenThrow(new InterruptedException("interrupted"));
+ setHttpAsyncClient(shenyuHttpClient, httpAsyncClient);
+
+ try {
+ Assert.assertThrows(IOException.class, () ->
shenyuHttpClient.doRequest(createRequest()));
+ Assert.assertTrue(Thread.currentThread().isInterrupted());
+ } finally {
+ Thread.interrupted();
+ }
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testCancelledRequestThrowsIOException() throws Exception {
+ HttpShenyuSdkClient shenyuHttpClient = new HttpShenyuSdkClient();
+ HttpAsyncClient httpAsyncClient = mock(HttpAsyncClient.class);
+ Future<HttpResponse> future = mock(Future.class);
+ when(httpAsyncClient.execute(Mockito.any(HttpUriRequest.class),
Mockito.<FutureCallback<HttpResponse>>any())).thenReturn(future);
+ when(future.get()).thenThrow(new CancellationException("cancelled"));
+ setHttpAsyncClient(shenyuHttpClient, httpAsyncClient);
+
+ Assert.assertThrows(IOException.class, () ->
shenyuHttpClient.doRequest(createRequest()));
+ }
+
+ private ShenyuRequest createRequest() {
+ return ShenyuRequest.create(ShenyuRequest.HttpMethod.GET,
"http://localhost/test",
+ new HashMap<>(), null, null, null);
+ }
+
+ private void setHttpAsyncClient(final HttpShenyuSdkClient client, final
HttpAsyncClient httpAsyncClient) throws Exception {
+ Field field =
HttpShenyuSdkClient.class.getDeclaredField("httpAsyncClient");
+ field.setAccessible(true);
+ field.set(client, httpAsyncClient);
+ }
+
}