View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.impl.routing;
29  
30  import java.net.InetAddress;
31  
32  import org.apache.hc.client5.http.HttpRoute;
33  import org.apache.hc.client5.http.RouteInfo.LayerType;
34  import org.apache.hc.client5.http.RouteInfo.TunnelType;
35  import org.apache.hc.client5.http.routing.HttpRouteDirector;
36  import org.apache.hc.core5.http.HttpHost;
37  import org.junit.jupiter.api.Assertions;
38  import org.junit.jupiter.api.Test;
39  
40  /**
41   * Tests for {@link BasicRouteDirector}.
42   */
43  public class TestRouteDirector {
44  
45      // a selection of constants for generating routes
46      public final static
47          HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
48      public final static
49          HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
50      // It is not necessary to have extra targets for https.
51      // The 'layered' and 'secure' flags are specified explicitly
52      // for routes, they will not be determined from the scheme.
53  
54      public final static
55          HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80);
56      public final static
57          HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080);
58      public final static
59          HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88);
60  
61      public final static InetAddress LOCAL41;
62      public final static InetAddress LOCAL42;
63      public final static InetAddress LOCAL61;
64      public final static InetAddress LOCAL62;
65  
66      // need static initializer to deal with exceptions
67      static {
68          try {
69              LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 });
70              LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 });
71  
72              LOCAL61 = InetAddress.getByAddress(new byte[]{
73                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
74              });
75              LOCAL62 = InetAddress.getByAddress(new byte[]{
76                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
77              });
78  
79          } catch (final Exception x) {
80              throw new ExceptionInInitializerError(x);
81          }
82      }
83  
84      @Test
85      public void testIllegal() {
86          final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
87          final HttpRoute route = new HttpRoute(TARGET1);
88          Assertions.assertThrows(NullPointerException.class, () ->
89                  rowdy.nextStep(null, route));
90      }
91  
92      @Test
93      public void testDirect() {
94  
95          final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
96          final HttpRoute route1   = new HttpRoute(TARGET1);
97          final HttpRoute route2   = new HttpRoute(TARGET2);
98          final HttpRoute route1p1 = new HttpRoute(TARGET1, null, PROXY1, false);
99  
100         int step = rowdy.nextStep(route1, null);
101         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1");
102 
103         step = rowdy.nextStep(route2, null);
104         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route2");
105 
106         step = rowdy.nextStep(route1, route1);
107         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1 not detected");
108 
109         step = rowdy.nextStep(route2, route2);
110         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route2 not detected");
111 
112         step = rowdy.nextStep(route1, route2);
113         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable target not detected");
114 
115         step = rowdy.nextStep(route1, route1p1);
116         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "invalid proxy not detected");
117     }
118 
119     @Test
120     public void testProxy() {
121 
122         final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
123         final HttpRoute route1p1 = new HttpRoute(TARGET1, null, PROXY1, false);
124         final HttpRoute route1p2 = new HttpRoute(TARGET1, null, PROXY2, false);
125         final HttpRoute route2p1 = new HttpRoute(TARGET2, null, PROXY1, false);
126         final HttpRoute route0   = new HttpRoute(PROXY1);
127         final HttpRoute route1   = new HttpRoute(TARGET1);
128 
129         int step = rowdy.nextStep(route1p1, null);
130         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1p1");
131 
132         step = rowdy.nextStep(route1p2, null);
133         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1p2");
134 
135         step = rowdy.nextStep(route1p1, route1p1);
136         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1p1 not detected");
137 
138         step = rowdy.nextStep(route1p2, route1p2);
139         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1p2 not detected");
140 
141         step = rowdy.nextStep(route2p1, route2p1);
142         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route2p1 not detected");
143 
144         step = rowdy.nextStep(route1p1, route1p2);
145         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route1p2 not detected");
146 
147         step = rowdy.nextStep(route1p1, route2p1);
148         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route2p1 not detected");
149 
150         step = rowdy.nextStep(route1p1, route0);
151         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route0 not detected");
152 
153         step = rowdy.nextStep(route1p1, route1);
154         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route1 not detected");
155     }
156 
157     @Test
158     public void testProxyChain() {
159         final HttpHost[] chainA = { PROXY1 };
160         final HttpHost[] chainB = { PROXY1, PROXY2 };
161         final HttpHost[] chainC = { PROXY2, PROXY1 };
162 
163         final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
164         final HttpRoute route1cA  = new HttpRoute(TARGET1, null, chainA, false,
165                                             TunnelType.PLAIN, LayerType.PLAIN);
166         final HttpRoute route1cB  = new HttpRoute(TARGET1, null, chainB, false,
167                                             TunnelType.PLAIN, LayerType.PLAIN);
168         final HttpRoute route1cC  = new HttpRoute(TARGET1, null, chainC, false,
169                                             TunnelType.PLAIN, LayerType.PLAIN);
170         final HttpRoute route1cD  = new HttpRoute(TARGET1, null, chainC, false,
171                                             TunnelType.PLAIN, LayerType.PLAIN);
172 
173         int step = rowdy.nextStep(route1cA, null);
174         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cA");
175 
176         step = rowdy.nextStep(route1cB, null);
177         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cB");
178 
179         step = rowdy.nextStep(route1cC, null);
180         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cC");
181 
182         step = rowdy.nextStep(route1cD, null);
183         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cD");
184 
185 
186         step = rowdy.nextStep(route1cB, route1cA);
187         Assertions.assertEquals(HttpRouteDirector.TUNNEL_PROXY, step, "wrong step to route 1cB from 1cA");
188 
189         step = rowdy.nextStep(route1cB, route1cB);
190         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route 1cB not detected");
191 
192         step = rowdy.nextStep(route1cB, route1cC);
193         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cB from 1cC not detected");
194 
195         step = rowdy.nextStep(route1cB, route1cD);
196         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cB from 1cD not detected");
197 
198 
199         step = rowdy.nextStep(route1cA, route1cB);
200         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cA from 1cB not detected");
201     }
202 
203     @Test
204     public void testLocalDirect() {
205 
206         final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
207         final HttpRoute route1l41 = new HttpRoute(TARGET1, LOCAL41, false);
208         final HttpRoute route1l42 = new HttpRoute(TARGET1, LOCAL42, false);
209         final HttpRoute route1l61 = new HttpRoute(TARGET1, LOCAL61, false);
210         final HttpRoute route1l00 = new HttpRoute(TARGET1, null, false);
211 
212         int step = rowdy.nextStep(route1l41, null);
213         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l41");
214 
215         step = rowdy.nextStep(route1l42, null);
216         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l42");
217 
218         step = rowdy.nextStep(route1l61, null);
219         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l61");
220 
221         step = rowdy.nextStep(route1l00, null);
222         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l00");
223 
224         step = rowdy.nextStep(route1l41, route1l41);
225         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l41 not detected");
226 
227         step = rowdy.nextStep(route1l42, route1l42);
228         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l42 not detected");
229 
230         step = rowdy.nextStep(route1l61, route1l61);
231         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l61 not detected");
232 
233         step = rowdy.nextStep(route1l00, route1l00);
234         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 not detected");
235 
236 
237         step = rowdy.nextStep(route1l41, route1l42);
238         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l42 not detected");
239 
240         step = rowdy.nextStep(route1l41, route1l61);
241         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l61 not detected");
242 
243         step = rowdy.nextStep(route1l41, route1l00);
244         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l00 not detected");
245 
246 
247         step = rowdy.nextStep(route1l00, route1l41);
248         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l41 not detected");
249 
250         step = rowdy.nextStep(route1l00, route1l42);
251         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l42 not detected");
252 
253         step = rowdy.nextStep(route1l00, route1l61);
254         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l61 not detected");
255     }
256 
257     @Test
258     public void testDirectSecure() {
259 
260         final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
261         final HttpRoute route1u   = new HttpRoute(TARGET1, null, false);
262         final HttpRoute route1s   = new HttpRoute(TARGET1, null, true);
263         final HttpRoute route1p1u = new HttpRoute(TARGET1, null, PROXY1, false);
264         final HttpRoute route1p1s = new HttpRoute(TARGET1, null, PROXY1, true);
265 
266         int step = rowdy.nextStep(route1u, null);
267         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1u");
268 
269         step = rowdy.nextStep(route1s, null);
270         Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1s");
271 
272         // unrequested security is currently not tolerated
273         step = rowdy.nextStep(route1u, route1s);
274         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1u from 1s not detected");
275 
276         // secure layering of direct connections is currently not supported
277         step = rowdy.nextStep(route1s, route1u);
278         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1u not detected");
279 
280 
281 
282         step = rowdy.nextStep(route1s, route1p1u);
283         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1p1u not detected");
284 
285         step = rowdy.nextStep(route1s, route1p1s);
286         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1p1s not detected");
287     }
288 
289     @Test
290     public void testProxyTLS() {
291 
292         final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
293         final HttpRoute route1    = new HttpRoute
294             (TARGET1, null, PROXY1, false,
295              TunnelType.PLAIN, LayerType.PLAIN);
296         final HttpRoute route1t   = new HttpRoute
297             (TARGET1, null, PROXY1, false,
298              TunnelType.TUNNELLED, LayerType.PLAIN);
299         final HttpRoute route1tl  = new HttpRoute
300             (TARGET1, null, PROXY1, false,
301              TunnelType.TUNNELLED, LayerType.LAYERED);
302         final HttpRoute route1s   = new HttpRoute
303             (TARGET1, null, PROXY1, true,
304              TunnelType.PLAIN, LayerType.PLAIN);
305         final HttpRoute route1ts  = new HttpRoute
306             (TARGET1, null, PROXY1, true,
307              TunnelType.TUNNELLED, LayerType.PLAIN);
308         final HttpRoute route1tls = new HttpRoute
309             (TARGET1, null, PROXY1, true,
310              TunnelType.TUNNELLED, LayerType.LAYERED);
311 
312         // we don't consider a route that is layered but not tunnelled
313 
314         int step = rowdy.nextStep(route1, null);
315         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1");
316 
317         step = rowdy.nextStep(route1t, null);
318         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1t");
319 
320         step = rowdy.nextStep(route1tl, null);
321         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1tl");
322 
323         step = rowdy.nextStep(route1s, null);
324         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1s");
325 
326         step = rowdy.nextStep(route1ts, null);
327         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1ts");
328 
329         step = rowdy.nextStep(route1tls, null);
330         Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1tls");
331 
332 
333         step = rowdy.nextStep(route1, route1);
334         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1 not detected");
335 
336         step = rowdy.nextStep(route1t, route1t);
337         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1t not detected");
338 
339         step = rowdy.nextStep(route1tl, route1tl);
340         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1tl not detected");
341 
342         step = rowdy.nextStep(route1s, route1s);
343         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1s not detected");
344 
345         step = rowdy.nextStep(route1ts, route1ts);
346         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1ts not detected");
347 
348         step = rowdy.nextStep(route1tls, route1tls);
349         Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1tls not detected");
350 
351 
352 
353         step = rowdy.nextStep(route1, route1t);
354         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1t not detected");
355 
356         step = rowdy.nextStep(route1, route1tl);
357         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1tl not detected");
358 
359         // unrequested security is currently not tolerated
360         step = rowdy.nextStep(route1, route1s);
361         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1s not detected");
362 
363         step = rowdy.nextStep(route1, route1ts);
364         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1ts not detected");
365 
366         step = rowdy.nextStep(route1, route1tls);
367         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1tls not detected");
368 
369 
370         // securing requires layering
371         step = rowdy.nextStep(route1s, route1);
372         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1s from 1 not detected");
373 
374         // securing requires layering, and multiple layers are not supported
375         step = rowdy.nextStep(route1tls, route1tl);
376         Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1tls from 1tl not detected");
377 
378 
379         // cases where tunnelling to the target is required
380         step = rowdy.nextStep(route1t, route1);
381         Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1t from 1");
382 
383         step = rowdy.nextStep(route1tl, route1);
384         Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1tl from 1");
385 
386         step = rowdy.nextStep(route1tls, route1);
387         Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1tls from 1");
388 
389 
390         // cases where layering on the tunnel is required
391         step = rowdy.nextStep(route1tl, route1t);
392         Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tl from 1t");
393 
394         step = rowdy.nextStep(route1tl, route1ts);
395         Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tl from 1ts");
396 
397         step = rowdy.nextStep(route1tls, route1t);
398         Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tls from 1t");
399 
400         step = rowdy.nextStep(route1tls, route1ts);
401         Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tls from 1ts");
402 
403         // There are some odd cases left over, like having a secure tunnel
404         // that becomes unsecure by layering, or a secure connection to a
405         // proxy that becomes unsecure by tunnelling to another proxy.
406     }
407 
408 }