Issue #8025. Ignoring HEADER and DATA frames for streams that may have existed in the past.

This commit is contained in:
shorea 2018-06-15 23:05:42 -07:00 committed by Norman Maurer
parent 718b7626e6
commit cd9761b2fb
2 changed files with 46 additions and 20 deletions

View File

@ -585,11 +585,11 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
ctx.channel(), frameName, streamId);
return true;
}
// Its possible that this frame would result in stream ID out of order creation (PROTOCOL ERROR) and its
// also possible that this frame is received on a CLOSED stream (STREAM_CLOSED after a RST_STREAM is
// sent). We don't have enough information to know for sure, so we choose the lesser of the two errors.
throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d",
frameName, streamId);
// If the stream could have existed in the past we assume this is a frame sent by the remote
// after a RST_STREAM has been sent. Since we don't retain metadata about streams that have been
// reset we can't know this for sure.
verifyStreamMayHaveExisted(streamId);
return true;
} else if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) {
// If we have sent a reset stream it is assumed the stream will be closed after the write completes.
// If we have not sent a reset, but the stream was created after a GoAway this is not supported by

View File

@ -272,6 +272,18 @@ public class DefaultHttp2ConnectionDecoderTest {
}
}
@Test(expected = Http2Exception.class)
public void dataReadForUnknownStreamThatNeverExistedShouldThrow() throws Exception {
when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false);
when(connection.stream(STREAM_ID)).thenReturn(null);
final ByteBuf data = dummyData();
try {
decode().onDataRead(ctx, STREAM_ID, data, 0, true);
} finally {
data.release();
}
}
@Test
public void dataReadForUnknownStreamShouldApplyFlowControl() throws Exception {
when(connection.stream(STREAM_ID)).thenReturn(null);
@ -279,19 +291,15 @@ public class DefaultHttp2ConnectionDecoderTest {
int padding = 10;
int processedBytes = data.readableBytes() + padding;
try {
try {
decode().onDataRead(ctx, STREAM_ID, data, padding, true);
fail();
} catch (Http2Exception e) {
verify(localFlow)
.receiveFlowControlledFrame(eq((Http2Stream) null), eq(data), eq(padding), eq(true));
verify(localFlow).consumeBytes(eq((Http2Stream) null), eq(processedBytes));
verify(localFlow).frameWriter(any(Http2FrameWriter.class));
verifyNoMoreInteractions(localFlow);
decode().onDataRead(ctx, STREAM_ID, data, padding, true);
verify(localFlow)
.receiveFlowControlledFrame(eq((Http2Stream) null), eq(data), eq(padding), eq(true));
verify(localFlow).consumeBytes(eq((Http2Stream) null), eq(processedBytes));
verify(localFlow).frameWriter(any(Http2FrameWriter.class));
verifyNoMoreInteractions(localFlow);
// Verify that the event was absorbed and not propagated to the observer.
verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean());
}
// Verify that the event was absorbed and not propagated to the observer.
verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean());
} finally {
data.release();
}
@ -430,10 +438,15 @@ public class DefaultHttp2ConnectionDecoderTest {
}
}
@Test(expected = Http2Exception.class)
public void headersReadForUnknownStreamShouldThrow() throws Exception {
@Test
public void headersReadForUnknownStreamThatMayHaveExistedShouldBeIgnored() throws Exception {
when(connection.stream(STREAM_ID)).thenReturn(null);
decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
// Verify that the event was absorbed and not propagated to the observer.
verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyBoolean());
verify(remote, never()).createStream(anyInt(), anyBoolean());
verify(stream, never()).open(anyBoolean());
}
@Test
@ -587,8 +600,21 @@ public class DefaultHttp2ConnectionDecoderTest {
verify(listener).onPushPromiseRead(eq(ctx), anyInt(), anyInt(), any(Http2Headers.class), anyInt());
}
@Test
public void pushPromiseReadForUnknownStreamThatMayHaveExistedShouldBeIgnored() throws Exception {
when(connection.stream(STREAM_ID)).thenReturn(null);
decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
// Verify that the event was absorbed and not propagated to the observer.
verify(listener, never()).onPushPromiseRead(eq(ctx), anyInt(), anyInt(),
any(Http2Headers.class), anyInt());
verify(remote, never()).createStream(anyInt(), anyBoolean());
verify(stream, never()).open(anyBoolean());
}
@Test(expected = Http2Exception.class)
public void pushPromiseReadForUnknownStreamShouldThrow() throws Exception {
public void pushPromiseReadForUnknownStreamThatNeverExistedShouldThrow() throws Exception {
when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false);
when(connection.stream(STREAM_ID)).thenReturn(null);
decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
}