diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java index 39340778d33..99945c09375 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java @@ -316,7 +316,8 @@ public class HttpClient implements Closeable { return HttpProxy.NO_PROXY; } - private Tuple createURI(HttpRequest request) { + // for testing + static Tuple createURI(HttpRequest request) { try { List qparams = new ArrayList<>(request.params.size()); request.params.forEach((k, v) -> qparams.add(new BasicNameValuePair(k, v))); @@ -327,9 +328,19 @@ public class HttpClient implements Closeable { unescapedPathParts = Collections.emptyList(); } else { final String[] pathParts = request.path.split("/"); + final boolean isPathEndsWithSlash = request.path.endsWith("/"); unescapedPathParts = new ArrayList<>(pathParts.length); - for (String part : pathParts) { - unescapedPathParts.add(URLDecoder.decode(part, StandardCharsets.UTF_8.name())); + for (int i = 0; i < pathParts.length; i++) { + String part = pathParts[i]; + boolean isLast = i == pathParts.length - 1; + if (Strings.isEmpty(part) == false) { + String appendPart = part; + boolean appendSlash = isPathEndsWithSlash && isLast; + if (appendSlash) { + appendPart += "/"; + } + unescapedPathParts.add(URLDecoder.decode(appendPart, StandardCharsets.UTF_8.name())); + } } } diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java index e33ac5c46d2..1f8ec6bb5d8 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.watcher.common.http; import com.carrotsearch.randomizedtesting.generators.RandomStrings; import com.sun.net.httpserver.HttpsServer; import org.apache.http.HttpHeaders; +import org.apache.http.HttpHost; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -16,6 +17,7 @@ import org.apache.lucene.util.automaton.CharacterRunAutomaton; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.bootstrap.JavaVersion; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.settings.Settings; @@ -45,6 +47,7 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.security.AccessController; @@ -738,6 +741,19 @@ public class HttpClientTests extends ESTestCase { assertThat(automaton.run(randomAlphaOfLength(10)), is(true)); } + public void testCreateUri() throws Exception { + assertCreateUri("https://example.org/foo/", "/foo/"); + assertCreateUri("https://example.org/foo", "/foo"); + assertCreateUri("https://example.org/", ""); + assertCreateUri("https://example.org", ""); + } + + private void assertCreateUri(String uri, String expectedPath) { + final HttpRequest request = HttpRequest.builder().fromUrl(uri).build(); + final Tuple tuple = HttpClient.createURI(request); + assertThat(tuple.v2().getPath(), is(expectedPath)); + } + public static ClusterService mockClusterService() { ClusterService clusterService = mock(ClusterService.class); ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, new HashSet<>(HttpSettings.getSettings()));