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