Home | History | Annotate | Download | only in http
      1 /*
      2  * Copyright (C) 2012 Square, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.squareup.okhttp.internal.http;
     17 
     18 import com.squareup.okhttp.Address;
     19 import com.squareup.okhttp.Connection;
     20 import com.squareup.okhttp.ConnectionPool;
     21 import com.squareup.okhttp.HostResolver;
     22 import com.squareup.okhttp.OkAuthenticator;
     23 import com.squareup.okhttp.Protocol;
     24 import com.squareup.okhttp.RouteDatabase;
     25 import com.squareup.okhttp.internal.SslContextBuilder;
     26 import java.io.IOException;
     27 import java.net.InetAddress;
     28 import java.net.InetSocketAddress;
     29 import java.net.Proxy;
     30 import java.net.ProxySelector;
     31 import java.net.SocketAddress;
     32 import java.net.URI;
     33 import java.net.UnknownHostException;
     34 import java.util.ArrayList;
     35 import java.util.Arrays;
     36 import java.util.List;
     37 import java.util.NoSuchElementException;
     38 import javax.net.SocketFactory;
     39 import javax.net.ssl.HostnameVerifier;
     40 import javax.net.ssl.SSLContext;
     41 import javax.net.ssl.SSLHandshakeException;
     42 import javax.net.ssl.SSLSocketFactory;
     43 import org.junit.Test;
     44 
     45 import static java.net.Proxy.NO_PROXY;
     46 import static org.junit.Assert.assertEquals;
     47 import static org.junit.Assert.assertFalse;
     48 import static org.junit.Assert.assertTrue;
     49 import static org.junit.Assert.fail;
     50 
     51 public final class RouteSelectorTest {
     52   private static final int proxyAPort = 1001;
     53   private static final String proxyAHost = "proxyA";
     54   private static final Proxy proxyA =
     55       new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAHost, proxyAPort));
     56   private static final int proxyBPort = 1002;
     57   private static final String proxyBHost = "proxyB";
     58   private static final Proxy proxyB =
     59       new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyBHost, proxyBPort));
     60   private static final URI uri;
     61   private static final String uriHost = "hostA";
     62   private static final int uriPort = 80;
     63 
     64   private static final SocketFactory socketFactory;
     65   private static final SSLContext sslContext = SslContextBuilder.localhost();
     66   private static final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
     67   private static final HostnameVerifier hostnameVerifier;
     68   private static final ConnectionPool pool;
     69 
     70   static {
     71     try {
     72       uri = new URI("http://" + uriHost + ":" + uriPort + "/path");
     73       socketFactory = SocketFactory.getDefault();
     74       pool = ConnectionPool.getDefault();
     75       hostnameVerifier = HttpsURLConnectionImpl.getDefaultHostnameVerifier();
     76     } catch (Exception e) {
     77       throw new AssertionError(e);
     78     }
     79   }
     80 
     81   private final OkAuthenticator authenticator = HttpAuthenticator.SYSTEM_DEFAULT;
     82   private final List<Protocol> protocols = Arrays.asList(Protocol.HTTP_11);
     83   private final FakeDns dns = new FakeDns();
     84   private final FakeProxySelector proxySelector = new FakeProxySelector();
     85 
     86   @Test public void singleRoute() throws Exception {
     87     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
     88         protocols);
     89     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
     90         new RouteDatabase());
     91 
     92     assertTrue(routeSelector.hasNext());
     93     dns.inetAddresses = makeFakeAddresses(255, 1);
     94     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
     95         false);
     96     dns.assertRequests(uriHost);
     97 
     98     assertFalse(routeSelector.hasNext());
     99     try {
    100       routeSelector.next("GET");
    101       fail();
    102     } catch (NoSuchElementException expected) {
    103     }
    104   }
    105 
    106   @Test public void singleRouteReturnsFailedRoute() throws Exception {
    107     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
    108         protocols);
    109     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    110         new RouteDatabase());
    111 
    112     assertTrue(routeSelector.hasNext());
    113     dns.inetAddresses = makeFakeAddresses(255, 1);
    114     Connection connection = routeSelector.next("GET");
    115     RouteDatabase routeDatabase = new RouteDatabase();
    116     routeDatabase.failed(connection.getRoute());
    117     routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, routeDatabase);
    118     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    119         false);
    120     assertFalse(routeSelector.hasNext());
    121     try {
    122       routeSelector.next("GET");
    123       fail();
    124     } catch (NoSuchElementException expected) {
    125     }
    126   }
    127 
    128   @Test public void explicitProxyTriesThatProxiesAddressesOnly() throws Exception {
    129     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator,
    130         proxyA, protocols);
    131     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    132         new RouteDatabase());
    133 
    134     assertTrue(routeSelector.hasNext());
    135     dns.inetAddresses = makeFakeAddresses(255, 2);
    136     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
    137         false);
    138     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
    139         false);
    140 
    141     assertFalse(routeSelector.hasNext());
    142     dns.assertRequests(proxyAHost);
    143     proxySelector.assertRequests(); // No proxy selector requests!
    144   }
    145 
    146   @Test public void explicitDirectProxy() throws Exception {
    147     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator,
    148         NO_PROXY, protocols);
    149     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    150         new RouteDatabase());
    151 
    152     assertTrue(routeSelector.hasNext());
    153     dns.inetAddresses = makeFakeAddresses(255, 2);
    154     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    155         false);
    156     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
    157         false);
    158 
    159     assertFalse(routeSelector.hasNext());
    160     dns.assertRequests(uri.getHost());
    161     proxySelector.assertRequests(); // No proxy selector requests!
    162   }
    163 
    164   @Test public void proxySelectorReturnsNull() throws Exception {
    165     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
    166         protocols);
    167 
    168     proxySelector.proxies = null;
    169     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    170         new RouteDatabase());
    171     proxySelector.assertRequests(uri);
    172 
    173     assertTrue(routeSelector.hasNext());
    174     dns.inetAddresses = makeFakeAddresses(255, 1);
    175     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    176         false);
    177     dns.assertRequests(uriHost);
    178 
    179     assertFalse(routeSelector.hasNext());
    180   }
    181 
    182   @Test public void proxySelectorReturnsNoProxies() throws Exception {
    183     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
    184         protocols);
    185     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    186         new RouteDatabase());
    187 
    188     assertTrue(routeSelector.hasNext());
    189     dns.inetAddresses = makeFakeAddresses(255, 2);
    190     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    191         false);
    192     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
    193         false);
    194 
    195     assertFalse(routeSelector.hasNext());
    196     dns.assertRequests(uri.getHost());
    197     proxySelector.assertRequests(uri);
    198   }
    199 
    200   @Test public void proxySelectorReturnsMultipleProxies() throws Exception {
    201     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
    202         protocols);
    203 
    204     proxySelector.proxies.add(proxyA);
    205     proxySelector.proxies.add(proxyB);
    206     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    207         new RouteDatabase());
    208     proxySelector.assertRequests(uri);
    209 
    210     // First try the IP addresses of the first proxy, in sequence.
    211     assertTrue(routeSelector.hasNext());
    212     dns.inetAddresses = makeFakeAddresses(255, 2);
    213     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
    214         false);
    215     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
    216         false);
    217     dns.assertRequests(proxyAHost);
    218 
    219     // Next try the IP address of the second proxy.
    220     assertTrue(routeSelector.hasNext());
    221     dns.inetAddresses = makeFakeAddresses(254, 1);
    222     assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort,
    223         false);
    224     dns.assertRequests(proxyBHost);
    225 
    226     // Finally try the only IP address of the origin server.
    227     assertTrue(routeSelector.hasNext());
    228     dns.inetAddresses = makeFakeAddresses(253, 1);
    229     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    230         false);
    231     dns.assertRequests(uriHost);
    232 
    233     assertFalse(routeSelector.hasNext());
    234   }
    235 
    236   @Test public void proxySelectorDirectConnectionsAreSkipped() throws Exception {
    237     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
    238         protocols);
    239 
    240     proxySelector.proxies.add(NO_PROXY);
    241     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    242         new RouteDatabase());
    243     proxySelector.assertRequests(uri);
    244 
    245     // Only the origin server will be attempted.
    246     assertTrue(routeSelector.hasNext());
    247     dns.inetAddresses = makeFakeAddresses(255, 1);
    248     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    249         false);
    250     dns.assertRequests(uriHost);
    251 
    252     assertFalse(routeSelector.hasNext());
    253   }
    254 
    255   @Test public void proxyDnsFailureContinuesToNextProxy() throws Exception {
    256     Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null,
    257         protocols);
    258 
    259     proxySelector.proxies.add(proxyA);
    260     proxySelector.proxies.add(proxyB);
    261     proxySelector.proxies.add(proxyA);
    262     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    263         new RouteDatabase());
    264     proxySelector.assertRequests(uri);
    265 
    266     assertTrue(routeSelector.hasNext());
    267     dns.inetAddresses = makeFakeAddresses(255, 1);
    268     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
    269         false);
    270     dns.assertRequests(proxyAHost);
    271 
    272     assertTrue(routeSelector.hasNext());
    273     dns.inetAddresses = null;
    274     try {
    275       routeSelector.next("GET");
    276       fail();
    277     } catch (UnknownHostException expected) {
    278     }
    279     dns.assertRequests(proxyBHost);
    280 
    281     assertTrue(routeSelector.hasNext());
    282     dns.inetAddresses = makeFakeAddresses(255, 1);
    283     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
    284         false);
    285     dns.assertRequests(proxyAHost);
    286 
    287     assertTrue(routeSelector.hasNext());
    288     dns.inetAddresses = makeFakeAddresses(254, 1);
    289     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    290         false);
    291     dns.assertRequests(uriHost);
    292 
    293     assertFalse(routeSelector.hasNext());
    294   }
    295 
    296   // https://github.com/square/okhttp/issues/442
    297   @Test public void nonSslErrorAddsAllTlsModesToFailedRoute() throws Exception {
    298     Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory,
    299         hostnameVerifier, authenticator, Proxy.NO_PROXY, protocols);
    300     RouteDatabase routeDatabase = new RouteDatabase();
    301     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    302         routeDatabase);
    303 
    304     dns.inetAddresses = makeFakeAddresses(255, 1);
    305     Connection connection = routeSelector.next("GET");
    306     routeSelector.connectFailed(connection, new IOException("Non SSL exception"));
    307     assertTrue(routeDatabase.failedRoutesCount() == 2);
    308     assertFalse(routeSelector.hasNext());
    309   }
    310 
    311   @Test public void sslErrorAddsOnlyFailedTlsModeToFailedRoute() throws Exception {
    312     Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory,
    313         hostnameVerifier, authenticator, Proxy.NO_PROXY, protocols);
    314     RouteDatabase routeDatabase = new RouteDatabase();
    315     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    316         routeDatabase);
    317 
    318     dns.inetAddresses = makeFakeAddresses(255, 1);
    319     Connection connection = routeSelector.next("GET");
    320     routeSelector.connectFailed(connection, new SSLHandshakeException("SSL exception"));
    321     assertTrue(routeDatabase.failedRoutesCount() == 1);
    322     assertTrue(routeSelector.hasNext());
    323   }
    324 
    325   @Test public void multipleProxiesMultipleInetAddressesMultipleTlsModes() throws Exception {
    326     Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory,
    327         hostnameVerifier, authenticator, null, protocols);
    328     proxySelector.proxies.add(proxyA);
    329     proxySelector.proxies.add(proxyB);
    330     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    331         new RouteDatabase());
    332 
    333     // Proxy A
    334     dns.inetAddresses = makeFakeAddresses(255, 2);
    335     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
    336         true);
    337     dns.assertRequests(proxyAHost);
    338     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
    339         false);
    340     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
    341         true);
    342     assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
    343         false);
    344 
    345     // Proxy B
    346     dns.inetAddresses = makeFakeAddresses(254, 2);
    347     assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort,
    348         true);
    349     dns.assertRequests(proxyBHost);
    350     assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort,
    351         false);
    352     assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[1], proxyBPort,
    353         true);
    354     assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[1], proxyBPort,
    355         false);
    356 
    357     // Origin
    358     dns.inetAddresses = makeFakeAddresses(253, 2);
    359     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    360         true);
    361     dns.assertRequests(uriHost);
    362     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
    363         false);
    364     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
    365         true);
    366     assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
    367         false);
    368 
    369     assertFalse(routeSelector.hasNext());
    370   }
    371 
    372   @Test public void failedRoutesAreLast() throws Exception {
    373     Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory,
    374         hostnameVerifier, authenticator, Proxy.NO_PROXY, protocols);
    375 
    376     RouteDatabase routeDatabase = new RouteDatabase();
    377     RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
    378         routeDatabase);
    379     dns.inetAddresses = makeFakeAddresses(255, 1);
    380 
    381     // Extract the regular sequence of routes from selector.
    382     List<Connection> regularRoutes = new ArrayList<Connection>();
    383     while (routeSelector.hasNext()) {
    384       regularRoutes.add(routeSelector.next("GET"));
    385     }
    386 
    387     // Check that we do indeed have more than one route.
    388     assertTrue(regularRoutes.size() > 1);
    389     // Add first regular route as failed.
    390     routeDatabase.failed(regularRoutes.get(0).getRoute());
    391     // Reset selector
    392     routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, routeDatabase);
    393 
    394     List<Connection> routesWithFailedRoute = new ArrayList<Connection>();
    395     while (routeSelector.hasNext()) {
    396       routesWithFailedRoute.add(routeSelector.next("GET"));
    397     }
    398 
    399     assertEquals(regularRoutes.get(0).getRoute(),
    400         routesWithFailedRoute.get(routesWithFailedRoute.size() - 1).getRoute());
    401     assertEquals(regularRoutes.size(), routesWithFailedRoute.size());
    402   }
    403 
    404   @Test public void getHostString() throws Exception {
    405     // Name proxy specification.
    406     InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("host", 1234);
    407     assertEquals("host", RouteSelector.getHostString(socketAddress));
    408     socketAddress = InetSocketAddress.createUnresolved("127.0.0.1", 1234);
    409     assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress));
    410 
    411     // InetAddress proxy specification.
    412     socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 1234);
    413     assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress));
    414     socketAddress = new InetSocketAddress(
    415         InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }), 1234);
    416     assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress));
    417     socketAddress = new InetSocketAddress(
    418         InetAddress.getByAddress("foobar", new byte[] { 127, 0, 0, 1 }), 1234);
    419     assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress));
    420   }
    421 
    422   private void assertConnection(Connection connection, Address address, Proxy proxy,
    423       InetAddress socketAddress, int socketPort, boolean modernTls) {
    424     assertEquals(address, connection.getRoute().getAddress());
    425     assertEquals(proxy, connection.getRoute().getProxy());
    426     assertEquals(socketAddress, connection.getRoute().getSocketAddress().getAddress());
    427     assertEquals(socketPort, connection.getRoute().getSocketAddress().getPort());
    428     assertEquals(modernTls, connection.getRoute().isModernTls());
    429   }
    430 
    431   private static InetAddress[] makeFakeAddresses(int prefix, int count) {
    432     try {
    433       InetAddress[] result = new InetAddress[count];
    434       for (int i = 0; i < count; i++) {
    435         result[i] =
    436             InetAddress.getByAddress(new byte[] { (byte) prefix, (byte) 0, (byte) 0, (byte) i });
    437       }
    438       return result;
    439     } catch (UnknownHostException e) {
    440       throw new AssertionError();
    441     }
    442   }
    443 
    444   private static class FakeDns implements HostResolver {
    445     List<String> requestedHosts = new ArrayList<String>();
    446     InetAddress[] inetAddresses;
    447 
    448     @Override public InetAddress[] getAllByName(String host) throws UnknownHostException {
    449       requestedHosts.add(host);
    450       if (inetAddresses == null) throw new UnknownHostException();
    451       return inetAddresses;
    452     }
    453 
    454     public void assertRequests(String... expectedHosts) {
    455       assertEquals(Arrays.asList(expectedHosts), requestedHosts);
    456       requestedHosts.clear();
    457     }
    458   }
    459 
    460   private static class FakeProxySelector extends ProxySelector {
    461     List<URI> requestedUris = new ArrayList<URI>();
    462     List<Proxy> proxies = new ArrayList<Proxy>();
    463     List<String> failures = new ArrayList<String>();
    464 
    465     @Override public List<Proxy> select(URI uri) {
    466       requestedUris.add(uri);
    467       return proxies;
    468     }
    469 
    470     public void assertRequests(URI... expectedUris) {
    471       assertEquals(Arrays.asList(expectedUris), requestedUris);
    472       requestedUris.clear();
    473     }
    474 
    475     @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
    476       InetSocketAddress socketAddress = (InetSocketAddress) sa;
    477       failures.add(
    478           String.format("%s %s:%d %s", uri, socketAddress.getHostName(), socketAddress.getPort(),
    479               ioe.getMessage()));
    480     }
    481   }
    482 }
    483