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