简介

上一篇文章中,我们提到了如何在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.1
Host: server.flydean.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: (SETTINGS payload)

然后是服务器端的响应,如果服务器端不支持升级,则返回:


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

如果服务器支持升级,则返回:

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: 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/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

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

netty系列之:使用netty实现支持http2的服务器的更多相关文章

  1. netty系列之:轻轻松松搭个支持中文的服务器

    目录 简介 netty的HTTP支持 netty中使用HTTP的原理 100 (Continue) Status 为netty搭建HTTP服务器 总结 简介 之前讲了那么多关于netty的文章,都是讲 ...

  2. netty系列之:使用netty搭建websocket服务器

    目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...

  3. netty系列之:使用netty搭建websocket客户端

    目录 简介 浏览器客户端 netty对websocket客户端的支持 WebSocketClientHandshaker WebSocketClientCompressionHandler netty ...

  4. netty系列之:在netty中处理CORS

    目录 简介 服务端的CORS配置 CorsConfigBuilder CorsHandler netty对cors的支持 总结 简介 CORS的全称是跨域资源共享,他是一个基于HTTP-header检 ...

  5. netty系列之:请netty再爱UDT一次

    目录 简介 netty对UDT的支持 搭建一个支持UDT的netty服务 异常来袭 TypeUDT和KindUDT 构建ChannelFactory SelectorProviderUDT 使用UDT ...

  6. netty系列之:在netty中使用native传输协议

    目录 简介 native传输协议的依赖 netty本地传输协议的使用 总结 简介 对于IO来说,除了传统的block IO,使用最多的就是NIO了,通常我们在netty程序中最常用到的就是NIO,比如 ...

  7. netty系列之: 在netty中使用 tls 协议请求 DNS 服务器

    目录 简介 支持DoT的DNS服务器 搭建支持DoT的netty客户端 TLS的客户端请求 总结 简介 在前面的文章中我们讲过了如何在netty中构造客户端分别使用tcp和udp协议向DNS服务器请求 ...

  8. netty系列之:在netty中使用protobuf协议

    目录 简介 定义protobuf 定义handler 设置ChannelPipeline 构建client和server端并运行 总结 简介 netty中有很多适配不同协议的编码工具,对于流行的goo ...

  9. netty系列之:搭建客户端使用http1.1的方式连接http2服务器

    目录 简介 使用http1.1的方式处理http2 处理TLS连接 处理h2c消息 发送消息 总结 简介 对于http2协议来说,它的底层跟http1.1是完全不同的,但是为了兼容http1.1协议, ...

随机推荐

  1. kubectl工具的windows\linux安装方法

    kubectl 安装 下载kubectl二进制文件 curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.10.0 ...

  2. HDU - 3790 最短路径问题 (dijkstra算法)

    HDU - 3790 最短路径问题 Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费 ...

  3. Java面向对象系列(8)- Super详解

    场景一 场景二 场景三 场景四 注意:调用父类的构造器,super()必须在子类构造器的第一行 场景五 场景六 super注意点 super调用父类得构造方法(即构造器),必须在构造方法得第一个 su ...

  4. Jmeter系类(31) - JSR223(1) | 控件介绍

    JSR233 介绍 JSR223控件执行JSR223脚本代码用于创建/更新所需的某些变量 JSR223可以使用其内置的变量,有助于精简脚本,提高开发测试的效率 由于JSR223脚本编译方式基本相同,J ...

  5. Groovy系列(5)- Groovy IO操作

    IO操作 Groovy为I/O操作提供了许多帮助方法,虽然你可以在Groovy中用标准Java代码来实现I/O操作,不过Groovy提供了大量的方便的方式来操作File.Stream.Reader等等 ...

  6. Linux系列(21) - 光盘、U盘挂载

    挂载光盘 mount命令.umount命令 step-1 建立挂载点 原理:相当于建立盘符,建个目录读取光盘内容 命令:[root@localhost ~]# mkdir /mnt/cdrom/ 备注 ...

  7. redis 设置密码 laravel框架配置redis

    * 参考资料 redis文档             http://www.redis.cn/documentation.html,  http://redisdoc.com/index.html r ...

  8. springBoot 基础入门

    来处:是spring项目中的一个子项目 优点  (被称为搭建项目的脚手架)         减少一切xml配置,做到开箱即用,快速上手,专注于业务而非配置     从创建项目上: -- 快速创建独立运 ...

  9. django 如何在HMTL中使用媒体media_url

    django 如何在HMTL中使用媒体media_url中指定的路径 第一种: 一. setting.py里,一般图片或者文件上传路径都是是以下设置, MEDIA_URL = '/media/'    ...

  10. 『GoLang』数组与切片

    数组 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列(这是一种同构的数据结构):这种类型可以是任意的原始类型例如整型.字符串或者自定义类型. 数组长度必须是一个常量表达式,并且必须是一个非负 ...