diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java index b399346d6b..e16928da9e 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java @@ -337,6 +337,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http if (!connection().isServer()) { // Clients must send the preface string as the first bytes on the connection. ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); + ctx.fireUserEventTriggered(Http2ConnectionPrefaceWrittenEvent.INSTANCE); } // Both client and server must send their initial settings. diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionPrefaceWrittenEvent.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionPrefaceWrittenEvent.java new file mode 100644 index 0000000000..37a7c6b82f --- /dev/null +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionPrefaceWrittenEvent.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017 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.util.internal.UnstableApi; + +/** + * Signifies that the connection preface has been sent. + * The client sends the preface, and the server receives the preface. The client shouldn't write any data until this + * event has been processed. + */ +@UnstableApi +public final class Http2ConnectionPrefaceWrittenEvent { + static final Http2ConnectionPrefaceWrittenEvent INSTANCE = new Http2ConnectionPrefaceWrittenEvent(); + + private Http2ConnectionPrefaceWrittenEvent() { + } +} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java index 3f575d78bc..ddf9284e4a 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java @@ -21,6 +21,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; @@ -256,6 +257,7 @@ public class DataCompressionHttp2Test { } private void bootstrapEnv(int serverOutSize) throws Exception { + final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); serverOut = new ByteArrayOutputStream(serverOutSize); serverLatch = new CountDownLatch(1); sb = new ServerBootstrap(); @@ -336,6 +338,14 @@ public class DataCompressionHttp2Test { .gracefulShutdownTimeoutMillis(0) .codec(decoder, clientEncoder).build(); p.addLast(clientHandler); + p.addLast(new ChannelInboundHandlerAdapter() { + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof Http2ConnectionPrefaceWrittenEvent) { + prefaceWrittenLatch.countDown(); + ctx.pipeline().remove(this); + } + } + }); } }); @@ -345,6 +355,7 @@ public class DataCompressionHttp2Test { ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)); assertTrue(ccf.awaitUninterruptibly().isSuccess()); clientChannel = ccf.channel(); + assertTrue(prefaceWrittenLatch.await(5, SECONDS)); assertTrue(serverChannelLatch.await(5, SECONDS)); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java index 6e86f8884b..f96042ad71 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java @@ -25,10 +25,12 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalServerChannel; @@ -178,7 +180,7 @@ public class Http2ConnectionRoundtripTest { } }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue(latch.await(5, SECONDS)); } @Test @@ -660,6 +662,7 @@ public class Http2ConnectionRoundtripTest { private void bootstrapEnv(int dataCountDown, int settingsAckCount, int requestCountDown, int trailersCountDown, int goAwayCountDown) throws Exception { + final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); requestLatch = new CountDownLatch(requestCountDown); serverSettingsAckLatch = new CountDownLatch(settingsAckCount); dataLatch = new CountDownLatch(dataCountDown); @@ -702,6 +705,14 @@ public class Http2ConnectionRoundtripTest { .validateHeaders(false) .gracefulShutdownTimeoutMillis(0) .build()); + p.addLast(new ChannelInboundHandlerAdapter() { + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof Http2ConnectionPrefaceWrittenEvent) { + prefaceWrittenLatch.countDown(); + ctx.pipeline().remove(this); + } + } + }); } }); @@ -710,8 +721,9 @@ public class Http2ConnectionRoundtripTest { ChannelFuture ccf = cb.connect(serverChannel.localAddress()); assertTrue(ccf.awaitUninterruptibly().isSuccess()); clientChannel = ccf.channel(); + assertTrue(prefaceWrittenLatch.await(5, SECONDS)); http2Client = clientChannel.pipeline().get(Http2ConnectionHandler.class); - assertTrue(serverInitLatch.await(5, TimeUnit.SECONDS)); + assertTrue(serverInitLatch.await(5, SECONDS)); http2Server = serverHandlerRef.get(); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java index 60480bfa2d..8683a515b7 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java @@ -21,6 +21,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; @@ -499,6 +500,7 @@ public class HttpToHttp2ConnectionHandlerTest { } private void bootstrapEnv(int requestCountDown, int serverSettingsAckCount, int trailersCount) throws Exception { + final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); final CountDownLatch serverChannelLatch = new CountDownLatch(1); requestLatch = new CountDownLatch(requestCountDown); serverSettingsAckLatch = new CountDownLatch(serverSettingsAckCount); @@ -536,6 +538,14 @@ public class HttpToHttp2ConnectionHandlerTest { .gracefulShutdownTimeoutMillis(0) .build(); p.addLast(handler); + p.addLast(new ChannelInboundHandlerAdapter() { + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof Http2ConnectionPrefaceWrittenEvent) { + prefaceWrittenLatch.countDown(); + ctx.pipeline().remove(this); + } + } + }); } }); @@ -544,6 +554,7 @@ public class HttpToHttp2ConnectionHandlerTest { ChannelFuture ccf = cb.connect(serverChannel.localAddress()); assertTrue(ccf.awaitUninterruptibly().isSuccess()); clientChannel = ccf.channel(); + assertTrue(prefaceWrittenLatch.await(5, SECONDS)); assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, TimeUnit.SECONDS)); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java index 4d93716dd5..1b2d304879 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java @@ -22,6 +22,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; @@ -745,6 +746,7 @@ public class InboundHttp2ToHttpAdapterTest { private void boostrapEnv(int clientLatchCount, int clientLatchCount2, int serverLatchCount, int serverLatchCount2, int settingsLatchCount) throws InterruptedException { + final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); clientDelegator = null; serverDelegator = null; serverConnectedChannel = null; @@ -818,6 +820,14 @@ public class InboundHttp2ToHttpAdapterTest { } } }); + p.addLast(new ChannelInboundHandlerAdapter() { + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof Http2ConnectionPrefaceWrittenEvent) { + prefaceWrittenLatch.countDown(); + ctx.pipeline().remove(this); + } + } + }); } }); @@ -826,6 +836,7 @@ public class InboundHttp2ToHttpAdapterTest { ChannelFuture ccf = cb.connect(serverChannel.localAddress()); assertTrue(ccf.awaitUninterruptibly().isSuccess()); clientChannel = ccf.channel(); + assertTrue(prefaceWrittenLatch.await(5, SECONDS)); assertTrue(serverChannelLatch.await(5, SECONDS)); }