1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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.jupiter.api.Assertions;
41 import org.junit.jupiter.api.Test;
42
43
44
45
46 @SuppressWarnings("boxing")
47 class TestRouteTracker {
48
49
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
55
56
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
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 void testCstrTargetLocal() {
91
92 RouteTracker rt = new RouteTracker(TARGET1, null);
93 Assertions.assertEquals(TARGET1, rt.getTargetHost(), "wrong target (target,null)");
94 Assertions.assertNull(rt.getLocalAddress(), "wrong local address (target,null)");
95 Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (target,null)");
96 Assertions.assertNull(rt.getProxyHost(), "wrong proxy (target,null)");
97 Assertions.assertNull(rt.toRoute(), "wrong route (target,null)");
98 checkCTLS(rt, false, false, false, false);
99
100
101 rt = new RouteTracker(TARGET2, LOCAL61);
102 Assertions.assertEquals(TARGET2, rt.getTargetHost(), "wrong target (target,local)");
103 Assertions.assertEquals(LOCAL61, rt.getLocalAddress(), "wrong local address (target,local)");
104 Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (target,local)");
105 Assertions.assertNull(rt.getProxyHost(), "wrong proxy (target,local)");
106 Assertions.assertNull(rt.toRoute(), "wrong route (target,local)");
107 checkCTLS(rt, false, false, false, false);
108
109 Assertions.assertThrows(NullPointerException.class, () -> new RouteTracker(null, LOCAL41));
110 }
111
112 @SuppressWarnings("unused")
113 @Test
114 void testCstrRoute() {
115
116 HttpRoute r = new HttpRoute(TARGET1);
117 RouteTracker rt = new RouteTracker(r);
118 Assertions.assertEquals(TARGET1, rt.getTargetHost(), "wrong target (r1)");
119 Assertions.assertNull(rt.getLocalAddress(), "wrong local address (r1)");
120 Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (r1)");
121 Assertions.assertNull(rt.getProxyHost(), "wrong proxy (r1)");
122 Assertions.assertNull(rt.toRoute(), "wrong route (r1)");
123 checkCTLS(rt, false, false, false, false);
124
125 r = new HttpRoute(TARGET2, LOCAL61, true);
126 rt = new RouteTracker(r);
127 Assertions.assertEquals(TARGET2, rt.getTargetHost(), "wrong target (r2)");
128 Assertions.assertEquals(LOCAL61, rt.getLocalAddress(), "wrong local address (r2)");
129 Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (r2)");
130 Assertions.assertNull(rt.getProxyHost(), "wrong proxy (r2)");
131 Assertions.assertNull(rt.toRoute(), "wrong route (r2)");
132 checkCTLS(rt, false, false, false, false);
133
134
135 r = new HttpRoute(TARGET1, LOCAL42, PROXY3, true);
136 rt = new RouteTracker(r);
137 Assertions.assertEquals(TARGET1, rt.getTargetHost(), "wrong target (r3)");
138 Assertions.assertEquals(LOCAL42, rt.getLocalAddress(), "wrong local address (r3)");
139 Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (r3)");
140 Assertions.assertNull(rt.getProxyHost(), "wrong proxy (r3)");
141 Assertions.assertNull(rt.toRoute(), "wrong route (r3)");
142 checkCTLS(rt, false, false, false, false);
143
144 Assertions.assertThrows(NullPointerException.class, () -> new RouteTracker(null));
145 }
146
147 @Test
148 void testIllegalArgs() {
149
150 final RouteTracker rt = new RouteTracker(TARGET2, null);
151
152 Assertions.assertThrows(NullPointerException.class, () -> rt.connectProxy(null, true));
153 Assertions.assertThrows(NullPointerException.class, () -> rt.connectProxy(null, false));
154
155 rt.connectProxy(PROXY1, false);
156
157 Assertions.assertThrows(NullPointerException.class, () -> rt.tunnelProxy(null, false));
158 Assertions.assertThrows(NullPointerException.class, () -> rt.tunnelProxy(null, true));
159 Assertions.assertThrows(IllegalArgumentException.class, () -> rt.getHopTarget(-1));
160 Assertions.assertThrows(IllegalArgumentException.class, () -> rt.getHopTarget(2));
161 }
162
163 @Test
164 void testIllegalStates() {
165
166 final RouteTracker rt = new RouteTracker(TARGET1, null);
167
168 Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelTarget(false));
169 Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelProxy(PROXY1, false));
170 Assertions.assertThrows(IllegalStateException.class, () -> rt.layerProtocol(true));
171
172
173 rt.connectTarget(false);
174
175 Assertions.assertThrows(IllegalStateException.class, () -> rt.connectTarget(false));
176 Assertions.assertThrows(IllegalStateException.class, () -> rt.connectProxy(PROXY2, false));
177 Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelTarget(false));
178 Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelProxy(PROXY1, false));
179 }
180
181 @Test
182 void testDirectRoutes() {
183
184 final HttpRouteDirector rd = BasicRouteDirector.INSTANCE;
185 HttpRoute r = new HttpRoute(TARGET1, LOCAL41, false);
186 RouteTracker rt = new RouteTracker(r);
187 boolean complete = checkVia(rt, r, rd, 2);
188 Assertions.assertTrue(complete, "incomplete route 1");
189
190 r = new HttpRoute(TARGET2, LOCAL62, true);
191 rt = new RouteTracker(r);
192 complete = checkVia(rt, r, rd, 2);
193 Assertions.assertTrue(complete, "incomplete route 2");
194 }
195
196 @Test
197 void testProxyRoutes() {
198
199 final HttpRouteDirector rd = BasicRouteDirector.INSTANCE;
200 HttpRoute r = new HttpRoute(TARGET2, null, PROXY1, false);
201 RouteTracker rt = new RouteTracker(r);
202 boolean complete = checkVia(rt, r, rd, 2);
203 Assertions.assertTrue(complete, "incomplete route 1");
204
205
206 r = new HttpRoute(TARGET1, LOCAL61, PROXY3, false,
207 TunnelType.TUNNELLED, LayerType.PLAIN);
208 rt = new RouteTracker(r);
209 complete = checkVia(rt, r, rd, 3);
210 Assertions.assertTrue(complete, "incomplete route 2");
211
212
213 r = new HttpRoute(TARGET1, LOCAL61, PROXY3, false,
214 TunnelType.TUNNELLED, LayerType.LAYERED);
215 rt = new RouteTracker(r);
216 complete = checkVia(rt, r, rd, 4);
217 Assertions.assertTrue(complete, "incomplete route 3");
218
219
220 r = new HttpRoute(TARGET1, LOCAL61, PROXY3, true);
221 rt = new RouteTracker(r);
222 complete = checkVia(rt, r, rd, 4);
223 Assertions.assertTrue(complete, "incomplete route 4");
224 }
225
226 @Test
227 void testProxyChainRoutes() {
228
229 final HttpRouteDirector rd = BasicRouteDirector.INSTANCE;
230 HttpHost[] proxies = { PROXY1, PROXY2 };
231 HttpRoute r = new HttpRoute(TARGET2, LOCAL42, proxies, false,
232 TunnelType.PLAIN, LayerType.PLAIN);
233 RouteTracker rt = new RouteTracker(r);
234 boolean complete = checkVia(rt, r, rd, 3);
235 Assertions.assertTrue(complete, "incomplete route 1");
236
237
238 proxies = new HttpHost[]{ PROXY3, PROXY2 };
239 r = new HttpRoute(TARGET1, null, proxies, false,
240 TunnelType.TUNNELLED, LayerType.PLAIN);
241 rt = new RouteTracker(r);
242 complete = checkVia(rt, r, rd, 4);
243 Assertions.assertTrue(complete, "incomplete route 2");
244
245
246 proxies = new HttpHost[]{ PROXY3, PROXY2, PROXY1 };
247 r = new HttpRoute(TARGET2, LOCAL61, proxies, false,
248 TunnelType.TUNNELLED, LayerType.LAYERED);
249 rt = new RouteTracker(r);
250 complete = checkVia(rt, r, rd, 6);
251 Assertions.assertTrue(complete, "incomplete route 3");
252
253
254 proxies = new HttpHost[]{ PROXY1, PROXY3 };
255 r = new HttpRoute(TARGET1, LOCAL61, proxies, true,
256 TunnelType.TUNNELLED, LayerType.LAYERED);
257 rt = new RouteTracker(r);
258 complete = checkVia(rt, r, rd, 5);
259 Assertions.assertTrue(complete, "incomplete route 4");
260 }
261
262 @Test
263 void testEqualsHashcodeCloneToString()
264 throws CloneNotSupportedException {
265
266 final RouteTracker rt0 = new RouteTracker(TARGET1, null);
267 final RouteTracker rt1 = new RouteTracker(TARGET2, null);
268 final RouteTracker rt2 = new RouteTracker(TARGET1, null);
269 final RouteTracker rt3 = new RouteTracker(TARGET1, null);
270 final RouteTracker rt4 = new RouteTracker(TARGET1, LOCAL41);
271 final RouteTracker rt6 = new RouteTracker(TARGET1, LOCAL62);
272
273 Assertions.assertNotEquals(null, rt0, "rt0");
274 Assertions.assertEquals(rt0, rt0, "rt0");
275 Assertions.assertNotEquals("rt0", rt0, rt0.toString());
276
277 Assertions.assertNotEquals(rt0, rt4, "rt0 == rt4");
278 Assertions.assertNotEquals(rt0, rt1, "rt0 == rt1");
279
280
281 Assertions.assertEquals(rt0, rt2, "rt0 != rt2");
282 rt2.connectTarget(false);
283 Assertions.assertNotEquals(rt0, rt2, "rt0 == rt2");
284
285 Assertions.assertEquals(rt0, rt3, "rt0 != rt3");
286 rt3.connectTarget(true);
287 Assertions.assertNotEquals(rt0, rt3, "rt0 == rt3");
288 Assertions.assertNotEquals(rt2, rt3, "rt2 == rt3");
289
290
291
292 Assertions.assertNotEquals(rt4, rt0, "rt4 == rt0");
293 Assertions.assertNotEquals(rt0, rt6, "rt0 == rt6");
294 Assertions.assertNotEquals(rt6, rt0, "rt6 == rt0");
295 Assertions.assertNotEquals(rt4, rt6, "rt4 == rt6");
296 Assertions.assertNotEquals(rt6, rt4, "rt6 == rt4");
297
298
299 Assertions.assertNotEquals(rt0.hashCode(), rt4.hashCode(), "rt0 == rt4 (hashcode)");
300 Assertions.assertNotEquals(rt0.hashCode(), rt6.hashCode(), "rt0 == rt6 (hashcode)");
301 Assertions.assertNotEquals(rt6.hashCode(), rt4.hashCode(), "rt6 == rt4 (hashcode)");
302
303 Assertions.assertEquals(rt0, rt0.clone(), "rt0 (clone)");
304 Assertions.assertEquals(rt4, rt4.clone(), "rt4 (clone)");
305 Assertions.assertEquals(rt6, rt6.clone(), "rt6 (clone)");
306
307
308
309
310
311
312 final Set<RouteTracker> hs = new HashSet<>();
313
314
315
316
317 final Set<Integer> hc0 = new HashSet<>();
318 final Set<Integer> hc4 = new HashSet<>();
319 final Set<Integer> hc6 = new HashSet<>();
320
321 RouteTracker rt = null;
322
323 Assertions.assertTrue(hs.add(rt0));
324 Assertions.assertTrue(hs.add(rt4));
325 Assertions.assertTrue(hs.add(rt6));
326
327 Assertions.assertTrue(hc0.add(rt0.hashCode()));
328 Assertions.assertTrue(hc4.add(rt4.hashCode()));
329 Assertions.assertTrue(hc6.add(rt6.hashCode()));
330
331 rt = (RouteTracker) rt0.clone();
332 rt.connectTarget(false);
333 Assertions.assertTrue(hs.add(rt));
334 Assertions.assertTrue(hc0.add(rt.hashCode()));
335
336 rt = (RouteTracker) rt0.clone();
337 rt.connectTarget(true);
338 Assertions.assertTrue(hs.add(rt));
339 Assertions.assertTrue(hc0.add(rt.hashCode()));
340
341
342
343 rt = (RouteTracker) rt4.clone();
344 rt.connectProxy(PROXY1, false);
345 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
346
347 Assertions.assertTrue(hc4.add(rt.hashCode()));
348
349 rt.tunnelTarget(false);
350 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
351 Assertions.assertTrue(hc4.add(rt.hashCode()));
352
353 rt.layerProtocol(true);
354 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
355 Assertions.assertTrue(hc4.add(rt.hashCode()));
356
357
358
359 rt = (RouteTracker) rt4.clone();
360 rt.connectProxy(PROXY1, true);
361 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
362
363 Assertions.assertTrue(hc4.add(rt.hashCode()));
364
365 rt.tunnelTarget(true);
366 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
367 Assertions.assertTrue(hc4.add(rt.hashCode()));
368
369 rt.layerProtocol(false);
370 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
371 Assertions.assertTrue(hc4.add(rt.hashCode()));
372
373
374
375 rt = (RouteTracker) rt6.clone();
376 rt.connectProxy(PROXY1, false);
377 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
378
379 Assertions.assertTrue(hc6.add(rt.hashCode()));
380
381 rt.tunnelProxy(PROXY2, false);
382 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
383
384 Assertions.assertTrue(hc6.add(rt.hashCode()));
385
386 rt.tunnelTarget(false);
387 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
388 Assertions.assertTrue(hc6.add(rt.hashCode()));
389
390 rt.layerProtocol(true);
391 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
392 Assertions.assertTrue(hc6.add(rt.hashCode()));
393
394
395
396 rt = (RouteTracker) rt6.clone();
397 rt.connectProxy(PROXY1, true);
398 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
399
400 Assertions.assertTrue(hc6.add(rt.hashCode()));
401
402 rt.tunnelProxy(PROXY2, true);
403 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
404
405 Assertions.assertTrue(hc6.add(rt.hashCode()));
406
407 rt.tunnelTarget(true);
408 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
409 Assertions.assertTrue(hc6.add(rt.hashCode()));
410
411 rt.layerProtocol(false);
412 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
413 Assertions.assertTrue(hc6.add(rt.hashCode()));
414
415
416
417 rt = (RouteTracker) rt6.clone();
418 rt.connectProxy(PROXY2, false);
419 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
420
421 Assertions.assertTrue(hc6.add(rt.hashCode()));
422
423 rt.tunnelProxy(PROXY1, false);
424 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
425
426
427
428 rt.tunnelTarget(false);
429 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
430
431
432
433 rt.layerProtocol(true);
434 Assertions.assertTrue(hs.add((RouteTracker) rt.clone()));
435
436
437
438
439
440 final Set<String> rtstrings = new HashSet<>();
441 for (final RouteTracker current: hs) {
442 final String rts = checkToString(current);
443 Assertions.assertTrue(rtstrings.add(rts), "duplicate toString: " + rts);
444 }
445 }
446
447
448
449 public final static void checkCTLS(final RouteTracker rt,
450 final boolean c, final boolean t,
451 final boolean l, final boolean s) {
452 final String rts = rt.toString();
453 Assertions.assertEquals(c, rt.isConnected(), "wrong flag connected: " + rts);
454 Assertions.assertEquals(t, rt.isTunnelled(), "wrong flag tunnelled: " + rts);
455 Assertions.assertEquals(t ? TunnelType.TUNNELLED : TunnelType.PLAIN,
456 rt.getTunnelType(), "wrong enum tunnelled: " + rts);
457 Assertions.assertEquals(l, rt.isLayered(), "wrong flag layered: " + rts);
458 Assertions.assertEquals(l ? LayerType.LAYERED : LayerType.PLAIN,
459 rt.getLayerType(), "wrong enum layered: " + rts);
460 Assertions.assertEquals(s, rt.isSecure(), "wrong flag secure: " + rts);
461 }
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476 public final static boolean checkVia(final RouteTracker rt, final HttpRoute r,
477 final HttpRouteDirector rd, final int steps) {
478
479 final String msg = r + " @ " + rt;
480
481 boolean complete = false;
482 int n = steps;
483 while (!complete && (n > 0)) {
484
485 final int action = rd.nextStep(r, rt.toRoute());
486 switch (action) {
487
488 case HttpRouteDirector.COMPLETE:
489 complete = true;
490 Assertions.assertEquals(r, rt.toRoute());
491 break;
492
493 case HttpRouteDirector.CONNECT_TARGET: {
494 final boolean sec = r.isSecure();
495 rt.connectTarget(sec);
496 checkCTLS(rt, true, false, false, sec);
497 Assertions.assertEquals(1, rt.getHopCount(), "wrong hop count "+msg);
498 Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(0), "wrong hop0 "+msg);
499 } break;
500
501 case HttpRouteDirector.CONNECT_PROXY: {
502
503 final boolean sec = false;
504 rt.connectProxy(r.getProxyHost(), sec);
505 checkCTLS(rt, true, false, false, sec);
506 Assertions.assertEquals(2, rt.getHopCount(), "wrong hop count "+msg);
507 Assertions.assertEquals(r.getProxyHost(), rt.getHopTarget(0), "wrong hop0 "+msg);
508 Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(1), "wrong hop1 "+msg);
509 } break;
510
511 case HttpRouteDirector.TUNNEL_TARGET: {
512 final int hops = rt.getHopCount();
513
514 final boolean sec = false;
515 rt.tunnelTarget(sec);
516 checkCTLS(rt, true, true, false, sec);
517 Assertions.assertEquals(hops, rt.getHopCount(), "wrong hop count "+msg);
518 Assertions.assertEquals(r.getProxyHost(), rt.getHopTarget(0), "wrong hop0 "+msg);
519 Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(hops-1), "wrong hopN "+msg);
520 } break;
521
522 case HttpRouteDirector.TUNNEL_PROXY: {
523 final int hops = rt.getHopCount();
524
525 final boolean sec = false;
526 final HttpHost pxy = r.getHopTarget(hops-1);
527 rt.tunnelProxy(pxy, sec);
528
529
530 checkCTLS(rt, true, false, false, sec);
531 Assertions.assertEquals(hops+1, rt.getHopCount(), "wrong hop count "+msg);
532 Assertions.assertEquals(r.getProxyHost(), rt.getHopTarget(0), "wrong hop0 "+msg);
533 Assertions.assertEquals(pxy, rt.getHopTarget(hops-1), "wrong hop"+hops+" "+msg);
534 Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(hops), "wrong hopN "+msg);
535 } break;
536
537 case HttpRouteDirector.LAYER_PROTOCOL: {
538 final int hops = rt.getHopCount();
539 final boolean tun = rt.isTunnelled();
540 final boolean sec = r.isSecure();
541 rt.layerProtocol(sec);
542 checkCTLS(rt, true, tun, true, sec);
543 Assertions.assertEquals(hops, rt.getHopCount(), "wrong hop count "+msg);
544 Assertions.assertEquals(r.getProxyHost(), rt.getProxyHost(), "wrong proxy "+msg);
545 Assertions.assertEquals(r.getTargetHost(), rt.getTargetHost(), "wrong target "+msg);
546 } break;
547
548
549
550 default:
551 Assertions.fail("unexpected action " + action + " from director, "+msg);
552 break;
553
554 }
555 n--;
556 }
557
558 return complete;
559 }
560
561
562
563
564
565
566
567
568
569 public final static String checkToString(final RouteTracker rt) {
570 if (rt == null) {
571 return null;
572 }
573
574 final String rts = rt.toString();
575
576 if (rt.getLocalAddress() != null) {
577 final String las = rt.getLocalAddress().toString();
578 Assertions.assertTrue(rts.contains(las), "no local address in toString(): " + rts);
579 }
580
581 for (int i=0; i<rt.getHopCount(); i++) {
582 final String hts = rt.getHopTarget(i).toString();
583 Assertions.assertTrue(rts.contains(hts), "hop " + i + " (" + hts + ") missing in toString(): " + rts);
584 }
585
586 return rts;
587 }
588
589 }