mirror of
https://github.com/square/okhttp.git
synced 2026-01-12 10:23:16 +03:00
Merge pull request #1089 from square/jwilson_1013_connection_configuration
Introduce ConnectionConfiguration.
This commit is contained in:
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package com.squareup.okhttp;
|
||||
|
||||
import com.squareup.okhttp.internal.Internal;
|
||||
import com.squareup.okhttp.internal.RecordingHostnameVerifier;
|
||||
import com.squareup.okhttp.internal.RecordingOkAuthenticator;
|
||||
import com.squareup.okhttp.internal.SingleInetAddressNetwork;
|
||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||
import com.squareup.okhttp.mockwebserver.Dispatcher;
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
@@ -29,6 +31,7 @@ import java.io.InputStream;
|
||||
import java.net.CookieManager;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Arrays;
|
||||
@@ -47,6 +50,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
@@ -625,6 +629,7 @@ public final class CallTest {
|
||||
|
||||
client.setSslSocketFactory(sslContext.getSocketFactory());
|
||||
client.setHostnameVerifier(new RecordingHostnameVerifier());
|
||||
Internal.instance.setNetwork(client, new SingleInetAddressNetwork());
|
||||
|
||||
executeSynchronously(new Request.Builder().url(server.getUrl("/")).build())
|
||||
.assertBody("abc");
|
||||
@@ -647,6 +652,43 @@ public final class CallTest {
|
||||
callback.await(request.url()).assertBody("abc");
|
||||
}
|
||||
|
||||
@Test public void noRecoveryFromTlsHandshakeFailureWhenTlsFallbackIsDisabled() throws Exception {
|
||||
client.setConnectionConfigurations(Arrays.asList(
|
||||
ConnectionConfiguration.MODERN_TLS, ConnectionConfiguration.CLEARTEXT));
|
||||
|
||||
server.useHttps(sslContext.getSocketFactory(), false);
|
||||
server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
|
||||
server.play();
|
||||
|
||||
client.setSslSocketFactory(sslContext.getSocketFactory());
|
||||
client.setHostnameVerifier(new RecordingHostnameVerifier());
|
||||
Internal.instance.setNetwork(client, new SingleInetAddressNetwork());
|
||||
|
||||
Request request = new Request.Builder().url(server.getUrl("/")).build();
|
||||
try {
|
||||
client.newCall(request).execute();
|
||||
fail();
|
||||
} catch (SSLProtocolException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void cleartextCallsFailWhenCleartextIsDisabled() throws Exception {
|
||||
// Configure the client with only TLS configurations. No cleartext!
|
||||
client.setConnectionConfigurations(Arrays.asList(
|
||||
ConnectionConfiguration.MODERN_TLS, ConnectionConfiguration.COMPATIBLE_TLS));
|
||||
|
||||
server.enqueue(new MockResponse());
|
||||
server.play();
|
||||
|
||||
Request request = new Request.Builder().url(server.getUrl("/")).build();
|
||||
try {
|
||||
client.newCall(request).execute();
|
||||
fail();
|
||||
} catch (SocketException expected) {
|
||||
assertTrue(expected.getMessage().contains("exhausted connection configurations"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void setFollowSslRedirectsFalse() throws Exception {
|
||||
server.useHttps(sslContext.getSocketFactory(), false);
|
||||
server.enqueue(new MockResponse()
|
||||
@@ -694,7 +736,7 @@ public final class CallTest {
|
||||
|
||||
client.setSslSocketFactory(sslContext.getSocketFactory());
|
||||
client.setHostnameVerifier(new RecordingHostnameVerifier());
|
||||
client.setCertificatePinner( new CertificatePinner.Builder()
|
||||
client.setCertificatePinner(new CertificatePinner.Builder()
|
||||
.add(server.getHostName(), "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") // publicobject.com's cert.
|
||||
.build());
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.junit.After;
|
||||
@@ -72,10 +73,13 @@ public final class ConnectionPoolTest {
|
||||
httpServer = new MockWebServer();
|
||||
spdyServer.useHttps(sslContext.getSocketFactory(), false);
|
||||
|
||||
List<ConnectionConfiguration> connectionConfigurations = Util.immutableList(
|
||||
ConnectionConfiguration.MODERN_TLS, ConnectionConfiguration.CLEARTEXT);
|
||||
|
||||
httpServer.play();
|
||||
httpAddress = new Address(httpServer.getHostName(), httpServer.getPort(), socketFactory, null,
|
||||
null, null, AuthenticatorAdapter.INSTANCE, null,
|
||||
Util.immutableList(Protocol.SPDY_3, Protocol.HTTP_1_1));
|
||||
Util.immutableList(Protocol.SPDY_3, Protocol.HTTP_1_1), connectionConfigurations);
|
||||
httpSocketAddress = new InetSocketAddress(InetAddress.getByName(httpServer.getHostName()),
|
||||
httpServer.getPort());
|
||||
|
||||
@@ -83,14 +87,14 @@ public final class ConnectionPoolTest {
|
||||
spdyAddress = new Address(spdyServer.getHostName(), spdyServer.getPort(), socketFactory,
|
||||
sslContext.getSocketFactory(), new RecordingHostnameVerifier(), CertificatePinner.DEFAULT,
|
||||
AuthenticatorAdapter.INSTANCE, null,
|
||||
Util.immutableList(Protocol.SPDY_3, Protocol.HTTP_1_1));
|
||||
Util.immutableList(Protocol.SPDY_3, Protocol.HTTP_1_1), connectionConfigurations);
|
||||
spdySocketAddress = new InetSocketAddress(InetAddress.getByName(spdyServer.getHostName()),
|
||||
spdyServer.getPort());
|
||||
|
||||
Route httpRoute = new Route(httpAddress, Proxy.NO_PROXY, httpSocketAddress,
|
||||
TlsConfiguration.PREFERRED);
|
||||
ConnectionConfiguration.CLEARTEXT);
|
||||
Route spdyRoute = new Route(spdyAddress, Proxy.NO_PROXY, spdySocketAddress,
|
||||
TlsConfiguration.PREFERRED);
|
||||
ConnectionConfiguration.MODERN_TLS);
|
||||
pool = new ConnectionPool(poolSize, KEEP_ALIVE_DURATION_MS);
|
||||
httpA = new Connection(pool, httpRoute);
|
||||
httpA.connect(200, 200, 200, null);
|
||||
@@ -136,7 +140,7 @@ public final class ConnectionPoolTest {
|
||||
assertNull(connection);
|
||||
|
||||
connection = new Connection(pool, new Route(httpAddress, Proxy.NO_PROXY, httpSocketAddress,
|
||||
TlsConfiguration.PREFERRED));
|
||||
ConnectionConfiguration.CLEARTEXT));
|
||||
connection.connect(200, 200, 200, null);
|
||||
connection.setOwner(owner);
|
||||
assertEquals(0, pool.getConnectionCount());
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.okhttp.internal;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* A network that resolves only one IP address per host. Use this when testing
|
||||
* route selection fallbacks to prevent the host machine's various IP addresses
|
||||
* from interfering.
|
||||
*/
|
||||
public class SingleInetAddressNetwork implements Network {
|
||||
@Override public InetAddress[] resolveInetAddresses(String host) throws UnknownHostException {
|
||||
InetAddress[] allInetAddresses = Network.DEFAULT.resolveInetAddresses(host);
|
||||
return new InetAddress[] { allInetAddresses[0] };
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,16 @@ 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.ConnectionConfiguration;
|
||||
import com.squareup.okhttp.ConnectionPool;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Protocol;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.TlsConfiguration;
|
||||
import com.squareup.okhttp.internal.Internal;
|
||||
import com.squareup.okhttp.internal.Network;
|
||||
import com.squareup.okhttp.internal.RouteDatabase;
|
||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -52,6 +53,11 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public final class RouteSelectorTest {
|
||||
public final List<ConnectionConfiguration> connectionConfigurations = Util.immutableList(
|
||||
ConnectionConfiguration.MODERN_TLS,
|
||||
ConnectionConfiguration.COMPATIBLE_TLS,
|
||||
ConnectionConfiguration.CLEARTEXT);
|
||||
|
||||
private static final int proxyAPort = 1001;
|
||||
private static final String proxyAHost = "proxyA";
|
||||
private static final Proxy proxyA =
|
||||
@@ -88,6 +94,7 @@ public final class RouteSelectorTest {
|
||||
.setSslSocketFactory(sslSocketFactory)
|
||||
.setHostnameVerifier(hostnameVerifier)
|
||||
.setProtocols(protocols)
|
||||
.setConnectionConfigurations(connectionConfigurations)
|
||||
.setConnectionPool(ConnectionPool.getDefault());
|
||||
Internal.instance.setNetwork(client, dns);
|
||||
|
||||
@@ -108,7 +115,7 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 1);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(uriHost);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
@@ -129,7 +136,7 @@ public final class RouteSelectorTest {
|
||||
routeDatabase.failed(connection.getRoute());
|
||||
routeSelector = RouteSelector.get(httpRequest, client);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
assertFalse(routeSelector.hasNext());
|
||||
try {
|
||||
routeSelector.nextUnconnected();
|
||||
@@ -140,16 +147,16 @@ public final class RouteSelectorTest {
|
||||
|
||||
@Test public void explicitProxyTriesThatProxysAddressesOnly() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, null, null, null, authenticator,
|
||||
proxyA, protocols);
|
||||
proxyA, protocols, connectionConfigurations);
|
||||
client.setProxy(proxyA);
|
||||
RouteSelector routeSelector = RouteSelector.get(httpRequest, client);
|
||||
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0],
|
||||
proxyAPort, TlsConfiguration.FALLBACK);
|
||||
proxyAPort, ConnectionConfiguration.CLEARTEXT);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1],
|
||||
proxyAPort, TlsConfiguration.FALLBACK);
|
||||
proxyAPort, ConnectionConfiguration.CLEARTEXT);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
dns.assertRequests(proxyAHost);
|
||||
@@ -158,16 +165,16 @@ public final class RouteSelectorTest {
|
||||
|
||||
@Test public void explicitDirectProxy() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, null, null, null, authenticator,
|
||||
NO_PROXY, protocols);
|
||||
NO_PROXY, protocols, connectionConfigurations);
|
||||
client.setProxy(NO_PROXY);
|
||||
RouteSelector routeSelector = RouteSelector.get(httpRequest, client);
|
||||
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
dns.assertRequests(uriHost);
|
||||
@@ -184,7 +191,7 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 1);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(uriHost);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
@@ -197,9 +204,9 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
dns.assertRequests(uriHost);
|
||||
@@ -218,23 +225,23 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0], proxyAPort,
|
||||
TlsConfiguration.FALLBACK);
|
||||
ConnectionConfiguration.CLEARTEXT);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1], proxyAPort,
|
||||
TlsConfiguration.FALLBACK);
|
||||
ConnectionConfiguration.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,
|
||||
TlsConfiguration.FALLBACK);
|
||||
ConnectionConfiguration.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,
|
||||
TlsConfiguration.FALLBACK);
|
||||
ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(uriHost);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
@@ -251,7 +258,7 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 1);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0], uriPort,
|
||||
TlsConfiguration.FALLBACK);
|
||||
ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(uriHost);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
@@ -269,7 +276,7 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 1);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0],
|
||||
proxyAPort, TlsConfiguration.FALLBACK);
|
||||
proxyAPort, ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(proxyAHost);
|
||||
|
||||
assertTrue(routeSelector.hasNext());
|
||||
@@ -284,13 +291,13 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(255, 1);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0],
|
||||
proxyAPort, TlsConfiguration.FALLBACK);
|
||||
proxyAPort, ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(proxyAHost);
|
||||
|
||||
assertTrue(routeSelector.hasNext());
|
||||
dns.inetAddresses = makeFakeAddresses(254, 1);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.CLEARTEXT);
|
||||
dns.assertRequests(uriHost);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
@@ -304,11 +311,11 @@ public final class RouteSelectorTest {
|
||||
dns.inetAddresses = makeFakeAddresses(255, 1);
|
||||
Connection connection = routeSelector.nextUnconnected();
|
||||
routeSelector.connectFailed(connection, new IOException("Non SSL exception"));
|
||||
assertEquals(RouteSelector.TLS_CONFIGURATIONS.size(), routeDatabase.failedRoutesCount());
|
||||
assertEquals(2, routeDatabase.failedRoutesCount());
|
||||
assertFalse(routeSelector.hasNext());
|
||||
}
|
||||
|
||||
@Test public void sslErrorAddsOnlyFailedTlsModeToFailedRoute() throws Exception {
|
||||
@Test public void sslErrorAddsOnlyFailedConfigurationToFailedRoute() throws Exception {
|
||||
client.setProxy(Proxy.NO_PROXY);
|
||||
RouteSelector routeSelector = RouteSelector.get(httpsRequest, client);
|
||||
|
||||
@@ -319,9 +326,9 @@ public final class RouteSelectorTest {
|
||||
assertTrue(routeSelector.hasNext());
|
||||
}
|
||||
|
||||
@Test public void multipleProxiesMultipleInetAddressesMultipleTlsModes() throws Exception {
|
||||
@Test public void multipleProxiesMultipleInetAddressesMultipleConfigurations() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory,
|
||||
hostnameVerifier, null, authenticator, null, protocols);
|
||||
hostnameVerifier, null, authenticator, null, protocols, connectionConfigurations);
|
||||
proxySelector.proxies.add(proxyA);
|
||||
proxySelector.proxies.add(proxyB);
|
||||
RouteSelector routeSelector = RouteSelector.get(httpsRequest, client);
|
||||
@@ -329,38 +336,38 @@ public final class RouteSelectorTest {
|
||||
// Proxy A
|
||||
dns.inetAddresses = makeFakeAddresses(255, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0],
|
||||
proxyAPort, TlsConfiguration.PREFERRED);
|
||||
proxyAPort, ConnectionConfiguration.MODERN_TLS);
|
||||
dns.assertRequests(proxyAHost);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[0],
|
||||
proxyAPort, TlsConfiguration.FALLBACK);
|
||||
proxyAPort, ConnectionConfiguration.COMPATIBLE_TLS);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1],
|
||||
proxyAPort, TlsConfiguration.PREFERRED);
|
||||
proxyAPort, ConnectionConfiguration.MODERN_TLS);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyA, dns.inetAddresses[1],
|
||||
proxyAPort, TlsConfiguration.FALLBACK);
|
||||
proxyAPort, ConnectionConfiguration.COMPATIBLE_TLS);
|
||||
|
||||
// Proxy B
|
||||
dns.inetAddresses = makeFakeAddresses(254, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[0],
|
||||
proxyBPort, TlsConfiguration.PREFERRED);
|
||||
proxyBPort, ConnectionConfiguration.MODERN_TLS);
|
||||
dns.assertRequests(proxyBHost);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[0],
|
||||
proxyBPort, TlsConfiguration.FALLBACK);
|
||||
proxyBPort, ConnectionConfiguration.COMPATIBLE_TLS);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[1],
|
||||
proxyBPort, TlsConfiguration.PREFERRED);
|
||||
proxyBPort, ConnectionConfiguration.MODERN_TLS);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, proxyB, dns.inetAddresses[1],
|
||||
proxyBPort, TlsConfiguration.FALLBACK);
|
||||
proxyBPort, ConnectionConfiguration.COMPATIBLE_TLS);
|
||||
|
||||
// Origin
|
||||
dns.inetAddresses = makeFakeAddresses(253, 2);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.PREFERRED);
|
||||
uriPort, ConnectionConfiguration.MODERN_TLS);
|
||||
dns.assertRequests(uriHost);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[0],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.COMPATIBLE_TLS);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1],
|
||||
uriPort, TlsConfiguration.PREFERRED);
|
||||
uriPort, ConnectionConfiguration.MODERN_TLS);
|
||||
assertConnection(routeSelector.nextUnconnected(), address, NO_PROXY, dns.inetAddresses[1],
|
||||
uriPort, TlsConfiguration.FALLBACK);
|
||||
uriPort, ConnectionConfiguration.COMPATIBLE_TLS);
|
||||
|
||||
assertFalse(routeSelector.hasNext());
|
||||
}
|
||||
@@ -395,18 +402,18 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
private void assertConnection(Connection connection, Address address, Proxy proxy,
|
||||
InetAddress socketAddress, int socketPort, TlsConfiguration tlsConfiguration) {
|
||||
InetAddress socketAddress, int socketPort, ConnectionConfiguration connectionConfiguration) {
|
||||
assertEquals(address, connection.getRoute().getAddress());
|
||||
assertEquals(proxy, connection.getRoute().getProxy());
|
||||
assertEquals(socketAddress, connection.getRoute().getSocketAddress().getAddress());
|
||||
assertEquals(socketPort, connection.getRoute().getSocketAddress().getPort());
|
||||
assertEquals(tlsConfiguration, connection.getRoute().getTlsConfiguration());
|
||||
assertEquals(connectionConfiguration, connection.getRoute().getConnectionConfiguration());
|
||||
}
|
||||
|
||||
/** Returns an address that's without an SSL socket factory or hostname verifier. */
|
||||
private Address httpAddress() {
|
||||
return new Address(uriHost, uriPort, socketFactory, null, null, null, authenticator, null,
|
||||
protocols);
|
||||
protocols, connectionConfigurations);
|
||||
}
|
||||
|
||||
private static InetAddress[] makeFakeAddresses(int prefix, int count) {
|
||||
|
||||
@@ -19,18 +19,18 @@ package com.squareup.okhttp.internal.http;
|
||||
import com.squareup.okhttp.AbstractResponseCache;
|
||||
import com.squareup.okhttp.Cache;
|
||||
import com.squareup.okhttp.Challenge;
|
||||
import com.squareup.okhttp.ConnectionConfiguration;
|
||||
import com.squareup.okhttp.ConnectionPool;
|
||||
import com.squareup.okhttp.Credentials;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.OkUrlFactory;
|
||||
import com.squareup.okhttp.Protocol;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.TlsConfiguration;
|
||||
import com.squareup.okhttp.internal.Internal;
|
||||
import com.squareup.okhttp.internal.Network;
|
||||
import com.squareup.okhttp.internal.RecordingAuthenticator;
|
||||
import com.squareup.okhttp.internal.RecordingHostnameVerifier;
|
||||
import com.squareup.okhttp.internal.RecordingOkAuthenticator;
|
||||
import com.squareup.okhttp.internal.SingleInetAddressNetwork;
|
||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import com.squareup.okhttp.internal.huc.CacheAdapter;
|
||||
@@ -858,12 +858,12 @@ public final class URLConnectionTest {
|
||||
.setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
|
||||
.setBody("bogus proxy connect response content");
|
||||
|
||||
// Configure a single IP address for the host, so we don't retry once for each. Enqueue a
|
||||
// response for all TLS configurations that will be attempted.
|
||||
Internal.instance.setNetwork(client.client(), networkWithFirstAddressOnly());
|
||||
for (TlsConfiguration tlsConfiguration : RouteSelector.TLS_CONFIGURATIONS) {
|
||||
server.enqueue(response);
|
||||
}
|
||||
// Configure a single IP address for the host and a single configuration, so we only need one
|
||||
// failure to fail permanently.
|
||||
Internal.instance.setNetwork(client.client(), new SingleInetAddressNetwork());
|
||||
client.client().setConnectionConfigurations(
|
||||
Util.immutableList(ConnectionConfiguration.MODERN_TLS));
|
||||
server.enqueue(response);
|
||||
server.play();
|
||||
client.client().setProxy(server.toProxyAddress());
|
||||
|
||||
@@ -3297,14 +3297,4 @@ public final class URLConnectionTest {
|
||||
server.setProtocolNegotiationEnabled(true);
|
||||
server.setProtocols(client.client().getProtocols());
|
||||
}
|
||||
|
||||
/** Returns a network that resolves only one IP address per host. */
|
||||
private Network networkWithFirstAddressOnly() {
|
||||
return new Network() {
|
||||
@Override public InetAddress[] resolveInetAddresses(String host) throws UnknownHostException {
|
||||
InetAddress[] allInetAddresses = Network.DEFAULT.resolveInetAddresses(host);
|
||||
return new InetAddress[] { allInetAddresses[0] };
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,11 +44,12 @@ public final class Address {
|
||||
final CertificatePinner certificatePinner;
|
||||
final Authenticator authenticator;
|
||||
final List<Protocol> protocols;
|
||||
final List<ConnectionConfiguration> connectionConfigurations;
|
||||
|
||||
public Address(String uriHost, int uriPort, SocketFactory socketFactory,
|
||||
SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier,
|
||||
CertificatePinner certificatePinner, Authenticator authenticator, Proxy proxy,
|
||||
List<Protocol> protocols) {
|
||||
List<Protocol> protocols, List<ConnectionConfiguration> connectionConfigurations) {
|
||||
if (uriHost == null) throw new NullPointerException("uriHost == null");
|
||||
if (uriPort <= 0) throw new IllegalArgumentException("uriPort <= 0: " + uriPort);
|
||||
if (authenticator == null) throw new IllegalArgumentException("authenticator == null");
|
||||
@@ -62,6 +63,7 @@ public final class Address {
|
||||
this.certificatePinner = certificatePinner;
|
||||
this.authenticator = authenticator;
|
||||
this.protocols = Util.immutableList(protocols);
|
||||
this.connectionConfigurations = Util.immutableList(connectionConfigurations);
|
||||
}
|
||||
|
||||
/** Returns the hostname of the origin server. */
|
||||
@@ -113,6 +115,10 @@ public final class Address {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
public List<ConnectionConfiguration> getConnectionConfigurations() {
|
||||
return connectionConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this address's explicitly-specified HTTP proxy, or null to
|
||||
* delegate to the HTTP client's proxy selector.
|
||||
|
||||
@@ -229,7 +229,7 @@ public final class Connection {
|
||||
SSLSocket sslSocket = (SSLSocket) socket;
|
||||
|
||||
// Configure the socket's ciphers, TLS versions, and extensions.
|
||||
route.tlsConfiguration.apply(sslSocket, route);
|
||||
route.connectionConfiguration.apply(sslSocket, route);
|
||||
|
||||
// Force handshake. This can throw!
|
||||
sslSocket.startHandshake();
|
||||
@@ -246,7 +246,7 @@ public final class Connection {
|
||||
handshake = Handshake.get(sslSocket.getSession());
|
||||
|
||||
String maybeProtocol;
|
||||
if (route.tlsConfiguration.supportsTlsExtensions()
|
||||
if (route.connectionConfiguration.supportsTlsExtensions()
|
||||
&& (maybeProtocol = platform.getSelectedProtocol(sslSocket)) != null) {
|
||||
protocol = Protocol.get(maybeProtocol); // Throws IOE on unknown.
|
||||
}
|
||||
|
||||
@@ -22,8 +22,11 @@ import java.util.List;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
/**
|
||||
* The TLS version and ciphers used to negotiate a secure connection. */
|
||||
public final class TlsConfiguration {
|
||||
* Configuration for the socket connection that HTTP traffic travels through.
|
||||
* For {@code https:} URLs, this includes the TLS version and ciphers to use
|
||||
* when negotiating a secure connection.
|
||||
*/
|
||||
public final class ConnectionConfiguration {
|
||||
/**
|
||||
* This is a subset of the cipher suites supported in Chrome 37, current as of 2014-10-5. All of
|
||||
* these suites are available on Android L; earlier releases support a subset of these suites.
|
||||
@@ -56,29 +59,46 @@ public final class TlsConfiguration {
|
||||
private static final String SSL_3_0 = "SSLv3"; // 1996.
|
||||
|
||||
/** A modern TLS configuration with extensions like SNI and ALPN available. */
|
||||
public static final TlsConfiguration PREFERRED = new TlsConfiguration(
|
||||
CIPHER_SUITES, new String[] { TLS_1_2, TLS_1_1, TLS_1_0, SSL_3_0 }, true);
|
||||
public static final ConnectionConfiguration MODERN_TLS = new ConnectionConfiguration(
|
||||
true, CIPHER_SUITES, new String[] { TLS_1_2, TLS_1_1, TLS_1_0, SSL_3_0 }, true);
|
||||
|
||||
/** A backwards-compatible fallback configuration for interop with obsolete servers. */
|
||||
public static final TlsConfiguration FALLBACK = new TlsConfiguration(
|
||||
CIPHER_SUITES, new String[] { SSL_3_0 }, true);
|
||||
public static final ConnectionConfiguration COMPATIBLE_TLS = new ConnectionConfiguration(
|
||||
true, CIPHER_SUITES, new String[] { SSL_3_0 }, true);
|
||||
|
||||
/** Unencrypted, unauthenticated connections for {@code http:} URLs. */
|
||||
public static final ConnectionConfiguration CLEARTEXT = new ConnectionConfiguration(
|
||||
false, new String[0], new String[0], false);
|
||||
|
||||
private final boolean tls;
|
||||
private final String[] cipherSuites;
|
||||
private final String[] tlsVersions;
|
||||
private final boolean supportsTlsExtensions;
|
||||
|
||||
/**
|
||||
* Caches the subset of this TLS configuration that's supported by the platform. It's possible
|
||||
* that the platform hosts multiple implementations of {@link SSLSocket}, in which case this cache
|
||||
* will be incorrect.
|
||||
* Caches the subset of this configuration that's supported by the host
|
||||
* platform. It's possible that the platform hosts multiple implementations of
|
||||
* {@link SSLSocket}, in which case this cache will be incorrect.
|
||||
*/
|
||||
private TlsConfiguration supportedConfiguration;
|
||||
private ConnectionConfiguration supportedConfiguration;
|
||||
|
||||
private TlsConfiguration(String[] cipherSuites, String[] tlsVersions,
|
||||
private ConnectionConfiguration(boolean tls, String[] cipherSuites, String[] tlsVersions,
|
||||
boolean supportsTlsExtensions) {
|
||||
this.tls = tls;
|
||||
this.cipherSuites = cipherSuites;
|
||||
this.tlsVersions = tlsVersions;
|
||||
this.supportsTlsExtensions = supportsTlsExtensions;
|
||||
|
||||
if (tls && (cipherSuites.length == 0 || tlsVersions.length == 0)) {
|
||||
throw new IllegalArgumentException("Unexpected configuration: " + this);
|
||||
}
|
||||
if (!tls && (cipherSuites.length != 0 || tlsVersions.length != 0 || supportsTlsExtensions)) {
|
||||
throw new IllegalArgumentException("Unexpected configuration: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTls() {
|
||||
return tls;
|
||||
}
|
||||
|
||||
public List<String> cipherSuites() {
|
||||
@@ -95,7 +115,7 @@ public final class TlsConfiguration {
|
||||
|
||||
/** Applies this configuration to {@code sslSocket} for {@code route}. */
|
||||
public void apply(SSLSocket sslSocket, Route route) {
|
||||
TlsConfiguration configurationToApply = supportedConfiguration;
|
||||
ConnectionConfiguration configurationToApply = supportedConfiguration;
|
||||
if (configurationToApply == null) {
|
||||
configurationToApply = supportedConfiguration(sslSocket);
|
||||
supportedConfiguration = configurationToApply;
|
||||
@@ -114,14 +134,22 @@ public final class TlsConfiguration {
|
||||
* Returns a copy of this that omits cipher suites and TLS versions not
|
||||
* supported by {@code sslSocket}.
|
||||
*/
|
||||
private TlsConfiguration supportedConfiguration(SSLSocket sslSocket) {
|
||||
private ConnectionConfiguration supportedConfiguration(SSLSocket sslSocket) {
|
||||
List<String> supportedCipherSuites = Util.intersect(Arrays.asList(cipherSuites),
|
||||
Arrays.asList(sslSocket.getSupportedCipherSuites()));
|
||||
List<String> supportedTlsVersions = Util.intersect(Arrays.asList(tlsVersions),
|
||||
Arrays.asList(sslSocket.getSupportedProtocols()));
|
||||
return new TlsConfiguration(
|
||||
return new ConnectionConfiguration(tls,
|
||||
supportedCipherSuites.toArray(new String[supportedCipherSuites.size()]),
|
||||
supportedTlsVersions.toArray(new String[supportedTlsVersions.size()]),
|
||||
supportsTlsExtensions);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "ConnectionConfiguration(tls=" + tls
|
||||
+ ", cipherSuites=" + Arrays.toString(cipherSuites)
|
||||
+ ", tlsVersions=" + Arrays.toString(tlsVersions)
|
||||
+ ", supportsTlsExtensions=" + supportsTlsExtensions
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,13 @@ import javax.net.ssl.SSLSocketFactory;
|
||||
* safely modified with further configuration changes.
|
||||
*/
|
||||
public class OkHttpClient implements Cloneable {
|
||||
private static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
|
||||
Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
|
||||
|
||||
private static final List<ConnectionConfiguration> DEFAULT_CONNECTION_CONFIGURATIONS =
|
||||
Util.immutableList(ConnectionConfiguration.MODERN_TLS, ConnectionConfiguration.COMPATIBLE_TLS,
|
||||
ConnectionConfiguration.CLEARTEXT);
|
||||
|
||||
static {
|
||||
Internal.instance = new Internal() {
|
||||
@Override public Transport newTransport(
|
||||
@@ -122,6 +129,7 @@ public class OkHttpClient implements Cloneable {
|
||||
private Dispatcher dispatcher;
|
||||
private Proxy proxy;
|
||||
private List<Protocol> protocols;
|
||||
private List<ConnectionConfiguration> connectionConfigurations;
|
||||
private ProxySelector proxySelector;
|
||||
private CookieHandler cookieHandler;
|
||||
|
||||
@@ -152,6 +160,7 @@ public class OkHttpClient implements Cloneable {
|
||||
this.dispatcher = okHttpClient.dispatcher;
|
||||
this.proxy = okHttpClient.proxy;
|
||||
this.protocols = okHttpClient.protocols;
|
||||
this.connectionConfigurations = okHttpClient.connectionConfigurations;
|
||||
this.proxySelector = okHttpClient.proxySelector;
|
||||
this.cookieHandler = okHttpClient.cookieHandler;
|
||||
this.cache = okHttpClient.cache;
|
||||
@@ -479,6 +488,16 @@ public class OkHttpClient implements Cloneable {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
public final OkHttpClient setConnectionConfigurations(
|
||||
List<ConnectionConfiguration> connectionConfigurations) {
|
||||
this.connectionConfigurations = Util.immutableList(connectionConfigurations);
|
||||
return this;
|
||||
}
|
||||
|
||||
public final List<ConnectionConfiguration> getConnectionConfigurations() {
|
||||
return connectionConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the {@code request} to be executed at some point in the future.
|
||||
*/
|
||||
@@ -526,7 +545,10 @@ public class OkHttpClient implements Cloneable {
|
||||
result.connectionPool = ConnectionPool.getDefault();
|
||||
}
|
||||
if (result.protocols == null) {
|
||||
result.protocols = Util.immutableList(Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
|
||||
result.protocols = DEFAULT_PROTOCOLS;
|
||||
}
|
||||
if (result.connectionConfigurations == null) {
|
||||
result.connectionConfigurations = DEFAULT_CONNECTION_CONFIGURATIONS;
|
||||
}
|
||||
if (result.network == null) {
|
||||
result.network = Network.DEFAULT;
|
||||
|
||||
@@ -37,25 +37,26 @@ public final class Route {
|
||||
final Address address;
|
||||
final Proxy proxy;
|
||||
final InetSocketAddress inetSocketAddress;
|
||||
final TlsConfiguration tlsConfiguration;
|
||||
|
||||
/** @deprecated replaced with a constructor that takes a {@link TlsConfiguration}. */
|
||||
public Route(Address address, Proxy proxy, InetSocketAddress inetSocketAddress,
|
||||
String tlsVersion) {
|
||||
this(address, proxy, inetSocketAddress,
|
||||
tlsVersion.equals("SSLv3") ? TlsConfiguration.FALLBACK : TlsConfiguration.PREFERRED);
|
||||
}
|
||||
final ConnectionConfiguration connectionConfiguration;
|
||||
|
||||
public Route(Address address, Proxy proxy, InetSocketAddress inetSocketAddress,
|
||||
TlsConfiguration tlsConfiguration) {
|
||||
if (address == null) throw new NullPointerException("address == null");
|
||||
if (proxy == null) throw new NullPointerException("proxy == null");
|
||||
if (inetSocketAddress == null) throw new NullPointerException("inetSocketAddress == null");
|
||||
if (tlsConfiguration == null) throw new NullPointerException("tlsConfiguration == null");
|
||||
ConnectionConfiguration connectionConfiguration) {
|
||||
if (address == null) {
|
||||
throw new NullPointerException("address == null");
|
||||
}
|
||||
if (proxy == null) {
|
||||
throw new NullPointerException("proxy == null");
|
||||
}
|
||||
if (inetSocketAddress == null) {
|
||||
throw new NullPointerException("inetSocketAddress == null");
|
||||
}
|
||||
if (connectionConfiguration == null) {
|
||||
throw new NullPointerException("connectionConfiguration == null");
|
||||
}
|
||||
this.address = address;
|
||||
this.proxy = proxy;
|
||||
this.inetSocketAddress = inetSocketAddress;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.connectionConfiguration = connectionConfiguration;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
@@ -73,17 +74,12 @@ public final class Route {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/** @deprecated replaced with {@link #getTlsConfiguration()}. */
|
||||
public String getTlsVersion() {
|
||||
return tlsConfiguration.tlsVersions().get(0);
|
||||
}
|
||||
|
||||
public InetSocketAddress getSocketAddress() {
|
||||
return inetSocketAddress;
|
||||
}
|
||||
|
||||
public TlsConfiguration getTlsConfiguration() {
|
||||
return tlsConfiguration;
|
||||
public ConnectionConfiguration getConnectionConfiguration() {
|
||||
return connectionConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +96,7 @@ public final class Route {
|
||||
return address.equals(other.address)
|
||||
&& proxy.equals(other.proxy)
|
||||
&& inetSocketAddress.equals(other.inetSocketAddress)
|
||||
&& tlsConfiguration.equals(other.tlsConfiguration);
|
||||
&& connectionConfiguration.equals(other.connectionConfiguration);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -110,7 +106,7 @@ public final class Route {
|
||||
result = 31 * result + address.hashCode();
|
||||
result = 31 * result + proxy.hashCode();
|
||||
result = 31 * result + inetSocketAddress.hashCode();
|
||||
result = 31 * result + tlsConfiguration.hashCode();
|
||||
result = 31 * result + connectionConfiguration.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,25 +18,25 @@ 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.ConnectionConfiguration;
|
||||
import com.squareup.okhttp.ConnectionPool;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Route;
|
||||
import com.squareup.okhttp.TlsConfiguration;
|
||||
import com.squareup.okhttp.internal.Internal;
|
||||
import com.squareup.okhttp.internal.Network;
|
||||
import com.squareup.okhttp.internal.RouteDatabase;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
@@ -52,9 +52,6 @@ import static com.squareup.okhttp.internal.Util.getEffectivePort;
|
||||
* recycled.
|
||||
*/
|
||||
public final class RouteSelector {
|
||||
public static final List<TlsConfiguration> TLS_CONFIGURATIONS = Util.immutableList(
|
||||
TlsConfiguration.PREFERRED, TlsConfiguration.FALLBACK);
|
||||
|
||||
private final Address address;
|
||||
private final URI uri;
|
||||
private final Network network;
|
||||
@@ -67,19 +64,19 @@ public final class RouteSelector {
|
||||
/* The most recently attempted route. */
|
||||
private Proxy lastProxy;
|
||||
private InetSocketAddress lastInetSocketAddress;
|
||||
private ConnectionConfiguration lastConfiguration;
|
||||
|
||||
/* State for negotiating the next proxy to use. */
|
||||
private boolean hasNextProxy;
|
||||
private Proxy userSpecifiedProxy;
|
||||
private Iterator<Proxy> proxySelectorProxies;
|
||||
private List<Proxy> proxies = Collections.emptyList();
|
||||
private int nextProxyIndex;
|
||||
|
||||
/* State for negotiating the next InetSocketAddress to use. */
|
||||
private InetAddress[] inetAddresses;
|
||||
private int nextSocketAddressIndex;
|
||||
private int socketPort;
|
||||
/* State for negotiating the next socket address to use. */
|
||||
private List<InetSocketAddress> inetSocketAddresses = Collections.emptyList();
|
||||
private int nextInetSocketAddressIndex;
|
||||
|
||||
/* TLS configuration to attempt with the connection. */
|
||||
private int nextTlsConfigurationIndex = TLS_CONFIGURATIONS.size();
|
||||
private List<ConnectionConfiguration> connectionConfigurations = Collections.emptyList();
|
||||
private int nextConfigurationIndex;
|
||||
|
||||
/* State for negotiating failed routes */
|
||||
private final List<Route> postponedRoutes = new ArrayList<>();
|
||||
@@ -114,7 +111,8 @@ public final class RouteSelector {
|
||||
|
||||
Address address = new Address(uriHost, getEffectivePort(request.url()),
|
||||
client.getSocketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner,
|
||||
client.getAuthenticator(), client.getProxy(), client.getProtocols());
|
||||
client.getAuthenticator(), client.getProxy(), client.getProtocols(),
|
||||
client.getConnectionConfigurations());
|
||||
|
||||
return new RouteSelector(address, request.uri(), client, request);
|
||||
}
|
||||
@@ -124,7 +122,7 @@ public final class RouteSelector {
|
||||
* least one route.
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
return hasNextTlsConfiguration()
|
||||
return hasNextConnectionConfiguration()
|
||||
|| hasNextInetSocketAddress()
|
||||
|| hasNextProxy()
|
||||
|| hasNextPostponed();
|
||||
@@ -150,7 +148,7 @@ public final class RouteSelector {
|
||||
}
|
||||
|
||||
// Compute the next route to attempt.
|
||||
if (!hasNextTlsConfiguration()) {
|
||||
if (!hasNextConnectionConfiguration()) {
|
||||
if (!hasNextInetSocketAddress()) {
|
||||
if (!hasNextProxy()) {
|
||||
if (!hasNextPostponed()) {
|
||||
@@ -159,18 +157,15 @@ public final class RouteSelector {
|
||||
return new Connection(pool, nextPostponed());
|
||||
}
|
||||
lastProxy = nextProxy();
|
||||
resetNextInetSocketAddress(lastProxy);
|
||||
}
|
||||
lastInetSocketAddress = nextInetSocketAddress();
|
||||
resetNextTlsConfiguration();
|
||||
}
|
||||
lastConfiguration = nextConnectionConfiguration();
|
||||
|
||||
TlsConfiguration tlsConfiguration = nextTlsConfiguration();
|
||||
Route route = new Route(address, lastProxy, lastInetSocketAddress, tlsConfiguration);
|
||||
Route route = new Route(address, lastProxy, lastInetSocketAddress, lastConfiguration);
|
||||
if (routeDatabase.shouldPostpone(route)) {
|
||||
postponedRoutes.add(route);
|
||||
// We will only recurse in order to skip previously failed routes. They will be
|
||||
// tried last.
|
||||
// We will only recurse in order to skip previously failed routes. They will be tried last.
|
||||
return nextUnconnected();
|
||||
}
|
||||
|
||||
@@ -193,65 +188,60 @@ public final class RouteSelector {
|
||||
|
||||
routeDatabase.failed(failedRoute);
|
||||
|
||||
// If the previously returned route's problem was not related to TLS, and
|
||||
// the next route only changes the TLS mode, we shouldn't even attempt it.
|
||||
// This suppresses it in both this selector and also in the route database.
|
||||
// If the previously returned route's problem was not related to the
|
||||
// connection's configuration, and the next route only changes that, we
|
||||
// shouldn't even attempt it. This suppresses it in both this selector
|
||||
// and also in the route database.
|
||||
if (!(failure instanceof SSLHandshakeException) && !(failure instanceof SSLProtocolException)) {
|
||||
while (hasNextTlsConfiguration()) {
|
||||
while (nextConfigurationIndex < connectionConfigurations.size()) {
|
||||
Route toSuppress = new Route(address, lastProxy, lastInetSocketAddress,
|
||||
nextTlsConfiguration());
|
||||
connectionConfigurations.get(nextConfigurationIndex++));
|
||||
routeDatabase.failed(toSuppress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets {@link #nextProxy} to the first option. */
|
||||
/** Prepares the proxy servers to try. */
|
||||
private void resetNextProxy(URI uri, Proxy proxy) {
|
||||
this.hasNextProxy = true; // This includes NO_PROXY!
|
||||
if (proxy != null) {
|
||||
this.userSpecifiedProxy = proxy;
|
||||
// If the user specifies a proxy, try that and only that.
|
||||
proxies = Collections.singletonList(proxy);
|
||||
} else {
|
||||
List<Proxy> proxyList = proxySelector.select(uri);
|
||||
if (proxyList != null) {
|
||||
this.proxySelectorProxies = proxyList.iterator();
|
||||
}
|
||||
// Try each of the ProxySelector choices until one connection succeeds. If none succeed
|
||||
// then we'll try a direct connection below.
|
||||
proxies = new ArrayList<>();
|
||||
List<Proxy> selectedProxies = proxySelector.select(uri);
|
||||
if (selectedProxies != null) proxies.addAll(selectedProxies);
|
||||
// Finally try a direct connection. We only try it once!
|
||||
proxies.removeAll(Collections.singleton(Proxy.NO_PROXY));
|
||||
proxies.add(Proxy.NO_PROXY);
|
||||
}
|
||||
nextProxyIndex = 0;
|
||||
}
|
||||
|
||||
/** Returns true if there's another proxy to try. */
|
||||
private boolean hasNextProxy() {
|
||||
return hasNextProxy;
|
||||
return nextProxyIndex < proxies.size();
|
||||
}
|
||||
|
||||
/** Returns the next proxy to try. May be PROXY.NO_PROXY but never null. */
|
||||
private Proxy nextProxy() {
|
||||
// If the user specifies a proxy, try that and only that.
|
||||
if (userSpecifiedProxy != null) {
|
||||
hasNextProxy = false;
|
||||
return userSpecifiedProxy;
|
||||
private Proxy nextProxy() throws IOException {
|
||||
if (!hasNextProxy()) {
|
||||
throw new SocketException("No route to " + address.getUriHost()
|
||||
+ "; exhausted proxy configurations: " + proxies);
|
||||
}
|
||||
|
||||
// Try each of the ProxySelector choices until one connection succeeds. If none succeed
|
||||
// then we'll try a direct connection below.
|
||||
if (proxySelectorProxies != null) {
|
||||
while (proxySelectorProxies.hasNext()) {
|
||||
Proxy candidate = proxySelectorProxies.next();
|
||||
if (candidate.type() != Proxy.Type.DIRECT) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally try a direct connection.
|
||||
hasNextProxy = false;
|
||||
return Proxy.NO_PROXY;
|
||||
Proxy result = proxies.get(nextProxyIndex++);
|
||||
resetNextInetSocketAddress(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Resets {@link #nextInetSocketAddress} to the first option. */
|
||||
/** Prepares the socket addresses to attempt for the current proxy or host. */
|
||||
private void resetNextInetSocketAddress(Proxy proxy) throws UnknownHostException {
|
||||
inetAddresses = null; // Clear the addresses. Necessary if getAllByName() below throws!
|
||||
// Clear the addresses. Necessary if getAllByName() below throws!
|
||||
inetSocketAddresses = new ArrayList<>();
|
||||
|
||||
String socketHost;
|
||||
int socketPort;
|
||||
if (proxy.type() == Proxy.Type.DIRECT) {
|
||||
socketHost = uri.getHost();
|
||||
socketPort = getEffectivePort(uri);
|
||||
@@ -267,47 +257,51 @@ public final class RouteSelector {
|
||||
}
|
||||
|
||||
// Try each address for best behavior in mixed IPv4/IPv6 environments.
|
||||
inetAddresses = network.resolveInetAddresses(socketHost);
|
||||
nextSocketAddressIndex = 0;
|
||||
for (InetAddress inetAddress : network.resolveInetAddresses(socketHost)) {
|
||||
inetSocketAddresses.add(new InetSocketAddress(inetAddress, socketPort));
|
||||
}
|
||||
nextInetSocketAddressIndex = 0;
|
||||
}
|
||||
|
||||
/** Returns true if there's another socket address to try. */
|
||||
private boolean hasNextInetSocketAddress() {
|
||||
return inetAddresses != null;
|
||||
return nextInetSocketAddressIndex < inetSocketAddresses.size();
|
||||
}
|
||||
|
||||
/** Returns the next socket address to try. */
|
||||
private InetSocketAddress nextInetSocketAddress() throws UnknownHostException {
|
||||
InetSocketAddress result =
|
||||
new InetSocketAddress(inetAddresses[nextSocketAddressIndex++], socketPort);
|
||||
if (nextSocketAddressIndex == inetAddresses.length) {
|
||||
inetAddresses = null; // So that hasNextInetSocketAddress() returns false.
|
||||
nextSocketAddressIndex = 0;
|
||||
private InetSocketAddress nextInetSocketAddress() throws IOException {
|
||||
if (!hasNextInetSocketAddress()) {
|
||||
throw new SocketException("No route to " + address.getUriHost()
|
||||
+ "; exhausted inet socket addresses: " + inetSocketAddresses);
|
||||
}
|
||||
|
||||
InetSocketAddress result = inetSocketAddresses.get(nextInetSocketAddressIndex++);
|
||||
resetConnectionConfigurations();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets {@link #nextTlsConfiguration} to the first option. For routes that
|
||||
* don't use TLS, this returns the fallback parameters so that there are no
|
||||
* unnecessary retries.
|
||||
*/
|
||||
private void resetNextTlsConfiguration() {
|
||||
nextTlsConfigurationIndex = address.getSslSocketFactory() != null
|
||||
? 0
|
||||
: TLS_CONFIGURATIONS.size() - 1;
|
||||
/** Prepares the connection configurations to attempt. */
|
||||
private void resetConnectionConfigurations() {
|
||||
connectionConfigurations = new ArrayList<>();
|
||||
for (ConnectionConfiguration configuration : address.getConnectionConfigurations()) {
|
||||
if (request.isHttps() == configuration.isTls()) {
|
||||
connectionConfigurations.add(configuration);
|
||||
}
|
||||
}
|
||||
nextConfigurationIndex = 0;
|
||||
}
|
||||
|
||||
/** Returns true if there's another TLS configuration to try. */
|
||||
private boolean hasNextTlsConfiguration() {
|
||||
return nextTlsConfigurationIndex < TLS_CONFIGURATIONS.size();
|
||||
/** Returns true if there's another connection configuration to try. */
|
||||
private boolean hasNextConnectionConfiguration() {
|
||||
return nextConfigurationIndex < connectionConfigurations.size();
|
||||
}
|
||||
|
||||
/** Returns the next TLS configuration to try. */
|
||||
private TlsConfiguration nextTlsConfiguration() {
|
||||
if (!hasNextTlsConfiguration()) throw new IllegalStateException("No next TLS configuration");
|
||||
return TLS_CONFIGURATIONS.get(nextTlsConfigurationIndex++);
|
||||
/** Returns the next connection configuration to try. */
|
||||
private ConnectionConfiguration nextConnectionConfiguration() throws IOException {
|
||||
if (!hasNextConnectionConfiguration()) {
|
||||
throw new SocketException("No route to " + address.getUriHost()
|
||||
+ "; exhausted connection configurations: " + connectionConfigurations);
|
||||
}
|
||||
return connectionConfigurations.get(nextConfigurationIndex++);
|
||||
}
|
||||
|
||||
/** Returns true if there is another postponed route to try. */
|
||||
|
||||
Reference in New Issue
Block a user