Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimone Bordet2015-12-21 07:02:04 -0500
committerSimone Bordet2015-12-21 07:02:04 -0500
commit3fb354f884e3a9c06ad9fe63f4201c08891e5089 (patch)
treee845103e10adf89a0138419a6f879a9a34b14746
parent24b9d39c0f54bc064dad7dac7b82d5e1297fcc51 (diff)
parent8f4cc73613e5f6953789eb32eb7aba8e26083854 (diff)
downloadorg.eclipse.jetty.project-3fb354f884e3a9c06ad9fe63f4201c08891e5089.tar.gz
org.eclipse.jetty.project-3fb354f884e3a9c06ad9fe63f4201c08891e5089.tar.xz
org.eclipse.jetty.project-3fb354f884e3a9c06ad9fe63f4201c08891e5089.zip
Merged branch 'jetty-9.3.x' into 'master'.
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java60
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java16
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java37
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java31
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java5
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java39
-rw-r--r--jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java2
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java22
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java47
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java5
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java5
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java9
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java32
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java2
-rw-r--r--jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java13
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java6
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java24
-rw-r--r--jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java5
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java12
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java4
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java42
-rw-r--r--jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java56
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java2
-rw-r--r--jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java1
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java12
-rw-r--r--tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java228
26 files changed, 587 insertions, 130 deletions
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
index e859d72eed..10887e00e8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
@@ -23,6 +23,8 @@ import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.Connection;
@@ -35,11 +37,15 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
public abstract class HttpConnection implements Connection
{
+ private static final Logger LOG = Log.getLogger(HttpConnection.class);
private static final HttpField CHUNKED_FIELD = new HttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
+ private final AtomicInteger idleTimeoutState = new AtomicInteger();
private final HttpDestination destination;
protected HttpConnection(HttpDestination destination)
@@ -72,10 +78,12 @@ public abstract class HttpConnection implements Connection
HttpExchange exchange = new HttpExchange(getHttpDestination(), (HttpRequest)request, listeners);
- send(exchange);
+ SendFailure result = send(exchange);
+ if (result != null)
+ request.abort(result.failure);
}
- protected abstract void send(HttpExchange exchange);
+ protected abstract SendFailure send(HttpExchange exchange);
protected void normalizeRequest(Request request)
{
@@ -167,6 +175,54 @@ public abstract class HttpConnection implements Connection
return builder;
}
+ protected SendFailure send(HttpChannel channel, HttpExchange exchange)
+ {
+ // Forbid idle timeouts for the time window where
+ // the request is associated to the channel and sent.
+ // Use a counter to support multiplexed requests.
+ boolean send = false;
+ while (true)
+ {
+ int current = idleTimeoutState.get();
+ if (current < 0)
+ break;
+ if (idleTimeoutState.compareAndSet(current, current + 1))
+ {
+ send = true;
+ break;
+ }
+ }
+
+ if (send)
+ {
+ HttpRequest request = exchange.getRequest();
+ SendFailure result;
+ if (channel.associate(exchange))
+ {
+ channel.send();
+ result = null;
+ }
+ else
+ {
+ channel.release();
+ result = new SendFailure(new HttpRequestException("Could not associate request to connection", request), false);
+ }
+ idleTimeoutState.decrementAndGet();
+ return result;
+ }
+ else
+ {
+ return new SendFailure(new TimeoutException(), true);
+ }
+ }
+
+ public boolean onIdleTimeout()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Idle timeout state {} - {}", idleTimeoutState, this);
+ return idleTimeoutState.compareAndSet(0, -1);
+ }
+
@Override
public String toString()
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index da899a07f8..9d0abc7ce4 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -320,13 +320,25 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
}
else
{
- send(connection, exchange);
+ SendFailure result = send(connection, exchange);
+ if (result != null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Send failed {} for {}", result, exchange);
+ if (result.retry)
+ {
+ if (enqueue(getHttpExchanges(), exchange))
+ return true;
+ }
+
+ request.abort(result.failure);
+ }
}
return getHttpExchanges().peek() != null;
}
}
- protected abstract void send(Connection connection, HttpExchange exchange);
+ protected abstract SendFailure send(Connection connection, HttpExchange exchange);
public void newConnection(Promise<Connection> promise)
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java b/jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java
new file mode 100644
index 0000000000..1aeaa69213
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/SendFailure.java
@@ -0,0 +1,37 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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;
+
+public class SendFailure
+{
+ public final Throwable failure;
+ public final boolean retry;
+
+ public SendFailure(Throwable failure, boolean retry)
+ {
+ this.failure = failure;
+ this.retry = retry;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s[failure=%s,retry=%b]", super.toString(), failure, retry);
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
index 5c60de2c66..7cffc1ea11 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
@@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
@@ -77,9 +78,9 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
delegate.send(request, listener);
}
- protected void send(HttpExchange exchange)
+ protected SendFailure send(HttpExchange exchange)
{
- delegate.send(exchange);
+ return delegate.send(exchange);
}
@Override
@@ -96,11 +97,11 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
}
@Override
- protected boolean onReadTimeout()
+ public boolean onIdleExpired()
{
- if (LOG.isDebugEnabled())
- LOG.debug("Idle timeout {}", this);
- close(new TimeoutException());
+ boolean close = delegate.onIdleTimeout();
+ if (close)
+ close(new TimeoutException("Idle timeout " + getEndPoint().getIdleTimeout() + "ms"));
return false;
}
@@ -142,7 +143,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
protected void close(Throwable failure)
{
- if (softClose())
+ if (closed.compareAndSet(false, true))
{
// First close then abort, to be sure that the connection cannot be reused
// from an onFailure() handler or by blocking code waiting for completion.
@@ -158,11 +159,6 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
}
}
- public boolean softClose()
- {
- return closed.compareAndSet(false, true);
- }
-
protected boolean abort(Throwable failure)
{
HttpExchange exchange = channel.getHttpExchange();
@@ -204,21 +200,18 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
}
@Override
- protected void send(HttpExchange exchange)
+ protected SendFailure send(HttpExchange exchange)
{
Request request = exchange.getRequest();
normalizeRequest(request);
- // Save the old idle timeout to restore it
+ // Save the old idle timeout to restore it.
EndPoint endPoint = getEndPoint();
idleTimeout = endPoint.getIdleTimeout();
endPoint.setIdleTimeout(request.getIdleTimeout());
- // One channel per connection, just delegate the send
- if (channel.associate(exchange))
- channel.send();
- else
- channel.release();
+ // One channel per connection, just delegate the send.
+ return send(channel, exchange);
}
@Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
index 284ce08fe5..cb022c8efa 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
@@ -22,6 +22,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.PoolingHttpDestination;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
public class HttpDestinationOverHTTP extends PoolingHttpDestination
@@ -32,8 +33,8 @@ public class HttpDestinationOverHTTP extends PoolingHttpDestination
}
@Override
- protected void send(Connection connection, HttpExchange exchange)
+ protected SendFailure send(Connection connection, HttpExchange exchange)
{
- ((HttpConnectionOverHTTP)connection).send(exchange);
+ return ((HttpConnectionOverHTTP)connection).send(exchange);
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index 404a3e8e3f..00d9706548 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -467,37 +467,24 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@Test
public void test_QueuedRequest_IsSent_WhenPreviousRequestClosedConnection() throws Exception
{
- start(new EmptyServerHandler());
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ if (target.endsWith("/one"))
+ baseRequest.getHttpChannel().getEndPoint().close();
+ else
+ baseRequest.setHandled(true);
+ }
+ });
client.setMaxConnectionsPerDestination(1);
- final long idleTimeout = 1000;
- client.setIdleTimeout(idleTimeout);
- final CountDownLatch latch = new CountDownLatch(3);
+ final CountDownLatch latch = new CountDownLatch(2);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.path("/one")
- .listener(new Request.Listener.Adapter()
- {
- @Override
- public void onBegin(Request request)
- {
- try
- {
- TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
- }
- catch (InterruptedException x)
- {
- x.printStackTrace();
- }
- }
-
- @Override
- public void onFailure(Request request, Throwable failure)
- {
- latch.countDown();
- }
- })
.onResponseFailure((response, failure) -> latch.countDown())
.send(null);
@@ -511,7 +498,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
})
.send(null);
- Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Test
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
index feb5cf0c58..1be2353908 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
@@ -171,7 +171,7 @@ public class HttpReceiverOverHTTPTest
FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
connection.getHttpChannel().receive();
// Simulate an idle timeout
- connection.onReadTimeout();
+ connection.onIdleExpired();
try
{
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java
index fb47ec7ef4..3704ce80b3 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpChannelOverFCGI.java
@@ -79,6 +79,7 @@ public class HttpChannelOverFCGI extends HttpChannel
if (exchange != null)
{
version = exchange.getRequest().getVersion();
+ idle.onOpen();
sender.send(exchange);
}
}
@@ -91,6 +92,7 @@ public class HttpChannelOverFCGI extends HttpChannel
protected boolean responseBegin(int code, String reason)
{
+ idle.notIdle();
HttpExchange exchange = getHttpExchange();
if (exchange == null)
return false;
@@ -106,12 +108,14 @@ public class HttpChannelOverFCGI extends HttpChannel
protected boolean responseHeaders()
{
+ idle.notIdle();
HttpExchange exchange = getHttpExchange();
return exchange != null && receiver.responseHeaders(exchange);
}
protected boolean content(ByteBuffer buffer, Callback callback)
{
+ idle.notIdle();
HttpExchange exchange = getHttpExchange();
if (exchange != null)
return receiver.responseContent(exchange, buffer, callback);
@@ -151,6 +155,7 @@ public class HttpChannelOverFCGI extends HttpChannel
private class FCGIIdleTimeout extends IdleTimeout
{
private final HttpConnectionOverFCGI connection;
+ private boolean open;
public FCGIIdleTimeout(HttpConnectionOverFCGI connection, long idleTimeout)
{
@@ -160,6 +165,21 @@ public class HttpChannelOverFCGI extends HttpChannel
}
@Override
+ public void onOpen()
+ {
+ open = true;
+ notIdle();
+ super.onOpen();
+ }
+
+ @Override
+ public void onClose()
+ {
+ super.onClose();
+ open = false;
+ }
+
+ @Override
protected void onIdleExpired(TimeoutException timeout)
{
if (LOG.isDebugEnabled())
@@ -170,7 +190,7 @@ public class HttpChannelOverFCGI extends HttpChannel
@Override
public boolean isOpen()
{
- return connection.getEndPoint().isOpen();
+ return open;
}
}
}
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
index 0d71cfd3ed..d3c588150c 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java
@@ -31,6 +31,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
@@ -82,15 +83,20 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
return destination;
}
+ protected Flusher getFlusher()
+ {
+ return flusher;
+ }
+
@Override
public void send(Request request, Response.CompleteListener listener)
{
delegate.send(request, listener);
}
- protected void send(HttpExchange exchange)
+ protected SendFailure send(HttpExchange exchange)
{
- delegate.send(exchange);
+ return delegate.send(exchange);
}
@Override
@@ -185,9 +191,13 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
}
@Override
- protected boolean onReadTimeout()
+ public boolean onIdleExpired()
{
- close(new TimeoutException());
+ boolean close = delegate.onIdleTimeout();
+ if (multiplexed)
+ close &= isFillInterested();
+ if (close)
+ close(new TimeoutException("Idle timeout " + getEndPoint().getIdleTimeout() + "ms"));
return false;
}
@@ -197,6 +207,11 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
destination.release(this);
}
+ public boolean isClosed()
+ {
+ return closed.get();
+ }
+
@Override
public void close()
{
@@ -212,10 +227,10 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
getHttpDestination().close(this);
getEndPoint().shutdownOutput();
if (LOG.isDebugEnabled())
- LOG.debug("{} oshut", this);
+ LOG.debug("Shutdown {}", this);
getEndPoint().close();
if (LOG.isDebugEnabled())
- LOG.debug("{} closed", this);
+ LOG.debug("Closed {}", this);
abort(failure);
}
@@ -270,6 +285,11 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
}
}
+ protected HttpChannelOverFCGI newHttpChannel(int id, Request request)
+ {
+ return new HttpChannelOverFCGI(this, getFlusher(), id, request.getIdleTimeout());
+ }
+
@Override
public String toString()
{
@@ -288,19 +308,17 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
}
@Override
- protected void send(HttpExchange exchange)
+ protected SendFailure send(HttpExchange exchange)
{
Request request = exchange.getRequest();
normalizeRequest(request);
// FCGI may be multiplexed, so create one channel for each request.
int id = acquireRequest();
- HttpChannelOverFCGI channel = new HttpChannelOverFCGI(HttpConnectionOverFCGI.this, flusher, id, request.getIdleTimeout());
+ HttpChannelOverFCGI channel = newHttpChannel(id, request);
channels.put(id, channel);
- if (channel.associate(exchange))
- channel.send();
- else
- channel.release();
+
+ return send(channel, exchange);
}
@Override
@@ -309,6 +327,11 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
HttpConnectionOverFCGI.this.close();
}
+ protected void close(Throwable failure)
+ {
+ HttpConnectionOverFCGI.this.close(failure);
+ }
+
@Override
public String toString()
{
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
index 2f3447d384..3ce6db818f 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpDestinationOverFCGI.java
@@ -22,6 +22,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.PoolingHttpDestination;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
public class HttpDestinationOverFCGI extends PoolingHttpDestination
@@ -32,8 +33,8 @@ public class HttpDestinationOverFCGI extends PoolingHttpDestination
}
@Override
- protected void send(Connection connection, HttpExchange exchange)
+ protected SendFailure send(Connection connection, HttpExchange exchange)
{
- ((HttpConnectionOverFCGI)connection).send(exchange);
+ return ((HttpConnectionOverFCGI)connection).send(exchange);
}
}
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
index 80bb63cc47..70c35b0043 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/MultiplexHttpDestinationOverFCGI.java
@@ -22,6 +22,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination
@@ -32,8 +33,8 @@ public class MultiplexHttpDestinationOverFCGI extends MultiplexHttpDestination
}
@Override
- protected void send(Connection connection, HttpExchange exchange)
+ protected SendFailure send(Connection connection, HttpExchange exchange)
{
- ((HttpConnectionOverFCGI)connection).send(exchange);
+ return ((HttpConnectionOverFCGI)connection).send(exchange);
}
}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java
index 48d5d9ce3a..e9b7602ac1 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java
@@ -113,11 +113,12 @@ public class HTTP2Connection extends AbstractConnection
}
@Override
- protected boolean onReadTimeout()
+ public boolean onIdleExpired()
{
- if (LOG.isDebugEnabled())
- LOG.debug("Idle timeout {}ms expired on {}", getEndPoint().getIdleTimeout(), this);
- session.onIdleTimeout();
+ boolean close = session.onIdleTimeout();
+ boolean idle = isFillInterested();
+ if (close && idle)
+ session.close(ErrorCode.NO_ERROR.code, "idle_timeout", Callback.NOOP);
return false;
}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
index e8a0dd1c57..999d25d154 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
@@ -535,7 +535,13 @@ public abstract class HTTP2Session implements ISession, Parser.Listener
{
if (closed.compareAndSet(current, CloseState.LOCALLY_CLOSED))
{
- byte[] payload = reason == null ? null : reason.getBytes(StandardCharsets.UTF_8);
+ byte[] payload = null;
+ if (reason != null)
+ {
+ // Trim the reason to avoid attack vectors.
+ reason = reason.substring(0, Math.min(reason.length(), 32));
+ payload = reason.getBytes(StandardCharsets.UTF_8);
+ }
GoAwayFrame frame = new GoAwayFrame(lastStreamId.get(), error, payload);
control(null, callback, frame);
return true;
@@ -826,30 +832,29 @@ public abstract class HTTP2Session implements ISession, Parser.Listener
* stuck because of TCP congestion), therefore we terminate.
* See {@link #onGoAway(GoAwayFrame)}.
*
+ * @return true if the session should be closed, false otherwise
* @see #onGoAway(GoAwayFrame)
* @see #close(int, String, Callback)
* @see #onShutdown()
*/
@Override
- public void onIdleTimeout()
+ public boolean onIdleTimeout()
{
switch (closed.get())
{
case NOT_CLOSED:
{
- // Real idle timeout, just close.
- close(ErrorCode.NO_ERROR.code, "idle_timeout", Callback.NOOP);
- break;
+ return notifyIdleTimeout(this);
}
case LOCALLY_CLOSED:
case REMOTELY_CLOSED:
{
abort(new TimeoutException());
- break;
+ return false;
}
default:
{
- break;
+ return false;
}
}
}
@@ -974,6 +979,19 @@ public abstract class HTTP2Session implements ISession, Parser.Listener
}
}
+ protected boolean notifyIdleTimeout(Session session)
+ {
+ try
+ {
+ return listener.onIdleTimeout(session);
+ }
+ catch (Throwable x)
+ {
+ LOG.info("Failure while notifying listener " + listener, x);
+ return true;
+ }
+ }
+
protected void notifyFailure(Session session, Throwable failure)
{
try
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
index 4e12150976..4a40bfb29a 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/ISession.java
@@ -118,7 +118,7 @@ public interface ISession extends Session
* @see #onShutdown()
* @see #close(int, String, Callback)
*/
- public void onIdleTimeout();
+ public boolean onIdleTimeout();
/**
* <p>Callback method invoked during an HTTP/1.1 to HTTP/2 upgrade requests
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
index 16bdc08f3b..89a7959887 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
@@ -201,6 +201,13 @@ public interface Session
public void onClose(Session session, GoAwayFrame frame);
/**
+ * <p>Callback method invoked when the idle timeout expired.</p>
+ * @param session the session
+ * @return whether the session should be closed
+ */
+ public boolean onIdleTimeout(Session session);
+
+ /**
* <p>Callback method invoked when a failure has been detected for this session.</p>
*
* @param session the session
@@ -246,6 +253,12 @@ public interface Session
}
@Override
+ public boolean onIdleTimeout(Session session)
+ {
+ return true;
+ }
+
+ @Override
public void onFailure(Session session, Throwable failure)
{
}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
index 6dd6c23051..3cb97035a4 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
@@ -175,6 +175,12 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
}
@Override
+ public boolean onIdleTimeout(Session session)
+ {
+ return connection.onIdleTimeout();
+ }
+
+ @Override
public void onFailure(Session session, Throwable failure)
{
connection.close(failure);
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
index d6624a54e8..1d3a4c6dbb 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
@@ -25,6 +25,7 @@ import org.eclipse.jetty.client.HttpChannel;
import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.util.Callback;
@@ -41,17 +42,26 @@ public class HttpConnectionOverHTTP2 extends HttpConnection
this.session = session;
}
+ public Session getSession()
+ {
+ return session;
+ }
+
@Override
- protected void send(HttpExchange exchange)
+ protected SendFailure send(HttpExchange exchange)
{
normalizeRequest(exchange.getRequest());
+
// One connection maps to N channels, so for each exchange we create a new channel.
- HttpChannel channel = new HttpChannelOverHTTP2(getHttpDestination(), this, session);
+ HttpChannel channel = newHttpChannel();
channels.add(channel);
- if (channel.associate(exchange))
- channel.send();
- else
- channel.release();
+
+ return send(channel, exchange);
+ }
+
+ protected HttpChannelOverHTTP2 newHttpChannel()
+ {
+ return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession());
}
protected void release(HttpChannel channel)
@@ -71,7 +81,7 @@ public class HttpConnectionOverHTTP2 extends HttpConnection
// First close then abort, to be sure that the connection cannot be reused
// from an onFailure() handler or by blocking code waiting for completion.
getHttpDestination().close(this);
- session.close(ErrorCode.NO_ERROR.code, null, Callback.NOOP);
+ session.close(ErrorCode.NO_ERROR.code, failure.getMessage(), Callback.NOOP);
abort(failure);
}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
index 114df6f3f6..14372f02c2 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpDestinationOverHTTP2.java
@@ -22,6 +22,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
public class HttpDestinationOverHTTP2 extends MultiplexHttpDestination
@@ -32,8 +33,8 @@ public class HttpDestinationOverHTTP2 extends MultiplexHttpDestination
}
@Override
- protected void send(Connection connection, HttpExchange exchange)
+ protected SendFailure send(Connection connection, HttpExchange exchange)
{
- ((HttpConnectionOverHTTP2)connection).send(exchange);
+ return ((HttpConnectionOverHTTP2)connection).send(exchange);
}
}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
index 1ae1a7a0c3..e86d17eb09 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java
@@ -61,6 +61,12 @@ public abstract class AbstractConnection implements Connection
listeners.add(listener);
}
+ @Override
+ public void removeListener(Listener listener)
+ {
+ listeners.remove(listener);
+ }
+
public int getInputBufferSize()
{
return _inputBufferSize;
@@ -215,6 +221,12 @@ public abstract class AbstractConnection implements Connection
}
@Override
+ public boolean onIdleExpired()
+ {
+ return true;
+ }
+
+ @Override
public int getMessagesIn()
{
return -1;
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
index ceab75bd02..fbf2ee9375 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java
@@ -370,6 +370,10 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
@Override
protected void onIdleExpired(TimeoutException timeout)
{
+ Connection connection = _connection;
+ if (connection != null && !_connection.onIdleExpired())
+ return;
+
boolean output_shutdown=isOutputShutdown();
boolean input_shutdown=isInputShutdown();
boolean fillFailed = _fillInterest.onFail(timeout);
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java
index b47c59058e..f6d4265150 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java
@@ -33,12 +33,28 @@ import org.eclipse.jetty.util.component.Container;
*/
public interface Connection extends Closeable
{
+ /**
+ * <p>Adds a listener of connection events.</p>
+ *
+ * @param listener the listener to add
+ */
public void addListener(Listener listener);
+ /**
+ * <p>Removes a listener of connection events.</p>
+ *
+ * @param listener the listener to remove
+ */
+ public void removeListener(Listener listener);
+
+ /**
+ * <p>Callback method invoked when this connection is opened.</p>
+ * <p>Creators of the connection implementation are responsible for calling this method.</p>
+ */
public void onOpen();
/**
- * <p>Callback method invoked when this {@link Connection} is closed.</p>
+ * <p>Callback method invoked when this connection is closed.</p>
* <p>Creators of the connection implementation are responsible for calling this method.</p>
*/
public void onClose();
@@ -57,6 +73,19 @@ public interface Connection extends Closeable
@Override
public void close();
+ /**
+ * <p>Callback method invoked upon an idle timeout event.</p>
+ * <p>Implementations of this method may return true to indicate that the idle timeout
+ * handling should proceed normally, typically failing the EndPoint and causing it to
+ * be closed.</p>
+ * <p>When false is returned, the handling of the idle timeout event is halted
+ * immediately and the EndPoint left in the state it was before the idle timeout event.</p>
+ *
+ * @return true to let the EndPoint handle the idle timeout,
+ * false to tell the EndPoint to halt the handling of the idle timeout.
+ */
+ public boolean onIdleExpired();
+
public int getMessagesIn();
public int getMessagesOut();
public long getBytesIn();
@@ -65,10 +94,11 @@ public interface Connection extends Closeable
public interface UpgradeFrom
{
- /* ------------------------------------------------------------ */
- /** Take the input buffer from the connection on upgrade.
+ /**
+ * <p>Takes the input buffer from the connection on upgrade.</p>
* <p>This method is used to take any unconsumed input from
- * a connection during an upgrade.
+ * a connection during an upgrade.</p>
+ *
* @return A buffer of unconsumed input. The caller must return the buffer
* to the bufferpool when consumed and this connection must not.
*/
@@ -78,7 +108,7 @@ public interface Connection extends Closeable
public interface UpgradeTo
{
/**
- * <p>Callback method invoked when this {@link Connection} is upgraded.</p>
+ * <p>Callback method invoked when this connection is upgraded.</p>
* <p>This must be called before {@link #onOpen()}.</p>
* @param prefilled An optional buffer that can contain prefilled data. Typically this
* results from an upgrade of one protocol to the other where the old connection has buffered
@@ -88,8 +118,6 @@ public interface Connection extends Closeable
void onUpgradeTo(ByteBuffer prefilled);
}
-
- /* ------------------------------------------------------------ */
/**
* <p>A Listener for connection events.</p>
* <p>Listeners can be added to a {@link Connection} to get open and close events.
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
index 014b2c3c33..f5bc318fec 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java
@@ -173,6 +173,12 @@ public class SslConnection extends AbstractConnection
}
@Override
+ public boolean onIdleExpired()
+ {
+ return getDecryptedEndPoint().getConnection().onIdleExpired();
+ }
+
+ @Override
public void onFillable()
{
// onFillable means that there are encrypted bytes ready to be filled.
@@ -329,33 +335,39 @@ public class SslConnection extends AbstractConnection
public DecryptedEndPoint()
{
- super(((AbstractEndPoint)getEndPoint()).getScheduler());
- setIdleTimeout(getEndPoint().getIdleTimeout());
+ // Disable idle timeout checking: no scheduler and -1 timeout for this instance.
+ super(null);
+ super.setIdleTimeout(-1);
}
-
-
@Override
- public InetSocketAddress getLocalAddress()
+ public long getIdleTimeout()
{
- return getEndPoint().getLocalAddress();
+ return getEndPoint().getIdleTimeout();
}
-
-
@Override
- public InetSocketAddress getRemoteAddress()
+ public void setIdleTimeout(long idleTimeout)
{
- return getEndPoint().getRemoteAddress();
+ getEndPoint().setIdleTimeout(idleTimeout);
}
+ @Override
+ public boolean isOpen()
+ {
+ return getEndPoint().isOpen();
+ }
+ @Override
+ public InetSocketAddress getLocalAddress()
+ {
+ return getEndPoint().getLocalAddress();
+ }
@Override
- public void setIdleTimeout(long idleTimeout)
+ public InetSocketAddress getRemoteAddress()
{
- super.setIdleTimeout(idleTimeout);
- getEndPoint().setIdleTimeout(idleTimeout);
+ return getEndPoint().getRemoteAddress();
}
@Override
@@ -462,10 +474,10 @@ public class SslConnection extends AbstractConnection
}
}
}
-
+
if (fillable)
getExecutor().execute(_runFillable);
- else
+ else
ensureFillInterested();
}
}
@@ -729,13 +741,13 @@ public class SslConnection extends AbstractConnection
// will return 0 (even if some handshake bytes were flushed and filled).
// it is the applications responsibility to call flush again - either in a busy loop
// or better yet by using EndPoint#write to do the flushing.
-
+
if (DEBUG)
{
for (ByteBuffer b : appOuts)
LOG.debug("{} flush {}", SslConnection.this, BufferUtil.toHexSummary(b));
}
-
+
try
{
if (_cannotAcceptMoreAppDataToFlush)
@@ -765,7 +777,7 @@ public class SslConnection extends AbstractConnection
}
if (DEBUG)
LOG.debug("{} wrap {}", SslConnection.this, wrapResult.toString().replace('\n',' '));
-
+
Status wrapResultStatus = wrapResult.getStatus();
boolean allConsumed=true;
@@ -924,7 +936,7 @@ public class SslConnection extends AbstractConnection
if (!SslConnection.this.isFillInterested())
SslConnection.this.fillInterested();
}
-
+
@Override
public boolean isOutputShutdown()
{
@@ -956,12 +968,6 @@ public class SslConnection extends AbstractConnection
}
@Override
- public boolean isOpen()
- {
- return getEndPoint().isOpen();
- }
-
- @Override
public Object getTransport()
{
return getEndPoint();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java
index 6141c693a1..01f4007117 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceCache.java
@@ -181,7 +181,7 @@ public class ResourceCache implements HttpContent.Factory
* Get either a valid entry object or create a new one if possible.
*
* @param pathInContext The key into the cache
- * @param maxBuffer The maximum buffer to allocated for this request. For cached content, a larger buffer may have
+ * @param maxBufferSize The maximum buffer to allocated for this request. For cached content, a larger buffer may have
* previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls.
* @return The entry matching <code>pathInContext</code>, or a new entry
* if no matching entry was found. If the content exists but is not cachable,
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
index 5cca5f0fae..0fe01d66ad 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
@@ -92,4 +92,3 @@ public class Loader
return loader==null ? ResourceBundle.getBundle(name, locale) : ResourceBundle.getBundle(name, locale, loader);
}
}
-
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
index bb52b254b9..72b4ec45a8 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AbstractTest.java
@@ -74,6 +74,12 @@ public abstract class AbstractTest
public void start(Handler handler) throws Exception
{
+ startServer(handler);
+ startClient();
+ }
+
+ protected void startServer(Handler handler) throws Exception
+ {
sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd");
@@ -81,12 +87,6 @@ public abstract class AbstractTest
sslContextFactory.setTrustStorePassword("storepwd");
sslContextFactory.setUseCipherSuitesOrder(true);
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
- startServer(handler);
- startClient();
- }
-
- private void startServer(Handler handler) throws Exception
- {
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java
new file mode 100644
index 0000000000..8ce1f3f256
--- /dev/null
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpChannelAssociationTest.java
@@ -0,0 +1,228 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2015 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.http.client;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.http.HttpChannelOverHTTP;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
+import org.eclipse.jetty.fcgi.client.http.HttpChannelOverFCGI;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.fcgi.client.http.HttpConnectionOverFCGI;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.http.HttpChannelOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HttpChannelAssociationTest extends AbstractTest
+{
+ public HttpChannelAssociationTest(Transport transport)
+ {
+ super(transport);
+ }
+
+ @Test
+ public void testAssociationFailedAbortsRequest() throws Exception
+ {
+ startServer(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ }
+ });
+
+ client = new HttpClient(newHttpClientTransport(transport, exchange -> false), sslContextFactory);
+ QueuedThreadPool clientThreads = new QueuedThreadPool();
+ clientThreads.setName("client");
+ client.setExecutor(clientThreads);
+ client.start();
+
+ CountDownLatch latch = new CountDownLatch(1);
+ client.newRequest(newURI())
+ .send(result ->
+ {
+ if (result.isFailed())
+ latch.countDown();
+ });
+
+ Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testIdleTimeoutJustBeforeAssociation() throws Exception
+ {
+ startServer(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ }
+ });
+
+ long idleTimeout = 1000;
+ client = new HttpClient(newHttpClientTransport(transport, exchange ->
+ {
+ // We idle timeout just before the association,
+ // we must be able to send the request successfully.
+ sleep(2 * idleTimeout);
+ return true;
+ }), sslContextFactory);
+ QueuedThreadPool clientThreads = new QueuedThreadPool();
+ clientThreads.setName("client");
+ client.setExecutor(clientThreads);
+ client.setIdleTimeout(idleTimeout);
+ client.start();
+
+ CountDownLatch latch = new CountDownLatch(1);
+ client.newRequest(newURI())
+ .send(result ->
+ {
+ if (result.isSucceeded())
+ latch.countDown();
+ });
+
+ Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ private HttpClientTransport newHttpClientTransport(Transport transport, Predicate<HttpExchange> code)
+ {
+ switch (transport)
+ {
+ case HTTP:
+ case HTTPS:
+ {
+ return new HttpClientTransportOverHTTP(1)
+ {
+ @Override
+ protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
+ {
+ return new HttpConnectionOverHTTP(endPoint, destination, promise)
+ {
+ @Override
+ protected HttpChannelOverHTTP newHttpChannel()
+ {
+ return new HttpChannelOverHTTP(this)
+ {
+ @Override
+ public boolean associate(HttpExchange exchange)
+ {
+ return code.test(exchange) && super.associate(exchange);
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+ case H2C:
+ case H2:
+ {
+ HTTP2Client http2Client = new HTTP2Client();
+ http2Client.setSelectors(1);
+ return new HttpClientTransportOverHTTP2(http2Client)
+ {
+ @Override
+ protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
+ {
+ return new HttpConnectionOverHTTP2(destination, session)
+ {
+ @Override
+ protected HttpChannelOverHTTP2 newHttpChannel()
+ {
+ return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession())
+ {
+ @Override
+ public boolean associate(HttpExchange exchange)
+ {
+ return code.test(exchange) && super.associate(exchange);
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+ case FCGI:
+ {
+ return new HttpClientTransportOverFCGI(1, false, "")
+ {
+ @Override
+ protected HttpConnectionOverFCGI newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
+ {
+ return new HttpConnectionOverFCGI(endPoint, destination, promise, isMultiplexed())
+ {
+ @Override
+ protected HttpChannelOverFCGI newHttpChannel(int id, org.eclipse.jetty.client.api.Request request)
+ {
+ return new HttpChannelOverFCGI(this, getFlusher(), id, request.getIdleTimeout())
+ {
+ @Override
+ public boolean associate(HttpExchange exchange)
+ {
+ return code.test(exchange) && super.associate(exchange);
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+ default:
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ private void sleep(long time)
+ {
+ try
+ {
+ Thread.sleep(time);
+ }
+ catch (InterruptedException x)
+ {
+ throw new RuntimeException(x);
+ }
+ }
+}

Back to the top