diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java index 9199bf665d8..cce85626b15 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Flusher.java @@ -223,7 +223,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable IStream stream = entry.stream; if (stream != null && !entry.isControl()) { - Object channel = stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); + Object channel = stream.getAttachment(); if (channel instanceof WriteFlusher.Listener) ((WriteFlusher.Listener)channel).onFlushed(update - Frame.HEADER_LENGTH); } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java index e8c10f3d85b..4cd67c4a24e 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Stream.java @@ -47,6 +47,7 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa { private static final Logger LOG = Log.getLogger(HTTP2Stream.class); + private final AtomicReference attachment = new AtomicReference<>(); private final AtomicReference> attributes = new AtomicReference<>(); private final AtomicReference closeState = new AtomicReference<>(CloseState.NOT_CLOSED); private final AtomicReference writing = new AtomicReference<>(); @@ -73,6 +74,18 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa return streamId; } + @Override + public Object getAttachment() + { + return attachment.get(); + } + + @Override + public void setAttachment(Object attachment) + { + this.attachment.set(attachment); + } + @Override public boolean isLocal() { @@ -460,7 +473,7 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa @Override public String toString() { - return String.format("%s@%x#%d{sendWindow=%s,recvWindow=%s,reset=%b,%s}", getClass().getSimpleName(), - hashCode(), getId(), sendWindow, recvWindow, isReset(), closeState); + return String.format("%s@%x#%d{sendWindow=%s,recvWindow=%s,reset=%b,%s,attachment=%s}", getClass().getSimpleName(), + hashCode(), getId(), sendWindow, recvWindow, isReset(), closeState, attachment); } } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java index 669e8c9807d..3fe92bbaf23 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/IStream.java @@ -32,12 +32,17 @@ import org.eclipse.jetty.util.Callback; public interface IStream extends Stream, Closeable { /** - *

The constant used as attribute key to store/retrieve the HTTP - * channel associated with this stream

- * - * @see #setAttribute(String, Object) + * @return the object attached to this stream + * @see #setAttachment(Object) */ - public static final String CHANNEL_ATTRIBUTE = IStream.class.getName() + ".channel"; + public Object getAttachment(); + + /** + * Attaches the given object to this stream for later retrieval. + * + * @param attachment the object to attach to this stream + */ + public void setAttachment(Object attachment); /** * @return whether this stream is local or remote diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java index b3372e49f6f..cdc4662fc1a 100644 --- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpChannelOverHTTP2.java @@ -120,4 +120,13 @@ public class HttpChannelOverHTTP2 extends HttpChannel super.exchangeTerminated(exchange, result); release(); } + + @Override + public String toString() + { + return String.format("%s[send=%s,recv=%s]", + super.toString(), + sender, + receiver); + } } diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java index 6a3a99e749c..4950c949d3d 100644 --- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java +++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.http2.IStream; import org.eclipse.jetty.http2.api.Stream; import org.eclipse.jetty.http2.frames.DataFrame; import org.eclipse.jetty.http2.frames.HeadersFrame; @@ -64,7 +65,8 @@ public class HttpSenderOverHTTP2 extends HttpSender @Override public void succeeded(Stream stream) { - getHttpChannel().setStream(stream); + channel.setStream(stream); + ((IStream)stream).setAttachment(channel); stream.setIdleTimeout(request.getIdleTimeout()); if (content.hasContent() && !expects100Continue(request)) diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java index fb18d564a72..f0cbf40091b 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java @@ -25,6 +25,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicLong; @@ -172,7 +173,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection { if (LOG.isDebugEnabled()) LOG.debug("Processing {} on {}", frame, stream); - HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); + HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttachment(); if (channel != null) { Runnable task = channel.onRequestContent(frame, callback); @@ -189,7 +190,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection { if (LOG.isDebugEnabled()) LOG.debug("Processing trailers {} on {}", frame, stream); - HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); + HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttachment(); if (channel != null) { Runnable task = channel.onRequestTrailers(frame); @@ -200,7 +201,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection public boolean onStreamTimeout(IStream stream, Throwable failure) { - HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); + HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttachment(); boolean result = channel != null && channel.onStreamTimeout(failure, task -> offerTask(task, true)); if (LOG.isDebugEnabled()) LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", stream, failure); @@ -211,7 +212,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection { if (LOG.isDebugEnabled()) LOG.debug("Processing failure on {}: {}", stream, failure); - HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); + HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttachment(); if (channel != null) { Runnable task = channel.onFailure(failure, callback); @@ -227,13 +228,13 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection public boolean onSessionTimeout(Throwable failure) { ISession session = getSession(); - boolean result = true; - for (Stream stream : session.getStreams()) - { - HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); - if (channel != null) - result &= channel.isRequestIdle(); - } + // Compute whether all requests are idle. + boolean result = session.getStreams().stream() + .map(stream -> (IStream)stream) + .map(stream -> (HttpChannelOverHTTP2)stream.getAttachment()) + .filter(Objects::nonNull) + .map(HttpChannelOverHTTP2::isRequestIdle) + .reduce(true, Boolean::logicalAnd); if (LOG.isDebugEnabled()) LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure); return result; @@ -284,7 +285,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection if (LOG.isDebugEnabled()) LOG.debug("Creating channel {} for {}", channel, this); } - stream.setAttribute(IStream.CHANNEL_ATTRIBUTE, channel); + stream.setAttachment(channel); return channel; } @@ -379,7 +380,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection @Override public void recycle() { - getStream().removeAttribute(IStream.CHANNEL_ATTRIBUTE); + getStream().setAttachment(null); super.recycle(); offerHttpChannel(this); } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java index b11817837fe..d862891c8f8 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java @@ -248,7 +248,7 @@ public class HttpTransportOverHTTP2 implements HttpTransport // Consume the existing queued data frames to // avoid stalling the session flow control. - HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE); + HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttachment(); if (channel != null) channel.consumeInput(); }