From ce6820b67bf53904ed26d8db1bea566fbcb40593 Mon Sep 17 00:00:00 2001 From: Thomas Becker Date: Fri, 17 Aug 2012 21:29:13 +0200 Subject: [PATCH] jetty-9: extract common stuff from HttpManyWaysToCommitTest and HttpManyWaysToAsyncCommitTest into abstract class --- .../jetty/server/AbstractHttpTest.java | 115 +++++++ .../server/HttpManyWaysToAsyncCommitTest.java | 293 ++--------------- .../server/HttpManyWaysToCommitTest.java | 295 ++---------------- .../jetty/server/TestHttpResponse.java | 54 ++++ .../jetty/server/util/SimpleHttpParser.java | 158 ++++++++++ 5 files changed, 375 insertions(+), 540 deletions(-) create mode 100644 jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java create mode 100644 jetty-server/src/test/java/org/eclipse/jetty/server/TestHttpResponse.java create mode 100644 jetty-server/src/test/java/org/eclipse/jetty/server/util/SimpleHttpParser.java diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java new file mode 100644 index 00000000000..1d9b30d827d --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractHttpTest.java @@ -0,0 +1,115 @@ +//======================================================================== +//Copyright 2012 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.server; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; +import java.net.URISyntaxException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.util.SimpleHttpParser; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.StdErrLog; +import org.junit.After; +import org.junit.Before; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public abstract class AbstractHttpTest +{ + protected static Server server; + protected static SelectChannelConnector connector; + protected String httpVersion; + protected SimpleHttpParser httpParser; + + public AbstractHttpTest(String httpVersion) + { + this.httpVersion = httpVersion; + } + + @Before + public void setUp() throws Exception + { + server = new Server(); + connector = new SelectChannelConnector(server); + server.setConnectors(new Connector[]{connector}); + httpParser = new SimpleHttpParser(httpVersion); + ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true); + } + + @After + public void tearDown() throws Exception + { + ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false); + server.stop(); + } + + protected TestHttpResponse executeRequest() throws URISyntaxException, IOException + { + Socket socket = new Socket("localhost", connector.getLocalPort()); + BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); + String request = "GET / " + httpVersion + "\r\n"; + + writer.write(request); + writer.write("Host: localhost"); + writer.println("\r\n"); + writer.flush(); + + return httpParser.readResponse(reader); + } + + protected void assertResponseBody(TestHttpResponse response, String expectedResponseBody) + { + assertThat("response body is" + expectedResponseBody, response.getBody(), is(expectedResponseBody)); + } + + protected void assertHeader(TestHttpResponse response, String headerName, String expectedValue) + { + assertThat(headerName + "=" + expectedValue, response.getHeaders().get(headerName), is(expectedValue)); + } + + private static class TestCommitException extends IllegalStateException + { + public TestCommitException() + { + super("Thrown by test"); + } + } + + protected class ThrowExceptionOnDemandHandler extends AbstractHandler + { + private final boolean throwException; + + protected ThrowExceptionOnDemandHandler(boolean throwException) + { + this.throwException = throwException; + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + if (throwException) + throw new TestCommitException(); + } + } +} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java index d83dceb91d0..604a907f41b 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToAsyncCommitTest.java @@ -12,21 +12,9 @@ //======================================================================== package org.eclipse.jetty.server; -import java.io.BufferedReader; -import java.io.EOFException; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.ServletResponse; @@ -34,11 +22,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.StdErrLog; -import org.junit.After; -import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,17 +29,13 @@ import org.junit.runners.Parameterized; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; //TODO: reset buffer tests //TODO: add protocol specific tests for connection: close and/or chunking @RunWith(value = Parameterized.class) -public class HttpManyWaysToAsyncCommitTest +public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest { - private static Server server; - private static SelectChannelConnector connector; private final String CONTEXT_ATTRIBUTE = getClass().getName() + ".asyncContext"; - private String httpVersion; private boolean dispatch; // if true we dispatch, otherwise we complete @Parameterized.Parameters @@ -69,33 +48,18 @@ public class HttpManyWaysToAsyncCommitTest public HttpManyWaysToAsyncCommitTest(String httpVersion, boolean dispatch) { + super(httpVersion); this.httpVersion = httpVersion; this.dispatch = dispatch; } - @Before - public void setUp() throws Exception - { - server = new Server(); - connector = new SelectChannelConnector(server); - server.setConnectors(new Connector[]{connector}); - ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true); - } - - @After - public void tearDown() throws Exception - { - ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false); - server.stop(); - } - @Test public void testHandlerDoesNotSetHandled() throws Exception { server.setHandler(new DoesNotSetHandledHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 404", response.getCode(), is("404")); } @@ -106,7 +70,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new DoesNotSetHandledHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 500", response.getCode(), is("500")); } @@ -149,7 +113,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new OnlySetHandledHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertHeader(response, "content-length", "0"); @@ -161,7 +125,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new OnlySetHandledHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 500", response.getCode(), is("500")); } @@ -206,7 +170,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetHandledWriteSomeDataHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertHeader(response, "content-length", "6"); @@ -218,7 +182,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetHandledWriteSomeDataHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 500", response.getCode(), is("500")); } @@ -270,7 +234,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new ExplicitFlushHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); @@ -284,7 +248,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new ExplicitFlushHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -340,7 +304,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetHandledAndFlushWithoutContentHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -353,7 +317,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetHandledAndFlushWithoutContentHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -407,7 +371,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new WriteFlushWriteMoreHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -420,7 +384,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new WriteFlushWriteMoreHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -477,7 +441,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new OverflowHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertResponseBody(response, "foobar"); @@ -490,7 +454,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new OverflowHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); // response not committed when we throw, so 500 expected assertThat("response code is 500", response.getCode(), is("500")); @@ -545,7 +509,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertThat("response body is foo", response.getBody(), is("foo")); @@ -558,7 +522,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); //TODO: should we expect 500 here? assertThat("response code is 200", response.getCode(), is("200")); @@ -615,7 +579,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); // jetty truncates the body when content-length is reached.! This is correct and desired behaviour? @@ -629,7 +593,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); // TODO: we throw before response is committed. should we expect 500? assertThat("response code is 200", response.getCode(), is("200")); @@ -686,7 +650,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new WriteAndSetContentLengthHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); //TODO: jetty ignores setContentLength and sends transfer-encoding header. Correct? @@ -698,7 +662,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new WriteAndSetContentLengthHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); } @@ -753,7 +717,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new WriteAndSetContentLengthTooSmallHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertResponseBody(response, "foobar"); @@ -768,7 +732,7 @@ public class HttpManyWaysToAsyncCommitTest server.setHandler(new WriteAndSetContentLengthTooSmallHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat(response.getCode(), is("500")); } @@ -815,213 +779,4 @@ public class HttpManyWaysToAsyncCommitTest super.handle(target, baseRequest, request, response); } } - - private Response executeRequest() throws URISyntaxException, IOException - { - Socket socket = new Socket("localhost", connector.getLocalPort()); - BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); - String request = "GET / " + httpVersion + "\r\n"; - - writer.write(request); - writer.write("Host: localhost"); - writer.println("\r\n"); - writer.flush(); - - return readResponse(reader); - } - - private Response readResponse(BufferedReader reader) throws IOException - { - // Simplified parser for HTTP responses - String line = reader.readLine(); - if (line == null) - throw new EOFException(); - Matcher responseLine = Pattern.compile("HTTP/1.1" + "\\s+(\\d+)").matcher(line); - assertThat("http version is 1.1", responseLine.lookingAt(), is(true)); - String code = responseLine.group(1); - - Map headers = new LinkedHashMap<>(); - while ((line = reader.readLine()) != null) - { - if (line.trim().length() == 0) - break; - - parseHeader(line, headers); - } - - StringBuilder body; - if (headers.containsKey("content-length")) - { - body = parseContentLengthDelimitedBody(reader, headers); - } - else if ("chunked".equals(headers.get("transfer-encoding"))) - { - body = parseChunkedBody(reader); - } - else - { - body = parseEOFDelimitedBody(reader, headers); - } - - return new Response(code, headers, body.toString().trim()); - } - - private void parseHeader(String line, Map headers) - { - Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line); - assertTrue(header.lookingAt()); - String headerName = header.group(1); - String headerValue = header.group(2); - headers.put(headerName.toLowerCase(), headerValue.toLowerCase()); - } - - private StringBuilder parseContentLengthDelimitedBody(BufferedReader reader, Map headers) throws IOException - { - StringBuilder body; - int readLen = 0; - int length = Integer.parseInt(headers.get("content-length")); - body = new StringBuilder(length); - try - { - //TODO: UTF-8 reader from joakim - for (int i = 0; i < length; ++i) - { - char c = (char)reader.read(); - body.append(c); - readLen++; - } - - } - catch (SocketTimeoutException e) - { - System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n", readLen, length); - throw e; - } - return body; - } - - private StringBuilder parseChunkedBody(BufferedReader reader) throws IOException - { - StringBuilder body; - String line; - body = new StringBuilder(64 * 1024); - while ((line = reader.readLine()) != null) - { - if ("0".equals(line)) - { - line = reader.readLine(); - assertThat("There's no more content after as 0 indicated the final chunk", line, is("")); - break; - } - - int length = Integer.parseInt(line, 16); - //TODO: UTF-8 reader from joakim - for (int i = 0; i < length; ++i) - { - char c = (char)reader.read(); - body.append(c); - } - reader.readLine(); - // assertThat("chunk is followed by an empty line", line, is("")); //TODO: is this right? - NO. Don't - // think you can really do chunks with read line generally, but maybe for this test is OK. - } - return body; - } - - private StringBuilder parseEOFDelimitedBody(BufferedReader reader, Map headers) throws IOException - { - StringBuilder body; - if ("HTTP/1.1".equals(httpVersion)) - assertThat("if no content-length or transfer-encoding header is set, " + - "connection: close header must be set", headers.get("connection"), - is("close")); - - // read until EOF - body = new StringBuilder(); - while (true) - { - //TODO: UTF-8 reader from joakim - int read = reader.read(); - if (read == -1) - break; - char c = (char)read; - body.append(c); - } - return body; - } - - private void assertResponseBody(Response response, String expectedResponseBody) - { - assertThat("response body is" + expectedResponseBody, response.getBody(), is(expectedResponseBody)); - } - - private void assertHeader(Response response, String headerName, String expectedValue) - { - assertThat(headerName + "=" + expectedValue, response.getHeaders().get(headerName), is(expectedValue)); - } - - private class Response - { - private final String code; - private final Map headers; - private final String body; - - private Response(String code, Map headers, String body) - { - this.code = code; - this.headers = headers; - this.body = body; - } - - public String getCode() - { - return code; - } - - public Map getHeaders() - { - return headers; - } - - public String getBody() - { - return body; - } - - @Override - public String toString() - { - return "Response{" + - "code='" + code + '\'' + - ", headers=" + headers + - ", body='" + body + '\'' + - '}'; - } - } - - private class ThrowExceptionOnDemandHandler extends AbstractHandler - { - private final boolean throwException; - - private ThrowExceptionOnDemandHandler(boolean throwException) - { - this.throwException = throwException; - } - - @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - if (throwException) - throw new TestCommitException(); - } - } - - private static class TestCommitException extends IllegalStateException - { - public TestCommitException() - { - super("Thrown by test"); - } - } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java index 33e30045693..715cd29c2fb 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java @@ -12,31 +12,14 @@ //======================================================================== package org.eclipse.jetty.server; -import java.io.BufferedReader; -import java.io.EOFException; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.StdErrLog; -import org.junit.After; -import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,17 +27,12 @@ import org.junit.runners.Parameterized; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; //TODO: reset buffer tests //TODO: add protocol specific tests for connection: close and/or chunking @RunWith(value = Parameterized.class) -public class HttpManyWaysToCommitTest +public class HttpManyWaysToCommitTest extends AbstractHttpTest { - private static Server server; - private static SelectChannelConnector connector; - private String httpVersion; - @Parameterized.Parameters public static Collection data() { @@ -64,23 +42,7 @@ public class HttpManyWaysToCommitTest public HttpManyWaysToCommitTest(String httpVersion) { - this.httpVersion = httpVersion; - } - - @Before - public void setUp() throws Exception - { - server = new Server(); - connector = new SelectChannelConnector(server); - server.setConnectors(new Connector[]{connector}); - ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true); - } - - @After - public void tearDown() throws Exception - { - ((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false); - server.stop(); + super(httpVersion); } @Test @@ -89,7 +51,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new DoesNotSetHandledHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 404", response.getCode(), is("404")); } @@ -100,7 +62,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new DoesNotSetHandledHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 500", response.getCode(), is("500")); } @@ -126,7 +88,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new OnlySetHandledHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertHeader(response, "content-length", "0"); @@ -138,7 +100,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new OnlySetHandledHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 500", response.getCode(), is("500")); } @@ -165,7 +127,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetHandledWriteSomeDataHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertHeader(response, "content-length", "6"); @@ -177,7 +139,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetHandledWriteSomeDataHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 500", response.getCode(), is("500")); } @@ -204,7 +166,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new ExplicitFlushHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); @@ -218,7 +180,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new ExplicitFlushHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -248,7 +210,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetHandledAndFlushWithoutContentHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -261,7 +223,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetHandledAndFlushWithoutContentHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -290,7 +252,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new WriteFlushWriteMoreHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -303,7 +265,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new WriteFlushWriteMoreHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); if ("HTTP/1.1".equals(httpVersion)) @@ -334,7 +296,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new OverflowHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertResponseBody(response, "foobar"); @@ -347,7 +309,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new OverflowHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); // response not committed when we throw, so 500 expected assertThat("response code is 500", response.getCode(), is("500")); @@ -376,7 +338,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertThat("response body is foo", response.getBody(), is("foo")); @@ -390,7 +352,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); //TODO: should we expect 500 here? assertThat("response code is 200", response.getCode(), is("200")); @@ -421,7 +383,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); // jetty truncates the body when content-length is reached.! This is correct and desired behaviour? @@ -435,7 +397,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); // TODO: we throw before response is committed. should we expect 500? assertThat("response code is 200", response.getCode(), is("200")); @@ -466,7 +428,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new WriteAndSetContentLengthHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); //TODO: jetty ignores setContentLength and sends transfer-encoding header. Correct? @@ -478,7 +440,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new WriteAndSetContentLengthHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); } @@ -507,7 +469,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new WriteAndSetContentLengthTooSmallHandler(false)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat("response code is 200", response.getCode(), is("200")); assertResponseBody(response, "foobar"); @@ -522,7 +484,7 @@ public class HttpManyWaysToCommitTest server.setHandler(new WriteAndSetContentLengthTooSmallHandler(true)); server.start(); - Response response = executeRequest(); + TestHttpResponse response = executeRequest(); assertThat(response.getCode(), is("500")); } @@ -543,213 +505,4 @@ public class HttpManyWaysToCommitTest super.handle(target, baseRequest, request, response); } } - - private Response executeRequest() throws URISyntaxException, IOException - { - Socket socket = new Socket("localhost", connector.getLocalPort()); - BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); - String request = "GET / " + httpVersion + "\r\n"; - - writer.write(request); - writer.write("Host: localhost"); - writer.println("\r\n"); - writer.flush(); - - return readResponse(reader); - } - - private Response readResponse(BufferedReader reader) throws IOException - { - // Simplified parser for HTTP responses - String line = reader.readLine(); - if (line == null) - throw new EOFException(); - Matcher responseLine = Pattern.compile("HTTP/1.1" + "\\s+(\\d+)").matcher(line); - assertThat("http version is 1.1", responseLine.lookingAt(), is(true)); - String code = responseLine.group(1); - - Map headers = new LinkedHashMap<>(); - while ((line = reader.readLine()) != null) - { - if (line.trim().length() == 0) - break; - - parseHeader(line, headers); - } - - StringBuilder body; - if (headers.containsKey("content-length")) - { - body = parseContentLengthDelimitedBody(reader, headers); - } - else if ("chunked".equals(headers.get("transfer-encoding"))) - { - body = parseChunkedBody(reader); - } - else - { - body = parseEOFDelimitedBody(reader, headers); - } - - return new Response(code, headers, body.toString().trim()); - } - - private void parseHeader(String line, Map headers) - { - Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line); - assertTrue(header.lookingAt()); - String headerName = header.group(1); - String headerValue = header.group(2); - headers.put(headerName.toLowerCase(), headerValue.toLowerCase()); - } - - private StringBuilder parseContentLengthDelimitedBody(BufferedReader reader, Map headers) throws IOException - { - StringBuilder body; - int readLen = 0; - int length = Integer.parseInt(headers.get("content-length")); - body = new StringBuilder(length); - try - { - //TODO: UTF-8 reader from joakim - for (int i = 0; i < length; ++i) - { - char c = (char)reader.read(); - body.append(c); - readLen++; - } - - } - catch (SocketTimeoutException e) - { - System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n", readLen, length); - throw e; - } - return body; - } - - private StringBuilder parseChunkedBody(BufferedReader reader) throws IOException - { - StringBuilder body; - String line; - body = new StringBuilder(64 * 1024); - while ((line = reader.readLine()) != null) - { - if ("0".equals(line)) - { - line = reader.readLine(); - assertThat("There's no more content after as 0 indicated the final chunk", line, is("")); - break; - } - - int length = Integer.parseInt(line, 16); - //TODO: UTF-8 reader from joakim - for (int i = 0; i < length; ++i) - { - char c = (char)reader.read(); - body.append(c); - } - reader.readLine(); - // assertThat("chunk is followed by an empty line", line, is("")); //TODO: is this right? - NO. Don't - // think you can really do chunks with read line generally, but maybe for this test is OK. - } - return body; - } - - private StringBuilder parseEOFDelimitedBody(BufferedReader reader, Map headers) throws IOException - { - StringBuilder body; - if ("HTTP/1.1".equals(httpVersion)) - assertThat("if no content-length or transfer-encoding header is set, " + - "connection: close header must be set", headers.get("connection"), - is("close")); - - // read until EOF - body = new StringBuilder(); - while (true) - { - //TODO: UTF-8 reader from joakim - int read = reader.read(); - if (read == -1) - break; - char c = (char)read; - body.append(c); - } - return body; - } - - private void assertResponseBody(Response response, String expectedResponseBody) - { - assertThat("response body is" + expectedResponseBody, response.getBody(), is(expectedResponseBody)); - } - - private void assertHeader(Response response, String headerName, String expectedValue) - { - assertThat(headerName + "=" + expectedValue, response.getHeaders().get(headerName), is(expectedValue)); - } - - private class Response - { - private final String code; - private final Map headers; - private final String body; - - private Response(String code, Map headers, String body) - { - this.code = code; - this.headers = headers; - this.body = body; - } - - public String getCode() - { - return code; - } - - public Map getHeaders() - { - return headers; - } - - public String getBody() - { - return body; - } - - @Override - public String toString() - { - return "Response{" + - "code='" + code + '\'' + - ", headers=" + headers + - ", body='" + body + '\'' + - '}'; - } - } - - private class ThrowExceptionOnDemandHandler extends AbstractHandler - { - private final boolean throwException; - - private ThrowExceptionOnDemandHandler(boolean throwException) - { - this.throwException = throwException; - } - - @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - if (throwException) - throw new TestCommitException(); - } - } - - private static class TestCommitException extends IllegalStateException - { - public TestCommitException() - { - super("Thrown by test"); - } - } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/TestHttpResponse.java b/jetty-server/src/test/java/org/eclipse/jetty/server/TestHttpResponse.java new file mode 100644 index 00000000000..ee43123004f --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/TestHttpResponse.java @@ -0,0 +1,54 @@ +//======================================================================== +//Copyright 2012 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.server; + +import java.util.Map; + +public class TestHttpResponse +{ + private final String code; + private final Map headers; + private final String body; + + public TestHttpResponse(String code, Map headers, String body) + { + this.code = code; + this.headers = headers; + this.body = body; + } + + public String getCode() + { + return code; + } + + public Map getHeaders() + { + return headers; + } + + public String getBody() + { + return body; + } + + @Override + public String toString() + { + return "Response{" + + "code='" + code + '\'' + + ", headers=" + headers + + ", body='" + body + '\'' + + '}'; + } +} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/util/SimpleHttpParser.java b/jetty-server/src/test/java/org/eclipse/jetty/server/util/SimpleHttpParser.java new file mode 100644 index 00000000000..a2154ada1e6 --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/util/SimpleHttpParser.java @@ -0,0 +1,158 @@ +//======================================================================== +//Copyright 2012 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.server.util; + +import java.io.BufferedReader; +import java.io.EOFException; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jetty.server.TestHttpResponse; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class SimpleHttpParser +{ + final String httpVersion; + + public SimpleHttpParser(String httpVersion) + { + this.httpVersion = httpVersion; + } + + public TestHttpResponse readResponse(BufferedReader reader) throws IOException + { + // Simplified parser for HTTP responses + String line = reader.readLine(); + if (line == null) + throw new EOFException(); + Matcher responseLine = Pattern.compile("HTTP/1.1" + "\\s+(\\d+)").matcher(line); + assertThat("http version is 1.1", responseLine.lookingAt(), is(true)); + String code = responseLine.group(1); + + Map headers = new LinkedHashMap<>(); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + + parseHeader(line, headers); + } + + StringBuilder body; + if (headers.containsKey("content-length")) + { + body = parseContentLengthDelimitedBody(reader, headers); + } + else if ("chunked".equals(headers.get("transfer-encoding"))) + { + body = parseChunkedBody(reader); + } + else + { + body = parseEOFDelimitedBody(reader, headers); + } + + return new TestHttpResponse(code, headers, body.toString().trim()); + } + + private void parseHeader(String line, Map headers) + { + Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line); + assertTrue(header.lookingAt()); + String headerName = header.group(1); + String headerValue = header.group(2); + headers.put(headerName.toLowerCase(), headerValue.toLowerCase()); + } + + private StringBuilder parseContentLengthDelimitedBody(BufferedReader reader, Map headers) throws IOException + { + StringBuilder body; + int readLen = 0; + int length = Integer.parseInt(headers.get("content-length")); + body = new StringBuilder(length); + try + { + //TODO: UTF-8 reader from joakim + for (int i = 0; i < length; ++i) + { + char c = (char)reader.read(); + body.append(c); + readLen++; + } + + } + catch (SocketTimeoutException e) + { + System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n", readLen, length); + throw e; + } + return body; + } + + private StringBuilder parseChunkedBody(BufferedReader reader) throws IOException + { + StringBuilder body; + String line; + body = new StringBuilder(64 * 1024); + while ((line = reader.readLine()) != null) + { + if ("0".equals(line)) + { + line = reader.readLine(); + assertThat("There's no more content after as 0 indicated the final chunk", line, is("")); + break; + } + + int length = Integer.parseInt(line, 16); + //TODO: UTF-8 reader from joakim + for (int i = 0; i < length; ++i) + { + char c = (char)reader.read(); + body.append(c); + } + reader.readLine(); + // assertThat("chunk is followed by an empty line", line, is("")); //TODO: is this right? - NO. Don't + // think you can really do chunks with read line generally, but maybe for this test is OK. + } + return body; + } + + private StringBuilder parseEOFDelimitedBody(BufferedReader reader, Map headers) throws IOException + { + StringBuilder body; + if ("HTTP/1.1".equals(httpVersion)) + assertThat("if no content-length or transfer-encoding header is set, " + + "connection: close header must be set", headers.get("connection"), + is("close")); + + // read until EOF + body = new StringBuilder(); + while (true) + { + //TODO: UTF-8 reader from joakim + int read = reader.read(); + if (read == -1) + break; + char c = (char)read; + body.append(c); + } + return body; + } +}