blob: 1890d281bd92ddd2ad6244a77b11cc05c39eb4c9 [file] [log] [blame]
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl.routing;
import java.net.InetAddress;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.RouteInfo.LayerType;
import org.apache.hc.client5.http.RouteInfo.TunnelType;
import org.apache.hc.client5.http.routing.HttpRouteDirector;
import org.apache.hc.core5.http.HttpHost;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* Tests for {@link BasicRouteDirector}.
*/
public class TestRouteDirector {
// a selection of constants for generating routes
public final static
HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
public final static
HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
// It is not necessary to have extra targets for https.
// The 'layered' and 'secure' flags are specified explicitly
// for routes, they will not be determined from the scheme.
public final static
HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80);
public final static
HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080);
public final static
HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88);
public final static InetAddress LOCAL41;
public final static InetAddress LOCAL42;
public final static InetAddress LOCAL61;
public final static InetAddress LOCAL62;
// need static initializer to deal with exceptions
static {
try {
LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 });
LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 });
LOCAL61 = InetAddress.getByAddress(new byte[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
});
LOCAL62 = InetAddress.getByAddress(new byte[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
});
} catch (final Exception x) {
throw new ExceptionInInitializerError(x);
}
}
@Test
public void testIllegal() {
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route = new HttpRoute(TARGET1);
Assertions.assertThrows(NullPointerException.class, () ->
rowdy.nextStep(null, route));
}
@Test
public void testDirect() {
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route1 = new HttpRoute(TARGET1);
final HttpRoute route2 = new HttpRoute(TARGET2);
final HttpRoute route1p1 = new HttpRoute(TARGET1, null, PROXY1, false);
int step = rowdy.nextStep(route1, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1");
step = rowdy.nextStep(route2, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route2");
step = rowdy.nextStep(route1, route1);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1 not detected");
step = rowdy.nextStep(route2, route2);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route2 not detected");
step = rowdy.nextStep(route1, route2);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable target not detected");
step = rowdy.nextStep(route1, route1p1);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "invalid proxy not detected");
}
@Test
public void testProxy() {
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route1p1 = new HttpRoute(TARGET1, null, PROXY1, false);
final HttpRoute route1p2 = new HttpRoute(TARGET1, null, PROXY2, false);
final HttpRoute route2p1 = new HttpRoute(TARGET2, null, PROXY1, false);
final HttpRoute route0 = new HttpRoute(PROXY1);
final HttpRoute route1 = new HttpRoute(TARGET1);
int step = rowdy.nextStep(route1p1, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1p1");
step = rowdy.nextStep(route1p2, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1p2");
step = rowdy.nextStep(route1p1, route1p1);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1p1 not detected");
step = rowdy.nextStep(route1p2, route1p2);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1p2 not detected");
step = rowdy.nextStep(route2p1, route2p1);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route2p1 not detected");
step = rowdy.nextStep(route1p1, route1p2);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route1p2 not detected");
step = rowdy.nextStep(route1p1, route2p1);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route2p1 not detected");
step = rowdy.nextStep(route1p1, route0);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route0 not detected");
step = rowdy.nextStep(route1p1, route1);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route1 not detected");
}
@Test
public void testProxyChain() {
final HttpHost[] chainA = { PROXY1 };
final HttpHost[] chainB = { PROXY1, PROXY2 };
final HttpHost[] chainC = { PROXY2, PROXY1 };
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route1cA = new HttpRoute(TARGET1, null, chainA, false,
TunnelType.PLAIN, LayerType.PLAIN);
final HttpRoute route1cB = new HttpRoute(TARGET1, null, chainB, false,
TunnelType.PLAIN, LayerType.PLAIN);
final HttpRoute route1cC = new HttpRoute(TARGET1, null, chainC, false,
TunnelType.PLAIN, LayerType.PLAIN);
final HttpRoute route1cD = new HttpRoute(TARGET1, null, chainC, false,
TunnelType.PLAIN, LayerType.PLAIN);
int step = rowdy.nextStep(route1cA, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cA");
step = rowdy.nextStep(route1cB, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cB");
step = rowdy.nextStep(route1cC, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cC");
step = rowdy.nextStep(route1cD, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cD");
step = rowdy.nextStep(route1cB, route1cA);
Assertions.assertEquals(HttpRouteDirector.TUNNEL_PROXY, step, "wrong step to route 1cB from 1cA");
step = rowdy.nextStep(route1cB, route1cB);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route 1cB not detected");
step = rowdy.nextStep(route1cB, route1cC);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cB from 1cC not detected");
step = rowdy.nextStep(route1cB, route1cD);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cB from 1cD not detected");
step = rowdy.nextStep(route1cA, route1cB);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cA from 1cB not detected");
}
@Test
public void testLocalDirect() {
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route1l41 = new HttpRoute(TARGET1, LOCAL41, false);
final HttpRoute route1l42 = new HttpRoute(TARGET1, LOCAL42, false);
final HttpRoute route1l61 = new HttpRoute(TARGET1, LOCAL61, false);
final HttpRoute route1l00 = new HttpRoute(TARGET1, null, false);
int step = rowdy.nextStep(route1l41, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l41");
step = rowdy.nextStep(route1l42, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l42");
step = rowdy.nextStep(route1l61, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l61");
step = rowdy.nextStep(route1l00, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l00");
step = rowdy.nextStep(route1l41, route1l41);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l41 not detected");
step = rowdy.nextStep(route1l42, route1l42);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l42 not detected");
step = rowdy.nextStep(route1l61, route1l61);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l61 not detected");
step = rowdy.nextStep(route1l00, route1l00);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 not detected");
step = rowdy.nextStep(route1l41, route1l42);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l42 not detected");
step = rowdy.nextStep(route1l41, route1l61);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l61 not detected");
step = rowdy.nextStep(route1l41, route1l00);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l00 not detected");
step = rowdy.nextStep(route1l00, route1l41);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l41 not detected");
step = rowdy.nextStep(route1l00, route1l42);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l42 not detected");
step = rowdy.nextStep(route1l00, route1l61);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l61 not detected");
}
@Test
public void testDirectSecure() {
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route1u = new HttpRoute(TARGET1, null, false);
final HttpRoute route1s = new HttpRoute(TARGET1, null, true);
final HttpRoute route1p1u = new HttpRoute(TARGET1, null, PROXY1, false);
final HttpRoute route1p1s = new HttpRoute(TARGET1, null, PROXY1, true);
int step = rowdy.nextStep(route1u, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1u");
step = rowdy.nextStep(route1s, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1s");
// unrequested security is currently not tolerated
step = rowdy.nextStep(route1u, route1s);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1u from 1s not detected");
// secure layering of direct connections is currently not supported
step = rowdy.nextStep(route1s, route1u);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1u not detected");
step = rowdy.nextStep(route1s, route1p1u);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1p1u not detected");
step = rowdy.nextStep(route1s, route1p1s);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1p1s not detected");
}
@Test
public void testProxyTLS() {
final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE;
final HttpRoute route1 = new HttpRoute
(TARGET1, null, PROXY1, false,
TunnelType.PLAIN, LayerType.PLAIN);
final HttpRoute route1t = new HttpRoute
(TARGET1, null, PROXY1, false,
TunnelType.TUNNELLED, LayerType.PLAIN);
final HttpRoute route1tl = new HttpRoute
(TARGET1, null, PROXY1, false,
TunnelType.TUNNELLED, LayerType.LAYERED);
final HttpRoute route1s = new HttpRoute
(TARGET1, null, PROXY1, true,
TunnelType.PLAIN, LayerType.PLAIN);
final HttpRoute route1ts = new HttpRoute
(TARGET1, null, PROXY1, true,
TunnelType.TUNNELLED, LayerType.PLAIN);
final HttpRoute route1tls = new HttpRoute
(TARGET1, null, PROXY1, true,
TunnelType.TUNNELLED, LayerType.LAYERED);
// we don't consider a route that is layered but not tunnelled
int step = rowdy.nextStep(route1, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1");
step = rowdy.nextStep(route1t, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1t");
step = rowdy.nextStep(route1tl, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1tl");
step = rowdy.nextStep(route1s, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1s");
step = rowdy.nextStep(route1ts, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1ts");
step = rowdy.nextStep(route1tls, null);
Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1tls");
step = rowdy.nextStep(route1, route1);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1 not detected");
step = rowdy.nextStep(route1t, route1t);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1t not detected");
step = rowdy.nextStep(route1tl, route1tl);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1tl not detected");
step = rowdy.nextStep(route1s, route1s);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1s not detected");
step = rowdy.nextStep(route1ts, route1ts);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1ts not detected");
step = rowdy.nextStep(route1tls, route1tls);
Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1tls not detected");
step = rowdy.nextStep(route1, route1t);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1t not detected");
step = rowdy.nextStep(route1, route1tl);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1tl not detected");
// unrequested security is currently not tolerated
step = rowdy.nextStep(route1, route1s);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1s not detected");
step = rowdy.nextStep(route1, route1ts);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1ts not detected");
step = rowdy.nextStep(route1, route1tls);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1tls not detected");
// securing requires layering
step = rowdy.nextStep(route1s, route1);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1s from 1 not detected");
// securing requires layering, and multiple layers are not supported
step = rowdy.nextStep(route1tls, route1tl);
Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1tls from 1tl not detected");
// cases where tunnelling to the target is required
step = rowdy.nextStep(route1t, route1);
Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1t from 1");
step = rowdy.nextStep(route1tl, route1);
Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1tl from 1");
step = rowdy.nextStep(route1tls, route1);
Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1tls from 1");
// cases where layering on the tunnel is required
step = rowdy.nextStep(route1tl, route1t);
Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tl from 1t");
step = rowdy.nextStep(route1tl, route1ts);
Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tl from 1ts");
step = rowdy.nextStep(route1tls, route1t);
Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tls from 1t");
step = rowdy.nextStep(route1tls, route1ts);
Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tls from 1ts");
// There are some odd cases left over, like having a secure tunnel
// that becomes unsecure by layering, or a secure connection to a
// proxy that becomes unsecure by tunnelling to another proxy.
}
}