diff --git a/okhttp/src/main/java/okhttp3/internal/connection/ExchangeFinder.kt b/okhttp/src/main/java/okhttp3/internal/connection/ExchangeFinder.kt index 2a7bec00a..3a35ae314 100644 --- a/okhttp/src/main/java/okhttp3/internal/connection/ExchangeFinder.kt +++ b/okhttp/src/main/java/okhttp3/internal/connection/ExchangeFinder.kt @@ -113,13 +113,23 @@ class ExchangeFinder( connectionRetryEnabled = connectionRetryEnabled ) - // Confirm that the connection is good. If it isn't, take it out of the pool and start again. - if (!candidate.isHealthy(doExtensiveHealthChecks)) { - candidate.noNewExchanges() - continue + // Confirm that the connection is good. + if (candidate.isHealthy(doExtensiveHealthChecks)) { + return candidate } - return candidate + // If it isn't, take it out of the pool. + candidate.noNewExchanges() + + // Make sure we have some routes left to try. One example where we may exhaust all the routes + // would happen if we made a new connection and it immediately is detected as unhealthy. + synchronized(connectionPool) { + val routesLeft = routeSelection?.hasNext() ?: true + val routesSelectionLeft = routeSelector?.hasNext() ?: true + if (nextRouteToTry == null && !routesLeft && !routesSelectionLeft) { + throw IOException("exhausted all routes") + } + } } } diff --git a/okhttp/src/test/java/okhttp3/CallTest.java b/okhttp/src/test/java/okhttp3/CallTest.java index 5f3e07a2f..767e7ef42 100644 --- a/okhttp/src/test/java/okhttp3/CallTest.java +++ b/okhttp/src/test/java/okhttp3/CallTest.java @@ -61,6 +61,7 @@ import okhttp3.CallEvent.ConnectionReleased; import okhttp3.CallEvent.ResponseFailed; import okhttp3.internal.DoubleInetAddressDns; import okhttp3.internal.RecordingOkAuthenticator; +import okhttp3.internal.Util; import okhttp3.internal.Version; import okhttp3.internal.http.RecordingProxySelector; import okhttp3.internal.io.InMemoryFileSystem; @@ -3792,6 +3793,20 @@ public final class CallTest { } } + @Test public void connectionIsImmediatelyUnhealthy() throws Exception { + EventListener listener = new EventListener() { + @Override public void connectionAcquired(Call call, Connection connection) { + Util.closeQuietly(connection.socket()); + } + }; + + client = client.newBuilder() + .eventListener(listener) + .build(); + + executeSynchronously("/").assertFailure(IOException.class); + } + @Test public void requestBodyThrowsUnrelatedToNetwork() throws Exception { server.enqueue(new MockResponse());