diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 853ab0ef0add..b191bacddeef 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -18,13 +18,17 @@ package org.eclipse.jetty.http2.server; +import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.http2.BufferingFlowControlStrategy; import org.eclipse.jetty.http2.FlowControlStrategy; import org.eclipse.jetty.http2.HTTP2Connection; +import org.eclipse.jetty.http2.api.Session; import org.eclipse.jetty.http2.api.server.ServerSessionListener; import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.SettingsFrame; @@ -38,12 +42,14 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.LifeCycle; @ManagedObject public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConnectionFactory { - private final Connection.Listener connectionListener = new ConnectionListener(); + private final HTTP2SessionContainer sessionContainer = new HTTP2SessionContainer(); private final HttpConfiguration httpConfiguration; private int maxDynamicTableSize = 4096; private int initialSessionRecvWindow = 1024 * 1024; @@ -66,6 +72,7 @@ protected AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration for (String p:protocols) if (!HTTP2ServerConnection.isSupportedProtocol(p)) throw new IllegalArgumentException("Unsupported HTTP2 Protocol variant: "+p); + addBean(sessionContainer); this.httpConfiguration = Objects.requireNonNull(httpConfiguration); addBean(httpConfiguration); setInputBufferSize(Frame.DEFAULT_MAX_LENGTH + Frame.HEADER_LENGTH); @@ -234,7 +241,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); - connection.addListener(connectionListener); + connection.addListener(sessionContainer); return configure(connection, connector, endPoint); } @@ -245,18 +252,50 @@ protected ServerParser newServerParser(Connector connector, ServerParser.Listene return new ServerParser(connector.getByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize()); } - private class ConnectionListener implements Connection.Listener + @ManagedObject("The container of HTTP/2 sessions") + public static class HTTP2SessionContainer implements Connection.Listener, Dumpable { + private final Set sessions = ConcurrentHashMap.newKeySet(); + @Override public void onOpened(Connection connection) { - addManaged((LifeCycle)((HTTP2Connection)connection).getSession()); + Session session = ((HTTP2Connection)connection).getSession(); + sessions.add(session); + LifeCycle.start(session); } @Override public void onClosed(Connection connection) { - removeBean(((HTTP2Connection)connection).getSession()); + Session session = ((HTTP2Connection)connection).getSession(); + if (sessions.remove(session)) + LifeCycle.stop(session); + } + + @ManagedAttribute(value = "The number of HTTP/2 sessions", readonly = true) + public int getSize() + { + return sessions.size(); + } + + @Override + public String dump() + { + return ContainerLifeCycle.dump(this); + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + ContainerLifeCycle.dumpObject(out, this); + ContainerLifeCycle.dump(out, indent, sessions); + } + + @Override + public String toString() + { + return String.format("%s@%x[size=%d]", getClass().getSimpleName(), hashCode(), sessions.size()); } } }