diff --git a/core/src/main/java/com/predic8/membrane/core/exchange/AbstractExchange.java b/core/src/main/java/com/predic8/membrane/core/exchange/AbstractExchange.java index 1f835a0aa9..655ca15a21 100644 --- a/core/src/main/java/com/predic8/membrane/core/exchange/AbstractExchange.java +++ b/core/src/main/java/com/predic8/membrane/core/exchange/AbstractExchange.java @@ -18,20 +18,17 @@ import com.predic8.membrane.core.http.*; import com.predic8.membrane.core.interceptor.*; import com.predic8.membrane.core.interceptor.Interceptor.*; -import com.predic8.membrane.core.interceptor.balancer.ExchangeNodeStatusTracker; +import com.predic8.membrane.core.interceptor.balancer.*; import com.predic8.membrane.core.model.*; -import com.predic8.membrane.core.proxies.Proxy; import com.predic8.membrane.core.proxies.*; +import com.predic8.membrane.core.proxies.Proxy; import org.slf4j.*; -import java.io.*; import java.net.*; import java.text.*; import java.util.*; -import static com.predic8.membrane.core.exchange.Exchange.TRACK_NODE_STATUS; import static com.predic8.membrane.core.exchange.ExchangeState.*; -import static java.lang.Boolean.TRUE; public abstract class AbstractExchange { private static final Logger log = LoggerFactory.getLogger(AbstractExchange.class.getName()); diff --git a/core/src/main/java/com/predic8/membrane/core/exchange/Exchange.java b/core/src/main/java/com/predic8/membrane/core/exchange/Exchange.java index 9381b85d12..fdb28183ed 100644 --- a/core/src/main/java/com/predic8/membrane/core/exchange/Exchange.java +++ b/core/src/main/java/com/predic8/membrane/core/exchange/Exchange.java @@ -37,15 +37,12 @@ public class Exchange extends AbstractExchange { * compatibility (i.e. for Java's internal HTTP client). */ public static final String ALLOW_H2 = "membrane.use.h2"; - public static final String TRACK_NODE_STATUS = "membrane.track.node.status"; public static final String SSL_CONTEXT = "membrane.ssl.context"; public static final String OAUTH2 = "membrane.oauth2"; public static final String SNI_SERVER_NAME = "membrane.sni.server.name"; public static final String WS_ORIGINAL_EXCHANGE = "membrane.ws.original.exchange"; public static final String SECURITY_SCHEMES = "membrane.security.schemes"; - private static final Logger log = LoggerFactory.getLogger(Exchange.class.getName()); - private AbstractHttpHandler handler; private String originalHostHeader = ""; diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/ExchangeNodeStatusTracker.java b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/ExchangeNodeStatusTracker.java index 99ba5388b1..1523aaa71a 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/ExchangeNodeStatusTracker.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/ExchangeNodeStatusTracker.java @@ -14,9 +14,6 @@ package com.predic8.membrane.core.interceptor.balancer; -import static com.predic8.membrane.core.exchange.Exchange.TRACK_NODE_STATUS; -import static java.lang.Boolean.TRUE; - /** * Used by the {@link LoadBalancingInterceptor} to track the status of nodes during load balancing. * diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/Connection.java b/core/src/main/java/com/predic8/membrane/core/transport/http/Connection.java index 4f5d6ffd10..de13824860 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/Connection.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/Connection.java @@ -77,6 +77,11 @@ public class Connection implements Closeable, MessageObserver, NonRelevantBodyOb private Exchange exchange; private boolean keepAttachedToExchange; + /** + * If the connection is tunneled through a proxy and the connection to the real backend uses TLS + */ + private boolean tunneled; + public static Connection open(String host, int port, String localHost, SSLProvider sslProvider, int connectTimeout) throws IOException { return open(host, port, localHost, sslProvider, null, connectTimeout); } @@ -120,18 +125,18 @@ public static Connection open(String host, int port, String localHost, SSLProvid log.debug("Opened connection on localPort: {}", con.socket.getLocalPort()); - setupStreams(con); + con.setupStreams(); return con; } - private static void setupStreams(Connection con) throws IOException { + private void setupStreams() throws IOException { if (ByteStreamLogging.isLoggingEnabled()) { String connectionName = chooseNewConnectionName(); - con.out = new BufferedOutputStream(wrapConnectionOutputStream(con.socket.getOutputStream(), connectionName + " out"), BUFFER_SIZE); - con.in = new BufferedInputStream(wrapConnectionInputStream(con.socket.getInputStream(), connectionName + " in"), BUFFER_SIZE); + out = new BufferedOutputStream(wrapConnectionOutputStream(socket.getOutputStream(), connectionName + " out"), BUFFER_SIZE); + in = new BufferedInputStream(wrapConnectionInputStream(socket.getInputStream(), connectionName + " in"), BUFFER_SIZE); } else { - con.out = new BufferedOutputStream(con.socket.getOutputStream(), BUFFER_SIZE); - con.in = new BufferedInputStream(con.socket.getInputStream(), BUFFER_SIZE); + out = new BufferedOutputStream(socket.getOutputStream(), BUFFER_SIZE); + in = new BufferedInputStream(socket.getInputStream(), BUFFER_SIZE); } } @@ -417,7 +422,8 @@ private void doTunnelHandshake(ProxyConfiguration proxy, Socket tunnel, String h throw new UnableToTunnelException("Unable to tunnel through %s:%d. Proxy returns '%s'".formatted(proxy.getHost(), proxy.getPort(), replyStr)); } - /* tunneling Handshake was successful! */ + /* tunneling Handshake was successful! */ + tunneled = true; } private static @NotNull String getHostString(ProxyConfiguration proxy) { @@ -425,7 +431,8 @@ private void doTunnelHandshake(ProxyConfiguration proxy, Socket tunnel, String h } private static byte @NotNull [] createConnectMessage(ProxyConfiguration proxy, String host, int port) { - var msg = "CONNECT %s:%d HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n".formatted(host, port, USERAGENT, getProxyAuthenticationHeader(proxy)); + var msg = "CONNECT %s:%d HTTP/1.0\r\nUser-Agent: %s\r\n%s\r\n" + .formatted(host, port, USERAGENT, getProxyAuthenticationHeader(proxy)); byte[] b; try { /* @@ -450,4 +457,11 @@ private void doTunnelHandshake(ProxyConfiguration proxy, Socket tunnel, String h public SSLProvider getSslProvider() { return sslProvider; } + + /** + * @return true If the connection is tunneled through a proxy and the connection to the real backend uses TLS + */ + public boolean isTunneled() { + return tunneled; + } } \ No newline at end of file diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/HostColonPort.java b/core/src/main/java/com/predic8/membrane/core/transport/http/HostColonPort.java index 967fed139a..f6af65832c 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/HostColonPort.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/HostColonPort.java @@ -13,6 +13,8 @@ limitations under the License. */ package com.predic8.membrane.core.transport.http; +import org.jetbrains.annotations.*; + import java.net.*; import java.util.regex.*; @@ -20,6 +22,12 @@ public record HostColonPort(boolean useSSL, String host, int port) { private static final Pattern pattern = Pattern.compile("^(https?)://([^:/#]+)(?::(\\d+))?.*"); + /** + * + * @param useSSL To compute default ports + * @param host Hostname, e.g. api.predic8.de, 127.0.0.1, [3:34:55] + * @param port port number + */ public HostColonPort(boolean useSSL, String host, int port) { this.useSSL = useSSL; this.host = host; @@ -30,6 +38,11 @@ public HostColonPort(boolean useSSL, String host, int port) { } } + /** + * + * @param useSSL To compute default ports + * @param hostAndPort e.g. api.predic8.de:443 + */ public HostColonPort(boolean useSSL, String hostAndPort) { this(useSSL, hostPart(hostAndPort), portPart(hostAndPort, useSSL ? 443 : 80)); } @@ -52,17 +65,17 @@ public URI toURI() throws URISyntaxException { } @Override - public String toString() { + public @NotNull String toString() { return host + ":" + port; } private static String hostPart(String addr) { - var colon = addr.indexOf(":"); + var colon = addr.lastIndexOf(":"); return (colon > -1 ? addr.substring(0, colon) : addr); } private static int portPart(String addr, int defaultValue) { - var colon = addr.indexOf(":"); + var colon = addr.lastIndexOf(":"); return (colon > -1 ? Integer.parseInt(addr.substring(colon + 1)) : defaultValue); } diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClient.java b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClient.java index 2f06172ff2..c156f27b48 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClient.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClient.java @@ -21,13 +21,13 @@ import com.predic8.membrane.core.transport.http.client.protocol.*; import com.predic8.membrane.core.transport.http.streampump.*; import com.predic8.membrane.core.util.*; +import org.jetbrains.annotations.*; import org.slf4j.*; -import javax.annotation.*; +import javax.annotation.Nullable; import java.io.*; import java.net.*; -import static com.predic8.membrane.core.exchange.Exchange.*; import static com.predic8.membrane.core.transport.http.client.protocol.Http2ProtocolHandler.*; import static com.predic8.membrane.core.util.HttpUtil.*; @@ -67,16 +67,18 @@ public HttpClient(@Nullable HttpClientConfiguration clientConfiguration, @Nullab } public void call(Exchange exc) throws Exception { - ProtocolHandler ph = protocolHandlerFactory.getHandler(exc, exc.getRequest().getHeader().getUpgradeProtocol()); + var ph = protocolHandlerFactory.getHandler(exc, exc.getRequest().getHeader().getUpgradeProtocol()); ph.checkUpgradeRequest(exc); configuration.getRetryHandler().executeWithRetries(exc, this::dispatchCall); ph.cleanup(exc); } private boolean dispatchCall(Exchange exc, String target, int attempt) throws Exception { - HostColonPort hcp = initializeRequest(exc, target); - - OutgoingConnectionType outConType = connectionFactory.getConnection(exc, hcp, attempt); + setAuthorizationHeader(exc); + var hcp = getHostColonPort(exc, target); + adjustHostHeader(exc, hcp); + var outConType = connectionFactory.getConnection(exc, hcp, attempt); + setRequestURI(exc.getRequest(), target, outConType.con()); if (configuration.getProxy() != null && outConType.sslProvider() == null) { // if we use a proxy for a plain HTTP (=non-HTTPS) request, attach the proxy credentials. @@ -89,38 +91,49 @@ private boolean dispatchCall(Exchange exc, String target, int attempt) throws Ex exc.getNodeStatusTracker().setNodeStatusCode(attempt, exc.getResponse().getStatusCode()); // Check for protocol upgrades - String upgradedProtocol = exc.getProperty(UPGRADED_PROTOCOL, String.class); + var upgradedProtocol = exc.getProperty(UPGRADED_PROTOCOL, String.class); if (upgradedProtocol == null) return false; - log.debug("Upgrading to {}",upgradedProtocol); + log.debug("Upgrading to {}", upgradedProtocol); StreamPump.setupConnectionForwarding(exc, outConType.con(), upgradedProtocol, streamPumpStats); outConType.con().setExchange(exc); return true; } - HostColonPort initializeRequest(Exchange exc, String dest) throws IOException { - setRequestURI(exc.getRequest(), dest); - if (configuration.getAuthentication() != null) - exc.getRequest().getHeader().setAuthorization(configuration.getAuthentication().getUsername(), configuration.getAuthentication().getPassword()); - - HostColonPort target = getTargetHostAndPort(exc.getRequest().isCONNECTRequest(), dest); + protected void adjustHostHeader(Exchange exc, HostColonPort target) { if (configuration.isAdjustHostHeader() && (exc.getProxy() == null || exc.getProxy().isTargetAdjustHostHeader())) { exc.getRequest().getHeader().setHost(target.toString()); } - return target; } + private void setAuthorizationHeader(Exchange exc) { + if (configuration.getAuthentication() != null) + exc.getRequest().getHeader().setAuthorization(configuration.getAuthentication().getUsername(), configuration.getAuthentication().getPassword()); + } - public void setStreamPumpStats(StreamPump.StreamPumpStats streamPumpStats) { - this.streamPumpStats = streamPumpStats; + /** + * @param exc Exchange + * @param dest URL for normal requests and host:port for CONNECT requests + * @return HostColonPort + */ + protected @NotNull HostColonPort getHostColonPort(Exchange exc, String dest) throws MalformedURLException { + if (exc.getRequest().isCONNECTRequest()) + return new HostColonPort(false, dest); + + try { + return HostColonPort.parse(dest); + } catch (MalformedURLException e) { + throw new MalformedURLException(""" + The exchange's destination URI %s is not valid. Specify a 'target' within + the API configuration or make sure the exchanges destinations list contains a valid URI. + """.formatted(dest)); + } } - private static boolean trackNodeStatus(Exchange exc) { - if (exc.getProperty(TRACK_NODE_STATUS) instanceof Boolean status) - return status; - return false; + public void setStreamPumpStats(StreamPump.StreamPumpStats streamPumpStats) { + this.streamPumpStats = streamPumpStats; } @Override @@ -134,30 +147,31 @@ public ConnectionFactory getConnectionFactory() { return connectionFactory; } - void setRequestURI(Request req, String dest) throws MalformedURLException { + void setRequestURI(Request req, String dest, @NotNull Connection con) throws MalformedURLException { + // Only on none TLS connections + if (req.isCONNECTRequest()) { + req.setUri(getHostColonPort(dest)); + return; + } + // Use complete URL with protocol and host. The proxy needs to know where to forward - if (configuration.getProxy() != null || req.isCONNECTRequest()) { + if (configuration.getProxy() != null && !con.isTunneled()) { req.setUri(dest); return; } if (!dest.startsWith("http")) { throw new MalformedURLException(""" - The exchange's destination URI %s does not start with 'http'. Specify a within the API configuration or make sure the exchanges destinations list contains a valid URI. + The exchange's destination URI %s does not start with 'http'. Specify a 'target' within + the API configuration or make sure the exchanges destinations list contains a valid URI. """.formatted(dest)); } req.setUri(getPathAndQueryString(dest)); } - /** - * @param connect If true, do not use TLS even when the URL starts with https - * @param dest URL - * @return HostColonPort - * @throws MalformedURLException - */ - private HostColonPort getTargetHostAndPort(boolean connect, String dest) throws MalformedURLException { - if (connect) - return new HostColonPort(false, dest); - return HostColonPort.parse(dest); + protected static @NotNull String getHostColonPort(String dest) throws MalformedURLException { + return dest.startsWith("http") + ? HostColonPort.parse(dest).toString() + : dest; } // TODO Rewrite all clients to use try with resources and then remove it diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/client/RetryHandler.java b/core/src/main/java/com/predic8/membrane/core/transport/http/client/RetryHandler.java index 629f7d6f98..3f313d66ea 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/client/RetryHandler.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/client/RetryHandler.java @@ -85,7 +85,7 @@ public void executeWithRetries(Exchange exc, RetryableCall call) throws Exceptio Exception exceptionInLastCall = null; double currentDelay = delay; for (int attempt = 0; attempt <= retries; attempt++) { - String dest = getDestination(exc, attempt); + var dest = getDestination(exc, attempt); log.debug("Attempt #{} from #{} to {}", attempt, retries + 1, dest); try { if (call.execute(exc, dest, attempt)) { diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/flow/AbstractFlowInterceptorTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/flow/AbstractFlowInterceptorTest.java index d2e6035d3d..c41f5008e1 100644 --- a/core/src/test/java/com/predic8/membrane/core/interceptor/flow/AbstractFlowInterceptorTest.java +++ b/core/src/test/java/com/predic8/membrane/core/interceptor/flow/AbstractFlowInterceptorTest.java @@ -1,3 +1,17 @@ +/* Copyright 2026 predic8 GmbH, www.predic8.com + + Licensed 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. */ + package com.predic8.membrane.core.interceptor.flow; import com.predic8.membrane.core.interceptor.log.*; diff --git a/core/src/test/java/com/predic8/membrane/core/proxies/ProxySSLTest.java b/core/src/test/java/com/predic8/membrane/core/proxies/ProxySSLTest.java index 00fe66ba52..153b695ed3 100644 --- a/core/src/test/java/com/predic8/membrane/core/proxies/ProxySSLTest.java +++ b/core/src/test/java/com/predic8/membrane/core/proxies/ProxySSLTest.java @@ -31,23 +31,28 @@ import java.util.concurrent.atomic.*; import static com.predic8.membrane.core.exchange.Exchange.*; +import static com.predic8.membrane.core.http.Request.get; import static org.junit.jupiter.api.Assertions.*; public class ProxySSLTest { public static Collection data() { + // @formatter:off return Arrays.asList(new Object[][] { - { false, false, 3032, 3033 }, { false, true, 3034, 3035 }, { true, false, 3036, 3037 }, { true, true, 3038, 3039 } + // backend TLS, proxy TLS, backend port, proxy port + { false, false, 3032, 3033 }, + { false, true, 3034, 3035 }, + { true, false, 3036, 3037 }, + { true, true, 3038, 3039 } }); + // @formatter:on } @ParameterizedTest @MethodSource("data") void test(boolean backendUsesSSL, boolean proxyUsesSSL, int backendPort, int proxyPort) throws Exception { - DefaultRouter backend = createBackend(backendUsesSSL, backendPort); - - AtomicInteger proxyCounter = new AtomicInteger(); - - DefaultRouter proxy = createProxy(proxyUsesSSL, proxyPort, proxyCounter); + var backend = createBackend(backendUsesSSL, backendPort); + var proxyCounter = new AtomicInteger(); + var proxy = createProxy(proxyUsesSSL, proxyPort, proxyCounter); testClient(backendUsesSSL, backendPort, createAndConfigureClient(proxyUsesSSL, proxyPort), proxyCounter); @@ -56,9 +61,9 @@ void test(boolean backendUsesSSL, boolean proxyUsesSSL, int backendPort, int pro } private static @NotNull DefaultRouter createProxy(boolean proxyUsesSSL, int proxyPort, AtomicInteger proxyCounter) throws IOException { - DefaultRouter proxy = new DefaultRouter(); + var proxy = new DefaultRouter(); proxy.getConfiguration().setHotDeploy(false); - ProxyRule rule = new ProxyRule(new ProxyRuleKey(proxyPort)); + var rule = new ProxyRule(new ProxyRuleKey(proxyPort)); rule.getFlow().add(new AbstractInterceptor() { @Override public Outcome handleRequest(Exchange exc) { @@ -76,13 +81,13 @@ public Outcome handleRequest(Exchange exc) { private static void testClient(boolean backendUsesSSL, int backendPort, HttpClient hc, AtomicInteger proxyCounter) throws Exception { // Step 4: Test client - Exchange exc = new Request.Builder().get("http" + (backendUsesSSL ? "s" : "") + "://localhost:" + backendPort + "/foo").buildExchange(); + var exc = get("http%s://localhost:%d/foo".formatted(backendUsesSSL ? "s" : "", backendPort)).buildExchange(); if (backendUsesSSL) { - SSLParser ssl = new SSLParser(); + var ssl = new SSLParser(); ssl.setTrustStore(new TrustStore()); ssl.getTrustStore().setLocation("classpath:/ssl-rsa-pub.keystore"); ssl.getTrustStore().setPassword("secret"); - ssl.setEndpointIdentificationAlgorithm(""); // workarond the fact that the certificate was not issued for 'localhost' + ssl.setEndpointIdentificationAlgorithm(""); // Workaround the fact that the certificate was not issued for 'localhost' exc.setProperty(SSL_CONTEXT, new StaticSSLContext(ssl, new ResolverMap(), null)); } hc.call(exc); @@ -93,27 +98,30 @@ private static void testClient(boolean backendUsesSSL, int backendPort, HttpClie private static @NotNull HttpClient createAndConfigureClient(boolean proxyUsesSSL, int proxyPort) { // Step 3: configure the client to access the backend through the proxy - HttpClientConfiguration httpClientConfiguration = new HttpClientConfiguration(); - ProxyConfiguration proxyConfiguration = new ProxyConfiguration(); - proxyConfiguration.setHost("localhost"); - proxyConfiguration.setPort(proxyPort); + var hcc = new HttpClientConfiguration(); + var pc = new ProxyConfiguration(); + pc.setHost("localhost"); + pc.setPort(proxyPort); if (proxyUsesSSL) { - SSLParser ssl = new SSLParser(); + var ssl = new SSLParser(); ssl.setTrustStore(new TrustStore()); ssl.getTrustStore().setLocation("classpath:/ssl-rsa-pub2.keystore"); ssl.getTrustStore().setPassword("secret"); ssl.setEndpointIdentificationAlgorithm(""); // workarond the fact that the certificate was not issued for 'localhost' - proxyConfiguration.setSslParser(ssl); + pc.setSslParser(ssl); } - httpClientConfiguration.setProxy(proxyConfiguration); - return new HttpClient(httpClientConfiguration); + hcc.setProxy(pc); + return new HttpClient(hcc); } private static @NotNull DefaultRouter createBackend(boolean backendUsesSSL, int backendPort) throws IOException { // Step 1: create the backend - DefaultRouter backend = new DefaultRouter(); + var backend = new DefaultRouter(); backend.getConfiguration().setHotDeploy(false); - ServiceProxy sp = new ServiceProxy(new ServiceProxyKey(backendPort), null, 0); + var ruleKey = new ServiceProxyKey(backendPort); + ruleKey.setPath("/foo"); + ruleKey.setUsePathPattern(true); + var sp = new ServiceProxy(ruleKey, null, 0); if (backendUsesSSL) { sp.setSslInboundParser(getSslParser("classpath:/ssl-rsa.keystore")); } @@ -124,7 +132,7 @@ private static void testClient(boolean backendUsesSSL, int backendPort, HttpClie } private static @NotNull SSLParser getSslParser(String location) { - SSLParser ssl = new SSLParser(); + var ssl = new SSLParser(); ssl.setKeyStore(new KeyStore()); ssl.getKeyStore().setLocation(location); ssl.getKeyStore().setKeyPassword("secret"); diff --git a/core/src/test/java/com/predic8/membrane/core/proxies/ServiceProxyTest.java b/core/src/test/java/com/predic8/membrane/core/proxies/ServiceProxyTest.java index 74db74f0be..f16e1df8f5 100644 --- a/core/src/test/java/com/predic8/membrane/core/proxies/ServiceProxyTest.java +++ b/core/src/test/java/com/predic8/membrane/core/proxies/ServiceProxyTest.java @@ -29,9 +29,9 @@ class ServiceProxyTest { private static Router router; @BeforeAll - public static void setup() throws Exception { + static void setup() throws Exception { router = new TestRouter(); - APIProxy proxyWithOutTarget = new APIProxy() {{ + var proxyWithOutTarget = new APIProxy() {{ key = new APIProxyKey(2000); }}; router.add(proxyWithOutTarget); @@ -39,7 +39,7 @@ public static void setup() throws Exception { } @AfterAll - public static void shutdown() { + static void shutdown() { router.stop(); } @@ -54,7 +54,7 @@ void foo() { .contentType(APPLICATION_PROBLEM_JSON) .body("type", equalTo("https://membrane-api.io/problems/user")) .body("detail", Matchers.containsString("/foo")) - .body("detail", Matchers.containsString("")); + .body("detail", Matchers.containsString("target")); // @formatter:on } diff --git a/core/src/test/java/com/predic8/membrane/core/transport/http/HostColonPortTest.java b/core/src/test/java/com/predic8/membrane/core/transport/http/HostColonPortTest.java index a5374bbbe7..d0e8711c2a 100644 --- a/core/src/test/java/com/predic8/membrane/core/transport/http/HostColonPortTest.java +++ b/core/src/test/java/com/predic8/membrane/core/transport/http/HostColonPortTest.java @@ -17,7 +17,7 @@ import java.net.*; -import static com.predic8.membrane.core.transport.http.HostColonPort.parse; +import static com.predic8.membrane.core.transport.http.HostColonPort.*; import static org.junit.jupiter.api.Assertions.*; @SuppressWarnings("HttpUrlsUsage") @@ -28,7 +28,7 @@ public class HostColonPortTest { static final HostColonPort HCP_HTTPS_LOCALHOST = new HostColonPort(true, "localhost", 443); static final HostColonPort HCP_HTTPS_LOCALHOST_8443 = new HostColonPort(true, "localhost", 8443); - static URI uriLocalhost, uriLocalhost8080, uriHttpsLocalhost, uriHttpsLocalhost8443; + static URI uriLocalhost, uriLocalhost8080, uriHttpsLocalhost, uriHttpsLocalhost8443; @BeforeAll static void setUp() throws URISyntaxException { @@ -80,6 +80,15 @@ void toURITests() throws URISyntaxException { assertEquals(uriHttpsLocalhost8443, HCP_HTTPS_LOCALHOST_8443.toURI()); } + + @Test + void ipv6() { + var hcp = new HostColonPort(false, "[2001:db8::1]:443"); + assertEquals("[2001:db8::1]", hcp.host()); + assertEquals(443, hcp.port()); + } + + @Nested class Parse { diff --git a/core/src/test/java/com/predic8/membrane/core/transport/http/HttpClientTest.java b/core/src/test/java/com/predic8/membrane/core/transport/http/HttpClientTest.java index 981029bcb7..e6fa562082 100644 --- a/core/src/test/java/com/predic8/membrane/core/transport/http/HttpClientTest.java +++ b/core/src/test/java/com/predic8/membrane/core/transport/http/HttpClientTest.java @@ -15,9 +15,10 @@ package com.predic8.membrane.core.transport.http; import com.predic8.membrane.core.exchange.*; -import com.predic8.membrane.core.http.*; import org.junit.jupiter.api.*; +import org.mockito.*; +import static com.predic8.membrane.core.http.Request.get; import static org.junit.jupiter.api.Assertions.*; class HttpClientTest { @@ -31,22 +32,22 @@ void setUp() { @Test void init() throws Exception { - Exchange exc = Request.get("/foo").buildExchange(); - client.initializeRequest(exc,"https://example.com"); + var exc = get("/foo").buildExchange(); + client.adjustHostHeader(exc, new HostColonPort(false,HttpClient.getHostColonPort( "https://example.com"))); assertEquals("example.com:443",exc.getRequest().getHeader().getHost()); } @Test void setRequestURISimple() throws Exception { - var request = Request.get("/bar").build(); - client.setRequestURI(request, "https://predic8.de/foo"); + var request = get("/bar").build(); + client.setRequestURI(request, "https://predic8.de/foo", Mockito.mock(Connection.class)); assertEquals("/foo", request.getUri()); } @Test void setRequestURIWithoutPath() throws Exception { - var request = Request.get("/bar").build(); - client.setRequestURI(request, "https://predic8.de"); + var request = get("/bar").build(); + client.setRequestURI(request, "https://predic8.de", Mockito.mock(Connection.class)); assertEquals("/", request.getUri()); } } \ No newline at end of file