Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimone Bordet2014-04-22 19:44:48 +0000
committerSimone Bordet2014-04-22 19:44:48 +0000
commit7ef50ecc78de1e1fe94d459a9d4688b7dabf83ff (patch)
treeedf34c270aed87eb5bd7d2ed18a4b8fc8bb2bfc7
parent231ae5b12d1bb8f3a5350a5871c378d0387eb6ba (diff)
parent65e3beccb7355be4e6eec55ad95a19a414082b35 (diff)
downloadorg.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'.
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java60
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientDuplexTest.java324
-rw-r--r--jetty-client/src/test/resources/jetty-logging.properties3
-rw-r--r--jetty-spdy/pom.xml12
-rw-r--r--jetty-websocket/src/test/resources/jetty-logging.properties2
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

Back to the top