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.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.core5.http.HttpHost;
38  import org.junit.jupiter.api.Assertions;
39  import org.junit.jupiter.api.Test;
40  
41  /**
42   * Tests for {@link HttpRoute}.
43   */
44  public class TestHttpRoute {
45  
46      // a selection of constants for generating routes
47      public final static
48          HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
49      public final static
50          HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
51      // It is not necessary to have extra targets for https.
52      // The 'layered' and 'secure' flags are specified explicitly
53      // for routes, they will not be determined from the scheme.
54  
55      public final static
56          HttpHost PROXY1 = new HttpHost("proxy1.test.invalid");
57      public final static
58          HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080);
59      public final static
60          HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88);
61  
62      public final static InetAddress LOCAL41;
63      public final static InetAddress LOCAL42;
64      public final static InetAddress LOCAL61;
65      public final static InetAddress LOCAL62;
66  
67      // need static initializer to deal with exceptions
68      static {
69          try {
70              LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 });
71              LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 });
72  
73              LOCAL61 = InetAddress.getByAddress(new byte[]{
74                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
75              });
76              LOCAL62 = InetAddress.getByAddress(new byte[]{
77                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
78              });
79  
80          } catch (final Exception x) {
81              throw new ExceptionInInitializerError(x);
82          }
83      }
84  
85      @Test
86      public void testCstrFullRoute() {
87          // create a route with all arguments and check the details
88          final HttpHost[] chain3 = { PROXY1, PROXY2, PROXY3 };
89  
90          final HttpRoute route = new HttpRoute(TARGET1, LOCAL41, chain3, false,
91                                          TunnelType.PLAIN, LayerType.PLAIN);
92          Assertions.assertEquals(TARGET1, route.getTargetHost(), "wrong target");
93          Assertions.assertEquals(LOCAL41, route.getLocalAddress(), "wrong local address");
94          Assertions.assertEquals(PROXY1, route.getProxyHost(), "wrong proxy host");
95          Assertions.assertEquals(4, route.getHopCount(), "wrong hop count");
96          Assertions.assertEquals(PROXY1, route.getHopTarget(0), "wrong hop 0");
97          Assertions.assertEquals(PROXY2, route.getHopTarget(1), "wrong hop 1");
98          Assertions.assertEquals(PROXY3, route.getHopTarget(2), "wrong hop 2");
99          Assertions.assertEquals(TARGET1, route.getHopTarget(3), "wrong hop 3");
100         Assertions.assertFalse(route.isSecure(), "wrong flag: secured");
101         Assertions.assertFalse(route.isTunnelled(), "wrong flag: tunnelled");
102         Assertions.assertFalse(route.isLayered(), "wrong flag: layered");
103 
104         final String routestr = route.toString();
105         Assertions.assertTrue(routestr.contains(TARGET1.getHostName()), "missing target in toString");
106         Assertions.assertTrue(routestr.contains(LOCAL41.toString()), "missing local address in toString");
107         Assertions.assertTrue(routestr.contains(PROXY1.getHostName()), "missing proxy 1 in toString");
108         Assertions.assertTrue(routestr.contains(PROXY2.getHostName()), "missing proxy 2 in toString");
109         Assertions.assertTrue(routestr.contains(PROXY3.getHostName()), "missing proxy 3 in toString");
110     }
111 
112     @Test
113     public void testCstrFullFlags() {
114         // tests the flag parameters in the full-blown constructor
115 
116         final HttpHost[] chain3 = { PROXY1, PROXY2, PROXY3 };
117 
118         final HttpRoute routefff = new HttpRoute
119             (TARGET1, LOCAL41, chain3, false,
120              TunnelType.PLAIN, LayerType.PLAIN);
121         final HttpRoute routefft = new HttpRoute
122             (TARGET1, LOCAL41, chain3, false,
123              TunnelType.PLAIN, LayerType.LAYERED);
124         final HttpRoute routeftf = new HttpRoute
125             (TARGET1, LOCAL41, chain3, false,
126              TunnelType.TUNNELLED, LayerType.PLAIN);
127         final HttpRoute routeftt = new HttpRoute
128             (TARGET1, LOCAL41, chain3, false,
129              TunnelType.TUNNELLED, LayerType.LAYERED);
130         final HttpRoute routetff = new HttpRoute
131             (TARGET1, LOCAL41, chain3, true,
132              TunnelType.PLAIN, LayerType.PLAIN);
133         final HttpRoute routetft = new HttpRoute
134             (TARGET1, LOCAL41, chain3, true,
135              TunnelType.PLAIN, LayerType.LAYERED);
136         final HttpRoute routettf = new HttpRoute
137             (TARGET1, LOCAL41, chain3, true,
138              TunnelType.TUNNELLED, LayerType.PLAIN);
139         final HttpRoute routettt = new HttpRoute
140             (TARGET1, LOCAL41, chain3, true,
141              TunnelType.TUNNELLED, LayerType.LAYERED);
142 
143         Assertions.assertFalse(routefff.isSecure(), "routefff.secure");
144         Assertions.assertFalse(routefff.isTunnelled(), "routefff.tunnel");
145         Assertions.assertFalse(routefff.isLayered(), "routefff.layer");
146 
147         Assertions.assertFalse(routefft.isSecure(), "routefft.secure");
148         Assertions.assertFalse(routefft.isTunnelled(), "routefft.tunnel");
149         Assertions.assertTrue (routefft.isLayered(), "routefft.layer");
150 
151         Assertions.assertFalse(routeftf.isSecure(), "routeftf.secure");
152         Assertions.assertTrue (routeftf.isTunnelled(), "routeftf.tunnel");
153         Assertions.assertFalse(routeftf.isLayered(), "routeftf.layer");
154 
155         Assertions.assertFalse(routeftt.isSecure(), "routeftt.secure");
156         Assertions.assertTrue (routeftt.isTunnelled(), "routeftt.tunnel");
157         Assertions.assertTrue (routeftt.isLayered(), "routeftt.layer");
158 
159         Assertions.assertTrue (routetff.isSecure(), "routetff.secure");
160         Assertions.assertFalse(routetff.isTunnelled(), "routetff.tunnel");
161         Assertions.assertFalse(routetff.isLayered(), "routetff.layer");
162 
163         Assertions.assertTrue (routetft.isSecure(), "routetft.secure");
164         Assertions.assertFalse(routetft.isTunnelled(), "routetft.tunnel");
165         Assertions.assertTrue (routetft.isLayered(), "routetft.layer");
166 
167         Assertions.assertTrue (routettf.isSecure(), "routettf.secure");
168         Assertions.assertTrue (routettf.isTunnelled(), "routettf.tunnel");
169         Assertions.assertFalse(routettf.isLayered(), "routettf.layer");
170 
171         Assertions.assertTrue (routettt.isSecure(), "routettt.secure");
172         Assertions.assertTrue (routettt.isTunnelled(), "routettt.tunnel");
173         Assertions.assertTrue (routettt.isLayered(), "routettt.layer");
174 
175 
176         final Set<HttpRoute> routes = new HashSet<>();
177         routes.add(routefff);
178         routes.add(routefft);
179         routes.add(routeftf);
180         routes.add(routeftt);
181         routes.add(routetff);
182         routes.add(routetft);
183         routes.add(routettf);
184         routes.add(routettt);
185         Assertions.assertEquals(8, routes.size(), "some flagged routes are equal");
186 
187         // we can't test hashCode in general due to its dependency
188         // on InetAddress and HttpHost, but we can check for the flags
189         final Set<Integer> routecodes = new HashSet<>();
190         routecodes.add(routefff.hashCode());
191         routecodes.add(routefft.hashCode());
192         routecodes.add(routeftf.hashCode());
193         routecodes.add(routeftt.hashCode());
194         routecodes.add(routetff.hashCode());
195         routecodes.add(routetft.hashCode());
196         routecodes.add(routettf.hashCode());
197         routecodes.add(routettt.hashCode());
198         Assertions.assertEquals(8, routecodes.size(), "some flagged routes have same hashCode");
199 
200         final Set<String> routestrings = new HashSet<>();
201         routestrings.add(routefff.toString());
202         routestrings.add(routefft.toString());
203         routestrings.add(routeftf.toString());
204         routestrings.add(routeftt.toString());
205         routestrings.add(routetff.toString());
206         routestrings.add(routetft.toString());
207         routestrings.add(routettf.toString());
208         routestrings.add(routettt.toString());
209         Assertions.assertEquals(8, routestrings.size(), "some flagged route.toString() are equal");
210     }
211 
212     @SuppressWarnings("unused")
213     @Test
214     public void testInvalidArguments() {
215         final HttpHost[] chain1 = { PROXY1 };
216 
217         // for reference: this one should succeed
218         final HttpRoute route = new HttpRoute(TARGET1, null, chain1, false,
219                                         TunnelType.TUNNELLED, LayerType.PLAIN);
220         Assertions.assertNotNull(route);
221 
222         Assertions.assertThrows(NullPointerException.class, () ->
223                 new HttpRoute(null, null, chain1, false,TunnelType.TUNNELLED, LayerType.PLAIN));
224         Assertions.assertThrows(IllegalArgumentException.class, () ->
225                 new HttpRoute(TARGET1, null, (HttpHost[]) null, false, TunnelType.TUNNELLED, LayerType.PLAIN));
226     }
227 
228     @Test
229     public void testNullEnums() {
230 
231         // tests the default values for the enum parameters
232         // also covers the accessors for the enum attributes
233 
234         final HttpRoute route = new HttpRoute(TARGET1, null, PROXY1, false,
235                                         null, null); // here are defaults
236 
237         Assertions.assertFalse(route.isTunnelled(), "default tunnelling");
238         Assertions.assertEquals(TunnelType.PLAIN, route.getTunnelType(), "untunnelled");
239 
240         Assertions.assertFalse(route.isLayered(), "default layering");
241         Assertions.assertEquals(LayerType.PLAIN, route.getLayerType(), "unlayered");
242     }
243 
244     @Test
245     public void testEqualsHashcodeClone() throws CloneNotSupportedException {
246         final HttpHost[] chain0 = { };
247         final HttpHost[] chain1 = { PROXY1 };
248         final HttpHost[] chain3 = { PROXY1, PROXY2, PROXY3 };
249         final HttpHost[] chain4 = { PROXY1, PROXY3, PROXY2 };
250 
251         // create some identical routes
252         final HttpRoute route1a = new HttpRoute(TARGET1, LOCAL41, chain3, false,
253                                           TunnelType.PLAIN, LayerType.PLAIN);
254         final HttpRoute route1b = new HttpRoute(TARGET1, LOCAL41, chain3, false,
255                                           TunnelType.PLAIN, LayerType.PLAIN);
256         final HttpRoute route1c = (HttpRoute) route1a.clone();
257 
258         Assertions.assertEquals(route1a, route1a, "1a 1a");
259         Assertions.assertEquals(route1a, route1b, "1a 1b");
260         Assertions.assertEquals(route1a, route1c, "1a 1c");
261 
262         Assertions.assertEquals(route1a.hashCode(), route1a.hashCode(), "hashcode 1a");
263         Assertions.assertEquals(route1a.hashCode(), route1b.hashCode(), "hashcode 1b");
264         Assertions.assertEquals(route1a.hashCode(), route1c.hashCode(), "hashcode 1c");
265 
266         Assertions.assertEquals(route1a.toString(), route1b.toString(), "toString 1b");
267         Assertions.assertEquals(route1a.toString(), route1a.toString(), "toString 1a");
268         Assertions.assertEquals(route1a.toString(), route1c.toString(), "toString 1c");
269 
270         // now create some differing routes
271         final HttpRoute route2a = new HttpRoute(TARGET2, LOCAL41, chain3, false,
272                                           TunnelType.PLAIN, LayerType.PLAIN);
273         final HttpRoute route2b = new HttpRoute(TARGET1, LOCAL42, chain3, false,
274                                           TunnelType.PLAIN, LayerType.PLAIN);
275         final HttpRoute route2c = new HttpRoute(TARGET1, LOCAL61, chain3, false,
276                                           TunnelType.PLAIN, LayerType.PLAIN);
277         final HttpRoute route2d = new HttpRoute(TARGET1, null, chain3, false,
278                                           TunnelType.PLAIN, LayerType.PLAIN);
279         final HttpRoute route2e = new HttpRoute(TARGET1, LOCAL41, (HttpHost[]) null,
280                                           false,
281                                           TunnelType.PLAIN, LayerType.PLAIN);
282         final HttpRoute route2f = new HttpRoute(TARGET1, LOCAL41, chain0, false,
283                                           TunnelType.PLAIN, LayerType.PLAIN);
284         final HttpRoute route2g = new HttpRoute(TARGET1, LOCAL41, chain1, false,
285                                           TunnelType.PLAIN, LayerType.PLAIN);
286         final HttpRoute route2h = new HttpRoute(TARGET1, LOCAL41, chain4, false,
287                                           TunnelType.PLAIN, LayerType.PLAIN);
288         final HttpRoute route2i = new HttpRoute(TARGET1, LOCAL41, chain3, true,
289                                           TunnelType.PLAIN, LayerType.PLAIN);
290         final HttpRoute route2j = new HttpRoute(TARGET1, LOCAL41, chain3, false,
291                                         TunnelType.TUNNELLED, LayerType.PLAIN);
292         final HttpRoute route2k = new HttpRoute(TARGET1, LOCAL41, chain3, false,
293                                           TunnelType.PLAIN, LayerType.LAYERED);
294 
295         // check a special case first: 2f should be the same as 2e
296         Assertions.assertEquals(route2e, route2f, "2e 2f");
297         Assertions.assertEquals(route2e.hashCode(), route2f.hashCode(), "hashcode 2e 2f");
298         Assertions.assertEquals(route2e.toString(), route2f.toString(), "toString 2e 2f");
299 
300         Assertions.assertNotEquals(route1a, route2a, "1a 2a");
301         Assertions.assertNotEquals(route1a, route2b, "1a 2b");
302         Assertions.assertNotEquals(route1a, route2c, "1a 2c");
303         Assertions.assertNotEquals(route1a, route2d, "1a 2d");
304         Assertions.assertNotEquals(route1a, route2e, "1a 2e");
305         Assertions.assertNotEquals(route1a, route2f, "1a 2f");
306         Assertions.assertNotEquals(route1a, route2g, "1a 2g");
307         Assertions.assertNotEquals(route1a, route2h, "1a 2h");
308         Assertions.assertNotEquals(route1a, route2i, "1a 2i");
309         Assertions.assertNotEquals(route1a, route2j, "1a 2j");
310         Assertions.assertNotEquals(route1a, route2k, "1a 2k");
311 
312         // repeat the checks in the other direction
313         // there could be problems with detecting null attributes
314 
315         Assertions.assertNotEquals(route2b, route1a, "2b 1a");
316         Assertions.assertNotEquals(route2c, route1a, "2c 1a");
317         Assertions.assertNotEquals(route2d, route1a, "2d 1a");
318         Assertions.assertNotEquals(route2e, route1a, "2e 1a");
319         Assertions.assertNotEquals(route2a, route1a, "2a 1a");
320         Assertions.assertNotEquals(route2f, route1a, "2f 1a");
321         Assertions.assertNotEquals(route2g, route1a, "2g 1a");
322         Assertions.assertNotEquals(route2h, route1a, "2h 1a");
323         Assertions.assertNotEquals(route2i, route1a, "2i 1a");
324         Assertions.assertNotEquals(route2j, route1a, "2j 1a");
325         Assertions.assertNotEquals(route2k, route1a, "2k 1a");
326 
327         // don't check hashCode, it's not guaranteed to be different
328 
329         Assertions.assertNotEquals(route1a.toString(), route2a.toString(), "toString 1a 2a");
330         Assertions.assertNotEquals(route1a.toString(), route2b.toString(), "toString 1a 2b");
331         Assertions.assertNotEquals(route1a.toString(), route2c.toString(), "toString 1a 2c");
332         Assertions.assertNotEquals(route1a.toString(), route2d.toString(), "toString 1a 2d");
333         Assertions.assertNotEquals(route1a.toString(), route2e.toString(), "toString 1a 2e");
334         Assertions.assertNotEquals(route1a.toString(), route2f.toString(), "toString 1a 2f");
335         Assertions.assertNotEquals(route1a.toString(), route2g.toString(), "toString 1a 2g");
336         Assertions.assertNotEquals(route1a.toString(), route2h.toString(), "toString 1a 2h");
337         Assertions.assertNotEquals(route1a.toString(), route2i.toString(), "toString 1a 2i");
338         Assertions.assertNotEquals(route1a.toString(), route2j.toString(), "toString 1a 2j");
339         Assertions.assertNotEquals(route1a.toString(), route2k.toString(), "toString 1a 2k");
340 
341         // now check that all of the routes are different from eachother
342         // except for those that aren't :-)
343         final Set<HttpRoute> routes = new HashSet<>();
344         routes.add(route1a);
345         routes.add(route2a);
346         routes.add(route2b);
347         routes.add(route2c);
348         routes.add(route2d);
349         routes.add(route2e);
350         //routes.add(route2f); // 2f is the same as 2e
351         routes.add(route2g);
352         routes.add(route2h);
353         routes.add(route2i);
354         routes.add(route2j);
355         routes.add(route2k);
356         Assertions.assertEquals(11, routes.size(), "some routes are equal");
357 
358         // and a run of cloning over the set
359         for (final HttpRoute origin : routes) {
360             final HttpRoute cloned = (HttpRoute) origin.clone();
361             Assertions.assertEquals(origin, cloned, "clone of " + origin);
362             Assertions.assertTrue(routes.contains(cloned), "clone of " + origin);
363         }
364 
365         // and don't forget toString
366         final Set<String> routestrings = new HashSet<>();
367         routestrings.add(route1a.toString());
368         routestrings.add(route2a.toString());
369         routestrings.add(route2b.toString());
370         routestrings.add(route2c.toString());
371         routestrings.add(route2d.toString());
372         routestrings.add(route2e.toString());
373         //routestrings.add(route2f.toString()); // 2f is the same as 2e
374         routestrings.add(route2g.toString());
375         routestrings.add(route2h.toString());
376         routestrings.add(route2i.toString());
377         routestrings.add(route2j.toString());
378         routestrings.add(route2k.toString());
379         Assertions.assertEquals(11, routestrings.size(), "some route.toString() are equal");
380 
381         // finally, compare with nonsense
382         Assertions.assertNotEquals(null, route1a, "route equals null");
383         Assertions.assertNotEquals("route1a", route1a, "route equals string");
384     }
385 
386     @Test
387     public void testHopping() {
388         // test getHopCount() and getHopTarget() with different proxy chains
389         final HttpHost[] proxies = null;
390         final HttpRoute  route   = new HttpRoute(TARGET1, null, proxies, true,
391                                            TunnelType.PLAIN, LayerType.PLAIN);
392         Assertions.assertEquals(1, route.getHopCount(), "A: hop count");
393         Assertions.assertEquals(TARGET1, route.getHopTarget(0), "A: hop 0");
394         Assertions.assertThrows(IllegalArgumentException.class, () -> route.getHopTarget(1));
395         Assertions.assertThrows(IllegalArgumentException.class, () ->  route.getHopTarget(-1));
396 
397         final HttpHost[] proxies2 = new HttpHost[]{ PROXY3 };
398         final HttpRoute route2   = new HttpRoute(TARGET1, LOCAL62, proxies2, false,
399                                 TunnelType.TUNNELLED, LayerType.PLAIN);
400         Assertions.assertEquals(2, route2.getHopCount(), "B: hop count");
401         Assertions.assertEquals(PROXY3, route2.getHopTarget(0), "B: hop 0");
402         Assertions.assertEquals(TARGET1, route2.getHopTarget(1), "B: hop 1");
403         Assertions.assertThrows(IllegalArgumentException.class, () -> route2.getHopTarget(2));
404         Assertions.assertThrows(IllegalArgumentException.class, () -> route2.getHopTarget(-2));
405 
406         final HttpHost[] proxies3 = new HttpHost[]{ PROXY3, PROXY1, PROXY2 };
407         final HttpRoute route3   = new HttpRoute(TARGET1, LOCAL42, proxies3, false,
408                                 TunnelType.PLAIN, LayerType.LAYERED);
409         Assertions.assertEquals(route3.getHopCount(), 4, "C: hop count");
410         Assertions.assertEquals(PROXY3 , route3.getHopTarget(0), "C: hop 0");
411         Assertions.assertEquals(PROXY1 , route3.getHopTarget(1), "C: hop 1");
412         Assertions.assertEquals(PROXY2 , route3.getHopTarget(2), "C: hop 2");
413         Assertions.assertEquals(TARGET1, route3.getHopTarget(3), "C: hop 3");
414         Assertions.assertThrows(IllegalArgumentException.class, () -> route3.getHopTarget(4));
415         Assertions.assertThrows(IllegalArgumentException.class, () -> route3.getHopTarget(Integer.MIN_VALUE));
416     }
417 
418     @Test
419     public void testCstr1() {
420         final HttpRoute route = new HttpRoute(TARGET2);
421         final HttpRoute should = new HttpRoute
422             (TARGET2, null, (HttpHost[]) null, false,
423              TunnelType.PLAIN, LayerType.PLAIN);
424         Assertions.assertEquals(route, should, "bad convenience route");
425     }
426 
427     @Test
428     public void testCstr3() {
429         // test convenience constructor with 3 arguments
430         HttpRoute route = new HttpRoute(TARGET2, LOCAL61, false);
431         HttpRoute should = new HttpRoute
432             (TARGET2, LOCAL61, (HttpHost[]) null, false,
433              TunnelType.PLAIN, LayerType.PLAIN);
434         Assertions.assertEquals(route, should, "bad convenience route 3/insecure");
435 
436         route = new HttpRoute(TARGET2, null, true);
437         should = new HttpRoute(TARGET2, null, (HttpHost[]) null, true,
438                                TunnelType.PLAIN, LayerType.PLAIN);
439         Assertions.assertEquals(route, should, "bad convenience route 3/secure");
440     }
441 
442     @SuppressWarnings("unused")
443     @Test
444     public void testCstr4() {
445         // test convenience constructor with 4 arguments
446         HttpRoute route = new HttpRoute(TARGET2, null, PROXY2, false);
447         HttpRoute should = new HttpRoute
448             (TARGET2, null, new HttpHost[]{ PROXY2 }, false,
449              TunnelType.PLAIN, LayerType.PLAIN);
450         Assertions.assertEquals(route, should, "bad convenience route 4/insecure");
451 
452         route = new HttpRoute(TARGET2, LOCAL42, PROXY1, true);
453         should = new HttpRoute
454             (TARGET2, LOCAL42, new HttpHost[]{ PROXY1 }, true,
455              TunnelType.TUNNELLED, LayerType.LAYERED);
456         Assertions.assertEquals(route, should, "bad convenience route 4/secure");
457 
458         // this constructor REQUIRES a proxy to be specified
459         Assertions.assertThrows(NullPointerException.class, () ->
460                 new HttpRoute(TARGET1, LOCAL61, null, false));
461     }
462 
463     @Test
464     public void testCstr6() {
465         // test convenience constructor with 6 arguments
466         HttpRoute route = new HttpRoute
467             (TARGET2, null, PROXY2, true,
468              TunnelType.TUNNELLED, LayerType.PLAIN);
469         HttpRoute should = new HttpRoute
470             (TARGET2, null, new HttpHost[]{ PROXY2 }, true,
471              TunnelType.TUNNELLED, LayerType.PLAIN);
472         Assertions.assertEquals(route, should, "bad convenience route 6/proxied");
473 
474         route = new HttpRoute
475             (TARGET2, null, (HttpHost) null, true,
476              TunnelType.PLAIN, LayerType.LAYERED);
477         should = new HttpRoute
478             (TARGET2, null, (HttpHost[]) null, true,
479              TunnelType.PLAIN, LayerType.LAYERED);
480         Assertions.assertEquals(route, should, "bad convenience route 6/direct");
481 
482         // handling of null vs. empty chain is checked in the equals tests
483     }
484 
485     @Test
486     public void testImmutable() throws CloneNotSupportedException {
487 
488         final HttpHost[] proxies = new HttpHost[]{ PROXY1, PROXY2, PROXY3 };
489         final HttpRoute route1 = new HttpRoute(TARGET1, null, proxies, false,
490                                          TunnelType.PLAIN, LayerType.PLAIN);
491         final HttpRoute route2 = (HttpRoute) route1.clone();
492         final HttpRoute route3 = new HttpRoute(TARGET1, null,
493                                          proxies.clone(), false,
494                                          TunnelType.PLAIN, LayerType.PLAIN);
495 
496         // modify the array that was passed to the constructor of route1
497         proxies[1] = PROXY3;
498         proxies[2] = PROXY2;
499 
500         Assertions.assertEquals(route2, route1, "route differs from clone");
501         Assertions.assertEquals(route3, route1, "route was modified");
502     }
503 
504 }