Skip to content

Commit 1c47c11

Browse files
committed
InvokeErrorDecoderForProtocolException
1 parent dcd1dfb commit 1c47c11

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

httpclient/src/main/java/feign/httpclient/ApacheHttpClient.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,40 @@ public Response execute(Request request, Request.Options options) throws IOExcep
8181
} catch (URISyntaxException e) {
8282
throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
8383
}
84-
HttpResponse httpResponse = client.execute(httpUriRequest);
85-
return toFeignResponse(httpResponse, request);
84+
try {
85+
HttpResponse httpResponse = client.execute(httpUriRequest);
86+
return toFeignResponse(httpResponse, request);
87+
} catch (IOException e) {
88+
Throwable cause = e.getCause();
89+
if (cause != null
90+
&& !(cause instanceof IOException)
91+
&& !(cause instanceof RuntimeException)
92+
&& !(cause instanceof Error)) {
93+
return createSyntheticErrorResponse(request, cause);
94+
}
95+
throw e;
96+
}
97+
}
98+
99+
private Response createSyntheticErrorResponse(Request request, Throwable cause) {
100+
// Determine status code based on exception type
101+
int status = 500; // Default to Internal Server Error
102+
String reason = cause.getClass().getSimpleName();
103+
104+
// For ProtocolException during redirects (e.g., missing Location header), use 3xx
105+
if (cause.getClass().getSimpleName().equals("ProtocolException")
106+
&& cause.getMessage() != null
107+
&& cause.getMessage().contains("redirect")) {
108+
status = 300; // Multiple Choices (generic redirect error)
109+
}
110+
111+
return Response.builder()
112+
.status(status)
113+
.reason(reason)
114+
.request(request)
115+
.headers(new java.util.HashMap<>())
116+
.body(cause.getMessage() != null ? cause.getMessage().getBytes() : new byte[0])
117+
.build();
86118
}
87119

88120
HttpUriRequest toHttpUriRequest(Request request, Request.Options options)

httpclient/src/test/java/feign/httpclient/ApacheHttpClientTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import feign.Feign.Builder;
2424
import feign.FeignException;
2525
import feign.Request.Options;
26+
import feign.Retryer;
2627
import feign.client.AbstractClientTest;
2728
import feign.jaxrs.JAXRSContract;
2829
import java.nio.charset.StandardCharsets;
@@ -89,6 +90,23 @@ void notFollowRedirectIsRespected() throws InterruptedException {
8990
assertThat(server.takeRequest().getPath()).isEqualTo("/withOptions");
9091
}
9192

93+
@Test
94+
void errorDecoderInvokedWhenClientThrowsCheckedExceptionDuringExecution() {
95+
JaxRsTestInterface api =
96+
Feign.builder()
97+
.contract(new JAXRSContract())
98+
.retryer(Retryer.NEVER_RETRY)
99+
.client(new ApacheHttpClient(HttpClientBuilder.create().build()))
100+
.target(JaxRsTestInterface.class, "http://localhost:" + server.getPort());
101+
102+
server.enqueue(new MockResponse().setResponseCode(303));
103+
104+
FeignException exception = assertThrows(FeignException.class, () -> api.withoutBody("foo"));
105+
assertThat(exception.status()).isEqualTo(300);
106+
assertThat(exception.getMessage())
107+
.contains("Received redirect response HTTP/1.1 303 Redirection but no location header");
108+
}
109+
92110
private JaxRsTestInterface buildTestInterface() {
93111
return Feign.builder()
94112
.contract(new JAXRSContract())

0 commit comments

Comments
 (0)