From dbb94514dc9d3fb21fe92080f57c314e7e06a148 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 9 Oct 2023 15:07:52 +0200 Subject: [PATCH] Fixes #10679 - Review HTTP/2 rate control. (#10681) * Bumped the rate control rate from 50 events/s to 128. * Added rate control for all CONTINUATION frames. * Added rate control for invalid PUSH_PROMISE frames. * Added rate control for RST_STREAM frames. * Added rate control for all SETTINGS frames. * Fixed growth of header block accumulation buffer. Signed-off-by: Simone Bordet --- .../jetty/http2/parser/BodyParser.java | 2 +- .../http2/parser/ContinuationBodyParser.java | 36 +++++++++---- .../http2/parser/HeaderBlockFragments.java | 35 ++++++++++--- .../jetty/http2/parser/HeadersBodyParser.java | 41 +++++++++++---- .../eclipse/jetty/http2/parser/Parser.java | 2 +- .../http2/parser/PushPromiseBodyParser.java | 13 +++-- .../jetty/http2/parser/ResetBodyParser.java | 8 +-- .../http2/parser/SettingsBodyParser.java | 11 ++-- .../jetty/http2/parser/UnknownBodyParser.java | 1 - .../http2/frames/ContinuationParseTest.java | 50 +++++++++++++++++++ .../jetty/http2/frames/FrameFloodTest.java | 31 ++++++++++-- .../AbstractHTTP2ServerConnectionFactory.java | 2 +- 12 files changed, 184 insertions(+), 48 deletions(-) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java index 3438ec5da81a..02da72d84840 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/BodyParser.java @@ -39,7 +39,7 @@ */ public abstract class BodyParser { - protected static final Logger LOG = LoggerFactory.getLogger(BodyParser.class); + private static final Logger LOG = LoggerFactory.getLogger(BodyParser.class); private final HeaderParser headerParser; private final Parser.Listener listener; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java index 60e5805b6101..0a4cfce519bd 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java @@ -75,16 +75,28 @@ public boolean parse(ByteBuffer buffer) int remaining = buffer.remaining(); if (remaining < length) { - headerBlockFragments.storeFragment(buffer, remaining, false); + ContinuationFrame frame = new ContinuationFrame(getStreamId(), false); + if (!rateControlOnEvent(frame)) + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_continuation_frame_rate"); + + if (!headerBlockFragments.storeFragment(buffer, remaining, false)) + return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_continuation_stream"); + length -= remaining; break; } else { - boolean last = hasFlag(Flags.END_HEADERS); - headerBlockFragments.storeFragment(buffer, length, last); + boolean endHeaders = hasFlag(Flags.END_HEADERS); + ContinuationFrame frame = new ContinuationFrame(getStreamId(), endHeaders); + if (!rateControlOnEvent(frame)) + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_continuation_frame_rate"); + + if (!headerBlockFragments.storeFragment(buffer, length, endHeaders)) + return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_continuation_stream"); + reset(); - if (last) + if (endHeaders) return onHeaders(buffer); return true; } @@ -103,17 +115,21 @@ private boolean onHeaders(ByteBuffer buffer) ByteBuffer headerBlock = headerBlockFragments.complete(); MetaData metaData = headerBlockParser.parse(headerBlock, headerBlock.remaining()); headerBlockFragments.getByteBufferPool().release(headerBlock); - if (metaData == null) - return true; + HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, headerBlockFragments.getPriorityFrame(), headerBlockFragments.isEndStream()); + headerBlockFragments.reset(); + if (metaData == HeaderBlockParser.SESSION_FAILURE) return false; - HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, headerBlockFragments.getPriorityFrame(), headerBlockFragments.isEndStream()); - if (metaData == HeaderBlockParser.STREAM_FAILURE) + + if (metaData != HeaderBlockParser.STREAM_FAILURE) + { + notifyHeaders(frame); + } + else { if (!rateControlOnEvent(frame)) - return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_continuation_frame_rate"); + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_headers_frame_rate"); } - notifyHeaders(frame); return true; } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java index 3de5c78555d7..6f6947247052 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java @@ -21,14 +21,22 @@ public class HeaderBlockFragments { private final ByteBufferPool byteBufferPool; + private final int maxCapacity; private PriorityFrame priorityFrame; - private boolean endStream; private int streamId; + private boolean endStream; private ByteBuffer storage; + @Deprecated public HeaderBlockFragments(ByteBufferPool byteBufferPool) + { + this(byteBufferPool, 8192); + } + + public HeaderBlockFragments(ByteBufferPool byteBufferPool, int maxCapacity) { this.byteBufferPool = byteBufferPool; + this.maxCapacity = maxCapacity; } public ByteBufferPool getByteBufferPool() @@ -36,18 +44,30 @@ public ByteBufferPool getByteBufferPool() return byteBufferPool; } - public void storeFragment(ByteBuffer fragment, int length, boolean last) + void reset() + { + priorityFrame = null; + streamId = 0; + endStream = false; + storage = null; + } + + public boolean storeFragment(ByteBuffer fragment, int length, boolean last) { if (storage == null) { - int space = last ? length : length * 2; - storage = byteBufferPool.acquire(space, fragment.isDirect()); + if (length > maxCapacity) + return false; + int capacity = last ? length : length * 2; + storage = byteBufferPool.acquire(capacity, fragment.isDirect()); storage.clear(); } // Grow the storage if necessary. if (storage.remaining() < length) { + if (storage.position() + length > maxCapacity) + return false; int space = last ? length : length * 2; int capacity = storage.position() + space; ByteBuffer newStorage = byteBufferPool.acquire(capacity, storage.isDirect()); @@ -63,6 +83,7 @@ public void storeFragment(ByteBuffer fragment, int length, boolean last) fragment.limit(fragment.position() + length); storage.put(fragment); fragment.limit(limit); + return true; } public PriorityFrame getPriorityFrame() @@ -87,10 +108,8 @@ public void setEndStream(boolean endStream) public ByteBuffer complete() { - ByteBuffer result = storage; - storage = null; - result.flip(); - return result; + storage.flip(); + return storage; } public int getStreamId() diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java index fbd4a0c50686..e63b6d5a6f5e 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java @@ -22,9 +22,13 @@ import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.frames.PriorityFrame; import org.eclipse.jetty.util.BufferUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HeadersBodyParser extends BodyParser { + private static final Logger LOG = LoggerFactory.getLogger(HeadersBodyParser.class); + private final HeaderBlockParser headerBlockParser; private final HeaderBlockFragments headerBlockFragments; private State state = State.PREPARE; @@ -71,8 +75,15 @@ else if (hasFlag(Flags.END_HEADERS)) } else { - headerBlockFragments.setStreamId(getStreamId()); - headerBlockFragments.setEndStream(isEndStream()); + if (headerBlockFragments.getStreamId() != 0) + { + connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_frame"); + } + else + { + headerBlockFragments.setStreamId(getStreamId()); + headerBlockFragments.setEndStream(isEndStream()); + } } } @@ -167,6 +178,18 @@ else if (hasFlag(Flags.PRIORITY)) break; } case HEADERS: + { + if (!hasFlag(Flags.END_HEADERS)) + { + headerBlockFragments.setStreamId(getStreamId()); + headerBlockFragments.setEndStream(isEndStream()); + if (hasFlag(Flags.PRIORITY)) + headerBlockFragments.setPriorityFrame(new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive)); + } + state = State.HEADER_BLOCK; + break; + } + case HEADER_BLOCK: { if (hasFlag(Flags.END_HEADERS)) { @@ -191,7 +214,7 @@ else if (hasFlag(Flags.PRIORITY)) { HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, null, isEndStream()); if (!rateControlOnEvent(frame)) - connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_headers_frame_rate"); + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_headers_frame_rate"); } } } @@ -200,16 +223,14 @@ else if (hasFlag(Flags.PRIORITY)) int remaining = buffer.remaining(); if (remaining < length) { - headerBlockFragments.storeFragment(buffer, remaining, false); + if (!headerBlockFragments.storeFragment(buffer, remaining, false)) + return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_frame"); length -= remaining; } else { - headerBlockFragments.setStreamId(getStreamId()); - headerBlockFragments.setEndStream(isEndStream()); - if (hasFlag(Flags.PRIORITY)) - headerBlockFragments.setPriorityFrame(new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive)); - headerBlockFragments.storeFragment(buffer, length, false); + if (!headerBlockFragments.storeFragment(buffer, length, false)) + return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_frame"); state = State.PADDING; loop = paddingLength == 0; } @@ -253,6 +274,6 @@ private void onHeaders(HeadersFrame frame) private enum State { - PREPARE, PADDING_LENGTH, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING + PREPARE, PADDING_LENGTH, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT, HEADERS, HEADER_BLOCK, PADDING } } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java index fcae69d7d147..e43be18e6e4a 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java @@ -85,7 +85,7 @@ public void init(Listener listener) this.listener = listener; unknownBodyParser = new UnknownBodyParser(headerParser, listener); HeaderBlockParser headerBlockParser = new HeaderBlockParser(headerParser, byteBufferPool, hpackDecoder, unknownBodyParser); - HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(byteBufferPool); + HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(byteBufferPool, hpackDecoder.getMaxHeaderListSize()); bodyParsers[FrameType.DATA.getType()] = new DataBodyParser(headerParser, listener); bodyParsers[FrameType.HEADERS.getType()] = new HeadersBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments); bodyParsers[FrameType.PRIORITY.getType()] = new PriorityBodyParser(headerParser, listener); diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java index b1f2d8a5bac4..fa41e4a0b63b 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java @@ -18,6 +18,7 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.ErrorCode; import org.eclipse.jetty.http2.Flags; +import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.frames.PushPromiseFrame; public class PushPromiseBodyParser extends BodyParser @@ -65,13 +66,9 @@ public boolean parse(ByteBuffer buffer) length = getBodyLength(); if (isPadding()) - { state = State.PADDING_LENGTH; - } else - { state = State.STREAM_ID; - } break; } case PADDING_LENGTH: @@ -131,7 +128,15 @@ public boolean parse(ByteBuffer buffer) state = State.PADDING; loop = paddingLength == 0; if (metaData != HeaderBlockParser.STREAM_FAILURE) + { onPushPromise(streamId, metaData); + } + else + { + HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, null, isEndStream()); + if (!rateControlOnEvent(frame)) + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_headers_frame_rate"); + } } break; } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java index 937dfc387a72..fd5b640488fb 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java @@ -58,7 +58,7 @@ public boolean parse(ByteBuffer buffer) { if (buffer.remaining() >= 4) { - return onReset(buffer.getInt()); + return onReset(buffer, buffer.getInt()); } else { @@ -73,7 +73,7 @@ public boolean parse(ByteBuffer buffer) --cursor; error += currByte << (8 * cursor); if (cursor == 0) - return onReset(error); + return onReset(buffer, error); break; } default: @@ -85,9 +85,11 @@ public boolean parse(ByteBuffer buffer) return false; } - private boolean onReset(int error) + private boolean onReset(ByteBuffer buffer, int error) { ResetFrame frame = new ResetFrame(getStreamId(), error); + if (!rateControlOnEvent(frame)) + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_rst_stream_frame_rate"); reset(); notifyReset(frame); return true; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java index 99d914a07d7a..e2516f0c5490 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java @@ -72,10 +72,7 @@ protected void emptyBody(ByteBuffer buffer) return; boolean isReply = hasFlag(Flags.ACK); SettingsFrame frame = new SettingsFrame(Collections.emptyMap(), isReply); - if (!isReply && !rateControlOnEvent(frame)) - connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_settings_frame_rate"); - else - onSettings(frame); + onSettings(buffer, frame); } private boolean validateFrame(ByteBuffer buffer, int streamId, int bodyLength) @@ -218,11 +215,13 @@ protected boolean onSettings(ByteBuffer buffer, Map settings) return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_max_frame_size"); SettingsFrame frame = new SettingsFrame(settings, hasFlag(Flags.ACK)); - return onSettings(frame); + return onSettings(buffer, frame); } - private boolean onSettings(SettingsFrame frame) + private boolean onSettings(ByteBuffer buffer, SettingsFrame frame) { + if (!rateControlOnEvent(frame)) + return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_settings_frame_rate"); reset(); notifySettings(frame); return true; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java index 97aebcb90104..fb1b9707fd01 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java @@ -35,7 +35,6 @@ public boolean parse(ByteBuffer buffer) boolean parsed = cursor == 0; if (parsed && !rateControlOnEvent(new UnknownFrame(getFrameType()))) return connectionFailure(buffer, ErrorCode.ENHANCE_YOUR_CALM_ERROR.code, "invalid_unknown_frame_rate"); - return parsed; } diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java index b4604bc4c8d6..4443893c2669 100644 --- a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java +++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java @@ -16,6 +16,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jetty.http.HostPortHttpField; import org.eclipse.jetty.http.HttpField; @@ -32,6 +33,8 @@ import org.eclipse.jetty.io.MappedByteBufferPool; import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -154,4 +157,51 @@ public void onConnectionFailure(int error, String reason) assertNull(priority); } } + + @Test + public void testLargeHeadersBlock() throws Exception + { + // Use a ByteBufferPool with a small factor, so that the accumulation buffer is not too large. + ByteBufferPool byteBufferPool = new MappedByteBufferPool(128); + // A small max headers size, used for both accumulation and decoding. + int maxHeadersSize = 512; + Parser parser = new Parser(byteBufferPool, maxHeadersSize); + // Specify headers block size to generate CONTINUATION frames. + int maxHeadersBlockFragment = 128; + HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder(), maxHeadersBlockFragment); + + int streamId = 13; + HttpFields fields = HttpFields.build() + .put("Accept", "text/html") + // Large header that generates a large headers block. + .put("User-Agent", "Jetty".repeat(256)); + MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields, -1); + + ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); + generator.generateHeaders(lease, streamId, metaData, null, true); + List byteBuffers = lease.getByteBuffers(); + assertThat(byteBuffers.stream().mapToInt(ByteBuffer::remaining).sum(), greaterThan(maxHeadersSize)); + + AtomicBoolean failed = new AtomicBoolean(); + parser.init(new Parser.Listener.Adapter() + { + @Override + public void onConnectionFailure(int error, String reason) + { + failed.set(true); + } + }); + // Set a large max headers size for decoding, to ensure + // the failure is due to accumulation, not decoding. + parser.getHpackDecoder().setMaxHeaderListSize(10 * maxHeadersSize); + + for (ByteBuffer byteBuffer : byteBuffers) + { + parser.parse(byteBuffer); + if (failed.get()) + break; + } + + assertTrue(failed.get()); + } } diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java index a5d51fc05cb2..adf3c07c666c 100644 --- a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java +++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java @@ -89,21 +89,29 @@ public void testPriorityFrameFlood() } @Test - public void testSettingsFrameFlood() + public void testEmptySettingsFrameFlood() { byte[] payload = new byte[0]; testFrameFlood(null, frameFrom(payload.length, FrameType.SETTINGS.getType(), 0, 0, payload)); } + @Test + public void testSettingsFrameFlood() + { + // | Key0 | Key1 | Value0 | Value1 | Value2 | Value3 | + byte[] payload = new byte[]{0, 8, 0, 0, 0, 1}; + testFrameFlood(null, frameFrom(payload.length, FrameType.SETTINGS.getType(), 0, 0, payload)); + } + @Test public void testPingFrameFlood() { byte[] payload = {0, 0, 0, 0, 0, 0, 0, 0}; testFrameFlood(null, frameFrom(payload.length, FrameType.PING.getType(), 0, 0, payload)); } - + @Test - public void testContinuationFrameFlood() + public void testEmptyContinuationFrameFlood() { int streamId = 13; byte[] headersPayload = new byte[0]; @@ -112,6 +120,23 @@ public void testContinuationFrameFlood() testFrameFlood(headersBytes, frameFrom(continuationPayload.length, FrameType.CONTINUATION.getType(), 0, streamId, continuationPayload)); } + @Test + public void testContinuationFrameFlood() + { + int streamId = 13; + byte[] headersPayload = new byte[0]; + byte[] headersBytes = frameFrom(headersPayload.length, FrameType.HEADERS.getType(), 0, streamId, headersPayload); + byte[] continuationPayload = new byte[1]; + testFrameFlood(headersBytes, frameFrom(continuationPayload.length, FrameType.CONTINUATION.getType(), 0, streamId, continuationPayload)); + } + + @Test + public void testResetStreamFrameFlood() + { + byte[] payload = {0, 0, 0, 0}; + testFrameFlood(null, frameFrom(payload.length, FrameType.RST_STREAM.getType(), 0, 13, payload)); + } + @Test public void testUnknownFrameFlood() { 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 b3deebbf1806..64e2f36d9458 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 @@ -63,7 +63,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH; private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS; private boolean connectProtocolEnabled = true; - private RateControl.Factory rateControlFactory = new WindowRateControl.Factory(50); + private RateControl.Factory rateControlFactory = new WindowRateControl.Factory(128); private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F); private long streamIdleTimeout; private boolean useInputDirectByteBuffers;