Author: markt
Date: Mon Jun 1 12:31:56 2015
New Revision: 1682885
URL: http://svn.apache.org/r1682885
Log:
Refactor
- put the test only code in the test classes
- reorganise to make it easier to test various connection scenarios
Implement a couple of TODO tests
Modified:
tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java
tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java?rev=1682885&r1=1682884&r2=1682885&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java Mon Jun 1 12:31:56 2015
@@ -17,7 +17,6 @@
package org.apache.coyote.http2;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.apache.juli.logging.Log;
@@ -92,61 +91,6 @@ class Http2Parser {
return true;
}
-
- boolean readHttpUpgradeResponse() throws IOException {
- // Only used by test code so safe to keep this just a little larger than
- // we are expecting.
- ByteBuffer data = ByteBuffer.allocate(128);
- byte[] singleByte = new byte[1];
- // Looking for \r\n\r\n
- int seen = 0;
- while (seen < 4) {
- input.fill(singleByte, true);
- switch (seen) {
- case 0:
- case 2: {
- if (singleByte[0] == '\r') {
- seen++;
- } else {
- seen = 0;
- }
- break;
- }
- case 1:
- case 3: {
- if (singleByte[0] == '\n') {
- seen++;
- } else {
- seen = 0;
- }
- break;
- }
- }
- data.put(singleByte[0]);
- }
-
- String response = new String(data.array(), data.arrayOffset(),
- data.arrayOffset() + data.position(), StandardCharsets.ISO_8859_1);
-
- String[] responseLines = response.split("\r\n");
-
- if (responseLines.length < 3) {
- return false;
- }
- if (!responseLines[0].startsWith("HTTP/1.1 101")) {
- return false;
- }
- // TODO: There may be other headers.
- if (!responseLines[1].equals("Connection: Upgrade")) {
- return false;
- }
- if (!responseLines[2].startsWith("Upgrade: h2c")) {
- return false;
- }
-
- return true;
- }
-
/**
* Interface that must be implemented by the source of data for the parser.
Modified: tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java?rev=1682885&r1=1682884&r2=1682885&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java (original)
+++ tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java Mon Jun 1 12:31:56 2015
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import javax.net.SocketFactory;
@@ -29,12 +30,13 @@ import javax.servlet.http.HttpServletReq
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
-import org.junit.Before;
import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.coyote.http2.Http2Parser.Input;
import org.apache.tomcat.util.codec.binary.Base64;
@@ -52,31 +54,34 @@ public abstract class Http2TestBase exte
}
private Socket s;
+ protected Input input;
protected Http2Parser parser;
protected OutputStream os;
- @Before
- public void setup() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- // Enable HTTP/2
- Connector connector = tomcat.getConnector();
+ protected void enableHttp2() {
+ Connector connector = getTomcatInstance().getConnector();
Http2Protocol http2Protocol = new Http2Protocol();
// Short timeouts for now. May need to increase these for CI systems.
http2Protocol.setReadTimeout(2000);
http2Protocol.setKeepAliveTimeout(5000);
http2Protocol.setWriteTimeout(2000);
connector.addUpgradeProtocol(http2Protocol);
+ }
+
+
+ protected void configureAndStartWebApplication() throws LifecycleException {
+ Tomcat tomcat = getTomcatInstance();
- // Add a web application
Context ctxt = tomcat.addContext("", null);
Tomcat.addServlet(ctxt, "simple", new SimpleServlet());
ctxt.addServletMapping("/*", "simple");
- // Start the Tomcat instance
tomcat.start();
+ }
+
+ protected void openClientConnection() throws IOException {
// Open a connection
s = SocketFactory.getDefault().createSocket("localhost", getPort());
s.setSoTimeout(30000);
@@ -84,23 +89,89 @@ public abstract class Http2TestBase exte
os = s.getOutputStream();
InputStream is = s.getInputStream();
- TestInput testInput = new TestInput(is);
- parser = new Http2Parser(testInput);
+ input = new TestInput(is);
+ parser = new Http2Parser(input);
}
- protected void doHttpUpgrade() throws IOException {
+ protected void doHttpUpgrade(String upgrade, boolean validate) throws IOException {
byte[] upgradeRequest = ("GET / HTTP/1.1\r\n" +
"Host: localhost:" + getPort() + "\r\n" +
"Connection: Upgrade, HTTP2-Settings\r\n" +
- "Upgrade: h2c\r\n" +
+ "Upgrade: " + upgrade + "\r\n" +
"HTTP2-Settings: "+ HTTP2_SETTINGS + "\r\n" +
"\r\n").getBytes(StandardCharsets.ISO_8859_1);
os.write(upgradeRequest);
os.flush();
- Assert.assertTrue("Failed to read HTTP Upgrade response", parser.readHttpUpgradeResponse());
+ if (validate) {
+ Assert.assertTrue("Failed to read HTTP Upgrade response",
+ readHttpUpgradeResponse());
+ }
+ }
+
+
+ boolean readHttpUpgradeResponse() throws IOException {
+ String[] responseHeaders = readHttpResponseHeaders();
+
+ if (responseHeaders.length < 3) {
+ return false;
+ }
+ if (!responseHeaders[0].startsWith("HTTP/1.1 101")) {
+ return false;
+ }
+ // TODO: There may be other headers.
+ if (!responseHeaders[1].equals("Connection: Upgrade")) {
+ return false;
+ }
+ if (!responseHeaders[2].startsWith("Upgrade: h2c")) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ String[] readHttpResponseHeaders() throws IOException {
+ // Only used by test code so safe to keep this just a little larger than
+ // we are expecting.
+ ByteBuffer data = ByteBuffer.allocate(256);
+ byte[] singleByte = new byte[1];
+ // Looking for \r\n\r\n
+ int seen = 0;
+ while (seen < 4) {
+ input.fill(singleByte, true);
+ switch (seen) {
+ case 0:
+ case 2: {
+ if (singleByte[0] == '\r') {
+ seen++;
+ } else {
+ seen = 0;
+ }
+ break;
+ }
+ case 1:
+ case 3: {
+ if (singleByte[0] == '\n') {
+ seen++;
+ } else {
+ seen = 0;
+ }
+ break;
+ }
+ }
+ data.put(singleByte[0]);
+ }
+
+ if (seen != 4) {
+ throw new IOException("End of headers not found");
+ }
+
+ String response = new String(data.array(), data.arrayOffset(),
+ data.arrayOffset() + data.position(), StandardCharsets.ISO_8859_1);
+ return response.split("\r\n");
}
@@ -141,10 +212,15 @@ public abstract class Http2TestBase exte
throws ServletException, IOException {
// Generate content with a simple known format.
resp.setContentType("application/octet-stream");
+
+ int count = 16 * 1024;
+ // Two bytes per entry
+ resp.setContentLengthLong(count * 2);
+
OutputStream os = resp.getOutputStream();
byte[] data = new byte[2];
// 1024 * 16 * 2 bytes = 32k of content.
- for (int i = 0; i < 1024 * 16; i++) {
+ for (int i = 0; i < count; i++) {
data[0] = (byte) (i & 0xFF);
data[1] = (byte) ((i >> 8) & 0xFF);
os.write(data);
Modified: tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_3_2.java?rev=1682885&r1=1682884&r2=1682885&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_3_2.java (original)
+++ tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_3_2.java Mon Jun 1 12:31:56
2015
@@ -18,7 +18,9 @@ package org.apache.coyote.http2;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import org.junit.Assert;
import org.junit.Test;
/**
@@ -35,13 +37,28 @@ public class TestHttp2Section_3_2 extend
// TODO: Test initial requests with bodies of various sizes
- // TODO: Test response with HTTP/2 is not enabled
+ @Test
+ public void testConnectionNoHttp2Support() throws Exception {
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade("h2c", false);
+ parseHttp11Response();
+ }
+
+
+ @Test
+ public void testConnectionUpgradeWrongProtocol() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade("h2", false);
+ parseHttp11Response();
+ }
- // TODO: Test HTTP upgrade with h2 rather tan h2c
@Test(timeout=10000)
- public void testConnectionNoPreface() throws IOException {
- doHttpUpgrade();
+ public void testConnectionNoPreface() throws Exception {
+ setupAsFarAsUpgrade();
// If we don't send the preface the server should kill the connection.
try {
@@ -54,8 +71,8 @@ public class TestHttp2Section_3_2 extend
@Test(timeout=10000)
- public void testConnectionIncompletePrefaceStart() throws IOException {
- doHttpUpgrade();
+ public void testConnectionIncompletePrefaceStart() throws Exception {
+ setupAsFarAsUpgrade();
// If we send an incomplete preface the server should kill the
// connection.
@@ -71,8 +88,8 @@ public class TestHttp2Section_3_2 extend
@Test(timeout=10000)
- public void testConnectionInvalidPrefaceStart() throws IOException {
- doHttpUpgrade();
+ public void testConnectionInvalidPrefaceStart() throws Exception {
+ setupAsFarAsUpgrade();
// If we send an incomplete preface the server should kill the
// connection.
@@ -93,4 +110,34 @@ public class TestHttp2Section_3_2 extend
// TODO: Test if client doesn't send SETTINGS as part of the preface
// TODO: Test response is received on stream 1
+
+
+ private void setupAsFarAsUpgrade() throws Exception {
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade("h2c", true);
+ }
+
+
+ private void parseHttp11Response() throws IOException {
+ String[] responseHeaders = readHttpResponseHeaders();
+ Assert.assertTrue(responseHeaders[0], responseHeaders[0].startsWith("HTTP/1.1 200"));
+
+ // Find the content length (chunked responses not handled)
+ for (int i = 1; i < responseHeaders.length; i++) {
+ if (responseHeaders[i].toLowerCase(Locale.ENGLISH).startsWith("content-length"))
{
+ String cl = responseHeaders[i];
+ int pos = cl.indexOf(':');
+ if (pos == -1) {
+ throw new IOException("Invalid: [" + cl + "]");
+ }
+ int len = Integer.parseInt(cl.substring(pos + 1).trim());
+ byte[] content = new byte[len];
+ input.fill(content, true);
+ return;
+ }
+ }
+ throw new IOException("No content-length");
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
|