mirror of
https://github.com/square/okhttp.git
synced 2025-11-29 06:23:09 +03:00
Keep the response body alive after the callback.
This commit is contained in:
@@ -34,11 +34,13 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
@@ -1285,6 +1287,44 @@ public final class CallTest {
|
|||||||
.assertRequestHeader("Accept-Encoding", "gzip");
|
.assertRequestHeader("Accept-Encoding", "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void asyncResponseCanBeConsumedLater() throws Exception {
|
||||||
|
server.enqueue(new MockResponse().setBody("abc"));
|
||||||
|
server.enqueue(new MockResponse().setBody("def"));
|
||||||
|
server.play();
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(server.getUrl("/"))
|
||||||
|
.header("User-Agent", "SyncApiTest")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final BlockingQueue<Response> responseRef = new SynchronousQueue<Response>();
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override public void onFailure(Request request, Throwable throwable) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onResponse(Response response) throws IOException {
|
||||||
|
try {
|
||||||
|
responseRef.put(response);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Response response = responseRef.take();
|
||||||
|
assertEquals(200, response.code());
|
||||||
|
assertEquals("abc", response.body().string());
|
||||||
|
|
||||||
|
// Make another request just to confirm that that connection can be reused...
|
||||||
|
executeSynchronously(new Request.Builder().url(server.getUrl("/")).build()).assertBody("def");
|
||||||
|
assertEquals(0, server.takeRequest().getSequenceNumber()); // New connection.
|
||||||
|
assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection reused.
|
||||||
|
|
||||||
|
// ... even before we close the response body!
|
||||||
|
response.body().close();
|
||||||
|
}
|
||||||
|
|
||||||
private RecordedResponse executeSynchronously(Request request) throws IOException {
|
private RecordedResponse executeSynchronously(Request request) throws IOException {
|
||||||
Response response = client.newCall(request).execute();
|
Response response = client.newCall(request).execute();
|
||||||
return new RecordedResponse(request, response, response.body().string(), null);
|
return new RecordedResponse(request, response, response.body().string(), null);
|
||||||
|
|||||||
@@ -146,13 +146,13 @@ public final class Call {
|
|||||||
responseCallback.onFailure(request, new IOException("Canceled"));
|
responseCallback.onFailure(request, new IOException("Canceled"));
|
||||||
} else {
|
} else {
|
||||||
signalledCallback = true;
|
signalledCallback = true;
|
||||||
|
engine.releaseConnection();
|
||||||
responseCallback.onResponse(response);
|
responseCallback.onResponse(response);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (signalledCallback) throw new RuntimeException(e); // Do not signal the callback twice!
|
if (signalledCallback) throw new RuntimeException(e); // Do not signal the callback twice!
|
||||||
responseCallback.onFailure(request, e);
|
responseCallback.onFailure(request, e);
|
||||||
} finally {
|
} finally {
|
||||||
engine.close(); // Close the connection if it isn't already.
|
|
||||||
dispatcher.finished(this);
|
dispatcher.finished(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ public interface Callback {
|
|||||||
/**
|
/**
|
||||||
* Called when the HTTP response was successfully returned by the remote
|
* Called when the HTTP response was successfully returned by the remote
|
||||||
* server. The callback may proceed to read the response body with {@link
|
* server. The callback may proceed to read the response body with {@link
|
||||||
* Response#body}.
|
* Response#body}. The response is still live until its response body is
|
||||||
|
* closed with {@code response.body().close()}. The recipient of the callback
|
||||||
|
* may even consume the response body on another thread.
|
||||||
*
|
*
|
||||||
* <p>Note that transport-layer success (receiving a HTTP response code,
|
* <p>Note that transport-layer success (receiving a HTTP response code,
|
||||||
* headers and body) does not necessarily indicate application-layer
|
* headers and body) does not necessarily indicate application-layer
|
||||||
|
|||||||
Reference in New Issue
Block a user