From 7bd6c5211d617b1b957acd08320933c9bd2ab2d8 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 9 Oct 2023 15:19:00 +0200 Subject: [PATCH] Fixes #10679 - Review HTTP/2 rate control. (#10682) * 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 --- .../http2/parser/ContinuationBodyParser.java | 36 ++++++++---- .../http2/parser/HeaderBlockFragments.java | 31 +++++++--- .../jetty/http2/parser/HeadersBodyParser.java | 37 ++++++++---- .../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 | 56 ++++++++++++++++++- .../jetty/http2/frames/FrameFloodTest.java | 31 +++++++++- .../AbstractHTTP2ServerConnectionFactory.java | 2 +- 11 files changed, 179 insertions(+), 49 deletions(-) diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java index f069ea4a5d4..3a801aea21f 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ContinuationBodyParser.java @@ -76,16 +76,28 @@ public class ContinuationBodyParser extends BodyParser 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; } @@ -104,17 +116,21 @@ public class ContinuationBodyParser extends BodyParser RetainableByteBuffer headerBlock = headerBlockFragments.complete(); MetaData metaData = headerBlockParser.parse(headerBlock.getByteBuffer(), headerBlock.remaining()); headerBlock.release(); - 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-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java index 9d098c0f844..f9c76773dde 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockFragments.java @@ -23,22 +23,34 @@ import org.eclipse.jetty.util.BufferUtil; public class HeaderBlockFragments { private final ByteBufferPool bufferPool; + private final int maxCapacity; private PriorityFrame priorityFrame; - private boolean endStream; private int streamId; + private boolean endStream; private RetainableByteBuffer storage; - public HeaderBlockFragments(ByteBufferPool bufferPool) + public HeaderBlockFragments(ByteBufferPool bufferPool, int maxCapacity) { this.bufferPool = bufferPool; + this.maxCapacity = maxCapacity; } - 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 = bufferPool.acquire(space, fragment.isDirect()); + if (length > maxCapacity) + return false; + int capacity = last ? length : length * 2; + storage = bufferPool.acquire(capacity, fragment.isDirect()); BufferUtil.flipToFill(storage.getByteBuffer()); } @@ -46,6 +58,8 @@ public class HeaderBlockFragments if (storage.remaining() < length) { ByteBuffer byteBuffer = storage.getByteBuffer(); + if (byteBuffer.position() + length > maxCapacity) + return false; int space = last ? length : length * 2; int capacity = byteBuffer.position() + space; RetainableByteBuffer newStorage = bufferPool.acquire(capacity, storage.isDirect()); @@ -61,6 +75,7 @@ public class HeaderBlockFragments fragment.limit(fragment.position() + length); storage.getByteBuffer().put(fragment); fragment.limit(limit); + return true; } public PriorityFrame getPriorityFrame() @@ -85,10 +100,8 @@ public class HeaderBlockFragments public RetainableByteBuffer complete() { - RetainableByteBuffer result = storage; - storage = null; - result.getByteBuffer().flip(); - return result; + storage.getByteBuffer().flip(); + return storage; } public int getStreamId() diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java index e4bda6471ee..e63b6d5a6f5 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeadersBodyParser.java @@ -75,8 +75,15 @@ public class HeadersBodyParser extends BodyParser } 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()); + } } } @@ -171,6 +178,18 @@ public class HeadersBodyParser extends BodyParser 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)) { @@ -195,7 +214,7 @@ public class HeadersBodyParser extends BodyParser { 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"); } } } @@ -204,16 +223,14 @@ public class HeadersBodyParser extends BodyParser 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; } @@ -257,6 +274,6 @@ public class HeadersBodyParser extends BodyParser 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-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java index 5c8aba5ce33..de213165d20 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/Parser.java @@ -74,7 +74,7 @@ public class Parser this.listener = listener; unknownBodyParser = new UnknownBodyParser(headerParser, listener); HeaderBlockParser headerBlockParser = new HeaderBlockParser(headerParser, bufferPool, hpackDecoder, unknownBodyParser); - HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(bufferPool); + HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(bufferPool, 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-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java index b1f2d8a5bac..fa41e4a0b63 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/PushPromiseBodyParser.java @@ -18,6 +18,7 @@ import java.nio.ByteBuffer; 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 class PushPromiseBodyParser extends BodyParser length = getBodyLength(); if (isPadding()) - { state = State.PADDING_LENGTH; - } else - { state = State.STREAM_ID; - } break; } case PADDING_LENGTH: @@ -131,7 +128,15 @@ public class PushPromiseBodyParser extends BodyParser 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-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java index 937dfc387a7..fd5b640488f 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/ResetBodyParser.java @@ -58,7 +58,7 @@ public class ResetBodyParser extends BodyParser { if (buffer.remaining() >= 4) { - return onReset(buffer.getInt()); + return onReset(buffer, buffer.getInt()); } else { @@ -73,7 +73,7 @@ public class ResetBodyParser extends BodyParser --cursor; error += currByte << (8 * cursor); if (cursor == 0) - return onReset(error); + return onReset(buffer, error); break; } default: @@ -85,9 +85,11 @@ public class ResetBodyParser extends BodyParser 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-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java index 2641b4ad8bc..b985a7b46ef 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java @@ -73,10 +73,7 @@ public class SettingsBodyParser extends BodyParser 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) @@ -219,11 +216,13 @@ public class SettingsBodyParser extends BodyParser 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-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java index 97aebcb9010..fb1b9707fd0 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/UnknownBodyParser.java @@ -35,7 +35,6 @@ public class UnknownBodyParser extends BodyParser 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-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java index 4c18f0f77c5..c98765fa97b 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/ContinuationParseTest.java @@ -16,6 +16,7 @@ package org.eclipse.jetty.http2.frames; 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.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; 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; @@ -41,7 +44,7 @@ public class ContinuationParseTest @Test public void testParseOneByteAtATime() throws Exception { - ByteBufferPool bufferPool = new ArrayByteBufferPool(); + ArrayByteBufferPool.Tracking bufferPool = new ArrayByteBufferPool.Tracking(); HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(bufferPool), new HpackEncoder()); final List frames = new ArrayList<>(); @@ -137,6 +140,7 @@ public class ContinuationParseTest parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()})); } } + accumulator.release(); assertEquals(1, frames.size()); HeadersFrame frame = frames.get(0); @@ -152,6 +156,56 @@ public class ContinuationParseTest } PriorityFrame priority = frame.getPriority(); assertNull(priority); + + assertEquals(0, bufferPool.getLeaks().size(), bufferPool.dumpLeaks()); } } + + @Test + public void testLargeHeadersBlock() throws Exception + { + // Use a ByteBufferPool with a small factor, so that the accumulation buffer is not too large. + ByteBufferPool bufferPool = new ArrayByteBufferPool(0, 128, -1); + // A small max headers size, used for both accumulation and decoding. + int maxHeadersSize = 512; + Parser parser = new Parser(bufferPool, maxHeadersSize); + // Specify headers block size to generate CONTINUATION frames. + int maxHeadersBlockFragment = 128; + HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(bufferPool), 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.Accumulator accumulator = new ByteBufferPool.Accumulator(); + generator.generateHeaders(accumulator, streamId, metaData, null, true); + List byteBuffers = accumulator.getByteBuffers(); + assertThat(byteBuffers.stream().mapToInt(ByteBuffer::remaining).sum(), greaterThan(maxHeadersSize)); + + AtomicBoolean failed = new AtomicBoolean(); + parser.init(new Parser.Listener() + { + @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; + } + accumulator.release(); + + assertTrue(failed.get()); + } } diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java index ee514fc1361..013fbe6ff1e 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java +++ b/jetty-core/jetty-http2/jetty-http2-common/src/test/java/org/eclipse/jetty/http2/frames/FrameFloodTest.java @@ -96,21 +96,29 @@ public class FrameFloodTest } @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]; @@ -119,6 +127,23 @@ public class FrameFloodTest 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-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 90a75035976..bcd57d9172e 100644 --- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -73,7 +73,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private int maxFrameSize = Frame.DEFAULT_MAX_SIZE; 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;