Lazy loaded image
技术分享
使用golang创建http2和h2c服务端
00 分钟
2024-2-25
2024-12-27
type
status
date
slug
summary
tags
category
icon
password

生成tls证书

注意:网上有说可以使用 golang.org/x/crypto/acme/autocert 去自动申请证书,但是这个是要在公网环境才行的,你使用了这个库,会提交一个申请,let's encrypt 会去访问你的应用程序来验证,但是在非公网环境是没法验证成功的,有的帖子说可以将自己的域名解析为 127.0.0.1,然后用 autocert 去申请自己域名的证书就可以,实测不行。
使用 openssl 生成证书的 shell 脚本。

服务端代码

验证

使用 curl 访问 8080 端口,这里需要带上 --insecure 参数跳过证书的验证。如果是使用浏览器访问,那么需要在浏览器中导入前面使用脚本生成的 rootCA.crtALPN, offering h2ALPN, offering http/1.1 ALPN是一种协议扩展机制,用于在 TLS 握手期间协商客户端和服务器之间要使用的应用层协议。在HTTP/2中,h2是用于指示使用HTTP/2协议的标识符。我猜测 h2 的优先级高于 http/1.1,所以这里在协商以后使用的是 h2。
使用 curl 访问 8090 端口。这里的表现和 8080 端口是一致的。
使用 curl 测试 9000 端口 的 h2c 服务端,因为 9000 端口的服务端没有证书,所以这里不能使用 https 去请求,同时也就不需要 --insecure
在 h2c 中,设置了 http2.Server{},这是一种非加密的 http2 服务端,在 curl 中,我们可以手动添加参数 --http2-prior-knowledge 指定使用 http2 来通信。

这里出了点问题,h2c server 没有返回任何数据?按照正常来说,server 应该要返回一个 “Protocol: HTTP/2.0” 才对,看这里 curl 的信息,确实使用了 http2 无疑,在 server 端也打印了一个 “Protocol: HTTP/2.0” 的信息,但是最终 client 没有接收到被写入到 http.ResponseWriter 的数据。
这里 server 端打印出来了 HTTP/2.0,说明我们是用的 HTTP/2.0 的客户端发送的请求,但是 golang 的 http.Server 在非 TLS 时,默认是支持 HTTP/1.1 的,因此服务端给的响应就是 HTTP/1.1 协议的,客户端接收到以后对不上了。这里如果我们使用 golang 的 client 去发送请求,就会是下面的情况。
HTTP/2.0 client
使用 H2 Client 发送请求

修改 H2C 服务端代码

由于 http.Server 默认不支持 H2,所以我们使用 http2 库来实现一个 H2 服务端,将第三个 server 的代码修改如下。此时再使用 curl 或者 go 的 client 去调用,就可以使用非加密的 H2 协议了。
使用 go client
使用 curl

结语

golang 的 net/http 默认支持 H2,但是需要配置了 TLS 加密才行,如果使用 H2C 服务端,那么就不能使用默认的 http.Server。另外,我抽了点时间看了下 http.Server 的源码,http.Server 在升级协议为 H2 时,并不是调的 http2 的库,而是在 http 库里面写了一个叫做 http2server 的结构体。

参考文档

https://github.com/thrawn01/h2c-golang-example/blob/master/README.md
https://colobu.com/2018/09/06/Go-http2-%E5%92%8C-h2c/
https://zhuanlan.zhihu.com/p/531003047
https://posener.github.io/http2/
https://studygolang.com/articles/10102
上一篇
使用k6压测web服务器
下一篇
ovirt-engine开发