Netty啟動對HTTP/3的孵化器支援

banq發表於2020-12-12

經過一個多月的開發,我們終於可以釋出我們的第一個孵化器版本Netty / Incubator / Codec / Quic。
為了能夠在不“影響”網路核心的情況下開發令人興奮的新功能,我們決定在“孵化器”中開始開發此類功能。這意味著這些功能將在單獨的儲存庫中開發,並且只有在我們可以保證不再需要破壞API並考慮將這些產品準備就緒後,才會合併回核心netty儲存庫。這有望有助於澄清程式碼的期望和穩定性,因此人們應該充分意識到API可能會發生變化。除此之外,它還允許使用不同的釋出時間表。
您需要為您的平臺包括正確的依賴項。目前,我們僅支援macOS x86_64或linux x86_64。也就是說,這種情況將來可能會改變。
Maven / Linux的示例為:

<dependency>
    <groupId>io.netty.incubator</groupId>
    <artifactId>netty-incubator-codec-quic</artifactId>
    <version>0.0.1.Final</version>
    <classifier>linux-x86_64</classifier>
</dependency>

或者,如果您使用macOS:

<dependency>
    <groupId>io.netty.incubator</groupId>
    <artifactId>netty-incubator-codec-quic</artifactId>
    <version>0.0.1.Final</version>
    <classifier>osx-x86_64</classifier>
</dependency>

 
如何設定QUIC伺服器/客戶端?

// We just want to support HTTP 0.9 as application protocol
byte[] proto = new byte[] {
        0x08, 'h', 't', 't', 'p', '/', '0', '.', '9'
};

NioEventLoopGroup group = new NioEventLoopGroup(1);
ChannelHandler codec = new QuicServerCodecBuilder()
        .certificateChain("./src/test/resources/cert.crt")
        .privateKey("./src/test/resources/cert.key")
        .applicationProtocols(proto)
        .maxIdleTimeout(5000, TimeUnit.MILLISECONDS)
        // Configure some limits for the maximal number of streams (and the data) that we want to handle.
        .initialMaxData(10000000)
        .initialMaxStreamDataBidirectionalLocal(1000000)
        .initialMaxStreamDataBidirectionalRemote(1000000)
        .initialMaxStreamsBidirectional(100)
        .initialMaxStreamsUnidirectional(100)

        // Setup a token handler. In a production system you would want to implement and provide your
        // custom one.
        .tokenHandler(InsecureQuicTokenHandler.INSTANCE)
        // ChannelHandler that is added into QuicChannel pipeline.
        .handler(new ChannelInboundHandlerAdapter() {
            @Override
            public void channelActive(ChannelHandlerContext ctx) {
                QuicChannel channel = (QuicChannel) ctx.channel();
                // Create streams etc..
            }

            public void channelInactive(ChannelHandlerContext ctx) {
                ((QuicChannel) ctx.channel()).collectStats().addListener(f -> {
                    if (f.isSuccess()) {
                        LOGGER.info("Connection closed: {}", f.getNow());
                    }
                });
            }

            @Override
            public boolean isSharable() {
                return true;
            }
        })
        .streamHandler(new ChannelInitializer<QuicStreamChannel>() {
            @Override
            protected void initChannel(QuicStreamChannel ch)  {
                // Add a LineBasedFrameDecoder here as we just want to do some simple HTTP 0.9 handling.
                ch.pipeline().addLast(new LineBasedFrameDecoder(1024))
                        .addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelRead(ChannelHandlerContext ctx, Object msg) {
                        ByteBuf byteBuf = (ByteBuf) msg;
                        try {
                            if (byteBuf.toString(CharsetUtil.US_ASCII).trim().equals("GET /")) {
                                ByteBuf buffer = ctx.alloc().directBuffer();
                                buffer.writeCharSequence("Hello World!\r\n", CharsetUtil.US_ASCII);
                                // Write the buffer and shutdown the output by writing a FIN.
                                ctx.writeAndFlush(buffer).addListener(QuicStreamChannel.SHUTDOWN_OUTPUT);
                            }
                        } finally {
                            byteBuf.release();
                        }
                    }
                });
            }
        }).build();
try {
    Bootstrap bs = new Bootstrap();
    Channel channel = bs.group(group)
            .channel(NioDatagramChannel.class)
            .handler(codec)
            .bind(new InetSocketAddress(9999)).sync().channel();
    channel.closeFuture().sync();
} finally {
    group.shutdownGracefully();
}


QUIC客戶端:

// We just want to support HTTP 0.9 as application protocol
byte[] proto = new byte[] {
        0x08, 'h', 't', 't', 'p', '/', '0', '.', '9'
};

NioEventLoopGroup group = new NioEventLoopGroup(1);
try {
    ChannelHandler codec = new QuicClientCodecBuilder()
            .applicationProtocols(proto)
            .maxIdleTimeout(5000, TimeUnit.MILLISECONDS)
            .initialMaxData(10000000)
            // As we don't want to support remote initiated streams just setup the limit for
            // local initiated streams in this example.
            .initialMaxStreamDataBidirectionalLocal(1000000)
            .build();

    Bootstrap bs = new Bootstrap();
    Channel channel = bs.group(group)
            .channel(NioDatagramChannel.class)
            .handler(codec)
            .bind(0).sync().channel();

    QuicChannel quicChannel = QuicChannel.newBootstrap(channel)
            .streamHandler(new ChannelInboundHandlerAdapter() {
                @Override
                public void channelActive(ChannelHandlerContext ctx) {
                    // As we did not allow any remote initiated streams we will never see
                    // this method called. That said just let us keep it here to demonstrate
                    // that this handle would be called for each remote initiated stream.
                    ctx.close();
                }
            })
            .remoteAddress(new InetSocketAddress(NetUtil.LOCALHOST4, 9999))
            .connect()
            .get();

    QuicStreamChannel streamChannel = quicChannel.createStream(QuicStreamType.BIDIRECTIONAL,
            new ChannelInboundHandlerAdapter() {
                @Override
                public void channelRead(ChannelHandlerContext ctx, Object msg) {
                    ByteBuf byteBuf = (ByteBuf) msg;
                    System.err.println(byteBuf.toString(CharsetUtil.US_ASCII));
                    byteBuf.release();
                }

                @Override
                public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
                    if (evt == ChannelInputShutdownReadComplete.INSTANCE) {
                        // Close the connection once the remote peer did send the FIN
                        // for this stream.
                        ((QuicChannel) ctx.channel().parent()).close(true, 0,
                                ctx.alloc().directBuffer(16)
                                        .writeBytes(new byte[]{'k', 't', 'h', 'x', 'b', 'y', 'e'}));
                    }
                }
            }).sync().getNow();
    // Write the data and send the FIN. After this its not possible anymore to write any more data.
    streamChannel.writeAndFlush(Unpooled.copiedBuffer("GET /\r\n", CharsetUtil.US_ASCII))
            .addListener(QuicStreamChannel.SHUTDOWN_OUTPUT);

    // Wait for the stream channel and quic channel to be closed (this will happen after we
    // received the FIN).  After this is done we will close the underlying datagram channel.
    streamChannel.closeFuture().sync();
    quicChannel.closeFuture().sync();
    channel.close().sync();
} finally {
    group.shutdownGracefully();
}

專案本身位於GitHub上

相關文章