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.Authenticator; 20 import com.squareup.okhttp.ConnectionSpec; 21 import com.squareup.okhttp.Protocol; 22 import com.squareup.okhttp.Route; 23 import com.squareup.okhttp.internal.RouteDatabase; 24 import com.squareup.okhttp.internal.SslContextBuilder; 25 import com.squareup.okhttp.internal.Util; 26 import java.net.InetAddress; 27 import java.net.InetSocketAddress; 28 import java.net.Proxy; 29 import java.net.UnknownHostException; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import java.util.NoSuchElementException; 34 import javax.net.SocketFactory; 35 import javax.net.ssl.HostnameVerifier; 36 import javax.net.ssl.HttpsURLConnection; 37 import javax.net.ssl.SSLContext; 38 import javax.net.ssl.SSLSocketFactory; 39 import org.junit.Before; 40 import org.junit.Test; 41 42 import static java.net.Proxy.NO_PROXY; 43 import static org.junit.Assert.assertEquals; 44 import static org.junit.Assert.assertFalse; 45 import static org.junit.Assert.assertTrue; 46 import static org.junit.Assert.fail; 47 48 public final class RouteSelectorTest { 49 public final List<ConnectionSpec> connectionSpecs = Util.immutableList( 50 ConnectionSpec.MODERN_TLS, 51 ConnectionSpec.COMPATIBLE_TLS, 52 ConnectionSpec.CLEARTEXT); 53 54 private static final int proxyAPort = 1001; 55 private static final String proxyAHost = "proxya"; 56 private static final Proxy proxyA = 57 new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(proxyAHost, proxyAPort)); 58 private static final int proxyBPort = 1002; 59 private static final String proxyBHost = "proxyb"; 60 private static final Proxy proxyB = 61 new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(proxyBHost, proxyBPort)); 62 private String uriHost = "hosta"; 63 private int uriPort = 1003; 64 65 private SocketFactory socketFactory; 66 private final SSLContext sslContext = SslContextBuilder.localhost(); 67 private final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); 68 private HostnameVerifier hostnameVerifier; 69 70 private final Authenticator authenticator = AuthenticatorAdapter.INSTANCE; 71 private final List<Protocol> protocols = Arrays.asList(Protocol.HTTP_1_1); 72 private final FakeDns dns = new FakeDns(); 73 private final RecordingProxySelector proxySelector = new RecordingProxySelector(); 74 private RouteDatabase routeDatabase = new RouteDatabase(); 75 setUp()76 @Before public void setUp() throws Exception { 77 socketFactory = SocketFactory.getDefault(); 78 hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); 79 } 80 singleRoute()81 @Test public void singleRoute() throws Exception { 82 Address address = httpAddress(); 83 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 84 85 assertTrue(routeSelector.hasNext()); 86 dns.addresses(makeFakeAddresses(255, 1)); 87 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 88 dns.assertRequests(uriHost); 89 90 assertFalse(routeSelector.hasNext()); 91 try { 92 routeSelector.next(); 93 fail(); 94 } catch (NoSuchElementException expected) { 95 } 96 } 97 singleRouteReturnsFailedRoute()98 @Test public void singleRouteReturnsFailedRoute() throws Exception { 99 Address address = httpAddress(); 100 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 101 102 assertTrue(routeSelector.hasNext()); 103 dns.addresses(makeFakeAddresses(255, 1)); 104 Route route = routeSelector.next(); 105 routeDatabase.failed(route); 106 routeSelector = new RouteSelector(address, routeDatabase); 107 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 108 assertFalse(routeSelector.hasNext()); 109 try { 110 routeSelector.next(); 111 fail(); 112 } catch (NoSuchElementException expected) { 113 } 114 } 115 explicitProxyTriesThatProxysAddressesOnly()116 @Test public void explicitProxyTriesThatProxysAddressesOnly() throws Exception { 117 Address address = new Address(uriHost, uriPort, dns, socketFactory, null, null, null, 118 authenticator, proxyA, protocols, connectionSpecs, proxySelector); 119 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 120 121 assertTrue(routeSelector.hasNext()); 122 dns.addresses(makeFakeAddresses(255, 2)); 123 assertRoute(routeSelector.next(), address, proxyA, dns.address(0), proxyAPort); 124 assertRoute(routeSelector.next(), address, proxyA, dns.address(1), proxyAPort); 125 126 assertFalse(routeSelector.hasNext()); 127 dns.assertRequests(proxyAHost); 128 proxySelector.assertRequests(); // No proxy selector requests! 129 } 130 explicitDirectProxy()131 @Test public void explicitDirectProxy() throws Exception { 132 Address address = new Address(uriHost, uriPort, dns, socketFactory, null, null, null, 133 authenticator, NO_PROXY, protocols, connectionSpecs, proxySelector); 134 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 135 136 assertTrue(routeSelector.hasNext()); 137 dns.addresses(makeFakeAddresses(255, 2)); 138 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 139 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(1), uriPort); 140 141 assertFalse(routeSelector.hasNext()); 142 dns.assertRequests(uriHost); 143 proxySelector.assertRequests(); // No proxy selector requests! 144 } 145 proxySelectorReturnsNull()146 @Test public void proxySelectorReturnsNull() throws Exception { 147 Address address = httpAddress(); 148 149 proxySelector.proxies = null; 150 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 151 proxySelector.assertRequests(address.url().uri()); 152 153 assertTrue(routeSelector.hasNext()); 154 dns.addresses(makeFakeAddresses(255, 1)); 155 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 156 dns.assertRequests(uriHost); 157 158 assertFalse(routeSelector.hasNext()); 159 } 160 proxySelectorReturnsNoProxies()161 @Test public void proxySelectorReturnsNoProxies() throws Exception { 162 Address address = httpAddress(); 163 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 164 165 assertTrue(routeSelector.hasNext()); 166 dns.addresses(makeFakeAddresses(255, 2)); 167 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 168 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(1), uriPort); 169 170 assertFalse(routeSelector.hasNext()); 171 dns.assertRequests(uriHost); 172 proxySelector.assertRequests(address.url().uri()); 173 } 174 proxySelectorReturnsMultipleProxies()175 @Test public void proxySelectorReturnsMultipleProxies() throws Exception { 176 Address address = httpAddress(); 177 178 proxySelector.proxies.add(proxyA); 179 proxySelector.proxies.add(proxyB); 180 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 181 proxySelector.assertRequests(address.url().uri()); 182 183 // First try the IP addresses of the first proxy, in sequence. 184 assertTrue(routeSelector.hasNext()); 185 dns.addresses(makeFakeAddresses(255, 2)); 186 assertRoute(routeSelector.next(), address, proxyA, dns.address(0), proxyAPort); 187 assertRoute(routeSelector.next(), address, proxyA, dns.address(1), proxyAPort); 188 dns.assertRequests(proxyAHost); 189 190 // Next try the IP address of the second proxy. 191 assertTrue(routeSelector.hasNext()); 192 dns.addresses(makeFakeAddresses(254, 1)); 193 assertRoute(routeSelector.next(), address, proxyB, dns.address(0), proxyBPort); 194 dns.assertRequests(proxyBHost); 195 196 // Finally try the only IP address of the origin server. 197 assertTrue(routeSelector.hasNext()); 198 dns.addresses(makeFakeAddresses(253, 1)); 199 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 200 dns.assertRequests(uriHost); 201 202 assertFalse(routeSelector.hasNext()); 203 } 204 proxySelectorDirectConnectionsAreSkipped()205 @Test public void proxySelectorDirectConnectionsAreSkipped() throws Exception { 206 Address address = httpAddress(); 207 208 proxySelector.proxies.add(NO_PROXY); 209 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 210 proxySelector.assertRequests(address.url().uri()); 211 212 // Only the origin server will be attempted. 213 assertTrue(routeSelector.hasNext()); 214 dns.addresses(makeFakeAddresses(255, 1)); 215 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 216 dns.assertRequests(uriHost); 217 218 assertFalse(routeSelector.hasNext()); 219 } 220 proxyDnsFailureContinuesToNextProxy()221 @Test public void proxyDnsFailureContinuesToNextProxy() throws Exception { 222 Address address = httpAddress(); 223 224 proxySelector.proxies.add(proxyA); 225 proxySelector.proxies.add(proxyB); 226 proxySelector.proxies.add(proxyA); 227 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 228 proxySelector.assertRequests(address.url().uri()); 229 230 assertTrue(routeSelector.hasNext()); 231 dns.addresses(makeFakeAddresses(255, 1)); 232 assertRoute(routeSelector.next(), address, proxyA, dns.address(0), proxyAPort); 233 dns.assertRequests(proxyAHost); 234 235 assertTrue(routeSelector.hasNext()); 236 dns.unknownHost(); 237 try { 238 routeSelector.next(); 239 fail(); 240 } catch (UnknownHostException expected) { 241 } 242 dns.assertRequests(proxyBHost); 243 244 assertTrue(routeSelector.hasNext()); 245 dns.addresses(makeFakeAddresses(255, 1)); 246 assertRoute(routeSelector.next(), address, proxyA, dns.address(0), proxyAPort); 247 dns.assertRequests(proxyAHost); 248 249 assertTrue(routeSelector.hasNext()); 250 dns.addresses(makeFakeAddresses(254, 1)); 251 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 252 dns.assertRequests(uriHost); 253 254 assertFalse(routeSelector.hasNext()); 255 } 256 multipleProxiesMultipleInetAddressesMultipleConfigurations()257 @Test public void multipleProxiesMultipleInetAddressesMultipleConfigurations() throws Exception { 258 Address address = httpsAddress(); 259 proxySelector.proxies.add(proxyA); 260 proxySelector.proxies.add(proxyB); 261 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 262 263 // Proxy A 264 dns.addresses(makeFakeAddresses(255, 2)); 265 assertRoute(routeSelector.next(), address, proxyA, dns.address(0), proxyAPort); 266 dns.assertRequests(proxyAHost); 267 assertRoute(routeSelector.next(), address, proxyA, dns.address(1), proxyAPort); 268 269 // Proxy B 270 dns.addresses(makeFakeAddresses(254, 2)); 271 assertRoute(routeSelector.next(), address, proxyB, dns.address(0), proxyBPort); 272 dns.assertRequests(proxyBHost); 273 assertRoute(routeSelector.next(), address, proxyB, dns.address(1), proxyBPort); 274 275 // Origin 276 dns.addresses(makeFakeAddresses(253, 2)); 277 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(0), uriPort); 278 dns.assertRequests(uriHost); 279 assertRoute(routeSelector.next(), address, NO_PROXY, dns.address(1), uriPort); 280 281 assertFalse(routeSelector.hasNext()); 282 } 283 failedRoutesAreLast()284 @Test public void failedRoutesAreLast() throws Exception { 285 Address address = httpsAddress(); 286 RouteSelector routeSelector = new RouteSelector(address, routeDatabase); 287 288 final int numberOfAddresses = 2; 289 dns.addresses(makeFakeAddresses(255, numberOfAddresses)); 290 291 // Extract the regular sequence of routes from selector. 292 List<Route> regularRoutes = new ArrayList<>(); 293 while (routeSelector.hasNext()) { 294 regularRoutes.add(routeSelector.next()); 295 } 296 297 // Check that we do indeed have more than one route. 298 assertEquals(numberOfAddresses, regularRoutes.size()); 299 // Add first regular route as failed. 300 routeDatabase.failed(regularRoutes.get(0)); 301 // Reset selector 302 routeSelector = new RouteSelector(address, routeDatabase); 303 304 List<Route> routesWithFailedRoute = new ArrayList<>(); 305 while (routeSelector.hasNext()) { 306 routesWithFailedRoute.add(routeSelector.next()); 307 } 308 309 assertEquals(regularRoutes.get(0), 310 routesWithFailedRoute.get(routesWithFailedRoute.size() - 1)); 311 assertEquals(regularRoutes.size(), routesWithFailedRoute.size()); 312 } 313 getHostString()314 @Test public void getHostString() throws Exception { 315 // Name proxy specification. 316 InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("host", 1234); 317 assertEquals("host", RouteSelector.getHostString(socketAddress)); 318 socketAddress = InetSocketAddress.createUnresolved("127.0.0.1", 1234); 319 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 320 321 // InetAddress proxy specification. 322 socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 1234); 323 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 324 socketAddress = new InetSocketAddress( 325 InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }), 1234); 326 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 327 socketAddress = new InetSocketAddress( 328 InetAddress.getByAddress("foobar", new byte[] { 127, 0, 0, 1 }), 1234); 329 assertEquals("127.0.0.1", RouteSelector.getHostString(socketAddress)); 330 } 331 assertRoute(Route route, Address address, Proxy proxy, InetAddress socketAddress, int socketPort)332 private void assertRoute(Route route, Address address, Proxy proxy, InetAddress socketAddress, 333 int socketPort) { 334 assertEquals(address, route.getAddress()); 335 assertEquals(proxy, route.getProxy()); 336 assertEquals(socketAddress, route.getSocketAddress().getAddress()); 337 assertEquals(socketPort, route.getSocketAddress().getPort()); 338 } 339 340 /** Returns an address that's without an SSL socket factory or hostname verifier. */ httpAddress()341 private Address httpAddress() { 342 return new Address(uriHost, uriPort, dns, socketFactory, null, null, null, authenticator, null, 343 protocols, connectionSpecs, proxySelector); 344 } 345 httpsAddress()346 private Address httpsAddress() { 347 return new Address(uriHost, uriPort, dns, socketFactory, sslSocketFactory, 348 hostnameVerifier, null, authenticator, null, protocols, connectionSpecs, proxySelector); 349 } 350 makeFakeAddresses(int prefix, int count)351 private static List<InetAddress> makeFakeAddresses(int prefix, int count) { 352 try { 353 List<InetAddress> result = new ArrayList<>(); 354 for (int i = 0; i < count; i++) { 355 result.add(InetAddress.getByAddress( 356 new byte[] { (byte) prefix, (byte) 0, (byte) 0, (byte) i })); 357 } 358 return result; 359 } catch (UnknownHostException e) { 360 throw new AssertionError(); 361 } 362 } 363 } 364