Merge branch 'master' into gcloud-session-manager
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
index dad32dc..f038895 100644
--- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
@@ -137,7 +137,18 @@
             setByteBufferPool(new MappedByteBufferPool());
 
         if (connectionFactory == null)
-            setClientConnectionFactory(new HTTP2ClientConnectionFactory());
+        {
+            HTTP2ClientConnectionFactory h2 = new HTTP2ClientConnectionFactory();
+            ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), h2, getProtocols());
+            setClientConnectionFactory((endPoint, context) ->
+            {
+                ClientConnectionFactory factory = h2;
+                SslContextFactory sslContextFactory = (SslContextFactory)context.get(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY);
+                if (sslContextFactory != null)
+                    factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), alpn);
+                return factory.newConnection(endPoint, context);
+            });
+        }
 
         if (sessions == null)
         {
@@ -356,17 +367,7 @@
             context.put(HTTP2ClientConnectionFactory.BYTE_BUFFER_POOL_CONTEXT_KEY, getByteBufferPool());
             context.put(HTTP2ClientConnectionFactory.EXECUTOR_CONTEXT_KEY, getExecutor());
             context.put(HTTP2ClientConnectionFactory.SCHEDULER_CONTEXT_KEY, getScheduler());
-
-            ClientConnectionFactory factory = getClientConnectionFactory();
-
-            SslContextFactory sslContextFactory = (SslContextFactory)context.get(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY);
-            if (sslContextFactory != null)
-            {
-                ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), factory, getProtocols());
-                factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), alpn);
-            }
-
-            return factory.newConnection(endpoint, context);
+            return getClientConnectionFactory().newConnection(endpoint, context);
         }
 
         @Override
diff --git a/jetty-http2/http2-http-client-transport/pom.xml b/jetty-http2/http2-http-client-transport/pom.xml
index be073c2..df28dd8 100644
--- a/jetty-http2/http2-http-client-transport/pom.xml
+++ b/jetty-http2/http2-http-client-transport/pom.xml
@@ -15,6 +15,38 @@
     </properties>
 
     <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.mortbay.jetty.alpn</groupId>
+                                    <artifactId>alpn-boot</artifactId>
+                                    <version>${alpn.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>false</overWrite>
+                                    <outputDirectory>${project.build.directory}/alpn</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
+                </configuration>
+            </plugin>
+        </plugins>
     </build>
 
     <dependencies>
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
index 82e4ba3..ea16c18 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
@@ -22,19 +22,24 @@
 import java.net.InetSocketAddress;
 import java.util.Map;
 
+import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.HttpClientTransport;
 import org.eclipse.jetty.client.HttpDestination;
 import org.eclipse.jetty.client.Origin;
 import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.http.HttpScheme;
 import org.eclipse.jetty.http2.api.Session;
 import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
 import org.eclipse.jetty.io.ClientConnectionFactory;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
 import org.eclipse.jetty.util.Promise;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
 
 @ManagedObject("The HTTP/2 client transport")
 public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements HttpClientTransport
@@ -68,7 +73,7 @@
         addBean(client);
         super.doStart();
 
-        this.connectionFactory = client.getClientConnectionFactory();
+        this.connectionFactory = new HTTP2ClientConnectionFactory();
         client.setClientConnectionFactory((endPoint, context) ->
         {
             HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
@@ -128,13 +133,21 @@
             }
         };
 
-        client.connect(httpClient.getSslContextFactory(), address, listener, promise, context);
+        SslContextFactory sslContextFactory = null;
+        if (HttpScheme.HTTPS.is(destination.getScheme()))
+            sslContextFactory = httpClient.getSslContextFactory();
+
+        client.connect(sslContextFactory, address, listener, promise, context);
     }
 
     @Override
     public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
     {
-        return connectionFactory.newConnection(endPoint, context);
+        ClientConnectionFactory factory = connectionFactory;
+        SslContextFactory sslContextFactory = (SslContextFactory)context.get(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY);
+        if (sslContextFactory != null)
+            factory = new ALPNClientConnectionFactory(client.getExecutor(), factory, client.getProtocols());
+        return factory.newConnection(endPoint, context);
     }
 
     protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java
index 40f3220..7a061ec 100644
--- a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java
@@ -21,9 +21,13 @@
 import java.util.concurrent.Executor;
 
 import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class HttpClientTransportOverHTTP2Test
@@ -51,4 +55,24 @@
 
         Assert.assertTrue(http2Client.isStopped());
     }
+
+    @Ignore
+    @Test
+    public void testExternalServer() throws Exception
+    {
+        HTTP2Client http2Client = new HTTP2Client();
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client), sslContextFactory);
+        Executor executor = new QueuedThreadPool();
+        httpClient.setExecutor(executor);
+
+        httpClient.start();
+
+//        ContentResponse response = httpClient.GET("https://http2.akamai.com/");
+        ContentResponse response = httpClient.GET("https://webtide.com/");
+
+        Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
+
+        httpClient.stop();
+    }
 }
diff --git a/jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties b/jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..287d283
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/resources/jetty-logging.properties
@@ -0,0 +1,5 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.client.LEVEL=DEBUG
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
+#org.eclipse.jetty.http2.LEVEL=DEBUG
+#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
index b213a5b..1ae1a7a 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
@@ -84,29 +84,42 @@
 
     protected void failedCallback(final Callback callback, final Throwable x)
     {
-        // TODO always dispatch failure ?
-        try
+        if (callback.isNonBlocking())
         {
-            getExecutor().execute(new Runnable()
+            try
             {
-                @Override
-                public void run()
-                {
-                    try
-                    {
-                        callback.failed(x);
-                    }
-                    catch (Exception e)
-                    {
-                        LOG.warn(e);
-                    }
-                }
-            });
+                callback.failed(x);
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
         }
-        catch(RejectedExecutionException e)
+        else
         {
-            LOG.debug(e);
-            callback.failed(x);
+            try
+            {
+                getExecutor().execute(new Runnable()
+                {
+                    @Override
+                    public void run()
+                    {
+                        try
+                        {
+                            callback.failed(x);
+                        }
+                        catch (Exception e)
+                        {
+                            LOG.warn(e);
+                        }
+                    }
+                });
+            }
+            catch(RejectedExecutionException e)
+            {
+                LOG.debug(e);
+                callback.failed(x);
+            }
         }
     }
 
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
index 73acb76..0444f78 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
@@ -216,8 +216,8 @@
                 isOpen()?"Open":"CLOSED",
                 isInputShutdown()?"ISHUT":"in",
                 isOutputShutdown()?"OSHUT":"out",
-                _fillInterest.isInterested()?"R":"-",
-                _writeFlusher.isInProgress()?"W":"-",
+                _fillInterest.toStateString(),
+                _writeFlusher.toStateString(),
                 getIdleFor(),
                 getIdleTimeout(),
                 getConnection()==null?null:getConnection().getClass().getSimpleName());
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java b/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
index 4eff032..b5c48c4 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/FillInterest.java
@@ -138,7 +138,13 @@
     @Override
     public String toString()
     {
-        return String.format("FillInterest@%x{%b,%s}", hashCode(), _interested.get(), _interested.get());
+        return String.format("FillInterest@%x{%b,%s}", hashCode(), _interested.get()!=null, _interested.get());
+    }
+
+    
+    public String toStateString()
+    {
+        return _interested.get()==null?"-":"FI";
     }
 
     /**
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
index 612fa56..f2dea7d 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java
@@ -522,4 +522,23 @@
     {
         return String.format("WriteFlusher@%x{%s}", hashCode(), _state.get());
     }
+    
+    public String toStateString()
+    {
+        switch(_state.get().getType())
+        {
+            case WRITING:
+                return "W";
+            case PENDING:
+                return "P";
+            case COMPLETING:
+                return "C";
+            case IDLE:
+                return "-";
+            case FAILED:
+                return "F";
+            default:
+                return "?";
+        }
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 0c8a463..d92cb3a 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -2435,25 +2435,6 @@
     @Override
     public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
     {
-        if (getContext() == null)
-            throw new ServletException ("Unable to instantiate "+handlerClass);
-
-        try
-        {
-            //Instantiate an instance and inject it
-            T h = getContext().createInstance(handlerClass);
-
-            //TODO handle the rest of the upgrade process
-
-            return h;
-        }
-        catch (Exception e)
-        {
-            if (e instanceof ServletException)
-                throw (ServletException)e;
-            throw new ServletException(e);
-        }
+        throw new ServletException("HttpServletRequest.upgrade() not supported in Jetty");
     }
-
-
 }
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
index 42b7086..1ae25d8 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
@@ -73,6 +73,7 @@
     private static final Logger LOG = Log.getLogger(ServletHolder.class);
     private int _initOrder = -1;
     private boolean _initOnStartup=false;
+    private boolean _initialized = false;
     private Map<String, String> _roleMap;
     private String _forcedPath;
     private String _runAsRole;
@@ -81,7 +82,6 @@
     private ServletRegistration.Dynamic _registration;
     private JspContainer _jspContainer;
 
-
     private transient Servlet _servlet;
     private transient Config _config;
     private transient long _unavailable;
@@ -396,21 +396,24 @@
     public void initialize ()
     throws Exception
     {
-        super.initialize();
-        if (_extInstance || _initOnStartup)
-        {
-            try
+        if(!_initialized){
+            super.initialize();
+            if (_extInstance || _initOnStartup)
             {
-                initServlet();
-            }
-            catch(Exception e)
-            {
-                if (_servletHandler.isStartWithUnavailable())
-                    LOG.ignore(e);
-                else
-                    throw e;
+                try
+                {
+                    initServlet();
+                }
+                catch(Exception e)
+                {
+                    if (_servletHandler.isStartWithUnavailable())
+                        LOG.ignore(e);
+                    else
+                        throw e;
+                }
             }
         }
+        _initialized = true;
     }
 
 
@@ -443,6 +446,7 @@
             _servlet=null;
 
         _config=null;
+        _initialized = false;
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java
index 825a4ae..51e9f0d 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherForwardTest.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
@@ -88,6 +90,7 @@
         // 2. assert query => a=1 one
         // 1. assert query => a=1 one
 
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         servlet1 = new HttpServlet()
         {
@@ -100,6 +103,7 @@
 
                 checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -120,6 +124,7 @@
                 "Connection: close\r\n" +
                 "\r\n";
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -131,7 +136,8 @@
         // 2. assert query => a=2
         // 1. assert query => a=1
 
-        final String query1 = "a=1$20one&b=2%20two";
+        CountDownLatch latch = new CountDownLatch(1);
+        final String query1 = "a=1%20one&b=2%20two";
         final String query2 = "a=3%20three";
         final String query3 = "a=3%20three&b=2%20two";
         servlet1 = new HttpServlet()
@@ -143,9 +149,10 @@
 
                 req.getRequestDispatcher("/two?" + query2).forward(req, resp);
 
-                checkThat(req.getQueryString(),Matchers.equalTo(query1));
+                checkThat(req.getQueryString(), Matchers.equalTo(query1));
                 checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
                 checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -167,6 +174,7 @@
                 "Connection: close\r\n" +
                 "\r\n";
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -178,6 +186,7 @@
         // 2. assert query => a=1&b=2
         // 1. assert query => a=1
 
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String query2 = "b=2%20two";
         final String query3 = "b=2%20two&a=1%20one";
@@ -192,6 +201,7 @@
 
                 checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -213,6 +223,7 @@
                 "Connection: close\r\n" +
                 "\r\n";
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -224,6 +235,7 @@
         // 2. assert query => a=1 + params => a=1,2
         // 1. assert query => a=1 + params => a=1,2
 
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String form = "a=2%20two";
         servlet1 = new HttpServlet()
@@ -240,6 +252,7 @@
                 checkThat(values, Matchers.notNullValue());
                 checkThat(2, Matchers.equalTo(values.length));
                 checkThat(values, Matchers.arrayContainingInAnyOrder("1 one", "2 two"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -266,6 +279,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -277,6 +291,7 @@
         // 2. assert query => a=3 + params => a=3,2,1
         // 1. assert query => a=1 + params => a=1,2
 
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String query2 = "a=3%20three";
         final String form = "a=2%20two";
@@ -294,6 +309,7 @@
                 checkThat(values, Matchers.notNullValue());
                 checkThat(2, Matchers.equalTo(values.length));
                 checkThat(values, Matchers.arrayContainingInAnyOrder("1 one", "2 two"));
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -320,6 +336,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -331,6 +348,7 @@
         // 2. assert query => a=1&c=3 + params => a=1&b=2&c=3
         // 1. assert query => a=1 + params => a=1&b=2
 
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String query2 = "c=3%20three";
         final String query3 = "c=3%20three&a=1%20one";
@@ -348,6 +366,7 @@
                 checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
                 checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -373,6 +392,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
@@ -385,6 +405,7 @@
         // 2. assert query => a=1&c=3 + params => a=1&b=2&c=3
         // 1. assert query => a=1 + params => a=1&b=2
 
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String query2 = "c=3%20three";
         final String query3 = "c=3%20three&a=1%20one";
@@ -404,6 +425,7 @@
                 checkThat(req.getParameter("a"),Matchers.equalTo("1 one"));
                 checkThat(req.getParameter("b"),Matchers.equalTo("2 two"));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -429,12 +451,14 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
     @Test
     public void testContentCanBeReadViaInputStreamAfterForwardWithoutQuery() throws Exception
     {
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String form = "c=3%20three";
         servlet1 = new HttpServlet()
@@ -448,6 +472,7 @@
 
                 checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -473,12 +498,14 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
     @Test
     public void testContentCanBeReadViaInputStreamAfterForwardWithQuery() throws Exception
     {
+        CountDownLatch latch = new CountDownLatch(1);
         final String query1 = "a=1%20one";
         final String query2 = "b=2%20two";
         final String query3 = "b=2%20two&a=1%20one";
@@ -494,6 +521,7 @@
 
                 checkThat(req.getQueryString(),Matchers.equalTo(query1));
                 checkThat(req.getParameter("c"), Matchers.nullValue());
+                latch.countDown();
             }
         };
         servlet2 = new HttpServlet()
@@ -520,6 +548,7 @@
                 "\r\n" +
                 form;
         String response = connector.getResponses(request);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
         Assert.assertTrue(response, response.startsWith("HTTP/1.1 200"));
     }
 
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DefaultServletStarvationTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DefaultServletStarvationTest.java
deleted file mode 100644
index e090808..0000000
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DefaultServletStarvationTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
-//

-//  ========================================================================

-//  Copyright (c) 1995-2015 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.servlets;

-

-import java.io.File;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.net.Socket;

-import java.nio.channels.SelectionKey;

-import java.nio.channels.SocketChannel;

-import java.nio.charset.StandardCharsets;

-import java.nio.file.Files;

-import java.nio.file.Path;

-import java.nio.file.Paths;

-import java.nio.file.StandardOpenOption;

-import java.util.ArrayList;

-import java.util.Arrays;

-import java.util.List;

-import java.util.concurrent.CountDownLatch;

-import java.util.concurrent.Executors;

-import java.util.concurrent.ScheduledFuture;

-import java.util.concurrent.TimeUnit;

-

-import org.eclipse.jetty.io.ManagedSelector;

-import org.eclipse.jetty.io.SelectChannelEndPoint;

-import org.eclipse.jetty.server.Server;

-import org.eclipse.jetty.server.ServerConnector;

-import org.eclipse.jetty.servlet.DefaultServlet;

-import org.eclipse.jetty.servlet.ServletContextHandler;

-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;

-import org.eclipse.jetty.toolchain.test.TestTracker;

-import org.eclipse.jetty.util.thread.QueuedThreadPool;

-import org.junit.After;

-import org.junit.Assert;

-import org.junit.Rule;

-import org.junit.Test;

-

-public class DefaultServletStarvationTest

-{

-    @Rule

-    public TestTracker tracker = new TestTracker();

-    private Server _server;

-

-    @After

-    public void dispose() throws Exception

-    {

-        if (_server != null)

-            _server.stop();

-    }

-

-    @Test

-    public void testDefaultServletStarvation() throws Exception

-    {

-        int maxThreads = 2;

-        QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);

-        threadPool.setDetailedDump(true);

-        _server = new Server(threadPool);

-

-        // Prepare a big file to download.

-        File directory = MavenTestingUtils.getTargetTestingDir();

-        Files.createDirectories(directory.toPath());

-        String resourceName = "resource.bin";

-        Path resourcePath = Paths.get(directory.getPath(), resourceName);

-        try (OutputStream output = Files.newOutputStream(resourcePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))

-        {

-            byte[] chunk = new byte[1024];

-            Arrays.fill(chunk,(byte)'X');

-            chunk[chunk.length-2]='\r';

-            chunk[chunk.length-1]='\n';

-            for (int i = 0; i < 256 * 1024; ++i)

-                output.write(chunk);

-        }

-

-        final CountDownLatch writePending = new CountDownLatch(1);

-        ServerConnector connector = new ServerConnector(_server, 0, 1)

-        {

-            @Override

-            protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException

-            {

-                return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout())

-                {

-                    @Override

-                    protected void onIncompleteFlush()

-                    {

-                        super.onIncompleteFlush();

-                        writePending.countDown();

-                    }

-                };

-            }

-        };

-        _server.addConnector(connector);

-

-        ServletContextHandler context = new ServletContextHandler(_server, "/");

-        context.setResourceBase(directory.toURI().toString());

-        context.addServlet(DefaultServlet.class, "/*").setAsyncSupported(false);

-        _server.setHandler(context);

-

-        _server.start();

-

-        List<Socket> sockets = new ArrayList<>();

-        for (int i = 0; i < maxThreads; ++i)

-        {

-            Socket socket = new Socket("localhost", connector.getLocalPort());

-            sockets.add(socket);

-            OutputStream output = socket.getOutputStream();

-            String request = "" +

-                    "GET /" + resourceName + " HTTP/1.1\r\n" +

-                    "Host: localhost\r\n" +

-//                    "Connection: close\r\n" +

-                    "\r\n";

-            output.write(request.getBytes(StandardCharsets.UTF_8));

-            output.flush();

-            Thread.sleep(100);

-        }

-

-

-        // Wait for a the servlet to block.

-        Assert.assertTrue(writePending.await(5, TimeUnit.SECONDS));

-

-        Thread.sleep(1000);

-        _server.dumpStdErr();

-        Thread.sleep(1000);

-

-

-        ScheduledFuture<?> dumper = Executors.newSingleThreadScheduledExecutor().schedule(new Runnable()

-        {

-            @Override

-            public void run()

-            {

-                _server.dumpStdErr();

-            }

-        }, 10, TimeUnit.SECONDS);

-

-

-        long expected = Files.size(resourcePath);

-        byte[] buffer = new byte[48 * 1024];

-        for (Socket socket : sockets)

-        {

-            String socketString = socket.toString();

-            long total = 0;

-            InputStream input = socket.getInputStream();

-

-            // look for CRLFCRLF

-            StringBuilder header = new StringBuilder();

-            int state=0;

-            while (state<4 && header.length()<2048)

-            {

-                int ch=input.read();

-                if (ch<0)

-                    break;

-                header.append((char)ch);

-                switch(state)

-                {

-                    case 0:

-                        if (ch=='\r')

-                            state=1;

-                        break;

-                    case 1:

-                        if (ch=='\n')

-                            state=2;

-                        else

-                            state=0;

-                        break;

-                    case 2:

-                        if (ch=='\r')

-                            state=3;

-                        else

-                            state=0;

-                        break;

-                    case 3:

-                        if (ch=='\n')

-                            state=4;

-                        else

-                            state=0;

-                        break;

-                }

-            }

-

-            while (total<expected)

-            {

-                int read=input.read(buffer);

-                if (read<0)

-                    break;

-                total+=read;

-            }

-

-            Assert.assertEquals(expected,total);

-        }

-

-        dumper.cancel(false);

-

-        // We could read everything, good.

-        for (Socket socket : sockets)

-            socket.close();

-    }

-}

diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java
new file mode 100644
index 0000000..9243ca7
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ThreadStarvationTest.java
@@ -0,0 +1,419 @@
+//

+//  ========================================================================

+//  Copyright (c) 1995-2015 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.servlets;

+

+import java.io.File;

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

+import java.net.Socket;

+import java.nio.ByteBuffer;

+import java.nio.channels.SelectionKey;

+import java.nio.channels.SocketChannel;

+import java.nio.charset.StandardCharsets;

+import java.nio.file.Files;

+import java.nio.file.Path;

+import java.nio.file.Paths;

+import java.nio.file.StandardOpenOption;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+import java.util.concurrent.BrokenBarrierException;

+import java.util.concurrent.CountDownLatch;

+import java.util.concurrent.CyclicBarrier;

+import java.util.concurrent.Exchanger;

+import java.util.concurrent.Executors;

+import java.util.concurrent.ScheduledFuture;

+import java.util.concurrent.TimeUnit;

+import java.util.concurrent.TimeoutException;

+import java.util.concurrent.atomic.AtomicBoolean;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import org.eclipse.jetty.io.ManagedSelector;

+import org.eclipse.jetty.io.SelectChannelEndPoint;

+import org.eclipse.jetty.server.HttpChannel;

+import org.eclipse.jetty.server.Request;

+import org.eclipse.jetty.server.Server;

+import org.eclipse.jetty.server.ServerConnector;

+import org.eclipse.jetty.server.handler.AbstractHandler;

+import org.eclipse.jetty.servlet.DefaultServlet;

+import org.eclipse.jetty.servlet.ServletContextHandler;

+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.log.Log;

+import org.eclipse.jetty.util.log.StdErrLog;

+import org.eclipse.jetty.util.thread.QueuedThreadPool;

+import org.junit.After;

+import org.junit.Assert;

+import org.junit.Rule;

+import org.junit.Test;

+

+public class ThreadStarvationTest

+{

+    @Rule

+    public TestTracker tracker = new TestTracker();

+    private Server _server;

+

+    @After

+    public void dispose() throws Exception

+    {

+        if (_server != null)

+            _server.stop();

+    }

+

+    @Test

+    @Slow

+    public void testDefaultServletSuccess() throws Exception

+    {

+        int maxThreads = 10;

+        QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);

+        threadPool.setDetailedDump(true);

+        _server = new Server(threadPool);

+

+        // Prepare a big file to download.

+        File directory = MavenTestingUtils.getTargetTestingDir();

+        Files.createDirectories(directory.toPath());

+        String resourceName = "resource.bin";

+        Path resourcePath = Paths.get(directory.getPath(), resourceName);

+        try (OutputStream output = Files.newOutputStream(resourcePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))

+        {

+            byte[] chunk = new byte[1024];

+            Arrays.fill(chunk,(byte)'X');

+            chunk[chunk.length-2]='\r';

+            chunk[chunk.length-1]='\n';

+            for (int i = 0; i < 256 * 1024; ++i)

+                output.write(chunk);

+        }

+

+        final CountDownLatch writePending = new CountDownLatch(1);

+        ServerConnector connector = new ServerConnector(_server, 0, 1)

+        {

+            @Override

+            protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException

+            {

+                return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout())

+                {

+                    @Override

+                    protected void onIncompleteFlush()

+                    {

+                        super.onIncompleteFlush();

+                        writePending.countDown();

+                    }

+                };

+            }

+        };

+        connector.setIdleTimeout(Long.MAX_VALUE);

+        _server.addConnector(connector);

+

+        ServletContextHandler context = new ServletContextHandler(_server, "/");

+        context.setResourceBase(directory.toURI().toString());

+        context.addServlet(DefaultServlet.class, "/*").setAsyncSupported(false);

+        _server.setHandler(context);

+

+        _server.start();

+

+        List<Socket> sockets = new ArrayList<>();

+        for (int i = 0; i < maxThreads*2; ++i)

+        {

+            Socket socket = new Socket("localhost", connector.getLocalPort());

+            sockets.add(socket);

+            OutputStream output = socket.getOutputStream();

+            String request = "" +

+                    "GET /" + resourceName + " HTTP/1.1\r\n" +

+                    "Host: localhost\r\n" +

+                    "\r\n";

+            output.write(request.getBytes(StandardCharsets.UTF_8));

+            output.flush();

+            Thread.sleep(100);

+        }

+

+        // Wait for a the servlet to block.

+        Assert.assertTrue(writePending.await(5, TimeUnit.SECONDS));

+

+        long expected = Files.size(resourcePath);

+        byte[] buffer = new byte[48 * 1024];

+        List<Exchanger<Long>> totals = new ArrayList<>();

+        for (Socket socket : sockets)

+        {

+            final Exchanger<Long> x = new Exchanger<>();

+            totals.add(x);

+            final InputStream input = socket.getInputStream();

+

+            new Thread()

+            {

+                @Override

+                public void run()

+                {

+                    long total=0;

+                    try

+                    {

+                        // look for CRLFCRLF

+                        StringBuilder header = new StringBuilder();

+                        int state=0;

+                        while (state<4 && header.length()<2048)

+                        {

+                            int ch=input.read();

+                            if (ch<0)

+                                break;

+                            header.append((char)ch);

+                            switch(state)

+                            {

+                                case 0:

+                                    if (ch=='\r')

+                                        state=1;

+                                    break;

+                                case 1:

+                                    if (ch=='\n')

+                                        state=2;

+                                    else

+                                        state=0;

+                                    break;

+                                case 2:

+                                    if (ch=='\r')

+                                        state=3;

+                                    else

+                                        state=0;

+                                    break;

+                                case 3:

+                                    if (ch=='\n')

+                                        state=4;

+                                    else

+                                        state=0;

+                                    break;

+                            }

+                        }

+

+                        while (total<expected)

+                        {

+                            int read=input.read(buffer);

+                            if (read<0)

+                                break;

+                            total+=read;

+                        }

+                    }

+                    catch (IOException e)

+                    {

+                        e.printStackTrace();

+                    }

+                    finally

+                    {

+                        try

+                        {

+                            x.exchange(total);

+                        }

+                        catch (InterruptedException e)

+                        {

+                            e.printStackTrace();

+                        }

+                    }

+                }

+            }.start();

+        }

+

+        for (Exchanger<Long> x : totals)

+        {

+            Long total = x.exchange(-1L,10000,TimeUnit.SECONDS);

+            Assert.assertEquals(expected,total.longValue());

+        }

+        

+        // We could read everything, good.

+        for (Socket socket : sockets)

+            socket.close();

+    }

+    

+    @Test

+    public void testFailureStarvation() throws Exception

+    {

+        try

+        {

+            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);

+

+            int acceptors = 0;

+            int selectors = 1;

+            int maxThreads = 10;

+            final int barried=maxThreads-acceptors-selectors;

+            final CyclicBarrier barrier = new CyclicBarrier(barried);

+

+

+            QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);

+            threadPool.setDetailedDump(true);

+            _server = new Server(threadPool);

+

+

+            ServerConnector connector = new ServerConnector(_server, acceptors, selectors)

+            {

+                @Override

+                protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException

+                {

+                    return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout())

+                    {

+

+                        @Override

+                        public boolean flush(ByteBuffer... buffers) throws IOException

+                        {

+                            super.flush(buffers[0]);

+                            throw new IOException("TEST FAILURE");

+                        }

+

+                    };

+                }

+            };

+            connector.setIdleTimeout(Long.MAX_VALUE);

+            _server.addConnector(connector);

+

+            final AtomicInteger count = new AtomicInteger(0);

+            _server.setHandler(new AbstractHandler()

+            {

+                @Override

+                public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException

+                {

+                    int c=count.getAndIncrement();

+                    try

+                    {

+                        if (c<barried)

+                        {

+                            barrier.await(10,TimeUnit.SECONDS);

+                        }

+                    }

+                    catch (InterruptedException | BrokenBarrierException | TimeoutException e)

+                    {

+                        throw new ServletException(e);

+                    }

+                    baseRequest.setHandled(true);

+                    response.setStatus(200);

+                    response.setContentLength(13);

+                    response.getWriter().print("Hello World!\n");

+                    response.getWriter().flush();

+                }

+            });

+

+            _server.start();

+

+            List<Socket> sockets = new ArrayList<>();

+            for (int i = 0; i < maxThreads*2; ++i)

+            {

+                Socket socket = new Socket("localhost", connector.getLocalPort());

+                sockets.add(socket);

+                OutputStream output = socket.getOutputStream();

+                String request = "" +

+                        "GET / HTTP/1.1\r\n" +

+                        "Host: localhost\r\n" +

+                        //                    "Connection: close\r\n" +

+                        "\r\n";

+                output.write(request.getBytes(StandardCharsets.UTF_8));

+                output.flush();

+            }

+

+

+            byte[] buffer = new byte[48 * 1024];

+            List<Exchanger<Integer>> totals = new ArrayList<>();

+            for (Socket socket : sockets)

+            {

+                final Exchanger<Integer> x = new Exchanger<>();

+                totals.add(x);

+                final InputStream input = socket.getInputStream();

+

+                new Thread()

+                {

+                    @Override

+                    public void run()

+                    {

+                        int read=0;

+                        try

+                        {

+                            // look for CRLFCRLF

+                            StringBuilder header = new StringBuilder();

+                            int state=0;

+                            while (state<4 && header.length()<2048)

+                            {

+                                int ch=input.read();

+                                if (ch<0)

+                                    break;

+                                header.append((char)ch);

+                                switch(state)

+                                {

+                                    case 0:

+                                        if (ch=='\r')

+                                            state=1;

+                                        break;

+                                    case 1:

+                                        if (ch=='\n')

+                                            state=2;

+                                        else

+                                            state=0;

+                                        break;

+                                    case 2:

+                                        if (ch=='\r')

+                                            state=3;

+                                        else

+                                            state=0;

+                                        break;

+                                    case 3:

+                                        if (ch=='\n')

+                                            state=4;

+                                        else

+                                            state=0;

+                                        break;

+                                }

+                            }

+

+                            read=input.read(buffer);

+                        }

+                        catch (IOException e)

+                        {

+                            // e.printStackTrace();

+                        }

+                        finally

+                        {

+                            try

+                            {

+                                x.exchange(read);

+                            }

+                            catch (InterruptedException e)

+                            {

+                                e.printStackTrace();

+                            }

+                        }

+                    }

+                }.start();

+            }

+

+            for (Exchanger<Integer> x : totals)

+            {

+                Integer read = x.exchange(-1,10,TimeUnit.SECONDS);

+                Assert.assertEquals(-1,read.intValue());

+            }

+

+            // We could read everything, good.

+            for (Socket socket : sockets)

+                socket.close();

+            

+            _server.stop();

+        }

+        finally

+        {

+            ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);

+        }

+    }

+}

diff --git a/tests/test-http-client-transport/pom.xml b/tests/test-http-client-transport/pom.xml
index ec8e245..5913b35 100644
--- a/tests/test-http-client-transport/pom.xml
+++ b/tests/test-http-client-transport/pom.xml
@@ -18,6 +18,36 @@
     <build>
         <plugins>
             <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.mortbay.jetty.alpn</groupId>
+                                    <artifactId>alpn-boot</artifactId>
+                                    <version>${alpn.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>false</overWrite>
+                                    <outputDirectory>${project.build.directory}/alpn</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
+                </configuration>
+            </plugin>
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-deploy-plugin</artifactId>
                 <configuration>
@@ -48,6 +78,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.eclipse.jetty.http2</groupId>
             <artifactId>http2-http-client-transport</artifactId>
             <version>${project.version}</version>
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
index 0cc5a0f..ca377c3 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
@@ -18,22 +18,28 @@
 
 package org.eclipse.jetty.http.client;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.HttpClientTransport;
 import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.http2.HTTP2Cipher;
 import org.eclipse.jetty.http2.client.HTTP2Client;
 import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
 import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
 import org.eclipse.jetty.server.ConnectionFactory;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.junit.After;
 import org.junit.Rule;
@@ -44,15 +50,16 @@
 public abstract class AbstractTest
 {
     @Parameterized.Parameters(name = "transport: {0}")
-    public static List<Object[]> parameters() throws Exception
+    public static Object[] parameters() throws Exception
     {
-        return Arrays.asList(new Object[]{Transport.HTTP}, new Object[]{Transport.HTTP2});
+        return new Object[]{Transport.HTTP, Transport.HTTPS, Transport.H2C, Transport.H2};
     }
 
     @Rule
     public final TestTracker tracker = new TestTracker();
 
     protected final Transport transport;
+    protected SslContextFactory sslContextFactory;
     protected Server server;
     protected ServerConnector connector;
     protected HttpClient client;
@@ -64,11 +71,18 @@
 
     public void start(Handler handler) throws Exception
     {
+        sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+        sslContextFactory.setUseCipherSuitesOrder(true);
+        sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
         startServer(handler);
         startClient();
     }
 
-    protected void startServer(Handler handler) throws Exception
+    private void startServer(Handler handler) throws Exception
     {
         QueuedThreadPool serverThreads = new QueuedThreadPool();
         serverThreads.setName("server");
@@ -79,26 +93,58 @@
         server.start();
     }
 
-    protected void startClient() throws Exception
+    private void startClient() throws Exception
     {
         QueuedThreadPool clientThreads = new QueuedThreadPool();
         clientThreads.setName("client");
-        client = new HttpClient(provideClientTransport(transport), null);
+        client = new HttpClient(provideClientTransport(transport), sslContextFactory);
         client.setExecutor(clientThreads);
         client.start();
     }
 
-    private ConnectionFactory provideServerConnectionFactory(Transport transport)
+    private ConnectionFactory[] provideServerConnectionFactory(Transport transport)
     {
+        List<ConnectionFactory> result = new ArrayList<>();
         switch (transport)
         {
             case HTTP:
-                return new HttpConnectionFactory(new HttpConfiguration());
-            case HTTP2:
-                return new HTTP2ServerConnectionFactory(new HttpConfiguration());
+            {
+                result.add(new HttpConnectionFactory(new HttpConfiguration()));
+                break;
+            }
+            case HTTPS:
+            {
+                HttpConfiguration configuration = new HttpConfiguration();
+                configuration.addCustomizer(new SecureRequestCustomizer());
+                HttpConnectionFactory http = new HttpConnectionFactory(configuration);
+                SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, http.getProtocol());
+                result.add(ssl);
+                result.add(http);
+                break;
+            }
+            case H2C:
+            {
+                result.add(new HTTP2CServerConnectionFactory(new HttpConfiguration()));
+                break;
+            }
+            case H2:
+            {
+                HttpConfiguration configuration = new HttpConfiguration();
+                configuration.addCustomizer(new SecureRequestCustomizer());
+                HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(configuration);
+                ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory("h2");
+                SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+                result.add(ssl);
+                result.add(alpn);
+                result.add(h2);
+                break;
+            }
             default:
+            {
                 throw new IllegalArgumentException();
+            }
         }
+        return result.toArray(new ConnectionFactory[result.size()]);
     }
 
     private HttpClientTransport provideClientTransport(Transport transport)
@@ -106,10 +152,12 @@
         switch (transport)
         {
             case HTTP:
+            case HTTPS:
             {
                 return new HttpClientTransportOverHTTP(1);
             }
-            case HTTP2:
+            case H2C:
+            case H2:
             {
                 HTTP2Client http2Client = new HTTP2Client();
                 http2Client.setSelectors(1);
@@ -122,6 +170,21 @@
         }
     }
 
+    protected String newURI()
+    {
+        switch (transport)
+        {
+            case HTTP:
+            case H2C:
+                return "http://localhost:" + connector.getLocalPort();
+            case HTTPS:
+            case H2:
+                return "https://localhost:" + connector.getLocalPort();
+            default:
+                throw new IllegalArgumentException();
+        }
+    }
+
     @After
     public void stop() throws Exception
     {
@@ -133,6 +196,6 @@
 
     protected enum Transport
     {
-        HTTP, HTTP2
+        HTTP, HTTPS, H2C, H2
     }
 }
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java
index 09ff0b0..052c902 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncRequestContentTest.java
@@ -53,7 +53,7 @@
 
         DeferredContentProvider contentProvider = new DeferredContentProvider();
         CountDownLatch latch = new CountDownLatch(1);
-        client.POST("http://localhost:" + connector.getLocalPort())
+        client.POST(newURI())
                 .content(contentProvider)
                 .send(result ->
                 {
@@ -73,7 +73,7 @@
 
         DeferredContentProvider contentProvider = new DeferredContentProvider();
         CountDownLatch latch = new CountDownLatch(1);
-        client.POST("http://localhost:" + connector.getLocalPort())
+        client.POST(newURI())
                 .content(contentProvider)
                 .send(result ->
                 {
@@ -95,7 +95,7 @@
         InputStreamContentProvider contentProvider =
                 new InputStreamContentProvider(new ByteArrayInputStream(new byte[0]));
         CountDownLatch latch = new CountDownLatch(1);
-        client.POST("http://localhost:" + connector.getLocalPort())
+        client.POST(newURI())
                 .content(contentProvider)
                 .send(result ->
                 {
@@ -116,7 +116,7 @@
         InputStreamContentProvider contentProvider =
                 new InputStreamContentProvider(new ByteArrayInputStream(new byte[1]));
         CountDownLatch latch = new CountDownLatch(1);
-        client.POST("http://localhost:" + connector.getLocalPort())
+        client.POST(newURI())
                 .content(contentProvider)
                 .send(result ->
                 {
@@ -136,7 +136,7 @@
 
         OutputStreamContentProvider contentProvider = new OutputStreamContentProvider();
         CountDownLatch latch = new CountDownLatch(1);
-        client.POST("http://localhost:" + connector.getLocalPort())
+        client.POST(newURI())
                 .content(contentProvider)
                 .send(result ->
                 {
@@ -156,7 +156,7 @@
 
         OutputStreamContentProvider contentProvider = new OutputStreamContentProvider();
         CountDownLatch latch = new CountDownLatch(1);
-        client.POST("http://localhost:" + connector.getLocalPort())
+        client.POST(newURI())
                 .content(contentProvider)
                 .send(result ->
                 {
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
index ff4d24d..e168a0f 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
@@ -27,8 +27,6 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.junit.Assert;
@@ -61,14 +59,10 @@
         client.start();
 
         final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort()).send(new Response.CompleteListener()
+        client.newRequest(newURI()).send(result ->
         {
-            @Override
-            public void onComplete(Result result)
-            {
-                if (result.isFailed())
-                    latch.countDown();
-            }
+            if (result.isFailed())
+                latch.countDown();
         });
 
         Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
@@ -89,16 +83,12 @@
         });
 
         final CountDownLatch latch = new CountDownLatch(1);
-        client.newRequest("localhost", connector.getLocalPort())
+        client.newRequest(newURI())
                 .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
-                .send(new Response.CompleteListener()
+                .send(result ->
                 {
-                    @Override
-                    public void onComplete(Result result)
-                    {
-                        if (result.isFailed())
-                            latch.countDown();
-                    }
+                    if (result.isFailed())
+                        latch.countDown();
                 });
 
         Assert.assertTrue(latch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
index 8bcb943..56ffdd1 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
@@ -61,7 +61,7 @@
             }
         });
 
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+        ContentResponse response = client.newRequest(newURI())
                 .timeout(5, TimeUnit.SECONDS)
                 .send();
 
@@ -95,7 +95,7 @@
             }
         });
 
-        org.eclipse.jetty.client.api.Request request = client.newRequest("localhost", connector.getLocalPort());
+        org.eclipse.jetty.client.api.Request request = client.newRequest(newURI());
         FutureResponseListener listener = new FutureResponseListener(request, length);
         request.timeout(10, TimeUnit.SECONDS).send(listener);
         ContentResponse response = listener.get();
@@ -139,7 +139,7 @@
             }
         });
 
-        org.eclipse.jetty.client.api.Request request = client.newRequest("localhost", connector.getLocalPort());
+        org.eclipse.jetty.client.api.Request request = client.newRequest(newURI());
         FutureResponseListener listener = new FutureResponseListener(request, 2 * length);
         request.timeout(10, TimeUnit.SECONDS).send(listener);
         ContentResponse response = listener.get();
@@ -183,7 +183,7 @@
             }
         });
 
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+        ContentResponse response = client.newRequest(newURI())
                 .method(HttpMethod.POST)
                 .content(new BytesContentProvider(bytes))
                 .timeout(15, TimeUnit.SECONDS)
@@ -228,7 +228,7 @@
         int chunkSize = 16;
         byte[][] bytes = IntStream.range(0, chunks).mapToObj(x -> new byte[chunkSize]).toArray(byte[][]::new);
         BytesContentProvider contentProvider = new BytesContentProvider("application/octet-stream", bytes);
-        ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+        ContentResponse response = client.newRequest(newURI())
                 .method(HttpMethod.POST)
                 .content(contentProvider)
                 .timeout(15, TimeUnit.SECONDS)
diff --git a/tests/test-http-client-transport/src/test/resources/keystore.jks b/tests/test-http-client-transport/src/test/resources/keystore.jks
new file mode 100644
index 0000000..428ba54
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/resources/keystore.jks
Binary files differ
diff --git a/tests/test-http-client-transport/src/test/resources/truststore.jks b/tests/test-http-client-transport/src/test/resources/truststore.jks
new file mode 100644
index 0000000..839cb8c
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/resources/truststore.jks
Binary files differ