432993 - Improve handling of ProxyTo and Prefix parameters in ProxyServlet.Transparent.

Fixed case of empty context path and missing Prefix parameter.
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
index 065937c..09217f0 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
@@ -597,15 +597,14 @@
     }
 
     /**
-     * Transparent Proxy.
-     * <p/>
-     * This convenience extension to ProxyServlet configures the servlet as a transparent proxy.
-     * The servlet is configured with init parameters:
+     * This convenience extension to {@link ProxyServlet} configures the servlet as a transparent proxy.
+     * This servlet is configured with the following init parameters:
      * <ul>
-     * <li>proxyTo - a URI like http://host:80/context to which the request is proxied.
-     * <li>prefix - a URI prefix that is striped from the start of the forwarded URI.
+     * <li>proxyTo - a mandatory URI like http://host:80/context to which the request is proxied.</li>
+     * <li>prefix - an optional URI prefix that is stripped from the start of the forwarded URI.</li>
      * </ul>
-     * For example, if a request is received at /foo/bar and the 'proxyTo' parameter is "http://host:80/context"
+     * <p/>
+     * For example, if a request is received at "/foo/bar", the 'proxyTo' parameter is "http://host:80/context"
      * and the 'prefix' parameter is "/foo", then the request would be proxied to "http://host:80/context/bar".
      */
     public static class Transparent extends ProxyServlet
@@ -630,21 +629,23 @@
 
             ServletConfig config = getServletConfig();
 
-            String prefix = config.getInitParameter("prefix");
-            _prefix = prefix == null ? _prefix : prefix;
-
-            // Adjust prefix value to account for context path
-            String contextPath = getServletContext().getContextPath();
-            _prefix = _prefix == null ? contextPath : (contextPath + _prefix);
-
             String proxyTo = config.getInitParameter("proxyTo");
             _proxyTo = proxyTo == null ? _proxyTo : proxyTo;
 
             if (_proxyTo == null)
                 throw new UnavailableException("Init parameter 'proxyTo' is required.");
 
-            if (!_prefix.startsWith("/"))
-                throw new UnavailableException("Init parameter 'prefix' parameter must start with a '/'.");
+            String prefix = config.getInitParameter("prefix");
+            if (prefix != null)
+            {
+                if (!prefix.startsWith("/"))
+                    throw new UnavailableException("Init parameter 'prefix' must start with a '/'.");
+                _prefix = prefix;
+            }
+
+            // Adjust prefix value to account for context path
+            String contextPath = getServletContext().getContextPath();
+            _prefix = _prefix == null ? contextPath : (contextPath + _prefix);
 
             _log.debug(config.getServletName() + " @ " + _prefix + " to " + _proxyTo);
         }
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
index ec32b8b..7c384b0 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
@@ -18,8 +18,6 @@
 
 package org.eclipse.jetty.proxy;
 
-import static java.nio.file.StandardOpenOption.CREATE;
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -30,6 +28,7 @@
 import java.nio.ByteBuffer;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -39,7 +38,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.zip.GZIPOutputStream;
-
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
@@ -94,6 +92,11 @@
 
     private void prepareProxy(ProxyServlet proxyServlet) throws Exception
     {
+        prepareProxy(proxyServlet, new HashMap<String, String>());
+    }
+
+    private void prepareProxy(ProxyServlet proxyServlet, Map<String, String> initParams) throws Exception
+    {
         proxy = new Server();
         proxyConnector = new ServerConnector(proxy);
         proxy.addConnector(proxyConnector);
@@ -101,6 +104,7 @@
         ServletContextHandler proxyCtx = new ServletContextHandler(proxy, "/", true, false);
         this.proxyServlet = proxyServlet;
         ServletHolder proxyServletHolder = new ServletHolder(proxyServlet);
+        proxyServletHolder.setInitParameters(initParams);
         proxyCtx.addServlet(proxyServletHolder, "/*");
 
         proxy.start();
@@ -379,7 +383,7 @@
         final Path temp = Files.createTempFile(targetTestsDir, "test_", null);
         byte[] kb = new byte[1024];
         new Random().nextBytes(kb);
-        try (OutputStream output = Files.newOutputStream(temp, CREATE))
+        try (OutputStream output = Files.newOutputStream(temp, StandardOpenOption.CREATE))
         {
             for (int i = 0; i < length; ++i)
                 output.write(kb);
@@ -736,6 +740,36 @@
     }
 
     @Test
+    public void testTransparentProxyWithoutPrefix() throws Exception
+    {
+        final String target = "/test";
+        prepareServer(new HttpServlet()
+        {
+            @Override
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
+                if (req.getHeader("Via") != null)
+                    resp.addHeader(PROXIED_HEADER, "true");
+                resp.setStatus(target.equals(req.getRequestURI()) ? 200 : 404);
+            }
+        });
+
+        final String proxyTo = "http://localhost:" + serverConnector.getLocalPort();
+        ProxyServlet.Transparent proxyServlet = new ProxyServlet.Transparent();
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("proxyTo", proxyTo);
+        prepareProxy(proxyServlet, initParams);
+
+        // Make the request to the proxy, it should transparently forward to the server
+        ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort())
+                .path(target)
+                .timeout(5, TimeUnit.SECONDS)
+                .send();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertTrue(response.getHeaders().containsKey(PROXIED_HEADER));
+    }
+
+    @Test
     public void testCachingProxy() throws Exception
     {
         final byte[] content = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF};