Merged branch 'master' into '431642'.
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index afe214f..9b6eec6 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -50,6 +50,7 @@
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.Fields;
 
 public class HttpRequest implements Request
@@ -449,12 +450,34 @@
     @Override
     public Request onResponseContent(final Response.ContentListener listener)
     {
-        this.responseListeners.add(new Response.ContentListener()
+        this.responseListeners.add(new Response.AsyncContentListener()
         {
             @Override
-            public void onContent(Response response, ByteBuffer content)
+            public void onContent(Response response, ByteBuffer content, Callback callback)
             {
-                listener.onContent(response, content);
+                try
+                {
+                    listener.onContent(response, content);
+                    callback.succeeded();
+                }
+                catch (Exception x)
+                {
+                    callback.failed(x);
+                }
+            }
+        });
+        return this;
+    }
+
+    @Override
+    public Request onResponseContentAsync(final Response.AsyncContentListener listener)
+    {
+        this.responseListeners.add(new Response.AsyncContentListener()
+        {
+            @Override
+            public void onContent(Response response, ByteBuffer content, Callback callback)
+            {
+                listener.onContent(response, content, callback);
             }
         });
         return this;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
index 6fa640c..4b82405 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
@@ -108,6 +108,10 @@
 
     public void notifyContent(List<Response.ResponseListener> listeners, Response response, ByteBuffer buffer)
     {
+        // TODO: we need to create a "cumulative" callback that keeps track of how many listeners
+        // TODO: are invoked, and how many of these actually invoked the callback, and eventually
+        // TODO: call the callback passed to this method.
+
         // Slice the buffer to avoid that listeners peek into data they should not look at.
         buffer = buffer.slice();
         if (!buffer.hasRemaining())
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
index cc3b5f8..b7caf08 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
@@ -338,12 +338,18 @@
     Request onResponseHeaders(Response.HeadersListener listener);
 
     /**
-     * @param listener a listener for response content events
+     * @param listener a consuming listener for response content events
      * @return this request object
      */
     Request onResponseContent(Response.ContentListener listener);
 
     /**
+     * @param listener an asynchronous listener for response content events
+     * @return this request object
+     */
+    Request onResponseContentAsync(Response.AsyncContentListener listener);
+
+    /**
      * @param listener a listener for response success event
      * @return this request object
      */
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
index 5f60ef8..f1ef5c5 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
@@ -26,6 +26,7 @@
 import org.eclipse.jetty.http.HttpField;
 import org.eclipse.jetty.http.HttpFields;
 import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.Callback;
 
 /**
  * <p>{@link Response} represents a HTTP response and offers methods to retrieve status code, HTTP version
@@ -152,6 +153,11 @@
         public void onContent(Response response, ByteBuffer content);
     }
 
+    public interface AsyncContentListener extends ResponseListener
+    {
+        public void onContent(Response response, ByteBuffer content, Callback callback);
+    }
+
     /**
      * Listener for the response succeeded event.
      */
@@ -204,7 +210,7 @@
     /**
      * Listener for all response events.
      */
-    public interface Listener extends BeginListener, HeaderListener, HeadersListener, ContentListener, SuccessListener, FailureListener, CompleteListener
+    public interface Listener extends BeginListener, HeaderListener, HeadersListener, ContentListener, AsyncContentListener, SuccessListener, FailureListener, CompleteListener
     {
         /**
          * An empty implementation of {@link Listener}
@@ -233,6 +239,20 @@
             }
 
             @Override
+            public void onContent(Response response, ByteBuffer content, Callback callback)
+            {
+                try
+                {
+                    onContent(response, content);
+                    callback.succeeded();
+                }
+                catch (Exception x)
+                {
+                    callback.failed(x);
+                }
+            }
+
+            @Override
             public void onSuccess(Response response)
             {
             }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
index 1b688e1..0447164 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
@@ -195,6 +195,11 @@
         if (exchange == null)
             return false;
 
+        // TODO: need to create the callback here, then check whether it has completed
+        // TODO: after the call to responseContent. If it has, return false.
+        // TODO: if it has not, return true, and when will be invoked, we need to
+        // TODO: proceed with parsing.
+
         responseContent(exchange, buffer);
         return false;
     }
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
index a0af295..73ad961 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
@@ -24,6 +24,7 @@
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -33,6 +34,7 @@
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.client.api.Response;
 import org.eclipse.jetty.util.ArrayQueue;
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.Callback;
 
 /**
@@ -83,10 +85,11 @@
  */
 public class DeferredContentProvider implements AsyncContentProvider, Closeable
 {
-    private static final ByteBuffer CLOSE = ByteBuffer.allocate(0);
+    private static final Callback EMPTY_CALLBACK = new Callback.Adapter();
+    private static final AsyncChunk CLOSE = new AsyncChunk(BufferUtil.EMPTY_BUFFER, EMPTY_CALLBACK);
 
     private final Object lock = this;
-    private final Queue<ByteBuffer> chunks = new ArrayQueue<>(4, 64, lock);
+    private final Queue<AsyncChunk> chunks = new ArrayQueue<>(4, 64, lock);
     private final AtomicReference<Listener> listener = new AtomicReference<>();
     private final Iterator<ByteBuffer> iterator = new DeferredContentProviderIterator();
     private final AtomicBoolean closed = new AtomicBoolean();
@@ -127,11 +130,21 @@
      */
     public boolean offer(ByteBuffer buffer)
     {
+        return offer(buffer, EMPTY_CALLBACK);
+    }
+
+    public boolean offer(ByteBuffer buffer, Callback callback)
+    {
+        return offer(new AsyncChunk(buffer, callback));
+    }
+
+    private boolean offer(AsyncChunk chunk)
+    {
         boolean result;
         synchronized (lock)
         {
-            result = chunks.offer(buffer);
-            if (result && buffer != CLOSE)
+            result = chunks.offer(chunk);
+            if (result && chunk != CLOSE)
                 ++size;
         }
         if (result)
@@ -186,7 +199,7 @@
 
     private class DeferredContentProviderIterator implements Iterator<ByteBuffer>, Callback
     {
-        private ByteBuffer current;
+        private AsyncChunk current;
 
         @Override
         public boolean hasNext()
@@ -202,10 +215,10 @@
         {
             synchronized (lock)
             {
-                ByteBuffer element = current = chunks.poll();
-                if (element == CLOSE)
+                AsyncChunk chunk = current = chunks.poll();
+                if (chunk == CLOSE)
                     throw new NoSuchElementException();
-                return element;
+                return chunk == null ? null : chunk.buffer;
             }
         }
 
@@ -218,24 +231,39 @@
         @Override
         public void succeeded()
         {
+            AsyncChunk chunk;
             synchronized (lock)
             {
-                if (current != null)
-                {
-                    --size;
-                    lock.notify();
-                }
+                chunk = current;
+                --size;
+                lock.notify();
             }
+            chunk.callback.succeeded();
         }
 
         @Override
         public void failed(Throwable x)
         {
+            AsyncChunk chunk;
             synchronized (lock)
             {
+                chunk = current;
                 failure = x;
                 lock.notify();
             }
+            chunk.callback.failed(x);
+        }
+    }
+
+    private static class AsyncChunk
+    {
+        private final ByteBuffer buffer;
+        private final Callback callback;
+
+        private AsyncChunk(ByteBuffer buffer, Callback callback)
+        {
+            this.buffer = Objects.requireNonNull(buffer);
+            this.callback = Objects.requireNonNull(callback);
         }
     }
 }
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index bedd348..14de5bd 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -69,6 +69,7 @@
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.FuturePromise;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -986,6 +987,13 @@
             }
 
             @Override
+            public void onContent(Response response, ByteBuffer content, Callback callback)
+            {
+                // Should not be invoked
+                counter.incrementAndGet();
+            }
+
+            @Override
             public void onSuccess(Response response)
             {
                 counter.incrementAndGet();
@@ -1012,6 +1020,7 @@
                 .onResponseHeader(listener)
                 .onResponseHeaders(listener)
                 .onResponseContent(listener)
+                .onResponseContentAsync(listener)
                 .onResponseSuccess(listener)
                 .onResponseFailure(listener)
                 .send(listener);
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
new file mode 100644
index 0000000..b59dff3
--- /dev/null
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
@@ -0,0 +1,190 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.proxy;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+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.api.ContentProvider;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.util.Callback;
+
+public class AsyncProxyServlet extends ProxyServlet
+{
+    private static final String WRITE_LISTENER_ATTRIBUTE = AsyncProxyServlet.class.getName() + ".writeListener";
+
+    @Override
+    protected ContentProvider proxyRequestContent(AsyncContext asyncContext, final int requestId) throws IOException
+    {
+        ServletInputStream input = asyncContext.getRequest().getInputStream();
+        DeferredContentProvider provider = new DeferredContentProvider();
+        input.setReadListener(new StreamReader(input, requestId, provider));
+        return provider;
+    }
+
+    @Override
+    protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback) throws IOException
+    {
+        StreamWriter writeListener = (StreamWriter)request.getAttribute(WRITE_LISTENER_ATTRIBUTE);
+        if (writeListener == null)
+        {
+            writeListener = new StreamWriter(request.getAsyncContext());
+            request.setAttribute(WRITE_LISTENER_ATTRIBUTE, writeListener);
+            response.getOutputStream().setWriteListener(writeListener);
+        }
+        _log.debug("{} proxying content to downstream: {} bytes", getRequestId(request), length);
+        if (writeListener.data(buffer, offset, length, callback))
+            writeListener.onWritePossible();
+        else
+            ;// TODO: fail callback
+    }
+
+    private class StreamReader implements ReadListener, Callback
+    {
+        private final byte[] buffer = new byte[512];
+        private final ServletInputStream input;
+        private final int requestId;
+        private final DeferredContentProvider provider;
+
+        public StreamReader(ServletInputStream input, int requestId, DeferredContentProvider provider)
+        {
+            this.input = input;
+            this.requestId = requestId;
+            this.provider = provider;
+        }
+
+        @Override
+        public void onDataAvailable() throws IOException
+        {
+            _log.debug("Asynchronous read start from {}", input);
+
+            // First check for isReady() because it has
+            // side effects, and then for isFinished().
+            while (input.isReady() && !input.isFinished())
+            {
+                int read = input.read(buffer);
+                _log.debug("Asynchronous read {} bytes from {}", read, input);
+                if (read > 0)
+                {
+                    _log.debug("{} proxying content to upstream: {} bytes", requestId, read);
+                    provider.offer(ByteBuffer.wrap(buffer, 0, read), this);
+                    // Do not call isReady() so that we can apply backpressure.
+                    break;
+                }
+            }
+            if (!input.isFinished())
+                _log.debug("Asynchronous read pending from {}", input);
+        }
+
+        @Override
+        public void onAllDataRead() throws IOException
+        {
+            _log.debug("{} proxying content to upstream completed", requestId);
+            provider.close();
+        }
+
+        @Override
+        public void onError(Throwable x)
+        {
+            failed(x);
+        }
+
+        @Override
+        public void succeeded()
+        {
+            // Notify the container that it may call onDataAvailable() again.
+            input.isReady();
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            // TODO: send a response error ?
+            // complete the async context since we cannot throw an exception from here.
+        }
+    }
+
+    private class StreamWriter implements WriteListener
+    {
+        private final AsyncContext asyncContext;
+        private byte[] buffer;
+        private int offset;
+        private int length;
+        private volatile Callback callback;
+
+        private StreamWriter(AsyncContext asyncContext)
+        {
+            this.asyncContext = asyncContext;
+        }
+
+        private boolean data(byte[] bytes, int offset, int length, Callback callback)
+        {
+            if (this.callback != null)
+                return false;
+
+            this.buffer = bytes;
+            this.offset = offset;
+            this.length = length;
+            this.callback = callback;
+            return true;
+        }
+
+        @Override
+        public void onWritePossible() throws IOException
+        {
+            if (callback == null)
+            {
+                ServletOutputStream output = asyncContext.getResponse().getOutputStream();
+                output.write(buffer, offset, length);
+                if (output.isReady())
+                    complete();
+            }
+            else
+            {
+                // If we have a pending callback, it means
+                // that the write blocked but is now complete.
+                complete();
+            }
+        }
+
+        private void complete()
+        {
+            this.buffer = null;
+            this.offset = 0;
+            this.length = 0;
+            Callback callback = this.callback;
+            this.callback = null;
+            callback.succeeded();
+        }
+
+        @Override
+        public void onError(Throwable t)
+        {
+            // TODO:
+        }
+    }
+}
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 09217f0..8799234 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
@@ -427,25 +427,6 @@
         addViaHeader(proxyRequest);
         addXForwardedHeaders(proxyRequest, request);
 
-        if (hasContent)
-        {
-            proxyRequest.content(new InputStreamContentProvider(request.getInputStream())
-            {
-                @Override
-                public long getLength()
-                {
-                    return request.getContentLength();
-                }
-
-                @Override
-                protected ByteBuffer onRead(byte[] buffer, int offset, int length)
-                {
-                    _log.debug("{} proxying content to upstream: {} bytes", requestId, length);
-                    return super.onRead(buffer, offset, length);
-                }
-            });
-        }
-
         final AsyncContext asyncContext = request.startAsync();
         // We do not timeout the continuation, but the proxy request
         asyncContext.setTimeout(0);
@@ -453,6 +434,9 @@
 
         customizeProxyRequest(proxyRequest, request);
 
+        if (hasContent)
+            proxyRequest.content(proxyRequestContent(asyncContext, requestId));
+
         if (_log.isDebugEnabled())
         {
             StringBuilder builder = new StringBuilder(request.getMethod());
@@ -490,6 +474,26 @@
         proxyRequest.send(new ProxyResponseListener(request, response));
     }
 
+    protected ContentProvider proxyRequestContent(final AsyncContext asyncContext, final int requestId) throws IOException
+    {
+        final HttpServletRequest request = (HttpServletRequest)asyncContext.getRequest();
+        return new InputStreamContentProvider(request.getInputStream())
+        {
+            @Override
+            public long getLength()
+            {
+                return request.getContentLength();
+            }
+
+            @Override
+            protected ByteBuffer onRead(byte[] buffer, int offset, int length)
+            {
+                _log.debug("{} proxying content to upstream: {} bytes", requestId, length);
+                return super.onRead(buffer, offset, length);
+            }
+        };
+    }
+
     protected void onRewriteFailed(HttpServletRequest request, HttpServletResponse response) throws IOException
     {
         response.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -525,7 +529,7 @@
         }
     }
 
-    protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length) throws IOException
+    protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback) throws IOException
     {
         response.getOutputStream().write(buffer, offset, length);
         _log.debug("{} proxying content to downstream: {} bytes", getRequestId(request), length);
@@ -603,8 +607,7 @@
      * <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>
-     * <p/>
-     * For example, if a request is received at "/foo/bar", the 'proxyTo' parameter is "http://host:80/context"
+     * For example, if a request is received at /foo/bar and 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
@@ -727,7 +730,7 @@
         }
 
         @Override
-        public void onContent(Response proxyResponse, ByteBuffer content)
+        public void onContent(Response proxyResponse, ByteBuffer content, Callback callback)
         {
             byte[] buffer;
             int offset;
@@ -746,7 +749,7 @@
 
             try
             {
-                onResponseContent(request, response, proxyResponse, buffer, offset, length);
+                onResponseContent(request, response, proxyResponse, buffer, offset, length, callback);
             }
             catch (IOException x)
             {
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 7c384b0..93ea5db 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
@@ -28,7 +28,7 @@
 import java.nio.ByteBuffer;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -38,6 +38,7 @@
 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;
@@ -62,10 +63,10 @@
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.toolchain.test.AdvancedRunner;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.IO;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.StdErrLog;
@@ -76,11 +77,22 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-@RunWith(AdvancedRunner.class)
+@RunWith(Parameterized.class)
 public class ProxyServletTest
 {
     private static final String PROXIED_HEADER = "X-Proxied";
+
+    @Parameterized.Parameters
+    public static Iterable<Object[]> data()
+    {
+        return Arrays.asList(new Object[][]{
+                {new ProxyServlet()},
+                {new AsyncProxyServlet()}
+        });
+    }
+
     @Rule
     public final TestTracker tracker = new TestTracker();
     private HttpClient client;
@@ -90,12 +102,17 @@
     private Server server;
     private ServerConnector serverConnector;
 
-    private void prepareProxy(ProxyServlet proxyServlet) throws Exception
+    public ProxyServletTest(ProxyServlet proxyServlet)
     {
-        prepareProxy(proxyServlet, new HashMap<String, String>());
+        this.proxyServlet = proxyServlet;
     }
 
-    private void prepareProxy(ProxyServlet proxyServlet, Map<String, String> initParams) throws Exception
+    private void prepareProxy() throws Exception
+    {
+        prepareProxy(proxyServlet);
+    }
+
+    private void prepareProxy(ProxyServlet proxyServlet) throws Exception
     {
         proxy = new Server();
         proxyConnector = new ServerConnector(proxy);
@@ -202,8 +219,8 @@
             });
 
             ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
-                .timeout(5, TimeUnit.SECONDS)
-                .send();
+                    .timeout(5, TimeUnit.SECONDS)
+                    .send();
 
             Assert.assertEquals(500, response.getStatus());
         }
@@ -263,16 +280,16 @@
             }
         });
 
-        for ( int i = 0; i < 10; ++i )
+        for (int i = 0; i < 10; ++i)
         {
-         // Request is for the target server
+            // Request is for the target server
             responses[i] = result.newRequest("localhost", serverConnector.getLocalPort())
                     .timeout(5, TimeUnit.SECONDS)
                     .send();
         }
 
 
-        for ( int i = 0; i < 10; ++i )
+        for (int i = 0; i < 10; ++i)
         {
             Assert.assertEquals(200, responses[i].getStatus());
             Assert.assertTrue(responses[i].getHeaders().containsKey(PROXIED_HEADER));
@@ -811,7 +828,7 @@
             }
 
             @Override
-            protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length) throws IOException
+            protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback) throws IOException
             {
                 // Accumulate the response content
                 ByteArrayOutputStream baos = temp.get(request.getRequestURI());
@@ -821,7 +838,7 @@
                     temp.put(request.getRequestURI(), baos);
                 }
                 baos.write(buffer, offset, length);
-                super.onResponseContent(request, response, proxyResponse, buffer, offset, length);
+                super.onResponseContent(request, response, proxyResponse, buffer, offset, length, callback);
             }
 
             @Override
@@ -908,12 +925,14 @@
     public void shouldHandleWrongContentLength() throws Exception
     {
         prepareProxy(new ProxyServlet());
-        prepareServer(new HttpServlet() {
+        prepareServer(new HttpServlet()
+        {
             @Override
-            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+            {
                 byte[] message = "tooshort".getBytes("ascii");
                 resp.setContentType("text/plain;charset=ascii");
-                resp.setHeader("Content-Length", Long.toString(message.length+1));
+                resp.setHeader("Content-Length", Long.toString(message.length + 1));
                 resp.getOutputStream().write(message);
             }
         });