2016-08-16 15:22:39 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2016 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package io.netty.handler.codec.http2;
|
|
|
|
|
|
|
|
import io.netty.bootstrap.Bootstrap;
|
|
|
|
import io.netty.bootstrap.ServerBootstrap;
|
|
|
|
import io.netty.buffer.ByteBuf;
|
|
|
|
import io.netty.buffer.Unpooled;
|
|
|
|
import io.netty.channel.Channel;
|
2017-08-11 07:51:45 +02:00
|
|
|
import io.netty.channel.ChannelHandler;
|
2016-08-16 15:22:39 +02:00
|
|
|
import io.netty.channel.ChannelHandler.Sharable;
|
2017-11-08 16:29:14 +01:00
|
|
|
import io.netty.channel.ChannelHandlerAdapter;
|
2016-08-16 15:22:39 +02:00
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
2017-09-06 09:05:51 +02:00
|
|
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
2016-11-16 23:03:57 +01:00
|
|
|
import io.netty.channel.ChannelInitializer;
|
2016-08-16 15:22:39 +02:00
|
|
|
import io.netty.channel.DefaultEventLoop;
|
|
|
|
import io.netty.channel.EventLoopGroup;
|
|
|
|
import io.netty.channel.local.LocalAddress;
|
|
|
|
import io.netty.channel.local.LocalChannel;
|
|
|
|
import io.netty.channel.local.LocalServerChannel;
|
|
|
|
import org.junit.After;
|
|
|
|
import org.junit.AfterClass;
|
2017-09-05 19:34:34 +02:00
|
|
|
import org.junit.Assert;
|
2016-08-16 15:22:39 +02:00
|
|
|
import org.junit.Before;
|
|
|
|
import org.junit.BeforeClass;
|
|
|
|
import org.junit.Test;
|
|
|
|
|
2016-11-16 23:03:57 +01:00
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
|
2016-08-16 15:22:39 +02:00
|
|
|
import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid;
|
2017-04-07 02:53:04 +02:00
|
|
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
2016-08-16 15:22:39 +02:00
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
|
import static org.junit.Assert.assertNotNull;
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
import static org.junit.Assert.assertFalse;
|
2016-08-16 15:22:39 +02:00
|
|
|
import static org.junit.Assert.assertTrue;
|
|
|
|
|
|
|
|
/**
|
2017-08-11 07:51:45 +02:00
|
|
|
* Unit tests for {@link Http2MultiplexCodec}.
|
2016-08-16 15:22:39 +02:00
|
|
|
*/
|
2017-08-11 07:51:45 +02:00
|
|
|
public class Http2MultiplexCodecBuilderTest {
|
2016-08-16 15:22:39 +02:00
|
|
|
|
|
|
|
private static EventLoopGroup group;
|
|
|
|
private Channel serverChannel;
|
2016-11-16 23:03:57 +01:00
|
|
|
private volatile Channel serverConnectedChannel;
|
2016-08-16 15:22:39 +02:00
|
|
|
private Channel clientChannel;
|
|
|
|
private LastInboundHandler serverLastInboundHandler;
|
|
|
|
|
|
|
|
@BeforeClass
|
|
|
|
public static void init() {
|
|
|
|
group = new DefaultEventLoop();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Before
|
|
|
|
public void setUp() throws InterruptedException {
|
2016-11-16 23:03:57 +01:00
|
|
|
final CountDownLatch serverChannelLatch = new CountDownLatch(1);
|
2016-08-16 15:22:39 +02:00
|
|
|
LocalAddress serverAddress = new LocalAddress(getClass().getName());
|
|
|
|
serverLastInboundHandler = new SharableLastInboundHandler();
|
|
|
|
ServerBootstrap sb = new ServerBootstrap()
|
|
|
|
.channel(LocalServerChannel.class)
|
|
|
|
.group(group)
|
2016-11-16 23:03:57 +01:00
|
|
|
.childHandler(new ChannelInitializer<Channel>() {
|
|
|
|
@Override
|
|
|
|
protected void initChannel(Channel ch) throws Exception {
|
|
|
|
serverConnectedChannel = ch;
|
2017-09-06 09:05:51 +02:00
|
|
|
ch.pipeline().addLast(new Http2MultiplexCodecBuilder(true, new ChannelInitializer<Channel>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void initChannel(Channel ch) throws Exception {
|
|
|
|
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
|
|
|
|
private boolean writable;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
writable |= ctx.channel().isWritable();
|
|
|
|
super.channelActive(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
writable |= ctx.channel().isWritable();
|
|
|
|
super.channelWritabilityChanged(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
assertTrue(writable);
|
|
|
|
super.channelInactive(ctx);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ch.pipeline().addLast(serverLastInboundHandler);
|
|
|
|
}
|
|
|
|
}).build());
|
2016-11-16 23:03:57 +01:00
|
|
|
serverChannelLatch.countDown();
|
|
|
|
}
|
|
|
|
});
|
2016-08-16 15:22:39 +02:00
|
|
|
serverChannel = sb.bind(serverAddress).sync().channel();
|
|
|
|
|
|
|
|
Bootstrap cb = new Bootstrap()
|
|
|
|
.channel(LocalChannel.class)
|
|
|
|
.group(group)
|
2017-09-05 19:34:34 +02:00
|
|
|
.handler(new Http2MultiplexCodecBuilder(false, new ChannelInitializer<Channel>() {
|
|
|
|
@Override
|
|
|
|
protected void initChannel(Channel ch) throws Exception {
|
|
|
|
Assert.fail("Should not be called for outbound streams");
|
|
|
|
}
|
|
|
|
}).build());
|
2016-08-16 15:22:39 +02:00
|
|
|
clientChannel = cb.connect(serverAddress).sync().channel();
|
2017-04-07 02:53:04 +02:00
|
|
|
assertTrue(serverChannelLatch.await(5, SECONDS));
|
2016-08-16 15:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@AfterClass
|
|
|
|
public static void shutdown() {
|
2017-04-07 02:53:04 +02:00
|
|
|
group.shutdownGracefully(0, 5, SECONDS);
|
2016-08-16 15:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@After
|
|
|
|
public void tearDown() throws Exception {
|
2017-02-20 20:16:56 +01:00
|
|
|
if (clientChannel != null) {
|
2017-04-07 02:53:04 +02:00
|
|
|
clientChannel.close().syncUninterruptibly();
|
2017-02-20 20:16:56 +01:00
|
|
|
clientChannel = null;
|
|
|
|
}
|
|
|
|
if (serverChannel != null) {
|
2017-04-07 02:53:04 +02:00
|
|
|
serverChannel.close().syncUninterruptibly();
|
2017-02-20 20:16:56 +01:00
|
|
|
serverChannel = null;
|
|
|
|
}
|
|
|
|
final Channel serverConnectedChannel = this.serverConnectedChannel;
|
2016-11-16 23:03:57 +01:00
|
|
|
if (serverConnectedChannel != null) {
|
2017-04-07 02:53:04 +02:00
|
|
|
serverConnectedChannel.close().syncUninterruptibly();
|
2017-02-20 20:16:56 +01:00
|
|
|
this.serverConnectedChannel = null;
|
2016-11-16 23:03:57 +01:00
|
|
|
}
|
2016-08-16 15:22:39 +02:00
|
|
|
}
|
|
|
|
|
2017-08-11 07:51:45 +02:00
|
|
|
private Http2StreamChannel newOutboundStream(ChannelHandler handler) {
|
|
|
|
return new Http2StreamChannelBootstrap(clientChannel).handler(handler).open().syncUninterruptibly().getNow();
|
|
|
|
}
|
|
|
|
|
2016-08-16 15:22:39 +02:00
|
|
|
@Test
|
2017-09-06 09:05:51 +02:00
|
|
|
public void multipleOutboundStreams() throws Exception {
|
2017-08-11 07:51:45 +02:00
|
|
|
Http2StreamChannel childChannel1 = newOutboundStream(new TestChannelInitializer());
|
2016-08-16 15:22:39 +02:00
|
|
|
assertTrue(childChannel1.isActive());
|
2017-08-11 07:51:45 +02:00
|
|
|
assertFalse(isStreamIdValid(childChannel1.stream().id()));
|
|
|
|
Http2StreamChannel childChannel2 = newOutboundStream(new TestChannelInitializer());
|
2016-08-16 15:22:39 +02:00
|
|
|
assertTrue(childChannel2.isActive());
|
2017-08-11 07:51:45 +02:00
|
|
|
assertFalse(isStreamIdValid(childChannel2.stream().id()));
|
2016-08-16 15:22:39 +02:00
|
|
|
|
|
|
|
Http2Headers headers1 = new DefaultHttp2Headers();
|
|
|
|
Http2Headers headers2 = new DefaultHttp2Headers();
|
|
|
|
// Test that streams can be made active (headers sent) in different order than the corresponding channels
|
|
|
|
// have been created.
|
|
|
|
childChannel2.writeAndFlush(new DefaultHttp2HeadersFrame(headers2));
|
|
|
|
childChannel1.writeAndFlush(new DefaultHttp2HeadersFrame(headers1));
|
|
|
|
|
|
|
|
Http2HeadersFrame headersFrame2 = serverLastInboundHandler.blockingReadInbound();
|
|
|
|
assertNotNull(headersFrame2);
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
assertEquals(3, headersFrame2.stream().id());
|
2016-08-16 15:22:39 +02:00
|
|
|
|
|
|
|
Http2HeadersFrame headersFrame1 = serverLastInboundHandler.blockingReadInbound();
|
|
|
|
assertNotNull(headersFrame1);
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
assertEquals(5, headersFrame1.stream().id());
|
2016-08-16 15:22:39 +02:00
|
|
|
|
2017-08-11 07:51:45 +02:00
|
|
|
assertEquals(3, childChannel2.stream().id());
|
|
|
|
assertEquals(5, childChannel1.stream().id());
|
2016-08-16 15:22:39 +02:00
|
|
|
|
|
|
|
childChannel1.close();
|
|
|
|
childChannel2.close();
|
2017-09-06 09:05:51 +02:00
|
|
|
|
|
|
|
serverLastInboundHandler.checkException();
|
2016-08-16 15:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2017-09-06 09:05:51 +02:00
|
|
|
public void createOutboundStream() throws Exception {
|
2017-08-11 07:51:45 +02:00
|
|
|
Channel childChannel = newOutboundStream(new TestChannelInitializer());
|
2016-08-16 15:22:39 +02:00
|
|
|
assertTrue(childChannel.isRegistered());
|
|
|
|
assertTrue(childChannel.isActive());
|
|
|
|
|
|
|
|
Http2Headers headers = new DefaultHttp2Headers();
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
2016-08-16 15:22:39 +02:00
|
|
|
ByteBuf data = Unpooled.buffer(100).writeZero(100);
|
2016-11-03 08:04:57 +01:00
|
|
|
childChannel.writeAndFlush(new DefaultHttp2DataFrame(data, true));
|
2016-08-16 15:22:39 +02:00
|
|
|
|
|
|
|
Http2HeadersFrame headersFrame = serverLastInboundHandler.blockingReadInbound();
|
|
|
|
assertNotNull(headersFrame);
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
assertEquals(3, headersFrame.stream().id());
|
2016-08-16 15:22:39 +02:00
|
|
|
assertEquals(headers, headersFrame.headers());
|
|
|
|
|
|
|
|
Http2DataFrame dataFrame = serverLastInboundHandler.blockingReadInbound();
|
|
|
|
assertNotNull(dataFrame);
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
assertEquals(3, dataFrame.stream().id());
|
2016-08-16 15:22:39 +02:00
|
|
|
assertEquals(data.resetReaderIndex(), dataFrame.content());
|
2017-08-11 07:51:45 +02:00
|
|
|
assertTrue(dataFrame.isEndStream());
|
2016-11-03 08:04:57 +01:00
|
|
|
dataFrame.release();
|
2016-08-16 15:22:39 +02:00
|
|
|
|
|
|
|
childChannel.close();
|
|
|
|
|
|
|
|
Http2ResetFrame rstFrame = serverLastInboundHandler.blockingReadInbound();
|
|
|
|
assertNotNull(rstFrame);
|
HTTP/2 Child Channel and FrameCodec Feature Parity.
Motivation:
This PR (unfortunately) does 4 things:
1) Add outbound flow control to the Http2MultiplexCodec:
The HTTP/2 child channel API should interact with HTTP/2 outbound/remote flow control. That is,
if a H2 stream used up all its flow control window, the corresponding child channel should be
marked unwritable and a writability-changed event should be fired. Similarly, a unwritable
child channel should be marked writable and a writability-event should be fired, once a
WINDOW_UPDATE frame has been received. The changes are (mostly) contained in ChannelOutboundBuffer,
AbstractHttp2StreamChannel and Http2MultiplexCodec.
2) Introduce a Http2Stream2 object, that is used instead of stream identifiers on stream frames. A
Http2Stream2 object allows an application to attach state to it, and so a application handler
no longer needs to maintain stream state (i.e. in a map(id -> state)) himself.
3) Remove stream state events, which are no longer necessary due to the introduction of Http2Stream2.
Also those stream state events have been found hard and complex to work with, when porting gRPC
to the Http2FrameCodec.
4) Add support for HTTP/2 frames that have not yet been implemented, like PING and SETTINGS. Also add
a Http2FrameCodecBuilder that exposes options from the Http2ConnectionHandler API that couldn't else
be used with the frame codec, like buffering outbound streams, window update ratio, frame logger, etc.
Modifications:
1) A child channel's writability and a H2 stream's outbound flow control window interact, as described
in the motivation. A channel handler is free to ignore the channel's writability, in which case the
parent channel is reponsible for buffering writes until a WINDOW_UPDATE is received.
The connection-level flow control window is ignored for now. That is, a child channel's writability
is only affected by the stream-level flow control window. So a child channel could be marked writable,
even though the connection-level flow control window is zero.
2) Modify Http2StreamFrame and the Http2FrameCodec to take a Http2Stream2 object intstead of a primitive
integer. Introduce a special Http2ChannelDuplexHandler that has newStream() and forEachActiveStream()
methods. It's recommended for a user to extend from this handler, to use those advanced features.
3) As explained in the documentation, a new inbound stream active can be detected by checking if the
Http2Stream2.managedState() of a Http2HeadersFrame is null. An outbound stream active can be detected
by adding a listener to the ChannelPromise of the write of the first Http2HeadersFrame. A stream
closed event can be listened to by adding a listener to the Http2Stream2.closeFuture().
4) Add a simple Http2FrameCodecBuilder and implement the missing frame types.
Result:
1) The Http2MultiplexCodec supports outbound flow control.
2) The Http2FrameCodec API makes it easy for a user to manage custom stream specific state and to create
new outbound streams.
3) The Http2FrameCodec API is much cleaner and easier to work with. Hacks like the ChannelCarryingHeadersFrame
are no longer necessary.
4) The Http2FrameCodec now also supports PING and SETTINGS frames. The Http2FrameCodecBuilder allows the Http2FrameCodec
to use some of the rich features of the Http2ConnectionHandler API.
2016-08-23 13:03:39 +02:00
|
|
|
assertEquals(3, rstFrame.stream().id());
|
2017-09-06 09:05:51 +02:00
|
|
|
|
|
|
|
serverLastInboundHandler.checkException();
|
2016-08-16 15:22:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Sharable
|
|
|
|
private static class SharableLastInboundHandler extends LastInboundHandler {
|
2017-09-06 09:05:51 +02:00
|
|
|
|
2016-08-16 15:22:39 +02:00
|
|
|
@Override
|
|
|
|
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
ctx.fireChannelActive();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
ctx.fireChannelInactive();
|
|
|
|
}
|
|
|
|
}
|
2017-11-08 16:29:14 +01:00
|
|
|
|
|
|
|
private static class SharableChannelHandler1 extends ChannelHandlerAdapter {
|
|
|
|
@Override
|
|
|
|
public boolean isSharable() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Sharable
|
|
|
|
private static class SharableChannelHandler2 extends ChannelHandlerAdapter {
|
|
|
|
}
|
|
|
|
|
|
|
|
private static class UnsharableChannelHandler extends ChannelHandlerAdapter {
|
|
|
|
@Override
|
|
|
|
public boolean isSharable() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testSharableCheck() {
|
|
|
|
assertNotNull(Http2MultiplexCodecBuilder.forServer(new SharableChannelHandler1()));
|
|
|
|
assertNotNull(Http2MultiplexCodecBuilder.forServer(new SharableChannelHandler2()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test(expected = IllegalArgumentException.class)
|
|
|
|
public void testUnsharableHandler() {
|
|
|
|
Http2MultiplexCodecBuilder.forServer(new UnsharableChannelHandler());
|
|
|
|
}
|
2016-08-16 15:22:39 +02:00
|
|
|
}
|