Issue #572 - Don't reject HTTP/2 requests without body in low threads mode.

* Made ProduceExecuteConsume the default ExecutionFactory for HTTP/2.
* Made the HTTP/2 fillable callback non-blocking.
* Introduced configuration for the server initial session recv window.
* Sending a WINDOW_UPDATE frame at session setup to inform the client
 about the server session recv window.
This commit is contained in:
Simone Bordet 2016-05-16 11:31:09 +02:00
parent 67ea8db5aa
commit dee3331ffb
6 changed files with 85 additions and 13 deletions

View File

@ -141,6 +141,7 @@ public class HTTP2Connection extends AbstractConnection
protected class HTTP2Producer implements ExecutionStrategy.Producer
{
private final Callback fillCallback = new FillCallback();
private ByteBuffer buffer;
@Override
@ -182,7 +183,7 @@ public class HTTP2Connection extends AbstractConnection
if (filled == 0)
{
release();
fillInterested();
getEndPoint().fillInterested(fillCallback);
return null;
}
else if (filled < 0)
@ -205,4 +206,19 @@ public class HTTP2Connection extends AbstractConnection
}
}
}
private class FillCallback implements Callback.NonBlocking
{
@Override
public void succeeded()
{
onFillable();
}
@Override
public void failed(Throwable x)
{
onFillInterestedFailed(x);
}
}
}

View File

@ -84,6 +84,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private int maxLocalStreams;
private int maxRemoteStreams;
private long streamIdleTimeout;
private int initialSessionRecvWindow;
private boolean pushEnabled;
private long idleTime;
@ -149,6 +150,17 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
this.streamIdleTimeout = streamIdleTimeout;
}
@ManagedAttribute("The initial size of session's flow control receive window")
public int getInitialSessionRecvWindow()
{
return initialSessionRecvWindow;
}
public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
{
this.initialSessionRecvWindow = initialSessionRecvWindow;
}
public EndPoint getEndPoint()
{
return endPoint;

View File

@ -36,6 +36,7 @@ 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.ExecutionStrategy;
import org.eclipse.jetty.util.thread.strategy.ProduceExecuteConsume;
@ManagedObject
public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConnectionFactory
@ -43,11 +44,12 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
private final Connection.Listener connectionListener = new ConnectionListener();
private final HttpConfiguration httpConfiguration;
private int maxDynamicTableSize = 4096;
private int initialStreamSendWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
private int initialStreamRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
private int initialSessionRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
private int maxConcurrentStreams = -1;
private int maxHeaderBlockFragment = 0;
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
private ExecutionStrategy.Factory executionStrategyFactory = ExecutionStrategy.Factory.getDefault();
private ExecutionStrategy.Factory executionStrategyFactory = new ProduceExecuteConsume.Factory();
public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration)
{
@ -72,15 +74,46 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
this.maxDynamicTableSize = maxDynamicTableSize;
}
@ManagedAttribute("The initial size of stream's flow control send window")
public int getInitialStreamSendWindow()
@ManagedAttribute("The initial size of session's flow control receive window")
public int getInitialSessionRecvWindow()
{
return initialStreamSendWindow;
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;
}
/**
* @deprecated use {@link #getInitialStreamRecvWindow()} instead,
* since "send" is meant on the client, but this is the server configuration
*/
@Deprecated
public int getInitialStreamSendWindow()
{
return getInitialStreamRecvWindow();
}
/**
* @deprecated use {@link #setInitialStreamRecvWindow(int)} instead,
* since "send" is meant on the client, but this is the server configuration
*/
@Deprecated
public void setInitialStreamSendWindow(int initialStreamSendWindow)
{
this.initialStreamSendWindow = initialStreamSendWindow;
setInitialStreamRecvWindow(initialStreamSendWindow);
}
@ManagedAttribute("The max number of concurrent streams per session")
@ -146,6 +179,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
// the typical case is that the connection will be busier and the
// stream idle timeout will expire earlier that the connection's.
session.setStreamIdleTimeout(endPoint.getIdleTimeout());
session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
ServerParser parser = newServerParser(connector, session);
HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(),

View File

@ -49,7 +49,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF
{
super(httpConfiguration);
}
protected HTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration,String... protocols)
{
super(httpConfiguration,protocols);
@ -93,7 +93,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF
{
Map<Integer, Integer> settings = new HashMap<>();
settings.put(SettingsFrame.HEADER_TABLE_SIZE, getMaxDynamicTableSize());
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamSendWindow());
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamRecvWindow());
int maxConcurrentStreams = getMaxConcurrentStreams();
if (maxConcurrentStreams >= 0)
settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxConcurrentStreams);

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.http2.generator.Generator;
import org.eclipse.jetty.http2.parser.ServerParser;
import org.eclipse.jetty.io.EndPoint;
@ -60,9 +61,17 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
Map<Integer, Integer> settings = notifyPreface(this);
if (settings == null)
settings = Collections.emptyMap();
SettingsFrame frame = new SettingsFrame(settings, false);
// TODO: consider sending a WINDOW_UPDATE to enlarge the session send window of the client.
frames(null, Callback.NOOP, frame, Frame.EMPTY_ARRAY);
SettingsFrame settingsFrame = new SettingsFrame(settings, false);
WindowUpdateFrame windowFrame = null;
int sessionWindow = getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
if (sessionWindow > 0)
windowFrame = new WindowUpdateFrame(0, sessionWindow);
if (windowFrame == null)
frames(null, Callback.NOOP, settingsFrame, Frame.EMPTY_ARRAY);
else
frames(null, Callback.NOOP, settingsFrame, windowFrame);
}
@Override

View File

@ -1,3 +1,4 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.http2.hpack.LEVEL=INFO
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.http2.LEVEL=DEBUG
org.eclipse.jetty.http2.hpack.LEVEL=INFO