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:
parent
67ea8db5aa
commit
dee3331ffb
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue