aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Becker2012-08-16 03:47:33 (EDT)
committerThomas Becker2012-08-16 03:47:33 (EDT)
commitaa22952368532b04565c411d8eecc30d306c73c4 (patch)
tree2bd68d67ba150f7c0ef8dbcd5e476ab1bddcaeb7
parent731eb31c7e7d089597bd7222bfcedf383ba9a11c (diff)
downloadorg.eclipse.jetty.project-aa22952368532b04565c411d8eecc30d306c73c4.zip
org.eclipse.jetty.project-aa22952368532b04565c411d8eecc30d306c73c4.tar.gz
org.eclipse.jetty.project-aa22952368532b04565c411d8eecc30d306c73c4.tar.bz2
interims commit
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java2
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpFiveWaysToCommitTest.java194
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java744
-rw-r--r--jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java183
4 files changed, 867 insertions, 256 deletions
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index b31d448..b55465c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -69,8 +69,6 @@ public abstract class HttpChannel
__currentChannel.set(channel);
}
-
-
private final Server _server;
private final Connection _connection;
private final HttpURI _uri;
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpFiveWaysToCommitTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpFiveWaysToCommitTest.java
deleted file mode 100644
index e22412b..0000000
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpFiveWaysToCommitTest.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package org.eclipse.jetty.server;
-//========================================================================
-//Copyright (c) 1999-2009 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.
-//========================================================================
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URISyntaxException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.util.IO;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * Resource Handler test
- * <p/>
- * TODO: increase the testing going on here
- */
-public class HttpFiveWaysToCommitTest
-{
- private static Server server;
- private static SelectChannelConnector connector;
-
-
- @Before
- public void setUp() throws Exception
- {
- server = new Server();
- connector = new SelectChannelConnector(server);
- server.setConnectors(new Connector[]{connector});
- }
-
- /* ------------------------------------------------------------ */
- @After
- public void tearDown() throws Exception
- {
- server.stop();
- }
-
- @Test
- public void testHandlerSetsHandledTrueOnly() throws Exception
- {
- server.setHandler(new OnlySetHandledHandler());
- server.start();
-
- StringWriter writer = executeRequest(HttpStatus.OK_200);
-
- System.out.println("RESPONSE: " + writer.toString());
- }
-
-
- private class OnlySetHandledHandler extends AbstractHandler
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- baseRequest.setHandled(true);
- }
- }
-
- @Test
- public void testHandlerExplicitFlush() throws Exception
- {
- server.setHandler(new ExplicitFlushHandler());
- server.start();
-
- StringWriter writer = executeRequest(HttpStatus.OK_200);
-
- System.out.println("RESPONSE: " + writer.toString());
- }
-
- private class ExplicitFlushHandler extends AbstractHandler
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- baseRequest.setHandled(true);
- response.getWriter().write("foobar");
- response.flushBuffer();
- }
- }
-
- @Test
- public void testHandlerDoesNotSetHandled() throws Exception
- {
- server.setHandler(new DoesNotSetHandledHandler());
- server.start();
-
- StringWriter writer = executeRequest(HttpStatus.NOT_FOUND_404);
-
- System.out.println("RESPONSE: " + writer.toString());
- }
-
- private class DoesNotSetHandledHandler extends AbstractHandler
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- baseRequest.setHandled(false);
- }
- }
-
- @Test
- public void testCommitWithMoreDataToWrite() throws Exception
- {
- server.setHandler(new CommitResponseWithMoreDataToWriteHandler());
- server.start();
-
- StringWriter writer = executeRequest(HttpStatus.OK_200);
-
- System.out.println("RESPONSE: " + writer.toString());
- }
-
- private class CommitResponseWithMoreDataToWriteHandler extends AbstractHandler
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- baseRequest.setHandled(true);
- response.getWriter().write("foo");
- response.flushBuffer();
- response.getWriter().write("bar");
- }
- }
-
- @Test
- public void testBufferOverflow() throws Exception
- {
- server.setHandler(new OverflowHandler());
- server.start();
-
- StringWriter writer = executeRequest(HttpStatus.OK_200);
-
- System.out.println("RESPONSE: " + writer.toString());
- }
-
- private class OverflowHandler extends AbstractHandler
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- baseRequest.setHandled(true);
- response.setContentLength(2);
- response.getWriter().write("foo");
- }
- }
-
- private StringWriter executeRequest(int expectedStatus) throws URISyntaxException, IOException
- {
- URI uri = new URI("http://localhost:" + connector.getLocalPort() + "/");
- HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection();
- connection.connect();
-
- for (String header : connection.getHeaderFields().keySet())
- {
- System.out.println(header + ": " + connection.getHeaderFields().get(header));
- }
- int responseCode = connection.getResponseCode();
- assertThat("return code is 200 ok", responseCode, is(expectedStatus));
-
- StringWriter writer = new StringWriter();
- if (responseCode == HttpStatus.OK_200)
- {
- InputStream inputStream = connection.getInputStream();
- InputStreamReader reader = new InputStreamReader(inputStream);
- IO.copy(reader, writer);
- }
-
- return writer;
- }
-
-}
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
new file mode 100644
index 0000000..5e8cb34
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpManyWaysToCommitTest.java
@@ -0,0 +1,744 @@
+//========================================================================
+//Copyright (c) 1999-2009 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.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.server.handler.AbstractHandler;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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
+@RunWith(value = Parameterized.class)
+public class HttpManyWaysToCommitTest
+{
+ private static Server server;
+ private static SelectChannelConnector connector;
+ private String httpVersion;
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data()
+ {
+ Object[][] data = new Object[][]{{"HTTP/1.0"}, {"HTTP/1.1"}};
+ return Arrays.asList(data);
+ }
+
+ 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});
+ }
+
+ /* ------------------------------------------------------------ */
+ @After
+ public void tearDown() throws Exception
+ {
+ server.stop();
+ }
+
+ @Test
+ public void testHandlerDoesNotSetHandled() throws Exception
+ {
+ server.setHandler(new DoesNotSetHandledHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 404", response.getCode(), is("404"));
+ }
+
+ @Test
+ public void testHandlerDoesNotSetHandledAndThrow() throws Exception
+ {
+ server.setHandler(new DoesNotSetHandledHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 500", response.getCode(), is("500"));
+ }
+
+ private class DoesNotSetHandledHandler extends ThrowExceptionOnDemandHandler
+ {
+ private DoesNotSetHandledHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(false); // not needed, but lets be explicit about what the test does
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testHandlerSetsHandledTrueOnly() throws Exception
+ {
+ server.setHandler(new OnlySetHandledHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "content-length", "0");
+ }
+
+ @Test
+ public void testHandlerSetsHandledTrueOnlyAndThrow() throws Exception
+ {
+ server.setHandler(new OnlySetHandledHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 500", response.getCode(), is("500"));
+ }
+
+
+ private class OnlySetHandledHandler extends ThrowExceptionOnDemandHandler
+ {
+ private OnlySetHandledHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testHandlerSetsHandledAndWritesSomeContent() throws Exception
+ {
+ server.setHandler(new SetHandledWriteSomeDataHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "content-length", "6");
+ }
+
+ @Test
+ public void testHandlerSetsHandledAndWritesSomeContentAndThrow() throws Exception
+ {
+ server.setHandler(new SetHandledWriteSomeDataHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ // This assertion is currently red. However I think it should be green. Nothing has been flushed to the
+ // client yet, when we throw an exception in the handler. So I expect HttpChannel to send a 500.
+ // Fails in HttpConnection line 619/620
+ assertThat("response code is 500", response.getCode(), is("500"));
+ }
+
+ private class SetHandledWriteSomeDataHandler extends ThrowExceptionOnDemandHandler
+ {
+ private SetHandledWriteSomeDataHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.getWriter().write("foobar");
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testHandlerExplicitFlush() throws Exception
+ {
+ server.setHandler(new ExplicitFlushHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "transfer-encoding", "chunked");
+ }
+
+ @Test
+ public void testHandlerExplicitFlushAndThrow() throws Exception
+ {
+ server.setHandler(new ExplicitFlushHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "transfer-encoding", "chunked");
+ }
+
+ private class ExplicitFlushHandler extends ThrowExceptionOnDemandHandler
+ {
+ private ExplicitFlushHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.getWriter().write("foobar");
+ response.flushBuffer();
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testHandledAndFlushWithoutContent() throws Exception
+ {
+ server.setHandler(new SetHandledAndFlushWithoutContentHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "transfer-encoding", "chunked");
+ }
+
+ @Test
+ public void testHandledAndFlushWithoutContentAndThrow() throws Exception
+ {
+ server.setHandler(new SetHandledAndFlushWithoutContentHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "transfer-encoding", "chunked");
+ }
+
+ private class SetHandledAndFlushWithoutContentHandler extends ThrowExceptionOnDemandHandler
+ {
+ private SetHandledAndFlushWithoutContentHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.flushBuffer();
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testWriteFlushWriteMore() throws Exception
+ {
+ server.setHandler(new WriteFlushWriteMoreHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "transfer-encoding", "chunked");
+ }
+
+ @Test
+ public void testWriteFlushWriteMoreAndThrow() throws Exception
+ {
+ server.setHandler(new WriteFlushWriteMoreHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertHeader(response, "transfer-encoding", "chunked");
+ }
+
+ private class WriteFlushWriteMoreHandler extends ThrowExceptionOnDemandHandler
+ {
+ private WriteFlushWriteMoreHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.getWriter().write("foo");
+ response.flushBuffer();
+ response.getWriter().write("bar");
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testBufferOverflow() throws Exception
+ {
+ server.setHandler(new OverflowHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertResponseBody(response, "foobar");
+ assertHeader(response, "content-length", "6");
+ }
+
+ @Test
+ public void testBufferOverflowAndThrow() throws Exception
+ {
+ server.setHandler(new OverflowHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ // response not committed when we throw, so 500 expected
+ assertThat("response code is 500", response.getCode(), is("500"));
+ }
+
+ private class OverflowHandler extends ThrowExceptionOnDemandHandler
+ {
+ private OverflowHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.setBufferSize(3);
+ response.getWriter().write("foobar");
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testSetContentLengthAndWriteExactlyThatAmountOfBytes() throws Exception
+ {
+ server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertThat("response body is foo", response.getBody(), is("foo"));
+ assertHeader(response, "content-length", "3");
+
+ }
+
+ @Test
+ public void testSetContentLengthAndWriteExactlyThatAmountOfBytesAndThrow() throws Exception
+ {
+ server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ //TODO: should we expect 500 here?
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertThat("response body is foo", response.getBody(), is("foo"));
+ assertHeader(response, "content-length", "3");
+ }
+
+ private class SetContentLengthAndWriteThatAmountOfBytesHandler extends ThrowExceptionOnDemandHandler
+ {
+ private SetContentLengthAndWriteThatAmountOfBytesHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.setContentLength(3);
+ response.getWriter().write("foo");
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testSetContentLengthAndWriteMoreBytes() throws Exception
+ {
+ server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ // TODO: seems like jetty truncates the body when content-length is reached?! Is this correct and desired
+ // behaviour?
+ assertThat("response body is foo", response.getBody(), is("foobar"));
+ assertHeader(response, "content-length", "6");
+ }
+
+ @Test
+ public void testSetContentLengthAndWriteMoreAndThrow() throws Exception
+ {
+ server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ // TODO: we throw before response is committed. should we expect 500?
+ assertThat("response code is 200", response.getCode(), is("200"));
+ // TODO: seems like jetty truncates the body when content-length is reached?! Is this correct and desired
+ // behaviour?
+ assertThat("response body is foo", response.getBody(), is("foobar"));
+ assertHeader(response, "content-length", "6");
+ }
+
+ private class SetContentLengthAndWriteMoreBytesHandler extends ThrowExceptionOnDemandHandler
+ {
+ private SetContentLengthAndWriteMoreBytesHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.setContentLength(3);
+ response.getWriter().write("foobar");
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testWriteAndSetContentLength() throws Exception
+ {
+ server.setHandler(new WriteAndSetContentLengthHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ //TODO: jetty ignores setContentLength and sends transfer-encoding header. Correct?
+ assertHeader(response,"transfer-encoding","chunked");
+ }
+
+ @Test
+ public void testWriteAndSetContentLengthAndThrow() throws Exception
+ {
+ server.setHandler(new WriteAndSetContentLengthHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ }
+
+ private class WriteAndSetContentLengthHandler extends ThrowExceptionOnDemandHandler
+ {
+ private WriteAndSetContentLengthHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.getWriter().write("foo");
+ response.flushBuffer();
+ response.setContentLength(3);
+ super.handle(target, baseRequest, request, response);
+ }
+ }
+
+ @Test
+ public void testWriteAndSetContentLengthTooSmall() throws Exception
+ {
+ server.setHandler(new WriteAndSetContentLengthTooSmallHandler(false));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ assertResponseBody(response, "foobar");
+ // TODO: once flushed setting contentLength is ignored and chunked is used. Correct?
+ assertHeader(response,"transfer-encoding","chunked");
+ }
+
+ @Test
+ public void testWriteAndSetContentLengthTooSmallAndThrow() throws Exception
+ {
+ server.setHandler(new WriteAndSetContentLengthTooSmallHandler(true));
+ server.start();
+
+ Response response = executeRequest();
+
+ assertThat("response code is 200", response.getCode(), is("200"));
+ }
+
+ private class WriteAndSetContentLengthTooSmallHandler extends ThrowExceptionOnDemandHandler
+ {
+ private WriteAndSetContentLengthTooSmallHandler(boolean throwException)
+ {
+ super(throwException);
+ }
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.getWriter().write("foobar");
+ response.flushBuffer();
+ response.setContentLength(3);
+ 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();
+
+ Response response = readResponse(reader);
+ System.out.println(response);//TODO: remove
+ return response;
+ }
+
+ 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(httpVersion + "\\s+(\\d+)").matcher(line);
+ assertThat("http version returned matches request version: " + httpVersion, responseLine.lookingAt(), is(true));
+ String code = responseLine.group(1);
+
+ Map<String, String> 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<String, String> 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<String, String> 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);
+ }
+ line = reader.readLine();
+ assertThat("chunk is followed by an empty line", line, is("")); //TODO: is this right?
+ }
+ return body;
+ }
+
+ private StringBuilder parseEOFDelimitedBody(BufferedReader reader, Map<String, String> 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<String, String> headers;
+ private final String body;
+
+ private Response(String code, Map<String, String> headers, String body)
+ {
+ this.code = code;
+ this.headers = headers;
+ this.body = body;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public Map<String, String> 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 IllegalStateException("explicit thrown on demand");
+ }
+ }
+
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java
index fa29394..130048b 100644
--- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java
+++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnectionFactory.java
@@ -96,7 +96,6 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
HTTPChannelOverSPDY channel = new HTTPChannelOverSPDY(connector.getServer(), endPoint.getConnection(),
stream);
stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
-
Headers headers = synInfo.getHeaders();
if (headers.isEmpty())
@@ -104,14 +103,11 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
// If the SYN has no headers, they may come later in a HEADERS frame
return this;
}
-
- channel.beginRequest(headers);
+ //TODO: beginRequest does two things, startRequest and close...should be doing one only?!
+ channel.beginRequest(headers, synInfo.isClose());
if (synInfo.isClose())
- {
- channel.endRequest();
return null;
- }
else
return this;
}
@@ -127,43 +123,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
{
logger.debug("Received {} on {}", headersInfo, stream);
HTTPChannelOverSPDY channel = (HTTPChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
- HttpChannel.EventHandler eventHandler = channel.getEventHandler();
-
- for (Headers.Header header : headersInfo.getHeaders())
- {
- String name = header.name();
-
- // Skip special SPDY headers, unless it's the "host" header
- HTTPSPDYHeader specialHeader = HTTPSPDYHeader.from(getVersion(), name);
- if (specialHeader != null)
- {
- if (specialHeader == HTTPSPDYHeader.HOST)
- name = "host";
- else
- continue;
- }
-
- switch (name)
- {
- case "connection":
- case "keep-alive":
- case "proxy-connection":
- case "transfer-encoding":
- {
- // Spec says to ignore these headers
- continue;
- }
- default:
- {
- HttpHeader httpHeader = HttpHeader.valueOf(header.name());
- // Spec says headers must be single valued
- String value = header.value();
- logger.debug("HTTP > {}: {}", name, value);
- //TODO: move stuff to HttpOverSPDYChannel?
- eventHandler.parsedHeader(httpHeader, header.name(), value);
- }
- }
- }
+ channel.parseHeaders(headersInfo.getHeaders());
if (headersInfo.isClose())
channel.endRequest();
@@ -230,6 +190,61 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
this.stream = stream;
}
+ @Override
+ public void handle()
+ {
+ switch (state)
+ {
+ case INITIAL:
+ {
+ break;
+ }
+ case REQUEST:
+ {
+ logger.debug("handle: REQUEST");
+ startRequest(headers);
+
+ updateState(State.HEADERS);
+ handle();
+ break;
+ }
+ case HEADERS:
+ {
+ parseHeaders(headers);
+ break;
+ }
+ case HEADERS_COMPLETE:
+ {
+ getEventHandler().headerComplete(false, false);
+ }
+ case CONTENT:
+ {
+ //TODO:
+ // final Buffer buffer = this.buffer;
+ // if (buffer != null && buffer.length() > 0)
+ // content(buffer);
+ break;
+ }
+ case FINAL:
+ {
+ getEventHandler().messageComplete(0);
+ super.handle();
+ break;
+ }
+ case ASYNC:
+ {
+ //TODO:
+ // handleRequest();
+ break;
+ }
+ default:
+ {
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+
private void post(Runnable task)
{
synchronized (tasks)
@@ -268,17 +283,71 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
}
}
- private void endRequest()
+ private void updateState(State newState)
{
- // TODO: hasBody is unused, persistent is false in spdy
- getEventHandler().headerComplete(false, false);
- // TODO: contentLength is unused
- getEventHandler().messageComplete(-1);
- //TODO: is this the way to go?
+ logger.debug("State update {} -> {}", state, newState);
+ state = newState;
+ }
+
+ private void close(Stream stream)
+ {
+ stream.getSession().goAway();
+ }
+
+ public void beginRequest(final Headers headers, final boolean endRequest)
+ {
+ this.headers = headers.isEmpty() ? null : headers;
+ post(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (!headers.isEmpty())
+ updateState(State.REQUEST);
+ handle();
+ if (endRequest)
+ performEndRequest();
+ }
+ });
+ }
+
+ public void headers(Headers headers)
+ {
+ this.headers = headers;
+ post(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ updateState(state == State.INITIAL ? State.REQUEST : State.HEADERS);
+ handle();
+ }
+ });
+ }
+
+ public void endRequest()
+ {
+ post(new Runnable()
+ {
+ public void run()
+ {
+ performEndRequest();
+ }
+ });
+ }
+
+ private void performEndRequest()
+ {
+ if (state == State.HEADERS)
+ {
+ updateState(State.HEADERS_COMPLETE);
+ handle();
+ }
+ updateState(State.FINAL);
handle();
}
- private void beginRequest(Headers headers)
+ private void startRequest(Headers headers)
{
this.headers = headers;
Headers.Header method = headers.get(HTTPSPDYHeader.METHOD.name(getVersion()));
@@ -300,7 +369,10 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
Headers.Header schemeHeader = headers.get(HTTPSPDYHeader.SCHEME.name(getVersion()));
// if (schemeHeader != null) //TODO: thomas
// _request.setScheme(schemeHeader.value());
+ }
+ private void parseHeaders(Headers headers)
+ {
for (Headers.Header header : headers)
{
String name = header.name();
@@ -400,7 +472,6 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
logger.debug("completeResponse");
getEventHandler().commit();
Response response = getResponse();
- logger.debug("completed");
Headers headers = new Headers();
headers.put(HTTPSPDYHeader.VERSION.name(getVersion()), HttpVersion.HTTP_1_1.asString());
StringBuilder status = new StringBuilder().append(response.getStatus());
@@ -456,7 +527,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
// {
// ServerHTTPSPDYAsyncConnection pushConnection =
// new ServerHTTPSPDYAsyncConnection(getConnector(), getEndPoint(), getServer(), getVersion(), connection, pushStrategy, pushStream);
- // pushConnection.beginRequest(requestHeaders, true);
+ // pushConnection.startRequest(requestHeaders, true);
// }
// });
}
@@ -513,12 +584,4 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
return null;
}
}
-
- /**
- * Needed in order to override generator methods that would generate HTTP, since we must generate SPDY instead.
- */
- private class HTTPSPDYGenerator
- {
-
- }
}