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  import java.util.HashSet;
32  import java.util.Set;
33  
34  import org.apache.hc.client5.http.HttpRoute;
35  import org.apache.hc.client5.http.RouteInfo.LayerType;
36  import org.apache.hc.client5.http.RouteInfo.TunnelType;
37  import org.apache.hc.client5.http.RouteTracker;
38  import org.apache.hc.client5.http.routing.HttpRouteDirector;
39  import org.apache.hc.core5.http.HttpHost;
40  import org.junit.Assert;
41  import org.junit.Test;
42  
43  /**
44   * Tests for {@link RouteTracker}.
45   */
46  @SuppressWarnings("boxing") // test code
47  public class TestRouteTracker {
48  
49      // a selection of constants for generating routes
50      public final static
51          HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
52      public final static
53          HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
54      // It is not necessary to have extra targets for https.
55      // The 'layered' and 'secure' flags are specified explicitly
56      // for routes, they will not be determined from the scheme.
57  
58      public final static
59          HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80);
60      public final static
61          HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080);
62      public final static
63          HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88);
64  
65      public final static InetAddress LOCAL41;
66      public final static InetAddress LOCAL42;
67      public final static InetAddress LOCAL61;
68      public final static InetAddress LOCAL62;
69  
70      // need static initializer to deal with exceptions
71      static {
72          try {
73              LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 });
74              LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 });
75  
76              LOCAL61 = InetAddress.getByAddress(new byte[]{
77                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
78              });
79              LOCAL62 = InetAddress.getByAddress(new byte[]{
80                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
81              });
82  
83          } catch (final Exception x) {
84              throw new ExceptionInInitializerError(x);
85          }
86      }
87  
88      @SuppressWarnings("unused")
89      @Test
90      public void testCstrTargetLocal() {
91  
92          RouteTracker rt = new RouteTracker(TARGET1, null);
93          Assert.assertEquals("wrong target (target,null)",
94                       TARGET1, rt.getTargetHost());
95          Assert.assertEquals("wrong local address (target,null)",
96                       null, rt.getLocalAddress());
97          Assert.assertEquals("wrong hop count (target,null)",
98                       0, rt.getHopCount());
99          Assert.assertEquals("wrong proxy (target,null)",
100                      null, rt.getProxyHost());
101         Assert.assertEquals("wrong route (target,null)",
102                      null, rt.toRoute());
103         checkCTLS(rt, false, false, false, false);
104 
105 
106         rt = new RouteTracker(TARGET2, LOCAL61);
107         Assert.assertEquals("wrong target (target,local)",
108                      TARGET2, rt.getTargetHost());
109         Assert.assertEquals("wrong local address (target,local)",
110                      LOCAL61, rt.getLocalAddress());
111         Assert.assertEquals("wrong hop count (target,local)",
112                      0, rt.getHopCount());
113         Assert.assertEquals("wrong proxy (target,local)",
114                      null, rt.getProxyHost());
115         Assert.assertEquals("wrong route (target,local)",
116                      null, rt.toRoute());
117         checkCTLS(rt, false, false, false, false);
118 
119 
120         rt = null;
121         try {
122             new RouteTracker(null, LOCAL41);
123             Assert.fail("null target not detected");
124         } catch (final NullPointerException iax) {
125             // expected
126         }
127     }
128 
129     @SuppressWarnings("unused")
130     @Test
131     public void testCstrRoute() {
132 
133         HttpRoute r  = new HttpRoute(TARGET1);
134         RouteTracker rt = new RouteTracker(r);
135         Assert.assertEquals("wrong target (r1)",
136                      TARGET1, rt.getTargetHost());
137         Assert.assertEquals("wrong local address (r1)",
138                      null, rt.getLocalAddress());
139         Assert.assertEquals("wrong hop count (r1)",
140                      0, rt.getHopCount());
141         Assert.assertEquals("wrong proxy (r1)",
142                      null, rt.getProxyHost());
143         Assert.assertEquals("wrong route (r1)",
144                      null, rt.toRoute());
145         checkCTLS(rt, false, false, false, false);
146 
147         r  = new HttpRoute(TARGET2, LOCAL61, true);
148         rt = new RouteTracker(r);
149         Assert.assertEquals("wrong target (r2)",
150                      TARGET2, rt.getTargetHost());
151         Assert.assertEquals("wrong local address (r2)",
152                      LOCAL61, rt.getLocalAddress());
153         Assert.assertEquals("wrong hop count (r2)",
154                      0, rt.getHopCount());
155         Assert.assertEquals("wrong proxy (r2)",
156                      null, rt.getProxyHost());
157         Assert.assertEquals("wrong route (r2)",
158                      null, rt.toRoute());
159         checkCTLS(rt, false, false, false, false);
160 
161 
162         r  = new HttpRoute(TARGET1, LOCAL42, PROXY3, true);
163         rt = new RouteTracker(r);
164         Assert.assertEquals("wrong target (r3)",
165                      TARGET1, rt.getTargetHost());
166         Assert.assertEquals("wrong local address (r3)",
167                      LOCAL42, rt.getLocalAddress());
168         Assert.assertEquals("wrong hop count (r3)",
169                      0, rt.getHopCount());
170         Assert.assertEquals("wrong proxy (r3)",
171                      null, rt.getProxyHost());
172         Assert.assertEquals("wrong route (r3)",
173                      null, rt.toRoute());
174         checkCTLS(rt, false, false, false, false);
175 
176 
177         rt = null;
178         try {
179             new RouteTracker(null);
180             Assert.fail("null route not detected");
181         } catch (final NullPointerException npx) {
182             // expected
183         }
184     }
185 
186     @Test
187     public void testIllegalArgs() {
188 
189         final RouteTracker rt = new RouteTracker(TARGET2, null);
190 
191         try {
192             rt.connectProxy(null, true);
193             Assert.fail("missing proxy argument not detected (connect/false)");
194         } catch (final NullPointerException iax) {
195             // expected
196         }
197 
198         try {
199             rt.connectProxy(null, false);
200             Assert.fail("missing proxy argument not detected (connect/true)");
201         } catch (final NullPointerException iax) {
202             // expected
203         }
204 
205         rt.connectProxy(PROXY1, false);
206 
207         try {
208             rt.tunnelProxy(null, false);
209             Assert.fail("missing proxy argument not detected (tunnel/false)");
210         } catch (final NullPointerException iax) {
211             // expected
212         }
213 
214         try {
215             rt.tunnelProxy(null, true);
216             Assert.fail("missing proxy argument not detected (tunnel/true)");
217         } catch (final NullPointerException iax) {
218             // expected
219         }
220 
221         try {
222             rt.getHopTarget(-1);
223             Assert.fail("negative hop index not detected");
224         } catch (final IllegalArgumentException iax) {
225             // expected
226         }
227 
228         try {
229             rt.getHopTarget(2);
230             Assert.fail("excessive hop index not detected");
231         } catch (final IllegalArgumentException iax) {
232             // expected
233         }
234     }
235 
236     @Test
237     public void testIllegalStates() {
238 
239         final RouteTracker rt = new RouteTracker(TARGET1, null);
240 
241         try {
242             rt.tunnelTarget(false);
243             Assert.fail("unconnectedness not detected (tunnelTarget)");
244         } catch (final IllegalStateException isx) {
245             // expected
246         }
247 
248         try {
249             rt.tunnelProxy(PROXY1, false);
250             Assert.fail("unconnectedness not detected (tunnelProxy)");
251         } catch (final IllegalStateException isx) {
252             // expected
253         }
254 
255         try {
256             rt.layerProtocol(true);
257             Assert.fail("unconnectedness not detected (layerProtocol)");
258         } catch (final IllegalStateException isx) {
259             // expected
260         }
261 
262 
263         // connect directly
264         rt.connectTarget(false);
265 
266         try {
267             rt.connectTarget(false);
268             Assert.fail("connectedness not detected (connectTarget)");
269         } catch (final IllegalStateException isx) {
270             // expected
271         }
272 
273         try {
274             rt.connectProxy(PROXY2, false);
275             Assert.fail("connectedness not detected (connectProxy)");
276         } catch (final IllegalStateException isx) {
277             // expected
278         }
279 
280         try {
281             rt.tunnelTarget(false);
282             Assert.fail("unproxiedness not detected (tunnelTarget)");
283         } catch (final IllegalStateException isx) {
284             // expected
285         }
286 
287         try {
288             rt.tunnelProxy(PROXY1, false);
289             Assert.fail("unproxiedness not detected (tunnelProxy)");
290         } catch (final IllegalStateException isx) {
291             // expected
292         }
293     }
294 
295     @Test
296     public void testDirectRoutes() {
297 
298         final HttpRouteDirector rd = new BasicRouteDirector();
299         HttpRoute r = new HttpRoute(TARGET1, LOCAL41, false);
300         RouteTracker rt = new RouteTracker(r);
301         boolean complete = checkVia(rt, r, rd, 2);
302         Assert.assertTrue("incomplete route 1", complete);
303 
304         r = new HttpRoute(TARGET2, LOCAL62, true);
305         rt = new RouteTracker(r);
306         complete = checkVia(rt, r, rd, 2);
307         Assert.assertTrue("incomplete route 2", complete);
308     }
309 
310     @Test
311     public void testProxyRoutes() {
312 
313         final HttpRouteDirector rd = new BasicRouteDirector();
314         HttpRoute r = new HttpRoute(TARGET2, null, PROXY1, false);
315         RouteTracker rt = new RouteTracker(r);
316         boolean complete = checkVia(rt, r, rd, 2);
317         Assert.assertTrue("incomplete route 1", complete);
318 
319         // tunnelled, but neither secure nor layered
320         r = new HttpRoute(TARGET1, LOCAL61, PROXY3, false,
321                           TunnelType.TUNNELLED, LayerType.PLAIN);
322         rt = new RouteTracker(r);
323         complete = checkVia(rt, r, rd, 3);
324         Assert.assertTrue("incomplete route 2", complete);
325 
326         // tunnelled, layered, but not secure
327         r = new HttpRoute(TARGET1, LOCAL61, PROXY3, false,
328                           TunnelType.TUNNELLED, LayerType.LAYERED);
329         rt = new RouteTracker(r);
330         complete = checkVia(rt, r, rd, 4);
331         Assert.assertTrue("incomplete route 3", complete);
332 
333         // tunnelled, layered, secure
334         r = new HttpRoute(TARGET1, LOCAL61, PROXY3, true);
335         rt = new RouteTracker(r);
336         complete = checkVia(rt, r, rd, 4);
337         Assert.assertTrue("incomplete route 4", complete);
338     }
339 
340     @Test
341     public void testProxyChainRoutes() {
342 
343         final HttpRouteDirector rd = new BasicRouteDirector();
344         HttpHost[] proxies = { PROXY1, PROXY2 };
345         HttpRoute r = new HttpRoute(TARGET2, LOCAL42, proxies, false,
346                                     TunnelType.PLAIN, LayerType.PLAIN);
347         RouteTracker rt = new RouteTracker(r);
348         boolean complete = checkVia(rt, r, rd, 3);
349         Assert.assertTrue("incomplete route 1", complete);
350 
351         // tunnelled, but neither secure nor layered
352         proxies = new HttpHost[]{ PROXY3, PROXY2 };
353         r = new HttpRoute(TARGET1, null, proxies, false,
354                           TunnelType.TUNNELLED, LayerType.PLAIN);
355         rt = new RouteTracker(r);
356         complete = checkVia(rt, r, rd, 4);
357         Assert.assertTrue("incomplete route 2", complete);
358 
359         // tunnelled, layered, but not secure
360         proxies = new HttpHost[]{ PROXY3, PROXY2, PROXY1 };
361         r = new HttpRoute(TARGET2, LOCAL61, proxies, false,
362                           TunnelType.TUNNELLED, LayerType.LAYERED);
363         rt = new RouteTracker(r);
364         complete = checkVia(rt, r, rd, 6);
365         Assert.assertTrue("incomplete route 3", complete);
366 
367         // tunnelled, layered, secure
368         proxies = new HttpHost[]{ PROXY1, PROXY3 };
369         r = new HttpRoute(TARGET1, LOCAL61, proxies, true,
370                           TunnelType.TUNNELLED, LayerType.LAYERED);
371         rt = new RouteTracker(r);
372         complete = checkVia(rt, r, rd, 5);
373         Assert.assertTrue("incomplete route 4", complete);
374     }
375 
376     @Test
377     public void testEqualsHashcodeCloneToString()
378         throws CloneNotSupportedException {
379 
380         final RouteTracker rt0 = new RouteTracker(TARGET1, null);
381         final RouteTracker rt1 = new RouteTracker(TARGET2, null);
382         final RouteTracker rt2 = new RouteTracker(TARGET1, null);
383         final RouteTracker rt3 = new RouteTracker(TARGET1, null);
384         final RouteTracker rt4 = new RouteTracker(TARGET1, LOCAL41);
385         final RouteTracker rt6 = new RouteTracker(TARGET1, LOCAL62);
386 
387         Assert.assertFalse("rt0", rt0.equals(null));
388         Assert.assertTrue("rt0", rt0.equals(rt0));
389         Assert.assertFalse("rt0", rt0.equals(rt0.toString()));
390 
391         Assert.assertFalse("rt0 == rt4", rt0.equals(rt4));
392         Assert.assertFalse("rt0 == rt1", rt0.equals(rt1)); // Check host takes part in equals
393 
394         // Check that connection takes part in equals
395         Assert.assertTrue("rt0 != rt2", rt0.equals(rt2));
396         rt2.connectTarget(false);
397         Assert.assertFalse("rt0 == rt2", rt0.equals(rt2));
398 
399         Assert.assertTrue("rt0 != rt3", rt0.equals(rt3));
400         rt3.connectTarget(true);
401         Assert.assertFalse("rt0 == rt3", rt0.equals(rt3));
402         Assert.assertFalse("rt2 == rt3", rt2.equals(rt3)); // Test secure takes part
403 
404         // TODO needs tests for tunnel and layered
405 
406         Assert.assertFalse("rt4 == rt0", rt4.equals(rt0));
407         Assert.assertFalse("rt0 == rt6", rt0.equals(rt6));
408         Assert.assertFalse("rt6 == rt0", rt6.equals(rt0));
409         Assert.assertFalse("rt4 == rt6", rt4.equals(rt6));
410         Assert.assertFalse("rt6 == rt4", rt6.equals(rt4));
411 
412         // it is likely but not guaranteed that the hashcodes are different
413         Assert.assertFalse("rt0 == rt4 (hashcode)", rt0.hashCode() == rt4.hashCode());
414         Assert.assertFalse("rt0 == rt6 (hashcode)", rt0.hashCode() == rt6.hashCode());
415         Assert.assertFalse("rt6 == rt4 (hashcode)", rt6.hashCode() == rt4.hashCode());
416 
417         Assert.assertEquals("rt0 (clone)", rt0, rt0.clone());
418         Assert.assertEquals("rt4 (clone)", rt4, rt4.clone());
419         Assert.assertEquals("rt6 (clone)", rt6, rt6.clone());
420 
421 
422         // we collect (clones of) the different tracked routes along the way
423         // rt0 -> direct connection
424         // rt1 -> via single proxy
425         // rt2 -> via proxy chain
426         final Set<RouteTracker> hs = new HashSet<>();
427 
428         // we also collect hashcodes for the different paths
429         // since we can't guarantee what influence the HttpHost hashcodes have,
430         // we keep separate sets here
431         final Set<Integer> hc0 = new HashSet<>();
432         final Set<Integer> hc4 = new HashSet<>();
433         final Set<Integer> hc6 = new HashSet<>();
434 
435         RouteTracker rt = null;
436 
437         Assert.assertTrue(hs.add(rt0));
438         Assert.assertTrue(hs.add(rt4));
439         Assert.assertTrue(hs.add(rt6));
440 
441         Assert.assertTrue(hc0.add(Integer.valueOf(rt0.hashCode())));
442         Assert.assertTrue(hc4.add(Integer.valueOf(rt4.hashCode())));
443         Assert.assertTrue(hc6.add(Integer.valueOf(rt6.hashCode())));
444 
445         rt = (RouteTracker) rt0.clone();
446         rt.connectTarget(false);
447         Assert.assertTrue(hs.add(rt));
448         Assert.assertTrue(hc0.add(Integer.valueOf(rt.hashCode())));
449 
450         rt = (RouteTracker) rt0.clone();
451         rt.connectTarget(true);
452         Assert.assertTrue(hs.add(rt));
453         Assert.assertTrue(hc0.add(Integer.valueOf(rt.hashCode())));
454 
455 
456         // proxy (insecure) -> tunnel (insecure) -> layer (secure)
457         rt = (RouteTracker) rt4.clone();
458         rt.connectProxy(PROXY1, false);
459         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
460         // this is not guaranteed to be unique...
461         Assert.assertTrue(hc4.add(Integer.valueOf(rt.hashCode())));
462 
463         rt.tunnelTarget(false);
464         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
465         Assert.assertTrue(hc4.add(Integer.valueOf(rt.hashCode())));
466 
467         rt.layerProtocol(true);
468         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
469         Assert.assertTrue(hc4.add(Integer.valueOf(rt.hashCode())));
470 
471 
472         // proxy (secure) -> tunnel (secure) -> layer (insecure)
473         rt = (RouteTracker) rt4.clone();
474         rt.connectProxy(PROXY1, true);
475         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
476         // this is not guaranteed to be unique...
477         Assert.assertTrue(hc4.add(Integer.valueOf(rt.hashCode())));
478 
479         rt.tunnelTarget(true);
480         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
481         Assert.assertTrue(hc4.add(Integer.valueOf(rt.hashCode())));
482 
483         rt.layerProtocol(false);
484         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
485         Assert.assertTrue(hc4.add(Integer.valueOf(rt.hashCode())));
486 
487 
488         // PROXY1/i -> PROXY2/i -> tunnel/i -> layer/s
489         rt = (RouteTracker) rt6.clone();
490         rt.connectProxy(PROXY1, false);
491         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
492         // this is not guaranteed to be unique...
493         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
494 
495         rt.tunnelProxy(PROXY2, false);
496         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
497         // this is not guaranteed to be unique...
498         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
499 
500         rt.tunnelTarget(false);
501         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
502         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
503 
504         rt.layerProtocol(true);
505         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
506         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
507 
508 
509         // PROXY1/s -> PROXY2/s -> tunnel/s -> layer/i
510         rt = (RouteTracker) rt6.clone();
511         rt.connectProxy(PROXY1, true);
512         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
513         // this is not guaranteed to be unique...
514         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
515 
516         rt.tunnelProxy(PROXY2, true);
517         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
518         // this is not guaranteed to be unique...
519         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
520 
521         rt.tunnelTarget(true);
522         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
523         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
524 
525         rt.layerProtocol(false);
526         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
527         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
528 
529 
530         // PROXY2/i -> PROXY1/i -> tunnel/i -> layer/s
531         rt = (RouteTracker) rt6.clone();
532         rt.connectProxy(PROXY2, false);
533         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
534         // this is not guaranteed to be unique...
535         Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
536 
537         rt.tunnelProxy(PROXY1, false);
538         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
539         // proxy chain sequence does not affect hashcode, so duplicate:
540         // Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
541 
542         rt.tunnelTarget(false);
543         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
544         // proxy chain sequence does not affect hashcode, so duplicate:
545         // Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
546 
547         rt.layerProtocol(true);
548         Assert.assertTrue(hs.add((RouteTracker) rt.clone()));
549         // proxy chain sequence does not affect hashcode, so duplicate:
550         // Assert.assertTrue(hc6.add(Integer.valueOf(rt.hashCode())));
551 
552 
553         // check that all toString are OK and different
554         final Set<String> rtstrings = new HashSet<>();
555         for (final RouteTracker current: hs) {
556             final String rts = checkToString(current);
557             Assert.assertTrue("duplicate toString: " + rts, rtstrings.add(rts));
558         }
559     }
560 
561 
562     /** Helper to check the status of the four flags. */
563     public final static void checkCTLS(final RouteTracker rt,
564                                        final boolean c, final boolean t,
565                                        final boolean l, final boolean s) {
566         final String rts = rt.toString();
567         Assert.assertEquals("wrong flag connected: " + rts, c, rt.isConnected());
568         Assert.assertEquals("wrong flag tunnelled: " + rts, t, rt.isTunnelled());
569         Assert.assertEquals("wrong enum tunnelled: " + rts,
570                      t ? TunnelType.TUNNELLED : TunnelType.PLAIN,
571                      rt.getTunnelType());
572         Assert.assertEquals("wrong flag layered: "   + rts, l, rt.isLayered());
573         Assert.assertEquals("wrong enum layered: "   + rts,
574                      l ? LayerType.LAYERED : LayerType.PLAIN,
575                      rt.getLayerType());
576         Assert.assertEquals("wrong flag secure: "    + rts, s, rt.isSecure());
577     }
578 
579 
580     /**
581      * Helper to check tracking of a route.
582      * This uses a {@link HttpRouteDirector} to fake establishing the route,
583      * checking the intermediate steps.
584      *
585      * @param rt        the tracker to check with
586      * @param r         the route to establish
587      * @param rd        the director to check with
588      * @param steps     the step count for this invocation
589      *
590      * @return  {@code true} iff the route is complete
591      */
592     public final static boolean checkVia(final RouteTracker rt, final HttpRoute r,
593                                          final HttpRouteDirector rd, final int steps) {
594 
595         final String msg = r + " @ " + rt;
596 
597         boolean complete = false;
598         int n = steps;
599         while (!complete && (n > 0)) {
600 
601             final int action = rd.nextStep(r, rt.toRoute());
602             switch (action) {
603 
604             case HttpRouteDirector.COMPLETE:
605                 complete = true;
606                 Assert.assertEquals(r, rt.toRoute());
607                 break;
608 
609             case HttpRouteDirector.CONNECT_TARGET: {
610                 final boolean sec = r.isSecure();
611                 rt.connectTarget(sec);
612                 checkCTLS(rt, true, false, false, sec);
613                 Assert.assertEquals("wrong hop count "+msg,
614                              1, rt.getHopCount());
615                 Assert.assertEquals("wrong hop0 "+msg,
616                              r.getTargetHost(), rt.getHopTarget(0));
617             } break;
618 
619             case HttpRouteDirector.CONNECT_PROXY: {
620                 // we assume an insecure proxy connection
621                 final boolean sec = false;
622                 rt.connectProxy(r.getProxyHost(), sec);
623                 checkCTLS(rt, true, false, false, sec);
624                 Assert.assertEquals("wrong hop count "+msg,
625                              2, rt.getHopCount());
626                 Assert.assertEquals("wrong hop0 "+msg,
627                              r.getProxyHost(), rt.getHopTarget(0));
628                 Assert.assertEquals("wrong hop1 "+msg,
629                              r.getTargetHost(), rt.getHopTarget(1));
630             } break;
631 
632             case HttpRouteDirector.TUNNEL_TARGET: {
633                 final int hops = rt.getHopCount();
634                 // we assume an insecure tunnel
635                 final boolean sec = false;
636                 rt.tunnelTarget(sec);
637                 checkCTLS(rt, true, true, false, sec);
638                 Assert.assertEquals("wrong hop count "+msg,
639                              hops, rt.getHopCount());
640                 Assert.assertEquals("wrong hop0 "+msg,
641                              r.getProxyHost(), rt.getHopTarget(0));
642                 Assert.assertEquals("wrong hopN "+msg,
643                              r.getTargetHost(), rt.getHopTarget(hops-1));
644             } break;
645 
646             case HttpRouteDirector.TUNNEL_PROXY: {
647                 final int hops = rt.getHopCount(); // before tunnelling
648                 // we assume an insecure tunnel
649                 final boolean  sec = false;
650                 final HttpHost pxy = r.getHopTarget(hops-1);
651                 rt.tunnelProxy(pxy, sec);
652                 // Since we're tunnelling to a proxy and not the target,
653                 // the 'tunelling' flag is false: no end-to-end tunnel.
654                 checkCTLS(rt, true, false, false, sec);
655                 Assert.assertEquals("wrong hop count "+msg,
656                              hops+1, rt.getHopCount());
657                 Assert.assertEquals("wrong hop0 "+msg,
658                              r.getProxyHost(), rt.getHopTarget(0));
659                 Assert.assertEquals("wrong hop"+hops+" "+msg,
660                              pxy, rt.getHopTarget(hops-1));
661                 Assert.assertEquals("wrong hopN "+msg,
662                              r.getTargetHost(), rt.getHopTarget(hops));
663             } break;
664 
665             case HttpRouteDirector.LAYER_PROTOCOL: {
666                 final int    hops = rt.getHopCount();
667                 final boolean tun = rt.isTunnelled();
668                 final boolean sec = r.isSecure();
669                 rt.layerProtocol(sec);
670                 checkCTLS(rt, true, tun, true, sec);
671                 Assert.assertEquals("wrong hop count "+msg,
672                              hops, rt.getHopCount());
673                 Assert.assertEquals("wrong proxy "+msg,
674                              r.getProxyHost(), rt.getProxyHost());
675                 Assert.assertEquals("wrong target "+msg,
676                              r.getTargetHost(), rt.getTargetHost());
677             } break;
678 
679 
680             // UNREACHABLE
681             default:
682                 Assert.fail("unexpected action " + action + " from director, "+msg);
683                 break;
684 
685             } // switch
686             n--;
687         }
688 
689         return complete;
690     } // checkVia
691 
692 
693     /**
694      * Checks the output of {@code toString}.
695      *
696      * @param rt        the tracker for which to check the output
697      *
698      * @return  the result of {@code rt.toString()}
699      */
700     public final static String checkToString(final RouteTracker rt) {
701         if (rt == null) {
702             return null;
703         }
704 
705         final String rts = rt.toString();
706 
707         if (rt.getLocalAddress() != null) {
708             final String las = rt.getLocalAddress().toString();
709             Assert.assertFalse("no local address in toString(): " + rts,
710                     !rts.contains(las));
711         }
712 
713         for (int i=0; i<rt.getHopCount(); i++) {
714             final String hts = rt.getHopTarget(i).toString();
715             Assert.assertFalse("hop "+i+" ("+hts+") missing in toString(): " + rts,
716                     !rts.contains(hts));
717         }
718 
719         return rts;
720     }
721 
722 }