diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java index 20f8fa324f3..381ccca5f9f 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java @@ -18,27 +18,6 @@ package org.eclipse.jetty.proxy; -import java.io.IOException; -import java.net.InetAddress; -import java.net.URI; -import java.net.UnknownHostException; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeoutException; - -import javax.servlet.AsyncContext; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.UnavailableException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; @@ -50,6 +29,21 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import javax.servlet.AsyncContext; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; + public abstract class AbstractProxyServlet extends HttpServlet { protected static final Set HOP_HEADERS; @@ -237,10 +231,10 @@ public abstract class AbstractProxyServlet extends HttpServlet HttpClient client = newHttpClient(); - // Redirects must be proxied as is, not followed + // Redirects must be proxied as is, not followed. client.setFollowRedirects(false); - // Must not store cookies, otherwise cookies of different clients will mix + // Must not store cookies, otherwise cookies of different clients will mix. client.setCookieStore(new HttpCookieStore.Empty()); Executor executor; @@ -291,9 +285,12 @@ public abstract class AbstractProxyServlet extends HttpServlet { client.start(); - // Content must not be decoded, otherwise the client gets confused + // Content must not be decoded, otherwise the client gets confused. client.getContentDecoderFactories().clear(); + // No protocol handlers, pass everything to the client. + client.getProtocolHandlers().clear(); + return client; } catch (Exception x) diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java index c97891da957..90605254fc0 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java @@ -18,27 +18,6 @@ package org.eclipse.jetty.proxy; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPOutputStream; - -import javax.servlet.AsyncContext; -import javax.servlet.ReadListener; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletOutputStream; -import javax.servlet.WriteListener; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.eclipse.jetty.client.ContentDecoder; import org.eclipse.jetty.client.GZIPContentDecoder; import org.eclipse.jetty.client.api.ContentProvider; @@ -55,6 +34,20 @@ import org.eclipse.jetty.util.CountingCallback; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.component.Destroyable; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPOutputStream; + @SuppressWarnings("serial") public class AsyncMiddleManServlet extends AbstractProxyServlet { @@ -275,13 +268,13 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet while (input.isReady() && !input.isFinished()) { int read = readClientRequestContent(input, buffer); - + if (_log.isDebugEnabled()) _log.debug("{} asynchronous read {} bytes on {}", getRequestId(clientRequest), read, input); if (read<0) return Action.SUCCEEDED; - + if (contentLength > 0 && read > 0) length += read; @@ -423,7 +416,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet length += contentBytes; - boolean finished = contentLength > 0 && length == contentLength; + boolean finished = contentLength >= 0 && length == contentLength; transform(transformer, content, finished, buffers); int newContentBytes = 0; @@ -455,7 +448,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet } else { - if (contentLength > 0) + if (contentLength >= 0) proxyResponse.setContentLength(-1); // Setting the WriteListener triggers an invocation to diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java index ad4e9356a09..ac4411dd668 100644 --- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java @@ -18,43 +18,9 @@ package org.eclipse.jetty.proxy; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpProxy; -import org.eclipse.jetty.client.api.ContentProvider; -import org.eclipse.jetty.client.api.ContentResponse; -import org.eclipse.jetty.client.api.Request; -import org.eclipse.jetty.client.api.Response; -import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.api.*; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.FutureResponseListener; @@ -83,6 +49,27 @@ import org.junit.Assert; import org.junit.Rule; import org.junit.Test; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + public class AsyncMiddleManServletTest { private static final Logger LOG = Log.getLogger(AsyncMiddleManServletTest.class); @@ -1263,6 +1250,45 @@ public class AsyncMiddleManServletTest Assert.assertEquals(value1, obj.get(key1)); } + @Test + public void testServer401() throws Exception + { + startServer(new HttpServlet() + { + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setStatus(HttpStatus.UNAUTHORIZED_401); + response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"test\""); + } + }); + final AtomicBoolean transformed = new AtomicBoolean(); + startProxy(new AsyncMiddleManServlet() + { + @Override + protected ContentTransformer newServerResponseContentTransformer(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse) + { + return new AfterContentTransformer() + { + @Override + public boolean transform(Source source, Sink sink) throws IOException + { + transformed.set(true); + return false; + } + }; + } + }); + startClient(); + + ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort()) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(HttpStatus.UNAUTHORIZED_401, response.getStatus()); + Assert.assertFalse(transformed.get()); + } + private Path prepareTargetTestsDir() throws IOException { final Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();