Allow GOAWAY to be sent from handlers after the Http2MultiplexCodec so that app developers can shed load by issuing GOAWAY

This commit is contained in:
Christopher Exell 2017-03-01 11:00:54 -08:00 committed by Scott Mitchell
parent 2902ca122a
commit 52aecabe43
2 changed files with 52 additions and 17 deletions

View File

@ -369,27 +369,36 @@ public final class Http2MultiplexCodec extends ChannelDuplexHandler {
@Override
protected void doWrite(Object msg) {
if (!(msg instanceof Http2StreamFrame)) {
ReferenceCountUtil.release(msg);
throw new IllegalArgumentException("Message must be an Http2StreamFrame: " + msg);
}
Http2StreamFrame frame = (Http2StreamFrame) msg;
ChannelPromise promise = ctx.newPromise();
if (isStreamIdValid(frame.streamId())) {
ReferenceCountUtil.release(frame);
throw new IllegalArgumentException("Stream id must not be set on the frame. Was: " + frame.streamId());
}
if (!isStreamIdValid(streamId())) {
if (!(frame instanceof Http2HeadersFrame)) {
throw new IllegalArgumentException("The first frame must be a headers frame. Was: " + frame.name());
if (msg instanceof Http2StreamFrame) {
Http2StreamFrame frame = (Http2StreamFrame) msg;
ChannelPromise promise = ctx.newPromise();
if (isStreamIdValid(frame.streamId())) {
ReferenceCountUtil.release(frame);
throw new IllegalArgumentException("Stream id must not be set on the frame. Was: "
+ frame.streamId());
}
frame = new ChannelCarryingHeadersFrame((Http2HeadersFrame) frame, this);
// Handle errors on stream creation
if (!isStreamIdValid(streamId())) {
if (!(frame instanceof Http2HeadersFrame)) {
ReferenceCountUtil.release(frame);
throw new IllegalArgumentException("The first frame must be a headers frame. Was: "
+ frame.name());
}
frame = new ChannelCarryingHeadersFrame((Http2HeadersFrame) frame, this);
// Handle errors on stream creation
promise.addListener(this);
} else {
frame.streamId(streamId());
}
writeFromStreamChannel(frame, promise, false);
} else if (msg instanceof Http2GoAwayFrame) {
ChannelPromise promise = ctx.newPromise();
promise.addListener(this);
writeFromStreamChannel(msg, promise, false);
} else {
frame.streamId(streamId());
ReferenceCountUtil.release(msg);
throw new IllegalArgumentException("Message must be an Http2GoAwayFrame or Http2StreamFrame: " + msg);
}
writeFromStreamChannel(frame, promise, false);
}
@Override

View File

@ -343,6 +343,32 @@ public class Http2MultiplexCodecTest {
assertEquals("bar", channel.attr(key).get());
}
@Test
public void outboundStreamShouldWriteGoAwayWithoutReset() {
childChannelInitializer.handler = new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(new DefaultHttp2GoAwayFrame(Http2Error.NO_ERROR));
ctx.fireChannelActive();
}
};
Http2StreamChannelBootstrap b = new Http2StreamChannelBootstrap();
b.parentChannel(parentChannel).handler(childChannelInitializer);
Channel childChannel = b.connect().channel();
assertTrue(childChannel.isActive());
Http2GoAwayFrame goAwayFrame = parentChannel.readOutbound();
assertNotNull(goAwayFrame);
goAwayFrame.release();
childChannel.close();
parentChannel.runPendingTasks();
Http2ResetFrame reset = parentChannel.readOutbound();
assertNull(reset);
}
private LastInboundHandler streamActiveAndWriteHeaders(int streamId) {
LastInboundHandler inboundHandler = new LastInboundHandler();
childChannelInitializer.handler = inboundHandler;