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.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
44
45 public class TestHttpRoute {
46
47
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
53
54
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
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
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
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
202
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
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
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
252 }
253 }
254
255 @Test
256 public void testNullEnums() {
257
258
259
260
261 final HttpRoute route = new HttpRoute(TARGET1, null, PROXY1, false,
262 null, null);
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
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
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
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
340
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
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
380
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
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
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
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
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
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
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
440 }
441 try {
442 final HttpHost before = route.getHopTarget(-1);
443 Assert.fail("A: hop -1 is " + before);
444 } catch (final IllegalArgumentException iax) {
445
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
460 }
461 try {
462 final HttpHost before = route.getHopTarget(-2);
463 Assert.fail("B: hop -2 is " + before);
464 } catch (final IllegalArgumentException iax) {
465
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
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
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
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
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
532 try {
533 new HttpRoute(TARGET1, LOCAL61, null, false);
534 Assert.fail("missing proxy not detected");
535 } catch (final NullPointerException iax) {
536
537 }
538 }
539
540 @Test
541 public void testCstr6() {
542
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
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
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 }