org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2017 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.http2.server;
import java.util.Objects;
import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
import org.eclipse.jetty.http2.FlowControlStrategy;
import org.eclipse.jetty.http2.HTTP2Connection;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.generator.Generator;
import org.eclipse.jetty.http2.parser.ServerParser;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.AbstractConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
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.LifeCycle;
import org.eclipse.jetty.util.thread.ReservedThreadExecutor;
@ManagedObject
public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConnectionFactory
{
private final Connection.Listener connectionListener = new ConnectionListener();
private final HttpConfiguration httpConfiguration;
private int maxDynamicTableSize = 4096;
private int initialSessionRecvWindow = 1024 * 1024;
private int initialStreamRecvWindow = 512 * 1024;
private int maxConcurrentStreams = 128;
private int maxHeaderBlockFragment = 0;
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
private long streamIdleTimeout;
private int reservedThreads;
public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
{
this(httpConfiguration,"h2");
}
protected AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration, @Name("protocols") String... protocols)
{
super(protocols);
for (String p:protocols)
if (!HTTP2ServerConnection.isSupportedProtocol(p))
throw new IllegalArgumentException("Unsupported HTTP2 Protocol variant: "+p);
this.httpConfiguration = Objects.requireNonNull(httpConfiguration);
addBean(httpConfiguration);
}
@ManagedAttribute("The HPACK dynamic table maximum size")
public int getMaxDynamicTableSize()
{
return maxDynamicTableSize;
}
public void setMaxDynamicTableSize(int maxDynamicTableSize)
{
this.maxDynamicTableSize = maxDynamicTableSize;
}
@ManagedAttribute("The initial size of session's flow control receive window")
public int getInitialSessionRecvWindow()
{
return initialSessionRecvWindow;
}
public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
{
this.initialSessionRecvWindow = initialSessionRecvWindow;
}
@ManagedAttribute("The initial size of stream's flow control receive window")
public int getInitialStreamRecvWindow()
{
return initialStreamRecvWindow;
}
public void setInitialStreamRecvWindow(int initialStreamRecvWindow)
{
this.initialStreamRecvWindow = initialStreamRecvWindow;
}
@ManagedAttribute("The max number of concurrent streams per session")
public int getMaxConcurrentStreams()
{
return maxConcurrentStreams;
}
public void setMaxConcurrentStreams(int maxConcurrentStreams)
{
this.maxConcurrentStreams = maxConcurrentStreams;
}
@ManagedAttribute("The max header block fragment")
public int getMaxHeaderBlockFragment()
{
return maxHeaderBlockFragment;
}
public void setMaxHeaderBlockFragment(int maxHeaderBlockFragment)
{
this.maxHeaderBlockFragment = maxHeaderBlockFragment;
}
public FlowControlStrategy.Factory getFlowControlStrategyFactory()
{
return flowControlStrategyFactory;
}
public void setFlowControlStrategyFactory(FlowControlStrategy.Factory flowControlStrategyFactory)
{
this.flowControlStrategyFactory = flowControlStrategyFactory;
}
@ManagedAttribute("The stream idle timeout in milliseconds")
public long getStreamIdleTimeout()
{
return streamIdleTimeout;
}
public void setStreamIdleTimeout(long streamIdleTimeout)
{
this.streamIdleTimeout = streamIdleTimeout;
}
/**
* @see ReservedThreadExecutor
* @return The number of reserved threads
*/
@ManagedAttribute("The number of threads reserved for high priority tasks")
public int getReservedThreads()
{
return reservedThreads;
}
public void setReservedThreads(int threads)
{
// TODO: see also HTTP2Connection.FillableCallback.
// TODO: currently disabled since the only value that works is 0.
// this.reservedThreads = threads;
}
public HttpConfiguration getHttpConfiguration()
{
return httpConfiguration;
}
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
ServerSessionListener listener = newSessionListener(connector, endPoint);
Generator generator = new Generator(connector.getByteBufferPool(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment());
FlowControlStrategy flowControl = getFlowControlStrategyFactory().newFlowControlStrategy();
HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, generator, listener, flowControl);
session.setMaxLocalStreams(getMaxConcurrentStreams());
session.setMaxRemoteStreams(getMaxConcurrentStreams());
// For a single stream in a connection, there will be a race between
// the stream idle timeout and the connection idle timeout. However,
// the typical case is that the connection will be busier and the
// stream idle timeout will expire earlier than the connection's.
long streamIdleTimeout = getStreamIdleTimeout();
if (streamIdleTimeout <= 0)
streamIdleTimeout = endPoint.getIdleTimeout();
session.setStreamIdleTimeout(streamIdleTimeout);
session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
ReservedThreadExecutor executor = provideReservedThreadExecutor(connector);
ServerParser parser = newServerParser(connector, session);
HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), executor,
endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener);
connection.addListener(connectionListener);
return configure(connection, connector, endPoint);
}
protected ReservedThreadExecutor provideReservedThreadExecutor(Connector connector)
{
synchronized (this)
{
ReservedThreadExecutor executor = getBean(ReservedThreadExecutor.class);
if (executor == null)
{
executor = new ReservedThreadExecutor(connector.getExecutor(), getReservedThreads());
addManaged(executor);
}
return executor;
}
}
protected abstract ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint);
protected ServerParser newServerParser(Connector connector, ServerParser.Listener listener)
{
return new ServerParser(connector.getByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize());
}
private class ConnectionListener implements Connection.Listener
{
@Override
public void onOpened(Connection connection)
{
addManaged((LifeCycle)((HTTP2Connection)connection).getSession());
}
@Override
public void onClosed(Connection connection)
{
removeBean(((HTTP2Connection)connection).getSession());
}
}
}