Http2MultiplexCodec now propagates SETTINGS and GOAWAY frames in pipeline.

Motivation:

Allow the observation of SETTINGS frame by other handlers in the pipeline. For my particular use case this allows me to observe the value of MAX_CONCURRENT_STREAMS for a ChannelPool abstraction that supports HTTP/2 multiplexing. Beside this also forward GOAWAY frames.

Modification:

Always forward SETTINGS and GOAWAY frames

Result:

Settings / Goaway can now be observed in the parent channel. Previously it was not possible (to my knowledge) to capture the settings when using Http2MultiplexCodec.
This commit is contained in:
shorea 2018-02-12 17:42:59 -08:00 committed by Norman Maurer
parent 02b7507a62
commit 650406c0a3
3 changed files with 26 additions and 10 deletions

View File

@ -66,7 +66,8 @@ import static java.lang.Math.min;
* communication, closing of the channel is delayed until any inbound queue is drained with {@link * communication, closing of the channel is delayed until any inbound queue is drained with {@link
* Channel#read()}, which follows the default behavior of channels in Netty. Applications are * Channel#read()}, which follows the default behavior of channels in Netty. Applications are
* free to close the channel in response to such events if they don't have use for any queued * free to close the channel in response to such events if they don't have use for any queued
* messages. * messages. Any connection level events like {@link Http2SettingsFrame} and {@link Http2GoAwayFrame}
* will be processed internally and also propagated down the pipeline for other handlers to act on.
* *
* <p>Outbound streams are supported via the {@link Http2StreamChannelBootstrap}. * <p>Outbound streams are supported via the {@link Http2StreamChannelBootstrap}.
* *
@ -164,8 +165,10 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
// Need to be volatile as accessed from within the DefaultHttp2StreamChannel in a multi-threaded fashion. // Need to be volatile as accessed from within the DefaultHttp2StreamChannel in a multi-threaded fashion.
volatile ChannelHandlerContext ctx; volatile ChannelHandlerContext ctx;
Http2MultiplexCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, Http2Settings initialSettings, Http2MultiplexCodec(Http2ConnectionEncoder encoder,
ChannelHandler inboundStreamHandler) { Http2ConnectionDecoder decoder,
Http2Settings initialSettings,
ChannelHandler inboundStreamHandler) {
super(encoder, decoder, initialSettings); super(encoder, decoder, initialSettings);
this.inboundStreamHandler = inboundStreamHandler; this.inboundStreamHandler = inboundStreamHandler;
} }
@ -218,11 +221,15 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
onHttp2StreamFrame(((Http2MultiplexCodecStream) streamFrame.stream()).channel, streamFrame); onHttp2StreamFrame(((Http2MultiplexCodecStream) streamFrame.stream()).channel, streamFrame);
} else if (frame instanceof Http2GoAwayFrame) { } else if (frame instanceof Http2GoAwayFrame) {
onHttp2GoAwayFrame(ctx, (Http2GoAwayFrame) frame); onHttp2GoAwayFrame(ctx, (Http2GoAwayFrame) frame);
// Allow other handlers to act on GOAWAY frame
ctx.fireChannelRead(frame);
} else if (frame instanceof Http2SettingsFrame) { } else if (frame instanceof Http2SettingsFrame) {
Http2Settings settings = ((Http2SettingsFrame) frame).settings(); Http2Settings settings = ((Http2SettingsFrame) frame).settings();
if (settings.initialWindowSize() != null) { if (settings.initialWindowSize() != null) {
initialOutboundStreamWindow = settings.initialWindowSize(); initialOutboundStreamWindow = settings.initialWindowSize();
} }
// Allow other handlers to act on SETTINGS frame
ctx.fireChannelRead(frame);
} else { } else {
// Send any other frames down the pipeline // Send any other frames down the pipeline
ctx.fireChannelRead(frame); ctx.fireChannelRead(frame);

View File

@ -231,7 +231,7 @@ public class CleartextHttp2ServerUpgradeHandlerTest {
ByteBuf settingsFrame = settingsFrameBuf(); ByteBuf settingsFrame = settingsFrameBuf();
assertFalse(channel.writeInbound(settingsFrame)); assertTrue(channel.writeInbound(settingsFrame));
assertEquals(1, userEvents.size()); assertEquals(1, userEvents.size());
assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent); assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent);

View File

@ -41,11 +41,13 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import static io.netty.util.ReferenceCountUtil.release; import static io.netty.util.ReferenceCountUtil.release;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
@ -175,11 +177,15 @@ public class Http2MultiplexCodecTest {
@Test @Test
public void unhandledHttp2FramesShouldBePropagated() { public void unhandledHttp2FramesShouldBePropagated() {
Http2PingFrame decodedFrame = new DefaultHttp2PingFrame(0); assertThat(parentChannel.readInbound(), instanceOf(Http2SettingsFrame.class));
codec.onHttp2Frame(decodedFrame); Http2PingFrame pingFrame = new DefaultHttp2PingFrame(0);
Http2PingFrame receivedPing = parentChannel.readInbound(); codec.onHttp2Frame(pingFrame);
assertSame(receivedPing, decodedFrame); assertSame(parentChannel.readInbound(), pingFrame);
DefaultHttp2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(1);
codec.onHttp2Frame(goAwayFrame);
assertSame(parentChannel.readInbound(), goAwayFrame);
} }
@Test @Test
@ -619,8 +625,10 @@ public class Http2MultiplexCodecTest {
*/ */
private final class TestableHttp2MultiplexCodec extends Http2MultiplexCodec { private final class TestableHttp2MultiplexCodec extends Http2MultiplexCodec {
public TestableHttp2MultiplexCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, public TestableHttp2MultiplexCodec(Http2ConnectionEncoder encoder,
Http2Settings initialSettings, ChannelHandler inboundStreamHandler) { Http2ConnectionDecoder decoder,
Http2Settings initialSettings,
ChannelHandler inboundStreamHandler) {
super(encoder, decoder, initialSettings, inboundStreamHandler); super(encoder, decoder, initialSettings, inboundStreamHandler);
} }
@ -682,6 +690,7 @@ public class Http2MultiplexCodecTest {
} }
private final class TestableHttp2MultiplexCodecBuilder extends Http2MultiplexCodecBuilder { private final class TestableHttp2MultiplexCodecBuilder extends Http2MultiplexCodecBuilder {
TestableHttp2MultiplexCodecBuilder(boolean server, ChannelHandler childHandler) { TestableHttp2MultiplexCodecBuilder(boolean server, ChannelHandler childHandler) {
super(server, childHandler); super(server, childHandler);
} }