diff options
author | Simone Bordet | 2014-04-22 19:44:48 +0000 |
---|---|---|
committer | Simone Bordet | 2014-04-22 19:44:48 +0000 |
commit | 7ef50ecc78de1e1fe94d459a9d4688b7dabf83ff (patch) | |
tree | edf34c270aed87eb5bd7d2ed18a4b8fc8bb2bfc7 | |
parent | 231ae5b12d1bb8f3a5350a5871c378d0387eb6ba (diff) | |
parent | 65e3beccb7355be4e6eec55ad95a19a414082b35 (diff) | |
download | org.eclipse.jetty.project-7ef50ecc78de1e1fe94d459a9d4688b7dabf83ff.tar.gz org.eclipse.jetty.project-7ef50ecc78de1e1fe94d459a9d4688b7dabf83ff.tar.xz org.eclipse.jetty.project-7ef50ecc78de1e1fe94d459a9d4688b7dabf83ff.zip |
Merged branch 'jetty-7' into 'jetty-8'.
5 files changed, 396 insertions, 5 deletions
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java index b97b9ffc50..3930c78849 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java @@ -91,6 +91,8 @@ public class HttpExchange public static final int STATUS_EXCEPTED = 9; public static final int STATUS_CANCELLING = 10; public static final int STATUS_CANCELLED = 11; + public static final int STATUS_SENDING_PARSING_HEADERS = 12; + public static final int STATUS_SENDING_PARSING_CONTENT = 13; // HTTP protocol fields private String _method = HttpMethods.GET; @@ -126,8 +128,11 @@ public class HttpExchange protected void expire(HttpDestination destination) { AbstractHttpConnection connection = _connection; - if (getStatus() < HttpExchange.STATUS_COMPLETED) - setStatus(HttpExchange.STATUS_EXPIRED); + int status = getStatus(); + if (status < STATUS_COMPLETED || + status == STATUS_SENDING_PARSING_HEADERS || + status == STATUS_SENDING_PARSING_CONTENT) + setStatus(STATUS_EXPIRED); destination.exchangeExpired(this); if (connection != null) connection.exchangeExpired(this); @@ -255,6 +260,9 @@ public class HttpExchange case STATUS_SENDING_REQUEST: switch (newStatus) { + case STATUS_PARSING_HEADERS: + set = _status.compareAndSet(oldStatus,STATUS_SENDING_PARSING_HEADERS); + break; case STATUS_WAITING_FOR_RESPONSE: if (set = _status.compareAndSet(oldStatus,newStatus)) getEventListener().onRequestCommitted(); @@ -350,17 +358,55 @@ public class HttpExchange case STATUS_START: set = _status.compareAndSet(oldStatus,newStatus); break; - case STATUS_COMPLETED: ignored = true; done(); break; - default: ignored = true; break; } break; + case STATUS_SENDING_PARSING_HEADERS: + switch (newStatus) + { + case STATUS_WAITING_FOR_RESPONSE: + if (set = _status.compareAndSet(oldStatus,STATUS_PARSING_HEADERS)) + getEventListener().onRequestCommitted(); + break; + case STATUS_PARSING_CONTENT: + if (set = _status.compareAndSet(oldStatus,STATUS_SENDING_PARSING_CONTENT)) + getEventListener().onResponseHeaderComplete(); + break; + case STATUS_CANCELLING: + case STATUS_EXCEPTED: + set = _status.compareAndSet(oldStatus,newStatus); + break; + case STATUS_EXPIRED: + set = setStatusExpired(newStatus,oldStatus); + break; + default: + break; + } + break; + case STATUS_SENDING_PARSING_CONTENT: + switch (newStatus) + { + case STATUS_WAITING_FOR_RESPONSE: + if (set = _status.compareAndSet(oldStatus,STATUS_PARSING_CONTENT)) + getEventListener().onRequestCommitted(); + break; + case STATUS_CANCELLING: + case STATUS_EXCEPTED: + set = _status.compareAndSet(oldStatus,newStatus); + break; + case STATUS_EXPIRED: + set = setStatusExpired(newStatus,oldStatus); + break; + default: + break; + } + break; default: // Here means I allowed to set a state that I don't recognize throw new AssertionError(oldStatus + " => " + newStatus); @@ -878,6 +924,12 @@ public class HttpExchange case STATUS_CANCELLED: state = "CANCELLED"; break; + case STATUS_SENDING_PARSING_HEADERS: + state = "SENDING+HEADERS"; + break; + case STATUS_SENDING_PARSING_CONTENT: + state = "SENDING+CONTENT"; + break; default: state = "UNKNOWN"; } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientDuplexTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientDuplexTest.java new file mode 100644 index 0000000000..57b9086610 --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientDuplexTest.java @@ -0,0 +1,324 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.ByteArrayBuffer; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class HttpClientDuplexTest +{ + private ServerSocket server; + private HttpClient client; + + @Before + public void prepare() throws Exception + { + server = new ServerSocket(0); + + client = new HttpClient(); + client.start(); + } + + @After + public void dispose() throws Exception + { + client.stop(); + server.close(); + } + + @Test + public void testResponseHeadersBeforeRequestContent() throws Exception + { + final byte[] chunk1 = new byte[]{'A'}; + final byte[] chunk2 = new byte[]{'B'}; + final CountDownLatch requestContentLatch = new CountDownLatch(1); + ContentExchange exchange = new ContentExchange(true) + { + private int chunks; + + @Override + public Buffer getRequestContentChunk(Buffer buffer) throws IOException + { + ++chunks; + if (chunks == 1) + { + if (!await(requestContentLatch, 5000)) + throw new IOException(); + return new ByteArrayBuffer(chunk1); + } + else if (chunks == 2) + { + // The test needs a second chunk to stay in "sending" + // state and trigger the condition we want to test. + return new ByteArrayBuffer(chunk2); + } + else + { + return null; + } + } + }; + exchange.setURL("http://localhost:" + server.getLocalPort()); + // The test needs a fake content source to invoke + // getRequestContentChunk() which will provide the content. + exchange.setRequestContentSource(new ClosedInputStream()); + exchange.setRequestHeader("Content-Length", String.valueOf(chunk1.length + chunk2.length)); + client.send(exchange); + + Socket socket = server.accept(); + BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); + OutputStream output = socket.getOutputStream(); + + // Read headers. + while (true) + { + String line = input.readLine(); + Assert.assertNotNull(line); + if (line.length() == 0) + break; + } + + byte[] responseContent = new byte[64]; + String responseHeaders = "" + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: " + responseContent.length + "\r\n" + + "\r\n"; + output.write(responseHeaders.getBytes("UTF-8")); + output.flush(); + + // Now send more request content. + requestContentLatch.countDown(); + + // Read request content on server. + for (int i = 0; i < chunk1.length; ++i) + Assert.assertNotEquals(-1, input.read()); + for (int i = 0; i < chunk2.length; ++i) + Assert.assertNotEquals(-1, input.read()); + + // Send response content to client. + output.write(responseContent); + output.flush(); + + Assert.assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); + Assert.assertEquals(200, exchange.getResponseStatus()); + + socket.close(); + } + + @Test + public void testResponseHeadersBeforeRequestContentThenExpire() throws Exception + { + final byte[] chunk1 = new byte[]{'A'}; + final byte[] chunk2 = new byte[]{'B'}; + final byte[] chunk3 = new byte[]{'C'}; + final CountDownLatch requestContentLatch = new CountDownLatch(1); + final long idleTimeout = 1000; + ContentExchange exchange = new ContentExchange(true) + { + private int chunks; + + @Override + public Buffer getRequestContentChunk(Buffer buffer) throws IOException + { + ++chunks; + if (chunks == 1) + { + if (!await(requestContentLatch, 5000)) + throw new IOException(); + return new ByteArrayBuffer(chunk1); + } + else if (chunks == 2) + { + // The test needs a second chunk to stay in "sending" + // state and trigger the condition we want to test. + return new ByteArrayBuffer(chunk2); + } + else if (chunks == 3) + { + // Idle timeout. + await(new CountDownLatch(1), idleTimeout * 2); + } + return null; + } + }; + exchange.setURL("http://localhost:" + server.getLocalPort()); + // The test needs a fake content source to invoke + // getRequestContentChunk() which will provide the content. + exchange.setRequestContentSource(new ClosedInputStream()); + exchange.setRequestHeader("Content-Length", String.valueOf(chunk1.length + chunk2.length + chunk3.length)); + exchange.setTimeout(idleTimeout); + client.send(exchange); + + Socket socket = server.accept(); + BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); + OutputStream output = socket.getOutputStream(); + + // Read headers. + while (true) + { + String line = input.readLine(); + Assert.assertNotNull(line); + if (line.length() == 0) + break; + } + + byte[] responseContent = new byte[64]; + String responseHeaders = "" + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: " + responseContent.length + "\r\n" + + "\r\n"; + output.write(responseHeaders.getBytes("UTF-8")); + output.flush(); + + // Now try to send more request content, but it will timeout. + requestContentLatch.countDown(); + + // Read request content on server. + for (int i = 0; i < chunk1.length; ++i) + Assert.assertNotEquals(-1, input.read()); + // Server could possibly read -1. + for (int i = 0; i < chunk2.length; ++i) + input.read(); + for (int i = 0; i < chunk3.length; ++i) + input.read(); + + // Send response content to client. + output.write(responseContent); + output.flush(); + + Assert.assertEquals(HttpExchange.STATUS_EXPIRED, exchange.waitForDone()); + + socket.close(); + } + + @Test + public void testResponseHeadersBeforeRequestContentThenThrow() throws Exception + { + final byte[] chunk1 = new byte[]{'A'}; + final byte[] chunk2 = new byte[]{'B'}; + final CountDownLatch requestContentLatch = new CountDownLatch(1); + ContentExchange exchange = new ContentExchange(true) + { + private int chunks; + + @Override + public Buffer getRequestContentChunk(Buffer buffer) throws IOException + { + ++chunks; + if (chunks == 1) + { + if (!await(requestContentLatch, 5000)) + throw new IOException(); + return new ByteArrayBuffer(chunk1); + } + else if (chunks == 2) + { + // The test needs a second chunk to stay in "sending" + // state and trigger the condition we want to test. + return new ByteArrayBuffer(chunk2); + } + else + { + throw new IOException(); + } + } + }; + exchange.setURL("http://localhost:" + server.getLocalPort()); + // The test needs a fake content source to invoke + // getRequestContentChunk() which will provide the content. + exchange.setRequestContentSource(new ClosedInputStream()); + exchange.setRequestHeader("Content-Length", String.valueOf(chunk1.length + chunk2.length)); + client.send(exchange); + + Socket socket = server.accept(); + BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); + OutputStream output = socket.getOutputStream(); + + // Read headers. + while (true) + { + String line = input.readLine(); + Assert.assertNotNull(line); + if (line.length() == 0) + break; + } + + byte[] responseContent = new byte[64]; + String responseHeaders = "" + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: " + responseContent.length + "\r\n" + + "\r\n"; + output.write(responseHeaders.getBytes("UTF-8")); + output.flush(); + + // Now send more request content. + requestContentLatch.countDown(); + + // Read request content on server. + for (int i = 0; i < chunk1.length; ++i) + Assert.assertNotEquals(-1, input.read()); + // Server could possibly read -1. + for (int i = 0; i < chunk2.length; ++i) + input.read(); + + // Send response content to client. + output.write(responseContent); + output.flush(); + + Assert.assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.waitForDone()); + + socket.close(); + } + + private boolean await(CountDownLatch latch, long millis) throws InterruptedIOException + { + try + { + return latch.await(millis, TimeUnit.MILLISECONDS); + } + catch (InterruptedException x) + { + throw new InterruptedIOException(); + } + } + + private class ClosedInputStream extends InputStream + { + @Override + public int read() throws IOException + { + return -1; + } + } +} diff --git a/jetty-client/src/test/resources/jetty-logging.properties b/jetty-client/src/test/resources/jetty-logging.properties new file mode 100644 index 0000000000..ac8bcd8ec1 --- /dev/null +++ b/jetty-client/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Setup default logging implementation for during testing +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.client.LEVEL=INFO diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml index 49c7a87466..bbe9b95c8d 100644 --- a/jetty-spdy/pom.xml +++ b/jetty-spdy/pom.xml @@ -194,6 +194,18 @@ <npn.version>1.1.6.v20130911</npn.version> </properties> </profile> + <profile> + <id>7u55</id> + <activation> + <property> + <name>java.version</name> + <value>1.7.0_55</value> + </property> + </activation> + <properties> + <npn.version>1.1.7.v20140316</npn.version> + </properties> + </profile> </profiles> </project> diff --git a/jetty-websocket/src/test/resources/jetty-logging.properties b/jetty-websocket/src/test/resources/jetty-logging.properties index 78d8a9d812..7633c48c8f 100644 --- a/jetty-websocket/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/src/test/resources/jetty-logging.properties @@ -1,4 +1,4 @@ # Setup default logging implementation for during testing org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.LEVEL=INFO -org.eclipse.jetty.websocket.LEVEL=DEBUG
\ No newline at end of file +#org.eclipse.jetty.websocket.LEVEL=DEBUG |