Skip to content

HTTP/2 UniformStreamByteDistributor stream window ignored #4545

Closed
@Scottmitch

Description

@Scottmitch

UniformStreamByteDistributor will write to a stream even if the stream's flow control window is negative. This is not allowed by the rfc.

https://tools.ietf.org/html/rfc7540#section-6.9.2

A change to SETTINGS_INITIAL_WINDOW_SIZE can cause the available
space in a flow-control window to become negative. A sender MUST
track the negative flow-control window and MUST NOT send new flow-
controlled frames until it receives WINDOW_UPDATE frames that cause
the flow-control window to become positive.

I guess the spec also means that you should not send new flow control frames until the flow-control window becomes non-negative? If the flow-control window is 0 and we have empty frames I don't think it should be a problem to send these.

While working on PR #4538 I developed the following test case to demonstrate the issue:

diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java
index 44010e4..c52b678 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java
@@ -185,11 +185,29 @@ public class UniformStreamByteDistributorTest {
         verifyNoMoreInteractions(writer);
     }

+    @Test
+    public void streamWindowExhaustedDoesNotWrite() throws Http2Exception {
+        updateStream(STREAM_A, 0, true, false);
+        updateStream(STREAM_B, 0, true);
+        updateStream(STREAM_C, 0, true);
+        updateStream(STREAM_D, 0, true, false);
+
+        assertFalse(write(10));
+        verifyWrite(STREAM_B, 0);
+        verifyWrite(STREAM_C, 0);
+        verifyNoMoreInteractions(writer);
+    }
+
     private Http2Stream stream(int streamId) {
         return connection.stream(streamId);
     }

     private void updateStream(final int streamId, final int streamableBytes, final boolean hasFrame) {
+        updateStream(streamId, streamableBytes, hasFrame, hasFrame);
+    }
+
+    private void updateStream(final int streamId, final int streamableBytes, final boolean hasFrame,
+            final boolean isWriteAllowed) {
         final Http2Stream stream = stream(streamId);
         distributor.updateStreamableBytes(new StreamByteDistributor.StreamState() {
             @Override
@@ -206,6 +224,11 @@ public class UniformStreamByteDistributorTest {
             public boolean hasFrame() {
                 return hasFrame;
             }
+
+            @Override
+            public boolean isWriteAllowed() {
+                return isWriteAllowed;
+            }
         });
     }

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions