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