mirror of
https://github.com/square/okhttp.git
synced 2026-01-18 20:40:58 +03:00
Merge pull request #1299 from nfuller/ConnectionRouteSelection2
Refactoring: Make RouteSelector independent of Connection
This commit is contained in:
@@ -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<Connection> regularRoutes = new ArrayList<>();
|
||||
List<Route> 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<Connection> routesWithFailedRoute = new ArrayList<>();
|
||||
List<Route> 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];
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user