• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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