diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/CallTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/CallTest.java index a3c425eab..9b194b670 100644 --- a/okhttp-tests/src/test/java/com/squareup/okhttp/CallTest.java +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/CallTest.java @@ -34,11 +34,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.UUID; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -1285,6 +1287,44 @@ public final class CallTest { .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 responseRef = new SynchronousQueue(); + 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 { Response response = client.newCall(request).execute(); return new RecordedResponse(request, response, response.body().string(), null); diff --git a/okhttp/src/main/java/com/squareup/okhttp/Call.java b/okhttp/src/main/java/com/squareup/okhttp/Call.java index ce17cdaf2..e9be59990 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Call.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Call.java @@ -146,13 +146,13 @@ public final class Call { responseCallback.onFailure(request, new IOException("Canceled")); } else { signalledCallback = true; + engine.releaseConnection(); responseCallback.onResponse(response); } } catch (IOException e) { if (signalledCallback) throw new RuntimeException(e); // Do not signal the callback twice! responseCallback.onFailure(request, e); } finally { - engine.close(); // Close the connection if it isn't already. dispatcher.finished(this); } } diff --git a/okhttp/src/main/java/com/squareup/okhttp/Callback.java b/okhttp/src/main/java/com/squareup/okhttp/Callback.java index 50e1899ff..963828b52 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Callback.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Callback.java @@ -29,7 +29,9 @@ public interface Callback { /** * Called when the HTTP response was successfully returned by the remote * 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. * *

Note that transport-layer success (receiving a HTTP response code, * headers and body) does not necessarily indicate application-layer