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 04d26d17162a..8f328002249c 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 @@ -80,16 +80,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; } @@ -107,17 +119,21 @@ private boolean onHeaders(ByteBuffer buffer) { ByteBuffer headerBlock = headerBlockFragments.complete(); MetaData metaData = headerBlockParser.parse(headerBlock, headerBlock.remaining()); - 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 be63ba46938d..3c69d2d248cf 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 @@ -24,22 +24,40 @@ public class HeaderBlockFragments { + private final int maxCapacity; private PriorityFrame priorityFrame; - private boolean endStream; private int streamId; + private boolean endStream; private ByteBuffer storage; - public void storeFragment(ByteBuffer fragment, int length, boolean last) + public HeaderBlockFragments(int maxCapacity) + { + this.maxCapacity = maxCapacity; + } + + 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 = ByteBuffer.allocate(space); + if (length > maxCapacity) + return false; + int capacity = last ? length : length * 2; + storage = ByteBuffer.allocate(capacity); } // 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 = ByteBuffer.allocate(capacity); @@ -53,6 +71,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() @@ -77,10 +96,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 08d4f7735ec2..909d49f93e17 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 @@ -76,8 +76,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()); + } } } @@ -172,6 +179,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)) { @@ -196,7 +215,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"); } } } @@ -205,16 +224,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; } @@ -258,6 +275,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 abafc7bb5a01..1d727f7dcdb2 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 @@ -92,7 +92,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(); + HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(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 2977d0fa91a5..7049e033078e 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 @@ -23,6 +23,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 @@ -70,13 +71,9 @@ public boolean parse(ByteBuffer buffer) length = getBodyLength(); if (isPadding()) - { state = State.PADDING_LENGTH; - } else - { state = State.STREAM_ID; - } break; } case PADDING_LENGTH: @@ -136,7 +133,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 bcc80f16e401..19b072a643dc 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 @@ -63,7 +63,7 @@ public boolean parse(ByteBuffer buffer) { if (buffer.remaining() >= 4) { - return onReset(buffer.getInt()); + return onReset(buffer, buffer.getInt()); } else { @@ -78,7 +78,7 @@ public boolean parse(ByteBuffer buffer) --cursor; error += currByte << (8 * cursor); if (cursor == 0) - return onReset(error); + return onReset(buffer, error); break; } default: @@ -90,9 +90,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 5ee02524e701..fe9eaf0fb3f9 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 @@ -73,12 +73,22 @@ public int getMaxKeys() @Override protected void emptyBody(ByteBuffer buffer) { + if (!validateFrame(buffer, getStreamId(), 0)) + 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) + { + // SPEC: wrong streamId is treated as connection error. + if (streamId != 0) + return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame"); + // SPEC: reply with body is treated as connection error. + if (hasFlag(Flags.ACK) && bodyLength > 0) + return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame"); + return true; } @Override @@ -95,9 +105,8 @@ private boolean parse(ByteBuffer buffer, int streamId, int bodyLength) { case PREPARE: { - // SPEC: wrong streamId is treated as connection error. - if (streamId != 0) - return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame"); + if (!validateFrame(buffer, streamId, bodyLength)) + return false; length = bodyLength; settings = new HashMap<>(); state = State.SETTING_ID; @@ -211,11 +220,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 86a8628137e1..23ca493baa1f 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 @@ -40,7 +40,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 cec347f25c36..10fac901f511 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 @@ -21,6 +21,8 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.IntStream; import org.eclipse.jetty.http.HostPortHttpField; import org.eclipse.jetty.http.HttpField; @@ -37,6 +39,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; @@ -159,4 +163,54 @@ 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 = new HttpFields(); + fields.put("Accept", "text/html"); + // Large header that generates a large headers block. + StringBuilder large = new StringBuilder(); + IntStream.range(0, 256).forEach(i -> large.append("Jetty")); + fields.put("User-Agent", large.toString()); + + 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 347dd9b5ce50..05b6c28f39ff 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 @@ -94,21 +94,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]; @@ -117,6 +125,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() { @@ -127,7 +152,7 @@ public void testUnknownFrameFlood() private void testFrameFlood(byte[] preamble, byte[] bytes) { AtomicBoolean failed = new AtomicBoolean(); - Parser parser = new Parser(byteBufferPool, 4096, new WindowRateControl(8, Duration.ofSeconds(1))); + Parser parser = new Parser(byteBufferPool, 8192, new WindowRateControl(8, Duration.ofSeconds(1))); parser.init(new Parser.Listener.Adapter() { @Override diff --git a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java index f021d7094aa2..45def3cb6b4f 100644 --- a/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java +++ b/jetty-http2/http2-common/src/test/java/org/eclipse/jetty/http2/frames/SettingsGenerateParseTest.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class SettingsGenerateParseTest @@ -44,8 +45,7 @@ public class SettingsGenerateParseTest @Test public void testGenerateParseNoSettings() { - - List frames = testGenerateParse(Collections.emptyMap()); + List frames = testGenerateParse(Collections.emptyMap(), true); assertEquals(1, frames.size()); SettingsFrame frame = frames.get(0); assertEquals(0, frame.getSettings().size()); @@ -62,7 +62,7 @@ public void testGenerateParseSettings() int key2 = 19; Integer value2 = 23; settings1.put(key2, value2); - List frames = testGenerateParse(settings1); + List frames = testGenerateParse(settings1, false); assertEquals(1, frames.size()); SettingsFrame frame = frames.get(0); Map settings2 = frame.getSettings(); @@ -71,12 +71,12 @@ public void testGenerateParseSettings() assertEquals(value2, settings2.get(key2)); } - private List testGenerateParse(Map settings) + private List testGenerateParse(Map settings, boolean reply) { SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, 4096); + Parser parser = new Parser(byteBufferPool, 8192); parser.init(new Parser.Listener.Adapter() { @Override @@ -90,7 +90,7 @@ public void onSettings(SettingsFrame frame) for (int i = 0; i < 2; ++i) { ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings, true); + generator.generateSettings(lease, settings, reply); frames.clear(); for (ByteBuffer buffer : lease.getByteBuffers()) @@ -111,7 +111,7 @@ public void testGenerateParseInvalidSettings() SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, 4096); + Parser parser = new Parser(byteBufferPool, 8192); parser.init(new Parser.Listener.Adapter() { @Override @@ -124,7 +124,7 @@ public void onConnectionFailure(int error, String reason) Map settings1 = new HashMap<>(); settings1.put(13, 17); ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings1, true); + generator.generateSettings(lease, settings1, false); // Modify the length of the frame to make it invalid ByteBuffer bytes = lease.getByteBuffers().get(0); bytes.putShort(1, (short)(bytes.getShort(1) - 1)); @@ -146,7 +146,7 @@ public void testGenerateParseOneByteAtATime() SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); List frames = new ArrayList<>(); - Parser parser = new Parser(byteBufferPool, 4096); + Parser parser = new Parser(byteBufferPool, 8192); parser.init(new Parser.Listener.Adapter() { @Override @@ -165,7 +165,7 @@ public void onSettings(SettingsFrame frame) for (int i = 0; i < 2; ++i) { ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool); - generator.generateSettings(lease, settings1, true); + generator.generateSettings(lease, settings1, false); frames.clear(); for (ByteBuffer buffer : lease.getByteBuffers()) @@ -181,7 +181,7 @@ public void onSettings(SettingsFrame frame) Map settings2 = frame.getSettings(); assertEquals(1, settings2.size()); assertEquals(value, settings2.get(key)); - assertTrue(frame.isReply()); + assertFalse(frame.isReply()); } } @@ -191,7 +191,7 @@ public void testGenerateParseTooManyDifferentSettingsInOneFrame() SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, 4096); + Parser parser = new Parser(byteBufferPool, 8192); int maxSettingsKeys = 32; parser.setMaxSettingsKeys(maxSettingsKeys); parser.init(new Parser.Listener.Adapter() @@ -231,7 +231,7 @@ public void testGenerateParseTooManySameSettingsInOneFrame() throws Exception int maxSettingsKeys = pairs / 2; AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, 4096); + Parser parser = new Parser(byteBufferPool, 8192); parser.setMaxSettingsKeys(maxSettingsKeys); parser.setMaxFrameSize(Frame.DEFAULT_MAX_LENGTH); parser.init(new Parser.Listener.Adapter() @@ -272,7 +272,7 @@ public void testGenerateParseTooManySettingsInMultipleFrames() SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator()); AtomicInteger errorRef = new AtomicInteger(); - Parser parser = new Parser(byteBufferPool, 4096); + Parser parser = new Parser(byteBufferPool, 8192); int maxSettingsKeys = 32; parser.setMaxSettingsKeys(maxSettingsKeys); parser.init(new Parser.Listener.Adapter() 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 83418b86d3b6..5ac9cc5c8a1b 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 @@ -62,7 +62,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private int maxHeaderBlockFragment = 0; private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH; private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS; - 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;