diff --git a/src/main/java/io/netty/incubator/codec/http3/QpackEncoder.java b/src/main/java/io/netty/incubator/codec/http3/QpackEncoder.java index 51b1771..f3cc179 100644 --- a/src/main/java/io/netty/incubator/codec/http3/QpackEncoder.java +++ b/src/main/java/io/netty/incubator/codec/http3/QpackEncoder.java @@ -163,7 +163,12 @@ void sectionAcknowledgment(long streamId) throws QpackException { * @param streamId which is cancelled. */ void streamCancellation(long streamId) throws QpackException { - assert streamSectionTrackers != null; + // If a configureDynamicTable(...) was called with a maxTableCapacity of 0 we will have not instanced + // streamSectionTrackers. The remote peer might still send a stream cancellation for a stream, while it + // is optional. See https://www.rfc-editor.org/rfc/rfc9204.html#section-2.2.2.2 + if (streamSectionTrackers == null) { + return; + } final Queue tracker = streamSectionTrackers.remove(streamId); if (tracker != null) { for (;;) { diff --git a/src/test/java/io/netty/incubator/codec/http3/QpackDecoderHandlerTest.java b/src/test/java/io/netty/incubator/codec/http3/QpackDecoderHandlerTest.java index a14996c..5f10371 100644 --- a/src/test/java/io/netty/incubator/codec/http3/QpackDecoderHandlerTest.java +++ b/src/test/java/io/netty/incubator/codec/http3/QpackDecoderHandlerTest.java @@ -240,6 +240,18 @@ public void streamCancelUnknownStream() throws Exception { finishStreams(); } + @Test + public void streamCancelDynamicTableWithMaxCapacity0() throws Exception { + setup(0); + encodeHeaders(headers -> headers.add(fooBar.name, fooBar.value)); + verifyRequiredInsertCount(0); + verifyKnownReceivedCount(0); + // Send a stream cancellation for a dynamic table of capacity 0. + // See https://www.rfc-editor.org/rfc/rfc9204.html#section-2.2.2.2 + sendStreamCancellation(decoderStream.streamId()); + finishStreams(false); + } + @Test public void invalidIncrement() throws Exception { setup(128); @@ -325,8 +337,12 @@ private void setup(long maxTableCapacity) throws Exception { } private void finishStreams() { + finishStreams(true); + } + + private void finishStreams(boolean encoderPendingMessage) { assertThat("Unexpected decoder stream message", decoderStream.finishAndReleaseAll(), is(false)); - assertThat("Unexpected encoder stream message", encoderStream.finishAndReleaseAll(), is(true)); + assertThat("Unexpected encoder stream message", encoderStream.finishAndReleaseAll(), is(encoderPendingMessage)); assertThat("Unexpected parent stream message", parent.finishAndReleaseAll(), is(false)); }