简介

上一篇文章中,咱们提到了如何在netty中配置TLS,让他反对HTTP2。事实上TLS并不是https的一个必须要求,它只是倡议的规范。那么除了TLS之外,还须要如何设置能力让netty反对http2呢?一起来看看吧。

根本流程

netty反对http2有两种状况,第一种状况是应用tls,在这种状况下须要增加一个ProtocolNegotiationHandler来对握手之后的协定进行协商,在协商之后,须要决定到底应用哪一种协定。

上一篇文章,咱们曾经介绍TLS反对http2的细节了,这里不再赘述,感兴趣的敌人能够查看我之前的文章。

如果不应用tls,那么有两种状况,一种是间接应用http1.1了,咱们须要为http1.1增加一个ChannelInboundHandler即可。

另一种状况就是应用clear text从HTTP1.1降级到HTTP2。

HTTP/2 ClearText也叫做h2c,咱们看一个简略的降级申请,首先是客户端申请:

GET /index HTTP/1.1Host: server.flydean.comConnection: Upgrade, HTTP2-SettingsUpgrade: h2c HTTP2-Settings: (SETTINGS payload) 

而后是服务器端的响应,如果服务器端不反对降级,则返回:

HTTP/1.1 200 OK Content-length: 100Content-type: text/html(... HTTP/1.1 response ...)

如果服务器反对降级,则返回:

HTTP/1.1 101 Switching Protocols Connection: UpgradeUpgrade: h2c(... HTTP/2 response ...)

CleartextHttp2ServerUpgradeHandler

有了下面的根本流程,咱们只须要在netty中提供对应的handler类就能够解决netty对http2的反对了。

不过下面的降级流程看起来比较复杂,所以netty为咱们提供了一个封装好的类:CleartextHttp2ServerUpgradeHandler来实现h2c的性能。

这个类须要传入3个参数,别离是HttpServerCodec、HttpServerUpgradeHandler和ChannelHandler。

HttpServerCodec就是解决http server的编码类,个别咱们应用HttpServerCodec。

HttpServerUpgradeHandler是从http1.1降级到http2的解决类。

netty也提供了一个现成的类:HttpServerUpgradeHandler,来解决降级的编码。

HttpServerUpgradeHandler须要两个参数,一个是sourceCodec,也就是http原始的编码类HttpServerCodec,一个是用来返回UpgradeCodec的工厂类,返回netty自带的Http2ServerUpgradeCodec。

    public HttpServerUpgradeHandler(SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory) {        this(sourceCodec, upgradeCodecFactory, 0);    }

ChannelHandler是真正解决HTTP2的handler,咱们能够依据须要对这个handler进行自定义。

有了UpgradeHandler,将其退出ChannelPipeline即可。

Http2ConnectionHandler

不论是HttpServerUpgradeHandler,还是CleartextHttp2ServerUpgradeHandler,都须要传入一个真正可能解决http2的handler。这个handler就是Http2ConnectionHandler。

Http2ConnectionHandler是一个实现类,它曾经实现了解决各种inbound frame events的事件,而后将这些事件委托给 Http2FrameListener。

所以Http2ConnectionHandler须要跟Http2FrameListener配合应用。

这里要具体解说一下Http2FrameListener,它次要解决HTTP2 frame的各种事件。

先来看下http2FrameListener中提供的event trigger办法:

从上图能够看到,次要是各种frame的事件触发办法,其中http2中有这样几种frame:

  • DATA frame
  • HEADERS frame
  • PRIORITY frame
  • RST_STREAM frame
  • SETTINGS acknowledgment frame
  • SETTINGS frame
  • PING frame
  • PING acknowledgment
  • PUSH_PROMISE frame
  • GO_AWAY frame
  • WINDOW_UPDATE frame
  • Unknown Frame

这几种frame基本上列举了http2 frame中所有的类型。

咱们要做的就是自定义一个handler类,继承Http2ConnectionHandler,而后实现Http2FrameListener接口即可。

    public final class CustHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener

在应用clear text从HTTP1.1降级到HTTP2的过程中,咱们须要解决两个事件,第一个事件就是解决http1.1应用http头降级到http2,能够重写继承自Http2ConnectionHandler的userEventTriggered办法,通过判断event的类型是否是UpgradeEvent,来触发对应的Http2FrameListener接口中的办法,比方这里的onHeadersRead:

    /**     * 解决HTTP upgrade事件     */    @Override    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {        if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {            HttpServerUpgradeHandler.UpgradeEvent upgradeEvent =                    (HttpServerUpgradeHandler.UpgradeEvent) evt;            onHeadersRead(ctx, 1, upgradeToHttp2Headers(upgradeEvent.upgradeRequest()), 0 , true);        }        super.userEventTriggered(ctx, evt);    }

upgradeToHttp2Headers办法将传入的FullHttpRequest,转换成为Http2Headers:

    private static Http2Headers upgradeToHttp2Headers(FullHttpRequest request) {        CharSequence host = request.headers().get(HttpHeaderNames.HOST);        Http2Headers http2Headers = new DefaultHttp2Headers()                .method(HttpMethod.GET.asciiName())                .path(request.uri())                .scheme(HttpScheme.HTTP.name());        if (host != null) {            http2Headers.authority(host);        }        return http2Headers;    }

还有一个要实现的办法,就是sendResponse办法,将数据写回给客户端,回写须要蕴含headers和data两局部,如下所示:

    /**     * 发送响应数据到客户端     */    private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {        Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());        encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());        encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());    }

总结

到此,一个解决clear text从HTTP1.1降级到HTTP2的handler就做好了。加上之前解说的TLS扩大协定的反对,就形成了一个残缺的反对http2的netty服务器。

本文的例子能够参考:learn-netty4

本文已收录于 http://www.flydean.com/27-netty-http2/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!