对 nginx 的 listen 指令中 HTTP 版本指定的一点研究
最近在公司用 nginx 代理 grpc 时发现,公司的 nginx 竟然在不启用 HTTP/2 的配置下也能支持对 grpc 的代理。 这引起了我的好奇心,因为 grpc 是基于 HTTP/2 的。难道是我记错了?于是找到相关人员询问了一下是如何做到的。 答曰:公司的 nginx 是经过改造的。OK,fine,不是我的问题。
不过,鉴于我对 HTTP/2 一直不太了解,我还是打算稍微研究一下:
- HTTP/2 可以在非 TLS/SSL 下工作吗?
- HTTP/2 兼容 HTTP/1.1 吗?
HTTP/2 可以在非 TLS/SSL 下工作吗?
nginx 说它是支持的:
The http2 parameter (1.9.5) configures the port to accept HTTP/2 connections. Normally, for this to work the ssl parameter should be specified as well, but nginx can also be configured to accept HTTP/2 connections without SSL.
nginx 配置如下:
1 2 3 4 5 6 7 |
|
尝试用 curl 直接请求一下:
1 2 |
|
看样子是不行的。
如果指定使用 HTTP/2 的版本呢:
1 2 |
|
嗯?什么情况这是?
看 curl 的文档发现,还有一个叫作 --http2-prior-knowledge
的选项:
1 2 |
|
终于可以了!
文档对这两个选项的说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
所以,--http2-prior-knowledge
是直接发 HTTP/2 请求,而推理出 --http2
是通过 HTTP/1.1 的 Upgrade 协议升级到 HTTP/2 (h2c)。
由于我们的 nginx 配置的是 HTTP/2。从实验结果看,不支持 HTTP/1.1。
综上,HTTP/2 可以在非 TLS/SSL 下工作。
HTTP/2 兼容 HTTP/1.1 吗?
所以 HTTP/2 真的兼容 HTTP/1.1 吗?
先说结论:是,也不是。 说“不是”是因为,从 curl 的请求结果来看,确实请求失败。 说“是”是因为:当请求一个配置了 TLS 的 HTTP/2 网站会发现,请求成功,因为借助了应用层协议协商(Application Layer Protocol Negotiation, ALPN),它来自于 TLS 的握手协议。
我尝试发了一个请求:
1 2 3 |
|
发现我博客确实是支持 HTTP/2 的。 然后我再尝试抓了一下包发现,curl 在不确定对方是否支持 HTTP/2 的情况下,在 TLS 的 Extension 里面提供了自己可以支持的协议:
然后服商端在紧接着的回包中,应答了双方应采用的协议:
所以,在进行正式的 HTTP 协议通信之前,客户端和服务端就已经协商好了要使用的 HTTP 协议版本。 严格地说,HTTP/2 确实不兼容 HTTP/1.1 协议(二进制层面)。
“二进制层面”是指数据在 TCP 连接上的流动。
总结
简单总结一下:
- HTTP/2 可以在 非 TLS/SSL 下工作;
- HTTP/2 不兼容 HTTP/1.1 (二进制层面);
- 如果配置了 TLS(证书),则 nginx 可以在 HTTP/2 模式下处理 HTTP/1.1 协议。