From 9988cf13fff6dd0dcabef340636e83b5d5ee5e06 Mon Sep 17 00:00:00 2001 From: KowalczykBartek Date: Tue, 4 Sep 2018 22:39:18 +0200 Subject: [PATCH] Allow to exclude host from being proxying #2600 Signed-off-by: KowalczykBartek Signed-off-by: KowalczykBartek --- src/main/asciidoc/dataobjects.adoc | 4 ++ .../asciidoc/java/override/dependencies.adoc | 4 +- .../vertx/core/net/ProxyOptionsConverter.java | 13 ++++++ .../core/http/impl/HttpChannelConnector.java | 10 ++++- .../vertx/core/http/impl/HttpClientImpl.java | 13 +++++- .../java/io/vertx/core/net/ProxyOptions.java | 40 ++++++++++++++++++- .../java/io/vertx/test/core/Http1xTest.java | 26 ++++++++++++ 7 files changed, 105 insertions(+), 5 deletions(-) diff --git a/src/main/asciidoc/dataobjects.adoc b/src/main/asciidoc/dataobjects.adoc index 5e52cee4069..8e1114168b0 100644 --- a/src/main/asciidoc/dataobjects.adoc +++ b/src/main/asciidoc/dataobjects.adoc @@ -2032,6 +2032,10 @@ Set the store as a buffer [frame="topbot"] |=== ^|Name | Type ^| Description +|[[excludedHosts]]`excludedHosts`|`Array of String`| ++++ +Set list of excluded hosts. ++++ |[[host]]`host`|`String`| +++ Set proxy host. diff --git a/src/main/asciidoc/java/override/dependencies.adoc b/src/main/asciidoc/java/override/dependencies.adoc index 56096b4a299..c953f098942 100644 --- a/src/main/asciidoc/java/override/dependencies.adoc +++ b/src/main/asciidoc/java/override/dependencies.adoc @@ -8,7 +8,7 @@ project descriptor to access the Vert.x Core API: io.vertx vertx-core - 3.5.3 + 3.5.4-SNAPSHOT ---- @@ -17,6 +17,6 @@ project descriptor to access the Vert.x Core API: [source,groovy,subs="+attributes"] ---- dependencies { - compile 'io.vertx:vertx-core:3.5.3' + compile 'io.vertx:vertx-core:3.5.4-SNAPSHOT' } ---- \ No newline at end of file diff --git a/src/main/generated/io/vertx/core/net/ProxyOptionsConverter.java b/src/main/generated/io/vertx/core/net/ProxyOptionsConverter.java index 498385a402e..272267d041d 100644 --- a/src/main/generated/io/vertx/core/net/ProxyOptionsConverter.java +++ b/src/main/generated/io/vertx/core/net/ProxyOptionsConverter.java @@ -27,6 +27,14 @@ class ProxyOptionsConverter { static void fromJson(JsonObject json, ProxyOptions obj) { + if (json.getValue("excludedHosts") instanceof JsonArray) { + java.util.LinkedHashSet list = new java.util.LinkedHashSet<>(); + json.getJsonArray("excludedHosts").forEach( item -> { + if (item instanceof String) + list.add((String)item); + }); + obj.setExcludedHosts(list); + } if (json.getValue("host") instanceof String) { obj.setHost((String)json.getValue("host")); } @@ -45,6 +53,11 @@ static void fromJson(JsonObject json, ProxyOptions obj) { } static void toJson(ProxyOptions obj, JsonObject json) { + if (obj.getExcludedHosts() != null) { + JsonArray array = new JsonArray(); + obj.getExcludedHosts().forEach(item -> array.add(item)); + json.put("excludedHosts", array); + } if (obj.getHost() != null) { json.put("host", obj.getHost()); } diff --git a/src/main/java/io/vertx/core/http/impl/HttpChannelConnector.java b/src/main/java/io/vertx/core/http/impl/HttpChannelConnector.java index 84a6bd5079e..e5b4900a8a7 100644 --- a/src/main/java/io/vertx/core/http/impl/HttpChannelConnector.java +++ b/src/main/java/io/vertx/core/http/impl/HttpChannelConnector.java @@ -27,6 +27,7 @@ import io.vertx.core.http.impl.pool.ConnectionListener; import io.vertx.core.http.impl.pool.ConnectionProvider; import io.vertx.core.impl.ContextImpl; +import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; import io.vertx.core.net.SocketAddress; import io.vertx.core.net.impl.ChannelProvider; @@ -116,6 +117,13 @@ public void deactivate(HttpClientConnection conn) { } } + private boolean isExcluded(ProxyOptions options, String targetProxy) { + if(options.getExcludedHosts() == null || options.getExcludedHosts().isEmpty()) { + return false; + } + return options.getExcludedHosts().contains(targetProxy); + } + private void doConnect( ConnectionListener listener, ContextImpl context, @@ -129,7 +137,7 @@ private void doConnect( ChannelProvider channelProvider; // http proxy requests are handled in HttpClientImpl, everything else can use netty proxy handler - if (options.getProxyOptions() == null || !ssl && options.getProxyOptions().getType()== ProxyType.HTTP ) { + if (options.getProxyOptions() == null || !ssl && options.getProxyOptions().getType() == ProxyType.HTTP || isExcluded(options.getProxyOptions(), host)) { channelProvider = ChannelProvider.INSTANCE; } else { channelProvider = ProxyChannelProvider.INSTANCE; diff --git a/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java b/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java index e433b1cea3e..8eceff9542f 100644 --- a/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java +++ b/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java @@ -1005,7 +1005,7 @@ private HttpClientRequest createRequest(HttpMethod method, String protocol, Stri } checkClosed(); HttpClientRequest req; - boolean useProxy = !useSSL && proxyType == ProxyType.HTTP; + boolean useProxy = useProxy(useSSL, options.getProxyOptions(), host); if (useProxy) { final int defaultPort = protocol.equals("ftp") ? 21 : 80; final String addPort = (port != -1 && port != defaultPort) ? (":" + port) : ""; @@ -1030,6 +1030,17 @@ private HttpClientRequest createRequest(HttpMethod method, String protocol, Stri return req; } + private boolean useProxy(boolean isSsl, ProxyOptions proxyOptions, String targetHost) { + if(!isSsl && proxyType == ProxyType.HTTP) { + if(proxyOptions.getExcludedHosts().isEmpty()) { + return true; + } else { + return !proxyOptions.getExcludedHosts().contains(targetHost); + } + } + return false; + } + private synchronized void checkClosed() { if (closed) { throw new IllegalStateException("Client is closed"); diff --git a/src/main/java/io/vertx/core/net/ProxyOptions.java b/src/main/java/io/vertx/core/net/ProxyOptions.java index f246dc0bff6..876455efd25 100644 --- a/src/main/java/io/vertx/core/net/ProxyOptions.java +++ b/src/main/java/io/vertx/core/net/ProxyOptions.java @@ -11,7 +11,7 @@ package io.vertx.core.net; -import java.util.Objects; +import java.util.*; import io.vertx.codegen.annotations.DataObject; import io.vertx.core.json.JsonObject; @@ -46,6 +46,7 @@ public class ProxyOptions { private String username; private String password; private ProxyType type; + private Set exludedHosts; /** * Default constructor. @@ -54,6 +55,7 @@ public ProxyOptions() { host = DEFAULT_HOST; port = DEFAULT_PORT; type = DEFAULT_TYPE; + exludedHosts = new HashSet<>(0); } /** @@ -67,6 +69,7 @@ public ProxyOptions(ProxyOptions other) { username = other.getUsername(); password = other.getPassword(); type = other.getType(); + exludedHosts = other.getExcludedHosts(); } /** @@ -199,6 +202,39 @@ public ProxyOptions setType(ProxyType type) { return this; } + /** + * Set list of excluded hosts. + * + * @param excludedHosts list of hosts to exclude from proxy logic + * @return a reference to this, so the API can be used fluently + */ + public ProxyOptions setExcludedHosts(final Set excludedHosts) { + Objects.requireNonNull(excludedHosts, "Excluded host cannot be null"); + this.exludedHosts = excludedHosts; + return this; + } + + /** + * Add host to existing excludedHost list. + * + * @param excludedHost host excluded from proxy logic + * @return a reference to this, so the API can be used fluently + */ + public ProxyOptions addExcludedHost(final String excludedHost) { + Objects.requireNonNull(excludedHost, "Excluded host cannot be null"); + this.exludedHosts.add(excludedHost); + return this; + } + + /** + * Get excluded hosts. + * + * @return list of hosts that should not be proxied. + */ + public Set getExcludedHosts() { + return exludedHosts; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -212,6 +248,7 @@ public boolean equals(Object o) { if (port != that.port) return false; if (!Objects.equals(password, that.password)) return false; if (!Objects.equals(username, that.username)) return false; + if (!Objects.equals(exludedHosts, that.exludedHosts)) return false; return true; } @@ -224,6 +261,7 @@ public int hashCode() { result = 31 * result + port; result = 31 * result + (password != null ? password.hashCode() : 0); result = 31 * result + (username != null ? username.hashCode() : 0); + result = 31 * result + exludedHosts.hashCode(); return result; } } diff --git a/src/test/java/io/vertx/test/core/Http1xTest.java b/src/test/java/io/vertx/test/core/Http1xTest.java index eec90a1ba6b..e05981c2b23 100644 --- a/src/test/java/io/vertx/test/core/Http1xTest.java +++ b/src/test/java/io/vertx/test/core/Http1xTest.java @@ -2955,6 +2955,32 @@ public void testHttpProxyRequest() throws Exception { testHttpProxyRequest2(client.get(DEFAULT_HTTP_PORT, DEFAULT_HTTP_HOST, "/")); } + @Test + public void testHttpHostExclusionOfProxyRequest() throws Exception { + startProxy(null, ProxyType.HTTP); + client.close(); + client = vertx.createHttpClient(new HttpClientOptions() + .setProxyOptions(new ProxyOptions().setType(ProxyType.HTTP).addExcludedHost("localhost").setHost("google.com").setPort(proxy.getPort()))); + + HttpClientRequest clientReq = client.get(DEFAULT_HTTP_PORT, DEFAULT_HTTP_HOST, "/"); + + server.requestHandler(req -> { + req.response().end(); + }); + + //because we excluded localhost from proxying logic, it will not reach google.com but localhost. + server.listen(onSuccess(s -> { + clientReq.handler(resp -> { + assertEquals(200, resp.statusCode()); + testComplete(); + }); + clientReq.exceptionHandler(this::fail); + clientReq.end(); + })); + + await(); + } + @Test public void testHttpProxyRequestOverrideClientSsl() throws Exception { startProxy(null, ProxyType.HTTP);