From 98e3a287944bbb4a71e53f675032b6c8609fb5b9 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 13 Feb 2015 13:41:14 +1100 Subject: [PATCH] 459845 - Support upgrade from http1 to http2/websocket Added test harness for h2c upgrade. --- .../eclipse/jetty/http2/HTTP2Connection.java | 4 +- .../http2/server/AbstractServerTest.java | 3 +- .../jetty/http2/server/HTTP2CServer.java | 23 +-- .../jetty/http2/server/HTTP2CServerTest.java | 176 ++++++++++++++++++ 4 files changed, 193 insertions(+), 13 deletions(-) create mode 100644 jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 836db814b4a..f90e55de1a3 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -63,6 +63,8 @@ public class HTTP2Connection extends AbstractConnection implements Connection.Up public void onUpgradeTo(ByteBuffer prefilled) { + if (LOG.isDebugEnabled()) + LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDebugString(prefilled)); producer.prefill(prefilled); } @@ -131,7 +133,7 @@ public class HTTP2Connection extends AbstractConnection implements Connection.Up if (task != null) return task; - boolean looping = false; + boolean looping = BufferUtil.hasContent(buffer); while (true) { if (buffer == null) diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java index de737b21226..326f52622a7 100644 --- a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java +++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/AbstractServerTest.java @@ -89,7 +89,8 @@ public class AbstractServerTest @After public void dispose() throws Exception { - server.stop(); + if (server!=null) + server.stop(); } protected boolean parseResponse(Socket client, Parser parser) throws IOException diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java index 90efb395bb9..2c66cbfbfd9 100644 --- a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java +++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServer.java @@ -22,10 +22,8 @@ import java.io.IOException; import java.util.Date; import javax.servlet.ServletException; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -34,25 +32,28 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; -public class HTTP2CServer +public class HTTP2CServer extends Server { - public static void main( String[] args ) throws Exception + public HTTP2CServer(int port) { - // The Server - Server server = new Server(); - // HTTP connector HttpConfiguration config = new HttpConfiguration(); - ServerConnector http = new ServerConnector(server,new HttpConnectionFactory(config), new HTTP2CServerConnectionFactory(config)); + ServerConnector http = new ServerConnector(this,new HttpConnectionFactory(config), new HTTP2CServerConnectionFactory(config)); http.setHost("localhost"); - http.setPort(8080); + http.setPort(port); http.setIdleTimeout(30000); // Set the connector - server.addConnector(http); + addConnector(http); // Set a handler - server.setHandler(new SimpleHandler()); + setHandler(new SimpleHandler()); + } + + public static void main(String... args ) throws Exception + { + // The Server + HTTP2CServer server = new HTTP2CServer(8080); // Start the server server.start(); diff --git a/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java new file mode 100644 index 00000000000..e0d41c04fed --- /dev/null +++ b/jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/HTTP2CServerTest.java @@ -0,0 +1,176 @@ +// +// ======================================================================== +// 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.http2.server; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HostPortHttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.http2.frames.DataFrame; +import org.eclipse.jetty.http2.frames.HeadersFrame; +import org.eclipse.jetty.http2.frames.PrefaceFrame; +import org.eclipse.jetty.http2.frames.SettingsFrame; +import org.eclipse.jetty.http2.generator.Generator; +import org.eclipse.jetty.http2.parser.Parser; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.server.NetworkConnector; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.IO; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class HTTP2CServerTest extends AbstractServerTest +{ + HTTP2CServer _server; + int _port; + + @Before + public void before() throws Exception + { + _server=new HTTP2CServer(0); + _server.start(); + _port=((NetworkConnector)_server.getConnectors()[0]).getLocalPort(); + } + + @After + public void after() throws Exception + { + _server.stop(); + } + + @Test + public void testHTTP_1_0_Simple() throws Exception + { + try (Socket client = new Socket("localhost", _port)) + { + client.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1)); + client.getOutputStream().flush(); + + String response = IO.toString(client.getInputStream()); + + assertThat(response,containsString("HTTP/1.1 200 OK")); + assertThat(response,containsString("Hello from Jetty using HTTP/1.0")); + } + } + + @Test + public void testHTTP_1_1_Simple() throws Exception + { + try (Socket client = new Socket("localhost", _port)) + { + client.getOutputStream().write("GET /one HTTP/1.1\r\nHost: localhost\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1)); + client.getOutputStream().write("GET /two HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1)); + client.getOutputStream().flush(); + + String response = IO.toString(client.getInputStream()); + + assertThat(response,containsString("HTTP/1.1 200 OK")); + assertThat(response,containsString("Hello from Jetty using HTTP/1.1")); + assertThat(response,containsString("uri=/one")); + assertThat(response,containsString("uri=/two")); + } + } + + @Test + public void testHTTP_2_0_Simple() throws Exception + { + final CountDownLatch latch = new CountDownLatch(3); + + byteBufferPool= new MappedByteBufferPool(); + generator = new Generator(byteBufferPool); + + ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + generator.control(lease, new PrefaceFrame()); + MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:"+_port), "/test", HttpVersion.HTTP_2, new HttpFields()); + + generator.control(lease, new HeadersFrame(1, metaData, null, true)); + + try (Socket client = new Socket("localhost", _port)) + { + OutputStream output = client.getOutputStream(); + for (ByteBuffer buffer : lease.getByteBuffers()) + { + output.write(BufferUtil.toArray(buffer)); + } + + final AtomicReference headersRef = new AtomicReference<>(); + final AtomicReference dataRef = new AtomicReference<>(); + Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() + { + @Override + public void onSettings(SettingsFrame frame) + { + latch.countDown(); + } + + @Override + public void onHeaders(HeadersFrame frame) + { + headersRef.set(frame); + latch.countDown(); + } + + @Override + public void onData(DataFrame frame) + { + dataRef.set(frame); + latch.countDown(); + } + }, 4096, 8192); + + parseResponse(client, parser); + + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + + HeadersFrame response = headersRef.get(); + Assert.assertNotNull(response); + MetaData.Response responseMetaData = (MetaData.Response)response.getMetaData(); + Assert.assertEquals(200, responseMetaData.getStatus()); + + DataFrame responseData = dataRef.get(); + Assert.assertNotNull(responseData); + + String s = BufferUtil.toString(responseData.getData()); + + assertThat(s,containsString("Hello from Jetty using HTTP/2.0")); + assertThat(s,containsString("uri=/test")); + } + } +}