From 52454c61993e4d4376d429652104ae404503c3ad Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 6 Jan 2015 11:57:34 +0000 Subject: [PATCH] Refactoring: Make RouteSelector independent of Connection Ultimate goal: to improve the TLS fallback behavior so that socket connections will not be created if the necessary TLS protocols will not be supported. To achieve this RouteSelector will be moved into Connection and so that it can be passed the Socket or protocol information during the route selection process. This is a small first step: Make RouteSelector independent of Connection so that it can later be moved inside of Connection. It puts the RouteSelector interface in terms of Routes, which seems logical. --- .../internal/http/RouteSelectorTest.java | 148 +++++++++--------- .../okhttp/internal/http/HttpEngine.java | 71 ++++++++- .../okhttp/internal/http/RouteSelector.java | 61 +------- 3 files changed, 151 insertions(+), 129 deletions(-) diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java index f69b10db0..8efd308f7 100644 --- a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java @@ -17,12 +17,12 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.Address; import com.squareup.okhttp.Authenticator; -import com.squareup.okhttp.Connection; -import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.ConnectionPool; +import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Protocol; import com.squareup.okhttp.Request; +import com.squareup.okhttp.Route; import com.squareup.okhttp.internal.Internal; import com.squareup.okhttp.internal.Network; import com.squareup.okhttp.internal.RouteDatabase; @@ -110,17 +110,17 @@ public final class RouteSelectorTest { @Test public void singleRoute() throws Exception { Address address = httpAddress(); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 1); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(uriHost); assertFalse(routeSelector.hasNext()); try { - routeSelector.nextUnconnected(); + routeSelector.next(); fail(); } catch (NoSuchElementException expected) { } @@ -128,18 +128,18 @@ public final class RouteSelectorTest { @Test public void singleRouteReturnsFailedRoute() throws Exception { Address address = httpAddress(); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 1); - Connection connection = routeSelector.nextUnconnected(); - routeDatabase.failed(connection.getRoute()); - routeSelector = RouteSelector.get(httpRequest, client); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + Route route = routeSelector.next(); + routeDatabase.failed(route); + routeSelector = RouteSelector.get(address, httpRequest, client); + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); assertFalse(routeSelector.hasNext()); try { - routeSelector.nextUnconnected(); + routeSelector.next(); fail(); } catch (NoSuchElementException expected) { } @@ -149,13 +149,13 @@ public final class RouteSelectorTest { Address address = new Address(uriHost, uriPort, socketFactory, null, null, null, authenticator, proxyA, protocols, connectionSpecs, proxySelector); client.setProxy(proxyA); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 2); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, ConnectionSpec.CLEARTEXT); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort, ConnectionSpec.CLEARTEXT); assertFalse(routeSelector.hasNext()); @@ -167,13 +167,13 @@ public final class RouteSelectorTest { Address address = new Address(uriHost, uriPort, socketFactory, null, null, null, authenticator, NO_PROXY, protocols, connectionSpecs, proxySelector); client.setProxy(NO_PROXY); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 2); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, ConnectionSpec.CLEARTEXT); assertFalse(routeSelector.hasNext()); @@ -185,12 +185,12 @@ public final class RouteSelectorTest { Address address = httpAddress(); proxySelector.proxies = null; - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); proxySelector.assertRequests(httpRequest.uri()); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 1); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(uriHost); @@ -199,13 +199,13 @@ public final class RouteSelectorTest { @Test public void proxySelectorReturnsNoProxies() throws Exception { Address address = httpAddress(); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 2); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, ConnectionSpec.CLEARTEXT); assertFalse(routeSelector.hasNext()); @@ -218,29 +218,30 @@ public final class RouteSelectorTest { proxySelector.proxies.add(proxyA); proxySelector.proxies.add(proxyB); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); proxySelector.assertRequests(httpRequest.uri()); // First try the IP addresses of the first proxy, in sequence. assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 2); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], proxyAPort, + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, ConnectionSpec.CLEARTEXT); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1], proxyAPort, + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(proxyAHost); // Next try the IP address of the second proxy. assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(254, 1); - assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[0], proxyBPort, + assertRoute(routeSelector.next(), address, proxyB, dns.inetAddresses[0], + proxyBPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(proxyBHost); // Finally try the only IP address of the origin server. assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(253, 1); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], uriPort, + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(uriHost); @@ -251,13 +252,13 @@ public final class RouteSelectorTest { Address address = httpAddress(); proxySelector.proxies.add(NO_PROXY); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); proxySelector.assertRequests(httpRequest.uri()); // Only the origin server will be attempted. assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 1); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], uriPort, + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(uriHost); @@ -270,19 +271,19 @@ public final class RouteSelectorTest { proxySelector.proxies.add(proxyA); proxySelector.proxies.add(proxyB); proxySelector.proxies.add(proxyA); - RouteSelector routeSelector = RouteSelector.get(httpRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpRequest, client); proxySelector.assertRequests(httpRequest.uri()); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 1); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(proxyAHost); assertTrue(routeSelector.hasNext()); dns.inetAddresses = null; try { - routeSelector.nextUnconnected(); + routeSelector.next(); fail(); } catch (UnknownHostException expected) { } @@ -290,13 +291,13 @@ public final class RouteSelectorTest { assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(255, 1); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(proxyAHost); assertTrue(routeSelector.hasNext()); dns.inetAddresses = makeFakeAddresses(254, 1); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.CLEARTEXT); dns.assertRequests(uriHost); @@ -305,99 +306,101 @@ public final class RouteSelectorTest { // https://github.com/square/okhttp/issues/442 @Test public void nonSslErrorAddsAllTlsModesToFailedRoute() throws Exception { + Address address = httpsAddress(); client.setProxy(Proxy.NO_PROXY); - RouteSelector routeSelector = RouteSelector.get(httpsRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpsRequest, client); dns.inetAddresses = makeFakeAddresses(255, 1); - Connection connection = routeSelector.nextUnconnected(); - routeSelector.connectFailed(connection, new IOException("Non SSL exception")); + Route route = routeSelector.next(); + routeSelector.connectFailed(route, new IOException("Non SSL exception")); assertEquals(2, routeDatabase.failedRoutesCount()); assertFalse(routeSelector.hasNext()); } @Test public void sslErrorAddsOnlyFailedConfigurationToFailedRoute() throws Exception { + Address address = httpsAddress(); client.setProxy(Proxy.NO_PROXY); - RouteSelector routeSelector = RouteSelector.get(httpsRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpsRequest, client); dns.inetAddresses = makeFakeAddresses(255, 1); - Connection connection = routeSelector.nextUnconnected(); - routeSelector.connectFailed(connection, new SSLHandshakeException("SSL exception")); + Route route = routeSelector.next(); + routeSelector.connectFailed(route, new SSLHandshakeException("SSL exception")); assertTrue(routeDatabase.failedRoutesCount() == 1); assertTrue(routeSelector.hasNext()); } @Test public void multipleProxiesMultipleInetAddressesMultipleConfigurations() throws Exception { - Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory, - hostnameVerifier, null, authenticator, null, protocols, connectionSpecs, proxySelector); + Address address = httpsAddress(); proxySelector.proxies.add(proxyA); proxySelector.proxies.add(proxyB); - RouteSelector routeSelector = RouteSelector.get(httpsRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpsRequest, client); // Proxy A dns.inetAddresses = makeFakeAddresses(255, 2); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, ConnectionSpec.MODERN_TLS); dns.assertRequests(proxyAHost); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, ConnectionSpec.COMPATIBLE_TLS); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort, ConnectionSpec.MODERN_TLS); - assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort, ConnectionSpec.COMPATIBLE_TLS); // Proxy B dns.inetAddresses = makeFakeAddresses(254, 2); - assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyB, dns.inetAddresses[0], proxyBPort, ConnectionSpec.MODERN_TLS); dns.assertRequests(proxyBHost); - assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, proxyB, dns.inetAddresses[0], proxyBPort, ConnectionSpec.COMPATIBLE_TLS); - assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, proxyB, dns.inetAddresses[1], proxyBPort, ConnectionSpec.MODERN_TLS); - assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, proxyB, dns.inetAddresses[1], proxyBPort, ConnectionSpec.COMPATIBLE_TLS); // Origin dns.inetAddresses = makeFakeAddresses(253, 2); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.MODERN_TLS); dns.assertRequests(uriHost); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, ConnectionSpec.COMPATIBLE_TLS); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, ConnectionSpec.MODERN_TLS); - assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1], + assertRoute(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, ConnectionSpec.COMPATIBLE_TLS); assertFalse(routeSelector.hasNext()); } @Test public void failedRoutesAreLast() throws Exception { + Address address = httpsAddress(); client.setProxy(Proxy.NO_PROXY); - RouteSelector routeSelector = RouteSelector.get(httpsRequest, client); + RouteSelector routeSelector = RouteSelector.get(address, httpsRequest, client); dns.inetAddresses = makeFakeAddresses(255, 1); // Extract the regular sequence of routes from selector. - List regularRoutes = new ArrayList<>(); + List regularRoutes = new ArrayList<>(); while (routeSelector.hasNext()) { - regularRoutes.add(routeSelector.nextUnconnected()); + regularRoutes.add(routeSelector.next()); } // Check that we do indeed have more than one route. assertTrue(regularRoutes.size() > 1); // Add first regular route as failed. - routeDatabase.failed(regularRoutes.get(0).getRoute()); + routeDatabase.failed(regularRoutes.get(0)); // Reset selector - routeSelector = RouteSelector.get(httpsRequest, client); + routeSelector = RouteSelector.get(address, httpsRequest, client); - List routesWithFailedRoute = new ArrayList<>(); + List routesWithFailedRoute = new ArrayList<>(); while (routeSelector.hasNext()) { - routesWithFailedRoute.add(routeSelector.nextUnconnected()); + routesWithFailedRoute.add(routeSelector.next()); } - assertEquals(regularRoutes.get(0).getRoute(), - routesWithFailedRoute.get(routesWithFailedRoute.size() - 1).getRoute()); + assertEquals(regularRoutes.get(0), + routesWithFailedRoute.get(routesWithFailedRoute.size() - 1)); assertEquals(regularRoutes.size(), routesWithFailedRoute.size()); } @@ -419,13 +422,13 @@ public final class RouteSelectorTest { assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); } - private void assertConnection(Connection connection, Address address, Proxy proxy, + private void assertRoute(Route route, Address address, Proxy proxy, InetAddress socketAddress, int socketPort, ConnectionSpec connectionSpec) { - assertEquals(address, connection.getRoute().getAddress()); - assertEquals(proxy, connection.getRoute().getProxy()); - assertEquals(socketAddress, connection.getRoute().getSocketAddress().getAddress()); - assertEquals(socketPort, connection.getRoute().getSocketAddress().getPort()); - assertEquals(connectionSpec, connection.getRoute().getConnectionSpec()); + assertEquals(address, route.getAddress()); + assertEquals(proxy, route.getProxy()); + assertEquals(socketAddress, route.getSocketAddress().getAddress()); + assertEquals(socketPort, route.getSocketAddress().getPort()); + assertEquals(connectionSpec, route.getConnectionSpec()); } /** Returns an address that's without an SSL socket factory or hostname verifier. */ @@ -434,6 +437,11 @@ public final class RouteSelectorTest { protocols, connectionSpecs, proxySelector); } + private Address httpsAddress() { + return new Address(uriHost, uriPort, socketFactory, sslSocketFactory, + hostnameVerifier, null, authenticator, null, protocols, connectionSpecs, proxySelector); + } + private static InetAddress[] makeFakeAddresses(int prefix, int count) { try { InetAddress[] result = new InetAddress[count]; diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java index d4d9b4cb1..643fa09e5 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java @@ -18,7 +18,9 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.Address; +import com.squareup.okhttp.CertificatePinner; import com.squareup.okhttp.Connection; +import com.squareup.okhttp.ConnectionPool; import com.squareup.okhttp.Headers; import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.MediaType; @@ -38,12 +40,15 @@ import java.net.CookieHandler; import java.net.ProtocolException; import java.net.Proxy; import java.net.URL; +import java.net.UnknownHostException; import java.security.cert.CertificateException; import java.util.Date; import java.util.List; import java.util.Map; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSocketFactory; import okio.Buffer; import okio.BufferedSink; import okio.BufferedSource; @@ -109,6 +114,7 @@ public final class HttpEngine { final OkHttpClient client; private Connection connection; + private Address address; private RouteSelector routeSelector; private Route route; private final Response priorResponse; @@ -232,7 +238,7 @@ public final class HttpEngine { if (networkRequest != null) { // Open a connection unless we inherited one from a redirect. if (connection == null) { - connect(networkRequest); + connect(); } transport = Internal.instance.newTransport(connection, this); @@ -302,17 +308,43 @@ public final class HttpEngine { } /** Connect to the origin server either directly or via a proxy. */ - private void connect(Request request) throws IOException { + private void connect() throws IOException { if (connection != null) throw new IllegalStateException(); if (routeSelector == null) { - routeSelector = RouteSelector.get(request, client); + address = createAddress(client, networkRequest); + routeSelector = RouteSelector.get(address, networkRequest, client); } - connection = routeSelector.next(this); + connection = nextConnection(); route = connection.getRoute(); } + /** + * Returns the next connection to attempt. + * + * @throws java.util.NoSuchElementException if there are no more routes to attempt. + */ + private Connection nextConnection() throws IOException { + Connection connection = createNextConnection(); + Internal.instance.connectAndSetOwner(client, connection, this, networkRequest); + return connection; + } + + private Connection createNextConnection() throws IOException { + ConnectionPool pool = client.getConnectionPool(); + + // Always prefer pooled connections over new connections. + for (Connection pooled; (pooled = pool.get(address)) != null; ) { + if (networkRequest.method().equals("GET") || Internal.instance.isReadable(pooled)) { + return pooled; + } + pooled.getSocket().close(); + } + Route route = routeSelector.next(); + return new Connection(pool, route); + } + /** * Called immediately before the transport transmits HTTP request headers. * This is used to observe the sent time should the request be cached. @@ -368,7 +400,7 @@ public final class HttpEngine { */ public HttpEngine recover(IOException e, Sink requestBodyOut) { if (routeSelector != null && connection != null) { - routeSelector.connectFailed(connection, e); + connectFailed(routeSelector, e); } boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink; @@ -386,6 +418,13 @@ public final class HttpEngine { forWebSocket, connection, routeSelector, (RetryableSink) requestBodyOut, priorResponse); } + private void connectFailed(RouteSelector routeSelector, IOException e) { + // If this is a recycled connection, don't count its failure against the route. + if (Internal.instance.recycleCount(connection) > 0) return; + Route failedRoute = connection.getRoute(); + routeSelector.connectFailed(failedRoute, e); + } + public HttpEngine recover(IOException e) { return recover(e, requestBodyOut); } @@ -1009,4 +1048,26 @@ public final class HttpEngine { && getEffectivePort(url) == getEffectivePort(followUp) && url.getProtocol().equals(followUp.getProtocol()); } + + private static Address createAddress(OkHttpClient client, Request request) + throws UnknownHostException { + String uriHost = request.url().getHost(); + if (uriHost == null || uriHost.length() == 0) { + throw new UnknownHostException(request.url().toString()); + } + + SSLSocketFactory sslSocketFactory = null; + HostnameVerifier hostnameVerifier = null; + CertificatePinner certificatePinner = null; + if (request.isHttps()) { + sslSocketFactory = client.getSslSocketFactory(); + hostnameVerifier = client.getHostnameVerifier(); + certificatePinner = client.getCertificatePinner(); + } + + return new Address(uriHost, getEffectivePort(request.url()), + client.getSocketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner, + client.getAuthenticator(), client.getProxy(), client.getProtocols(), + client.getConnectionSpecs(), client.getProxySelector()); + } } diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java index 0700cb955..db9ae3b6c 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java @@ -16,9 +16,6 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.Address; -import com.squareup.okhttp.CertificatePinner; -import com.squareup.okhttp.Connection; -import com.squareup.okhttp.ConnectionPool; import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; @@ -38,10 +35,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLProtocolException; -import javax.net.ssl.SSLSocketFactory; import static com.squareup.okhttp.internal.Util.getEffectivePort; @@ -55,7 +50,6 @@ public final class RouteSelector { private final URI uri; private final Network network; private final OkHttpClient client; - private final ConnectionPool pool; private final RouteDatabase routeDatabase; private final Request request; @@ -83,7 +77,6 @@ public final class RouteSelector { this.address = address; this.uri = uri; this.client = client; - this.pool = client.getConnectionPool(); this.routeDatabase = Internal.instance.routeDatabase(client); this.network = Internal.instance.network(client); this.request = request; @@ -91,26 +84,8 @@ public final class RouteSelector { resetNextProxy(uri, address.getProxy()); } - public static RouteSelector get(Request request, OkHttpClient client) throws IOException { - String uriHost = request.url().getHost(); - if (uriHost == null || uriHost.length() == 0) { - throw new UnknownHostException(request.url().toString()); - } - - SSLSocketFactory sslSocketFactory = null; - HostnameVerifier hostnameVerifier = null; - CertificatePinner certificatePinner = null; - if (request.isHttps()) { - sslSocketFactory = client.getSslSocketFactory(); - hostnameVerifier = client.getHostnameVerifier(); - certificatePinner = client.getCertificatePinner(); - } - - Address address = new Address(uriHost, getEffectivePort(request.url()), - client.getSocketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner, - client.getAuthenticator(), client.getProxy(), client.getProtocols(), - client.getConnectionSpecs(), client.getProxySelector()); - + public static RouteSelector get(Address address, Request request, OkHttpClient client) + throws IOException { return new RouteSelector(address, request.uri(), client, request); } @@ -125,25 +100,7 @@ public final class RouteSelector { || hasNextPostponed(); } - /** Selects a route to attempt and connects it if it isn't already. */ - public Connection next(HttpEngine owner) throws IOException { - Connection connection = nextUnconnected(); - Internal.instance.connectAndSetOwner(client, connection, owner, request); - return connection; - } - - /** - * Returns the next connection to attempt. - * - * @throws NoSuchElementException if there are no more routes to attempt. - */ - Connection nextUnconnected() throws IOException { - // Always prefer pooled connections over new connections. - for (Connection pooled; (pooled = pool.get(address)) != null; ) { - if (request.method().equals("GET") || Internal.instance.isReadable(pooled)) return pooled; - pooled.getSocket().close(); - } - + public Route next() throws IOException { // Compute the next route to attempt. if (!hasNextConnectionSpec()) { if (!hasNextInetSocketAddress()) { @@ -151,7 +108,7 @@ public final class RouteSelector { if (!hasNextPostponed()) { throw new NoSuchElementException(); } - return new Connection(pool, nextPostponed()); + return nextPostponed(); } lastProxy = nextProxy(); } @@ -165,10 +122,10 @@ public final class RouteSelector { if (routeDatabase.shouldPostpone(route)) { postponedRoutes.add(route); // We will only recurse in order to skip previously failed routes. They will be tried last. - return nextUnconnected(); + return next(); } - return new Connection(pool, route); + return route; } private boolean shouldSendTlsFallbackIndicator(ConnectionSpec connectionSpec) { @@ -180,11 +137,7 @@ public final class RouteSelector { * Clients should invoke this method when they encounter a connectivity * failure on a connection returned by this route selector. */ - public void connectFailed(Connection connection, IOException failure) { - // If this is a recycled connection, don't count its failure against the route. - if (Internal.instance.recycleCount(connection) > 0) return; - - Route failedRoute = connection.getRoute(); + public void connectFailed(Route failedRoute, IOException failure) { if (failedRoute.getProxy().type() != Proxy.Type.DIRECT && address.getProxySelector() != null) { // Tell the proxy selector when we fail to connect on a fresh connection. address.getProxySelector().connectFailed(uri, failedRoute.getProxy().address(), failure);