Skip to content

Commit 9bbd865

Browse files
Carter KozakAdrian Cole
authored andcommitted
Allow responses to be streamed.
Never expands >8kb responses into memory
1 parent f774a42 commit 9bbd865

4 files changed

Lines changed: 35 additions & 3 deletions

File tree

core/src/main/java/feign/Response.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
/**
3737
* An immutable response to an http invocation which only returns string content.
3838
*/
39-
public final class Response {
39+
public final class Response implements Closeable {
4040

4141
private final int status;
4242
private final String reason;
@@ -114,6 +114,11 @@ public String toString() {
114114
return builder.toString();
115115
}
116116

117+
@Override
118+
public void close() {
119+
Util.ensureClosed(body);
120+
}
121+
117122
public interface Body extends Closeable {
118123

119124
/**

core/src/main/java/feign/SynchronousMethodHandler.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
final class SynchronousMethodHandler implements MethodHandler {
3434

35+
private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;
36+
3537
private final MethodMetadata metadata;
3638
private final Target<?> target;
3739
private final Client client;
@@ -101,6 +103,7 @@ Object executeAndDecode(RequestTemplate template) throws Throwable {
101103
}
102104
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
103105

106+
boolean shouldClose = true;
104107
try {
105108
if (logLevel != Logger.Level.NONE) {
106109
response =
@@ -110,6 +113,11 @@ Object executeAndDecode(RequestTemplate template) throws Throwable {
110113
if (response.body() == null) {
111114
return response;
112115
}
116+
if (response.body().length() == null ||
117+
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
118+
shouldClose = false;
119+
return response;
120+
}
113121
// Ensure the response body is disconnected
114122
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
115123
return Response.create(response.status(), response.reason(), response.headers(), bodyData);
@@ -131,7 +139,9 @@ Object executeAndDecode(RequestTemplate template) throws Throwable {
131139
}
132140
throw errorReading(request, response, e);
133141
} finally {
134-
ensureClosed(response.body());
142+
if (shouldClose) {
143+
ensureClosed(response.body());
144+
}
135145
}
136146
}
137147

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ Response.Body toFeignBody(HttpResponse httpResponse) throws IOException {
194194

195195
@Override
196196
public Integer length() {
197-
return entity.getContentLength() < 0 ? (int) entity.getContentLength() : null;
197+
return entity.getContentLength() >= 0 ? (int) entity.getContentLength() : null;
198198
}
199199

200200
@Override

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public void parsesRequestAndResponse() throws IOException, InterruptedException
6161
assertThat(response.headers())
6262
.containsEntry("Content-Length", asList("3"))
6363
.containsEntry("Foo", asList("Bar"));
64+
assertThat(response.body().length()).isEqualTo(3);
6465
assertThat(response.body().asInputStream())
6566
.hasContentEqualTo(new ByteArrayInputStream("foo".getBytes(UTF_8)));
6667

@@ -70,6 +71,22 @@ public void parsesRequestAndResponse() throws IOException, InterruptedException
7071
.hasBody("foo");
7172
}
7273

74+
@Test
75+
public void parsesResponseMissingLength() throws IOException, InterruptedException {
76+
server.enqueue(new MockResponse().setChunkedBody("foo", 1));
77+
78+
TestInterface api = Feign.builder()
79+
.client(new ApacheHttpClient())
80+
.target(TestInterface.class, "http://localhost:" + server.getPort());
81+
82+
Response response = api.post("testing");
83+
assertThat(response.status()).isEqualTo(200);
84+
assertThat(response.reason()).isEqualTo("OK");
85+
assertThat(response.body().length()).isNull();
86+
assertThat(response.body().asInputStream())
87+
.hasContentEqualTo(new ByteArrayInputStream("foo".getBytes(UTF_8)));
88+
}
89+
7390
@Test
7491
public void parsesErrorResponse() throws IOException, InterruptedException {
7592
thrown.expect(FeignException.class);

0 commit comments

Comments
 (0)