RouteSelectorTest.java revision 797fdc6cc6bf16372e9464f189b535148f944ce9
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 */ 16package com.squareup.okhttp.internal.http; 17 18import com.squareup.okhttp.Address; 19import com.squareup.okhttp.Connection; 20import com.squareup.okhttp.ConnectionPool; 21import com.squareup.okhttp.HostResolver; 22import com.squareup.okhttp.OkAuthenticator; 23import com.squareup.okhttp.Protocol; 24import com.squareup.okhttp.RouteDatabase; 25import com.squareup.okhttp.internal.SslContextBuilder; 26import java.io.IOException; 27import java.net.InetAddress; 28import java.net.InetSocketAddress; 29import java.net.Proxy; 30import java.net.ProxySelector; 31import java.net.SocketAddress; 32import java.net.URI; 33import java.net.UnknownHostException; 34import java.util.ArrayList; 35import java.util.Arrays; 36import java.util.List; 37import java.util.NoSuchElementException; 38import javax.net.SocketFactory; 39import javax.net.ssl.HostnameVerifier; 40import javax.net.ssl.SSLContext; 41import javax.net.ssl.SSLSocketFactory; 42import org.junit.Test; 43 44import static java.net.Proxy.NO_PROXY; 45import static org.junit.Assert.assertEquals; 46import static org.junit.Assert.assertFalse; 47import static org.junit.Assert.assertTrue; 48import static org.junit.Assert.fail; 49 50public final class RouteSelectorTest { 51 private static final int proxyAPort = 1001; 52 private static final String proxyAHost = "proxyA"; 53 private static final Proxy proxyA = 54 new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAHost, proxyAPort)); 55 private static final int proxyBPort = 1002; 56 private static final String proxyBHost = "proxyB"; 57 private static final Proxy proxyB = 58 new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyBHost, proxyBPort)); 59 private static final URI uri; 60 private static final String uriHost = "hostA"; 61 private static final int uriPort = 80; 62 63 private static final SocketFactory socketFactory; 64 private static final SSLContext sslContext = SslContextBuilder.localhost(); 65 private static final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); 66 private static final HostnameVerifier hostnameVerifier; 67 private static final ConnectionPool pool; 68 69 static { 70 try { 71 uri = new URI("http://" + uriHost + ":" + uriPort + "/path"); 72 socketFactory = SocketFactory.getDefault(); 73 pool = ConnectionPool.getDefault(); 74 hostnameVerifier = HttpsURLConnectionImpl.getDefaultHostnameVerifier(); 75 } catch (Exception e) { 76 throw new AssertionError(e); 77 } 78 } 79 80 private final OkAuthenticator authenticator = HttpAuthenticator.SYSTEM_DEFAULT; 81 private final List<Protocol> protocols = Arrays.asList(Protocol.HTTP_11); 82 private final FakeDns dns = new FakeDns(); 83 private final FakeProxySelector proxySelector = new FakeProxySelector(); 84 85 @Test public void singleRoute() throws Exception { 86 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 87 protocols); 88 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 89 new RouteDatabase()); 90 91 assertTrue(routeSelector.hasNext()); 92 dns.inetAddresses = makeFakeAddresses(255, 1); 93 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 94 dns.assertRequests(uriHost); 95 96 assertFalse(routeSelector.hasNext()); 97 try { 98 routeSelector.next("GET"); 99 fail(); 100 } catch (NoSuchElementException expected) { 101 } 102 } 103 104 @Test public void singleRouteReturnsFailedRoute() throws Exception { 105 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 106 protocols); 107 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 108 new RouteDatabase()); 109 110 assertTrue(routeSelector.hasNext()); 111 dns.inetAddresses = makeFakeAddresses(255, 1); 112 Connection connection = routeSelector.next("GET"); 113 RouteDatabase routeDatabase = new RouteDatabase(); 114 routeDatabase.failed(connection.getRoute()); 115 routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, routeDatabase); 116 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 117 assertFalse(routeSelector.hasNext()); 118 try { 119 routeSelector.next("GET"); 120 fail(); 121 } catch (NoSuchElementException expected) { 122 } 123 } 124 125 @Test public void explicitProxyTriesThatProxiesAddressesOnly() throws Exception { 126 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, 127 proxyA, protocols); 128 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 129 new RouteDatabase()); 130 131 assertTrue(routeSelector.hasNext()); 132 dns.inetAddresses = makeFakeAddresses(255, 2); 133 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort); 134 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort); 135 136 assertFalse(routeSelector.hasNext()); 137 dns.assertRequests(proxyAHost); 138 proxySelector.assertRequests(); // No proxy selector requests! 139 } 140 141 @Test public void explicitDirectProxy() throws Exception { 142 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, 143 NO_PROXY, protocols); 144 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 145 new RouteDatabase()); 146 147 assertTrue(routeSelector.hasNext()); 148 dns.inetAddresses = makeFakeAddresses(255, 2); 149 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 150 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort); 151 152 assertFalse(routeSelector.hasNext()); 153 dns.assertRequests(uri.getHost()); 154 proxySelector.assertRequests(); // No proxy selector requests! 155 } 156 157 @Test public void proxySelectorReturnsNull() throws Exception { 158 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 159 protocols); 160 161 proxySelector.proxies = null; 162 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 163 new RouteDatabase()); 164 proxySelector.assertRequests(uri); 165 166 assertTrue(routeSelector.hasNext()); 167 dns.inetAddresses = makeFakeAddresses(255, 1); 168 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 169 dns.assertRequests(uriHost); 170 171 assertFalse(routeSelector.hasNext()); 172 } 173 174 @Test public void proxySelectorReturnsNoProxies() throws Exception { 175 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 176 protocols); 177 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 178 new RouteDatabase()); 179 180 assertTrue(routeSelector.hasNext()); 181 dns.inetAddresses = makeFakeAddresses(255, 2); 182 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 183 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort); 184 185 assertFalse(routeSelector.hasNext()); 186 dns.assertRequests(uri.getHost()); 187 proxySelector.assertRequests(uri); 188 } 189 190 @Test public void proxySelectorReturnsMultipleProxies() throws Exception { 191 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 192 protocols); 193 194 proxySelector.proxies.add(proxyA); 195 proxySelector.proxies.add(proxyB); 196 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 197 new RouteDatabase()); 198 proxySelector.assertRequests(uri); 199 200 // First try the IP addresses of the first proxy, in sequence. 201 assertTrue(routeSelector.hasNext()); 202 dns.inetAddresses = makeFakeAddresses(255, 2); 203 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort); 204 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort); 205 dns.assertRequests(proxyAHost); 206 207 // Next try the IP address of the second proxy. 208 assertTrue(routeSelector.hasNext()); 209 dns.inetAddresses = makeFakeAddresses(254, 1); 210 assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort); 211 dns.assertRequests(proxyBHost); 212 213 // Finally try the only IP address of the origin server. 214 assertTrue(routeSelector.hasNext()); 215 dns.inetAddresses = makeFakeAddresses(253, 1); 216 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 217 dns.assertRequests(uriHost); 218 219 assertFalse(routeSelector.hasNext()); 220 } 221 222 @Test public void proxySelectorDirectConnectionsAreSkipped() throws Exception { 223 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 224 protocols); 225 226 proxySelector.proxies.add(NO_PROXY); 227 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 228 new RouteDatabase()); 229 proxySelector.assertRequests(uri); 230 231 // Only the origin server will be attempted. 232 assertTrue(routeSelector.hasNext()); 233 dns.inetAddresses = makeFakeAddresses(255, 1); 234 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 235 dns.assertRequests(uriHost); 236 237 assertFalse(routeSelector.hasNext()); 238 } 239 240 @Test public void proxyDnsFailureContinuesToNextProxy() throws Exception { 241 Address address = new Address(uriHost, uriPort, socketFactory, null, null, authenticator, null, 242 protocols); 243 244 proxySelector.proxies.add(proxyA); 245 proxySelector.proxies.add(proxyB); 246 proxySelector.proxies.add(proxyA); 247 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 248 new RouteDatabase()); 249 proxySelector.assertRequests(uri); 250 251 assertTrue(routeSelector.hasNext()); 252 dns.inetAddresses = makeFakeAddresses(255, 1); 253 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort); 254 dns.assertRequests(proxyAHost); 255 256 assertTrue(routeSelector.hasNext()); 257 dns.inetAddresses = null; 258 try { 259 routeSelector.next("GET"); 260 fail(); 261 } catch (UnknownHostException expected) { 262 } 263 dns.assertRequests(proxyBHost); 264 265 assertTrue(routeSelector.hasNext()); 266 dns.inetAddresses = makeFakeAddresses(255, 1); 267 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort); 268 dns.assertRequests(proxyAHost); 269 270 assertTrue(routeSelector.hasNext()); 271 dns.inetAddresses = makeFakeAddresses(254, 1); 272 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 273 dns.assertRequests(uriHost); 274 275 assertFalse(routeSelector.hasNext()); 276 } 277 278 @Test public void multipleProxiesMultipleInetAddressesMultipleTlsModes() throws Exception { 279 Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory, 280 hostnameVerifier, authenticator, null, protocols); 281 proxySelector.proxies.add(proxyA); 282 proxySelector.proxies.add(proxyB); 283 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 284 new RouteDatabase()); 285 286 // Proxy A 287 dns.inetAddresses = makeFakeAddresses(255, 2); 288 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort); 289 dns.assertRequests(proxyAHost); 290 assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort); 291 292 // Proxy B 293 dns.inetAddresses = makeFakeAddresses(254, 2); 294 assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort); 295 dns.assertRequests(proxyBHost); 296 assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[1], proxyBPort); 297 298 // Origin 299 dns.inetAddresses = makeFakeAddresses(253, 2); 300 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort); 301 dns.assertRequests(uriHost); 302 assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort); 303 304 assertFalse(routeSelector.hasNext()); 305 } 306 307 @Test public void failedRoutesAreLast() throws Exception { 308 Address address = new Address(uriHost, uriPort, socketFactory, sslSocketFactory, 309 hostnameVerifier, authenticator, Proxy.NO_PROXY, protocols); 310 311 RouteDatabase routeDatabase = new RouteDatabase(); 312 RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, 313 routeDatabase); 314 dns.inetAddresses = makeFakeAddresses(255, 2); 315 316 // Extract the regular sequence of routes from selector. 317 List<Connection> regularRoutes = new ArrayList<Connection>(); 318 while (routeSelector.hasNext()) { 319 regularRoutes.add(routeSelector.next("GET")); 320 } 321 322 // Check that we do indeed have more than one route. 323 assertTrue(regularRoutes.size() > 1); 324 // Add first regular route as failed. 325 routeDatabase.failed(regularRoutes.get(0).getRoute()); 326 // Reset selector 327 routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, routeDatabase); 328 329 List<Connection> routesWithFailedRoute = new ArrayList<Connection>(); 330 while (routeSelector.hasNext()) { 331 routesWithFailedRoute.add(routeSelector.next("GET")); 332 } 333 334 assertEquals(regularRoutes.get(0).getRoute(), 335 routesWithFailedRoute.get(routesWithFailedRoute.size() - 1).getRoute()); 336 assertEquals(regularRoutes.size(), routesWithFailedRoute.size()); 337 } 338 339 @Test public void getHostString() throws Exception { 340 // Name proxy specification. 341 InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("host", 1234); 342 assertEquals("host", RouteSelector.getHostString(socketAddress)); 343 socketAddress = InetSocketAddress.createUnresolved("127.0.0.1", 1234); 344 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 345 346 // InetAddress proxy specification. 347 socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 1234); 348 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 349 socketAddress = new InetSocketAddress( 350 InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }), 1234); 351 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 352 socketAddress = new InetSocketAddress( 353 InetAddress.getByAddress("foobar", new byte[] { 127, 0, 0, 1 }), 1234); 354 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 355 } 356 357 private void assertConnection(Connection connection, Address address, Proxy proxy, 358 InetAddress socketAddress, int socketPort) { 359 assertEquals(address, connection.getRoute().getAddress()); 360 assertEquals(proxy, connection.getRoute().getProxy()); 361 assertEquals(socketAddress, connection.getRoute().getSocketAddress().getAddress()); 362 assertEquals(socketPort, connection.getRoute().getSocketAddress().getPort()); 363 } 364 365 private static InetAddress[] makeFakeAddresses(int prefix, int count) { 366 try { 367 InetAddress[] result = new InetAddress[count]; 368 for (int i = 0; i < count; i++) { 369 result[i] = 370 InetAddress.getByAddress(new byte[] { (byte) prefix, (byte) 0, (byte) 0, (byte) i }); 371 } 372 return result; 373 } catch (UnknownHostException e) { 374 throw new AssertionError(); 375 } 376 } 377 378 private static class FakeDns implements HostResolver { 379 List<String> requestedHosts = new ArrayList<String>(); 380 InetAddress[] inetAddresses; 381 382 @Override public InetAddress[] getAllByName(String host) throws UnknownHostException { 383 requestedHosts.add(host); 384 if (inetAddresses == null) throw new UnknownHostException(); 385 return inetAddresses; 386 } 387 388 public void assertRequests(String... expectedHosts) { 389 assertEquals(Arrays.asList(expectedHosts), requestedHosts); 390 requestedHosts.clear(); 391 } 392 } 393 394 private static class FakeProxySelector extends ProxySelector { 395 List<URI> requestedUris = new ArrayList<URI>(); 396 List<Proxy> proxies = new ArrayList<Proxy>(); 397 List<String> failures = new ArrayList<String>(); 398 399 @Override public List<Proxy> select(URI uri) { 400 requestedUris.add(uri); 401 return proxies; 402 } 403 404 public void assertRequests(URI... expectedUris) { 405 assertEquals(Arrays.asList(expectedUris), requestedUris); 406 requestedUris.clear(); 407 } 408 409 @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { 410 InetSocketAddress socketAddress = (InetSocketAddress) sa; 411 failures.add( 412 String.format("%s %s:%d %s", uri, socketAddress.getHostName(), socketAddress.getPort(), 413 ioe.getMessage())); 414 } 415 } 416} 417