Fixes #2313 - Dump HTTP/2 channel state.

Dumping also HttpChannelOverHTTP2 when dumping the HTTP2Stream.
The channel is now stored as a field in HTTP2Stream rather than
as an attribute to save the attributes map allocation.
Consequently, IStream has now getter/setter for the property
"attachment" that is used to store the channel without having
a type dependency on HttpChannel.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2018-03-10 15:11:40 +01:00
parent 4c24c457de
commit 21bdb367fd
7 changed files with 53 additions and 23 deletions

View File

@ -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);
}

View File

@ -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<Object> attachment = new AtomicReference<>();
private final AtomicReference<ConcurrentMap<String, Object>> attributes = new AtomicReference<>();
private final AtomicReference<CloseState> closeState = new AtomicReference<>(CloseState.NOT_CLOSED);
private final AtomicReference<Callback> 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);
}
}

View File

@ -32,12 +32,17 @@ import org.eclipse.jetty.util.Callback;
public interface IStream extends Stream, Closeable
{
/**
* <p>The constant used as attribute key to store/retrieve the HTTP
* channel associated with this stream</p>
*
* @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

View File

@ -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);
}
}

View File

@ -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))

View File

@ -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);
}

View File

@ -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();
}