You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by GitBox <gi...@apache.org> on 2018/10/17 22:32:20 UTC

[GitHub] merlimat closed pull request #2801: Allow user to configure proxy as reverse HTTP proxy

merlimat closed pull request #2801: Allow user to configure proxy as reverse HTTP proxy
URL: https://github.com/apache/pulsar/pull/2801
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/buildtools/src/main/resources/log4j2.xml b/buildtools/src/main/resources/log4j2.xml
index a658b55b32..2fdc2d05ae 100644
--- a/buildtools/src/main/resources/log4j2.xml
+++ b/buildtools/src/main/resources/log4j2.xml
@@ -29,6 +29,7 @@
         <Root level="warn">
             <AppenderRef ref="Console" />
         </Root>
+        <Logger name="org.eclipse.jetty" level="info"/>
         <Logger name="org.apache.pulsar" level="info"/>
         <Logger name="org.apache.bookkeeper" level="info"/>
         <Logger name="org.apache.kafka" level="info"/>
diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/common/configuration/PulsarConfigurationLoader.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/common/configuration/PulsarConfigurationLoader.java
index c90580507f..df0a1ec88f 100644
--- a/pulsar-broker-common/src/main/java/org/apache/pulsar/common/configuration/PulsarConfigurationLoader.java
+++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/common/configuration/PulsarConfigurationLoader.java
@@ -78,8 +78,15 @@
         }
     }
 
+    /**
+     * Creates PulsarConfiguration and loads it with populated attribute values from provided Properties object.
+     *
+     * @param properties The properties to populate the attributed from
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    protected static <T extends PulsarConfiguration> T create(Properties properties,
+    public static <T extends PulsarConfiguration> T create(Properties properties,
             Class<? extends PulsarConfiguration> clazz) throws IOException, IllegalArgumentException {
         checkNotNull(properties);
         T configuration = null;
diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java
index 0155baad7b..18a09a6d32 100644
--- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java
+++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java
@@ -18,15 +18,25 @@
  */
 package org.apache.pulsar.proxy.server;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.pulsar.broker.authorization.PulsarAuthorizationProvider;
 import org.apache.pulsar.common.configuration.PulsarConfiguration;
 
 import com.google.common.collect.Sets;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 public class ProxyConfiguration implements PulsarConfiguration {
+    private final static Logger log = LoggerFactory.getLogger(ProxyConfiguration.class);
 
     // Local-Zookeeper quorum connection string
     private String zookeeperServers;
@@ -119,7 +129,10 @@
     // Specify whether Client certificates are required for TLS
     // Reject the Connection if the Client Certificate is not trusted.
     private boolean tlsRequireTrustedClientCertOnConnect = false;
-    
+
+    // Http redirects to redirect to non-pulsar services
+    private Set<HttpReverseProxyConfig> httpReverseProxyConfigs = Sets.newHashSet();
+
     private Properties properties = new Properties();
 
     public boolean forwardAuthorizationCredentials() {
@@ -370,6 +383,29 @@ public Properties getProperties() {
 
     public void setProperties(Properties properties) {
         this.properties = properties;
+
+        Map<String, Map<String, String>> redirects = new HashMap<>();
+        Pattern redirectPattern = Pattern.compile("^httpReverseProxy\\.([^\\.]*)\\.(.+)$");
+        Map<String, List<Matcher>> groups = properties.stringPropertyNames().stream()
+            .map((s) -> redirectPattern.matcher(s))
+            .filter(Matcher::matches)
+            .collect(Collectors.groupingBy((m) -> m.group(1))); // group by name
+
+        groups.entrySet().forEach((e) -> {
+                Map<String, String> keyToFullKey = e.getValue().stream().collect(
+                        Collectors.toMap(m -> m.group(2), m -> m.group(0)));
+                if (!keyToFullKey.containsKey("path")) {
+                    throw new IllegalArgumentException(
+                            String.format("httpReverseProxy.%s.path must be specified exactly once", e.getKey()));
+                }
+                if (!keyToFullKey.containsKey("proxyTo")) {
+                    throw new IllegalArgumentException(
+                            String.format("httpReverseProxy.%s.proxyTo must be specified exactly once", e.getKey()));
+                }
+                httpReverseProxyConfigs.add(new HttpReverseProxyConfig(e.getKey(),
+                                                    properties.getProperty(keyToFullKey.get("path")),
+                                                    properties.getProperty(keyToFullKey.get("proxyTo"))));
+            });
     }
 
     public Set<String> getTlsProtocols() {
@@ -411,4 +447,41 @@ public boolean getTlsRequireTrustedClientCertOnConnect() {
     public void setTlsRequireTrustedClientCertOnConnect(boolean tlsRequireTrustedClientCertOnConnect) {
         this.tlsRequireTrustedClientCertOnConnect = tlsRequireTrustedClientCertOnConnect;
     }
+
+    public Set<HttpReverseProxyConfig> getHttpReverseProxyConfigs() {
+        return httpReverseProxyConfigs;
+    }
+
+    public void setHttpReverseProxyConfigs(Set<HttpReverseProxyConfig> reverseProxyConfigs) {
+        this.httpReverseProxyConfigs = reverseProxyConfigs;
+    }
+
+    public static class HttpReverseProxyConfig {
+        private final String name;
+        private final String path;
+        private final String proxyTo;
+
+        HttpReverseProxyConfig(String name, String path, String proxyTo) {
+            this.name = name;
+            this.path = path;
+            this.proxyTo = proxyTo;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public String getProxyTo() {
+            return proxyTo;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("HttpReverseProxyConfig(%s, path=%s, proxyTo=%s)", name, path, proxyTo);
+        }
+    }
 }
diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyServiceStarter.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyServiceStarter.java
index 578431294e..f677fd139d 100644
--- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyServiceStarter.java
+++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyServiceStarter.java
@@ -19,18 +19,15 @@
 package org.apache.pulsar.proxy.server;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Lists.newArrayList;
 import static java.lang.Thread.setDefaultUncaughtExceptionHandler;
 import static org.apache.commons.lang3.StringUtils.isBlank;
 import static org.apache.commons.lang3.StringUtils.isEmpty;
 import static org.slf4j.bridge.SLF4JBridgeHandler.install;
 import static org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger;
 
-import java.util.List;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
 import org.apache.pulsar.broker.authentication.AuthenticationService;
 import org.apache.pulsar.common.configuration.PulsarConfigurationLoader;
+import org.eclipse.jetty.proxy.ProxyServlet;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,6 +39,7 @@
 import io.prometheus.client.hotspot.DefaultExports;
 import org.apache.pulsar.common.configuration.VipStatus;
 
+
 /**
  * Starts an instance of the Pulsar ProxyService
  *
@@ -137,22 +135,36 @@ public ProxyServiceStarter(String[] args) throws Exception {
 
         // Setup metrics
         DefaultExports.initialize();
+        addWebServerHandlers(server, config, proxyService.getDiscoveryProvider());
+
+        // start web-service
+        server.start();
+    }
+
+    public static void main(String[] args) throws Exception {
+        new ProxyServiceStarter(args);
+    }
+
+    static void addWebServerHandlers(WebServer server,
+                                     ProxyConfiguration config,
+                                     BrokerDiscoveryProvider discoveryProvider) {
         server.addServlet("/metrics", new ServletHolder(MetricsServlet.class));
         server.addRestResources("/", VipStatus.class.getPackage().getName(),
                 VipStatus.ATTRIBUTE_STATUS_FILE_PATH, config.getStatusFilePath());
 
-        AdminProxyHandler adminProxyHandler = new AdminProxyHandler(config, proxyService.getDiscoveryProvider());
+        AdminProxyHandler adminProxyHandler = new AdminProxyHandler(config, discoveryProvider);
         ServletHolder servletHolder = new ServletHolder(adminProxyHandler);
         servletHolder.setInitParameter("preserveHost", "true");
         server.addServlet("/admin", servletHolder);
         server.addServlet("/lookup", servletHolder);
 
-        // start web-service
-        server.start();
-    }
-
-    public static void main(String[] args) throws Exception {
-        new ProxyServiceStarter(args);
+        for (ProxyConfiguration.HttpReverseProxyConfig revProxy : config.getHttpReverseProxyConfigs()) {
+            log.debug("Adding reverse proxy with config {}", revProxy);
+            ServletHolder proxyHolder = new ServletHolder(ProxyServlet.Transparent.class);
+            proxyHolder.setInitParameter("proxyTo", revProxy.getProxyTo());
+            proxyHolder.setInitParameter("prefix", "/");
+            server.addServlet(revProxy.getPath(), proxyHolder);
+        }
     }
 
     private static final Logger log = LoggerFactory.getLogger(ProxyServiceStarter.class);
diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java
index 815d1ab95c..97e457f110 100644
--- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java
+++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java
@@ -27,9 +27,11 @@
 import java.net.URI;
 import java.security.GeneralSecurityException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.TimeZone;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -70,9 +72,11 @@
     private final Server server;
     private final ExecutorService webServiceExecutor;
     private final AuthenticationService authenticationService;
+    private final List<String> servletPaths = Lists.newArrayList();
     private final List<Handler> handlers = Lists.newArrayList();
     private final ProxyConfiguration config;
-    protected final int externalServicePort;
+    protected int externalServicePort;
+    private URI serviceURI = null;
 
     public WebServer(ProxyConfiguration config, AuthenticationService authenticationService) {
         this.webServiceExecutor = Executors.newFixedThreadPool(32, new DefaultThreadFactory("pulsar-external-web"));
@@ -109,7 +113,7 @@ public WebServer(ProxyConfiguration config, AuthenticationService authentication
     }
 
     public URI getServiceUri() {
-        return this.server.getURI();
+        return serviceURI;
     }
 
     public void addServlet(String basePath, ServletHolder servletHolder) {
@@ -117,6 +121,13 @@ public void addServlet(String basePath, ServletHolder servletHolder) {
     }
 
     public void addServlet(String basePath, ServletHolder servletHolder, List<Pair<String, Object>> attributes) {
+        Optional<String> existingPath = servletPaths.stream().filter(p -> p.startsWith(basePath)).findFirst();
+        if (existingPath.isPresent()) {
+            throw new IllegalArgumentException(
+                    String.format("Cannot add servlet at %s, path %s already exists", basePath, existingPath.get()));
+        }
+        servletPaths.add(basePath);
+
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
         context.setContextPath(basePath);
         context.addServlet(servletHolder, "/*");
@@ -169,6 +180,20 @@ public void start() throws Exception {
 
         try {
             server.start();
+
+            Arrays.stream(server.getConnectors())
+                .filter(c -> c instanceof ServerConnector)
+                .findFirst().ifPresent(c -> {
+                        WebServer.this.externalServicePort = ((ServerConnector) c).getPort();
+                    });
+
+            // server reports URI of first servlet, we want to strip that path off
+            URI reportedURI = server.getURI();
+            serviceURI = new URI(reportedURI.getScheme(),
+                                 null,
+                                 reportedURI.getHost(),
+                                 reportedURI.getPort(),
+                                 null, null, null);
         } catch (Exception e) {
             List<Integer> ports = new ArrayList<>();
             for (Connector c : server.getConnectors()) {
diff --git a/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyIsAHttpProxyTest.java b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyIsAHttpProxyTest.java
new file mode 100644
index 0000000000..d08619da03
--- /dev/null
+++ b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyIsAHttpProxyTest.java
@@ -0,0 +1,299 @@
+/**
+ * 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.
+ */
+package org.apache.pulsar.proxy.server;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+
+import org.apache.pulsar.broker.auth.MockedPulsarServiceBaseTest;
+import org.apache.pulsar.broker.authentication.AuthenticationService;
+import org.apache.pulsar.common.configuration.PulsarConfigurationLoader;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ProxyIsAHttpProxyTest extends MockedPulsarServiceBaseTest {
+
+    private static final Logger log = LoggerFactory.getLogger(ProxyIsAHttpProxyTest.class);
+
+    private Server backingServer1;
+    private Server backingServer2;
+    private Client client = ClientBuilder.newClient(new ClientConfig().register(LoggingFeature.class));
+
+    @Override
+    @BeforeClass
+    protected void setup() throws Exception {
+        internalSetup();
+
+        backingServer1 = new Server(0);
+        backingServer1.setHandler(newHandler("server1"));
+        backingServer1.start();
+
+        backingServer2 = new Server(0);
+        backingServer2.setHandler(newHandler("server2"));
+        backingServer2.start();
+    }
+
+    private static AbstractHandler newHandler(String text) {
+        return new AbstractHandler() {
+            @Override
+            public void handle(String target, Request baseRequest,
+                               HttpServletRequest request,HttpServletResponse response)
+                    throws IOException, ServletException {
+                response.setContentType("text/plain;charset=utf-8");
+                response.setStatus(HttpServletResponse.SC_OK);
+                baseRequest.setHandled(true);
+                response.getWriter().println(String.format("%s,%s", text, request.getRequestURI()));
+            }
+        };
+    }
+
+    @Override
+    @AfterClass
+    protected void cleanup() throws Exception {
+        internalCleanup();
+
+        backingServer1.stop();
+        backingServer2.stop();
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testRedirectNotSpecified() throws Exception {
+        Properties props = new Properties();
+
+        props.setProperty("httpReverseProxy.foobar.path", "/ui");
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testPathNotSpecified() throws Exception {
+        Properties props = new Properties();
+
+        props.setProperty("httpReverseProxy.foobar.proxyTo", backingServer1.getURI().toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+    }
+
+    @Test
+    public void testSingleRedirect() throws Exception {
+        Properties props = new Properties();
+
+        props.setProperty("httpReverseProxy.foobar.path", "/ui");
+        props.setProperty("httpReverseProxy.foobar.proxyTo", backingServer1.getURI().toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+        webServer.start();
+        try {
+            Response r = client.target(webServer.getServiceUri()).path("/ui/foobar").request().get();
+            Assert.assertEquals(r.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r.readEntity(String.class).trim(), "server1,/foobar");
+        } finally {
+            webServer.stop();
+        }
+    }
+
+    @Test
+    public void testMultipleRedirect() throws Exception {
+        Properties props = new Properties();
+
+        props.setProperty("httpReverseProxy.0.path", "/server1");
+        props.setProperty("httpReverseProxy.0.proxyTo", backingServer1.getURI().toString());
+        props.setProperty("httpReverseProxy.1.path", "/server2");
+        props.setProperty("httpReverseProxy.1.proxyTo", backingServer2.getURI().toString());
+
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+        webServer.start();
+        try {
+            Response r1 = client.target(webServer.getServiceUri()).path("/server1/foobar").request().get();
+            Assert.assertEquals(r1.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r1.readEntity(String.class).trim(), "server1,/foobar");
+
+            Response r2 = client.target(webServer.getServiceUri()).path("/server2/blahblah").request().get();
+            Assert.assertEquals(r2.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r2.readEntity(String.class).trim(), "server2,/blahblah");
+
+        } finally {
+            webServer.stop();
+        }
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testTryingToUseExistingPath() throws Exception {
+        Properties props = new Properties();
+
+        props.setProperty("httpReverseProxy.foobar.path", "/admin");
+        props.setProperty("httpReverseProxy.foobar.proxyTo", backingServer1.getURI().toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+
+    }
+
+    @Test
+    public void testLongPathInProxyTo() throws Exception {
+        Properties props = new Properties();
+        props.setProperty("httpReverseProxy.foobar.path", "/ui");
+        props.setProperty("httpReverseProxy.foobar.proxyTo",
+                          backingServer1.getURI().resolve("/foo/bar/blah/yadda/yadda/yadda").toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+        webServer.start();
+        try {
+            Response r = client.target(webServer.getServiceUri()).path("/ui/foobar").request().get();
+            Assert.assertEquals(r.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r.readEntity(String.class).trim(), "server1,/foo/bar/blah/yadda/yadda/yadda/foobar");
+        } finally {
+            webServer.stop();
+        }
+
+    }
+
+    @Test
+    public void testProxyToEndsInSlash() throws Exception {
+        Properties props = new Properties();
+        props.setProperty("httpReverseProxy.foobar.path", "/ui");
+        props.setProperty("httpReverseProxy.foobar.proxyTo",
+                          backingServer1.getURI().resolve("/foo/").toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+        webServer.start();
+        try {
+            Response r = client.target(webServer.getServiceUri()).path("/ui/foobar").request().get();
+            Assert.assertEquals(r.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r.readEntity(String.class).trim(), "server1,/foo/foobar");
+        } finally {
+            webServer.stop();
+        }
+
+    }
+
+    @Test
+    public void testLongPath() throws Exception {
+        Properties props = new Properties();
+        props.setProperty("httpReverseProxy.foobar.path", "/foo/bar/blah");
+        props.setProperty("httpReverseProxy.foobar.proxyTo", backingServer1.getURI().toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+        webServer.start();
+        try {
+            Response r = client.target(webServer.getServiceUri()).path("/foo/bar/blah/foobar").request().get();
+            Assert.assertEquals(r.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r.readEntity(String.class).trim(), "server1,/foobar");
+        } finally {
+            webServer.stop();
+        }
+    }
+
+    @Test
+    public void testPathEndsInSlash() throws Exception {
+        Properties props = new Properties();
+        props.setProperty("httpReverseProxy.foobar.path", "/ui/");
+        props.setProperty("httpReverseProxy.foobar.proxyTo", backingServer1.getURI().toString());
+        props.setProperty("servicePort", "0");
+        props.setProperty("webServicePort", "0");
+
+        ProxyConfiguration proxyConfig = PulsarConfigurationLoader.create(props, ProxyConfiguration.class);
+        AuthenticationService authService = new AuthenticationService(
+                PulsarConfigurationLoader.convertFrom(proxyConfig));
+
+        WebServer webServer = new WebServer(proxyConfig, authService);
+        ProxyServiceStarter.addWebServerHandlers(webServer, proxyConfig,
+                                                 new BrokerDiscoveryProvider(proxyConfig, mockZooKeeperClientFactory));
+        webServer.start();
+        try {
+            Response r = client.target(webServer.getServiceUri()).path("/ui/foobar").request().get();
+            Assert.assertEquals(r.getStatus(), Response.Status.OK.getStatusCode());
+            Assert.assertEquals(r.readEntity(String.class).trim(), "server1,/foobar");
+        } finally {
+            webServer.stop();
+        }
+
+    }
+
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services