Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,17 @@ public void onWindowUpdate(WindowUpdateFrame frame)
}
else
{
if (!isStreamClosed(streamId))
if (isStreamClosed(streamId))
{
// SPEC: this case must not be treated as an error.
// However, we want to rate control it.
if (!rateControlOnEvent(frame))
onConnectionFailure(ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_window_update_frame_rate");
}
else
{
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_window_update_frame");
}
}
}
else
Expand Down Expand Up @@ -815,14 +824,26 @@ public void ping(PingFrame frame, Callback callback)

void reset(HTTP2Stream stream, ResetFrame frame, Callback callback)
{
control(stream, Callback.from(() ->
if (rateControlOnEvent(frame))
{
if (stream != null)
control(stream, Callback.from(() ->
{
stream.close();
removeStream(stream);
}
}, callback), frame);
if (stream != null)
{
stream.close();
removeStream(stream);
}
}, callback), frame);
}
else
{
onConnectionFailure(ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_rst_stream_frame_rate");
}
}

private boolean rateControlOnEvent(Object event)
{
return getParser().rateControlOnEvent(event);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ private void notifyConnectionFailure(int error, String reason)
protected boolean streamFailure(int streamId, int error, String reason)
{
notifyStreamFailure(streamId, error, reason);
return false;
return true;
}

private void notifyStreamFailure(int streamId, int error, String reason)
Expand All @@ -243,6 +243,6 @@ private void notifyStreamFailure(int streamId, int error, String reason)

protected boolean rateControlOnEvent(Object o)
{
return headerParser.getRateControl().onEvent(o);
return headerParser.rateControlOnEvent(o);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public RateControl getRateControl()
return rateControl;
}

boolean rateControlOnEvent(Object event)
{
return getRateControl().onEvent(event);
}

protected void reset()
{
state = State.LENGTH;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ public void init(Listener listener)
bodyParsers[FrameType.CONTINUATION.getType()] = new ContinuationBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments);
}

public boolean rateControlOnEvent(Object event)
{
return headerParser.rateControlOnEvent(event);
}

protected Listener getListener()
{
return listener;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,16 @@ public boolean parse(ByteBuffer buffer)
private boolean onWindowUpdate(ByteBuffer buffer, int windowDelta)
{
int streamId = getStreamId();
WindowUpdateFrame frame = new WindowUpdateFrame(streamId, windowDelta);
reset();
if (windowDelta == 0)
{
if (streamId == 0)
return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_window_update_frame");
else
if (rateControlOnEvent(frame))
return streamFailure(streamId, ErrorCode.PROTOCOL_ERROR.code, "invalid_window_update_frame");
return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_window_update_frame_rate");
}
WindowUpdateFrame frame = new WindowUpdateFrame(streamId, windowDelta);
reset();
notifyWindowUpdate(frame);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@

import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.Flags;
import org.eclipse.jetty.http2.WindowRateControl;
import org.eclipse.jetty.http2.hpack.HpackEncoder;
Expand All @@ -29,6 +30,7 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class FrameFloodTest
{
Expand Down Expand Up @@ -144,6 +146,13 @@ public void testResetStreamFrameFlood()
testFrameFlood(null, frameFrom(payload.length, FrameType.RST_STREAM.getType(), 0, 13, payload));
}

@Test
public void testWindowUpdateFrameFlood()
{
byte[] payload = {0, 0, 0, 0};
testFrameFlood(null, frameFrom(payload.length, FrameType.WINDOW_UPDATE.getType(), 0, 13, payload));
}

@Test
public void testUnknownFrameFlood()
{
Expand All @@ -153,14 +162,14 @@ public void testUnknownFrameFlood()

private void testFrameFlood(byte[] preamble, byte[] bytes)
{
AtomicBoolean failed = new AtomicBoolean();
AtomicInteger failed = new AtomicInteger();
Parser parser = new Parser(bufferPool, 8192, new WindowRateControl(8, Duration.ofSeconds(1)));
parser.init(new Parser.Listener()
{
@Override
public void onConnectionFailure(int error, String reason)
{
failed.set(true);
failed.set(error);
}
});

Expand All @@ -174,7 +183,7 @@ public void onConnectionFailure(int error, String reason)
}

int count = 0;
while (!failed.get())
while (failed.get() == 0)
{
ByteBuffer buffer = ByteBuffer.wrap(bytes);
while (buffer.hasRemaining())
Expand All @@ -183,5 +192,7 @@ public void onConnectionFailure(int error, String reason)
}
assertThat("too many frames allowed", ++count, lessThan(1024));
}

assertEquals(ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, failed.get());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<Set name="maxSettingsKeys"><Property name="jetty.http2.maxSettingsKeys" default="64"/></Set>
<Set name="rateControlFactory">
<New class="org.eclipse.jetty.http2.WindowRateControl$Factory">
<Arg type="int"><Property name="jetty.http2.rateControl.maxEventsPerSecond" default="50"/></Arg>
<Arg type="int"><Property name="jetty.http2.rateControl.maxEventsPerSecond" default="128"/></Arg>
</New>
</Set>
</New>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<Set name="maxSettingsKeys" property="jetty.http2c.maxSettingsKeys"/>
<Set name="rateControlFactory">
<New class="org.eclipse.jetty.http2.WindowRateControl$Factory">
<Arg type="int"><Property name="jetty.http2c.rateControl.maxEventsPerSecond" default="50"/></Arg>
<Arg type="int"><Property name="jetty.http2c.rateControl.maxEventsPerSecond" default="128"/></Arg>
</New>
</Set>
</New>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ etc/jetty-http2.xml

## Specifies the maximum number of bad frames and pings per second,
## after which a session is closed to avoid denial of service attacks.
# jetty.http2.rateControl.maxEventsPerSecond=50
# jetty.http2.rateControl.maxEventsPerSecond=128
# end::documentation[]
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ etc/jetty-http2c.xml

## Specifies the maximum number of bad frames and pings per second,
## after which a session is closed to avoid denial of service attacks.
# jetty.http2c.rateControl.maxEventsPerSecond=50
# jetty.http2c.rateControl.maxEventsPerSecond=128
# end::documentation[]
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.RateControl;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
Expand Down Expand Up @@ -69,6 +70,8 @@ protected void prepareServer(ConnectionFactory... connectionFactories)
public void testConcurrentWithSmallServerThreadPool() throws Exception
{
start(new LoadHandler());
AbstractHTTP2ServerConnectionFactory h2 = connector.getConnectionFactory(AbstractHTTP2ServerConnectionFactory.class);
h2.setRateControlFactory(new RateControl.Factory() {});

// Only one connection to the server.
Session session = newClientSession(new Session.Listener() {});
Expand Down
Loading