❗ 本文最后更新于 3302 天前,文中所描述的信息可能已发生改变,请谨慎使用。
HTTP/2 引入了二进制分帧层,将 HTTP/1.1 中的请求和响应拆成颗粒度更细的帧(frame),从而实现了优先级、流量控制和 Server Push 等功能;HTTP/2 在单条 TCP 连接上可以打开多个流,从而实现了多路复用;HTTP/2 使用静态字典、动态字典以及哈夫曼编码,对请求 / 响应头部进行压缩。总之,HTTP/2 从协议层面解决了 HTTP/1.1 的诸多问题。
在我之前写的《搭建 ngrok 服务实现内网穿透》这篇文章里,我介绍了如何通过 ngrok 让内网 WEB 在其它网络环境中能够被访问。本文要实现的服务与 ngrok 类似,我把它称之为 Pangolin,中文是穿山甲的意思(名字来自于同事的类似项目,在此表示感谢)。Pangolin 客户端和服务端之间的报文转发,是用 node-http2 这个 Node.js 模块提供的 HTTP/2 服务来实现的。
Pangolin 的需求来自于本博客用户评论(via)。实际上,能实现类似功能的软件很多,有使用私有协议进行转发的,有使用 WebSocket 进行转发的。而我认为 HTTP/2 应该是个不错的选择,打算试一下。最终我花了一个小时实现了一个初步能用的版本,除开 node-http2,全部代码不超过 200 行。代码我放在了 github 上,有兴趣的同学可以玩一下。
下面简单介绍它的原理,我画了一张草图:
最左侧是最终用来访问服务的浏览器,它可能位于公网,也可能位于其它内网;最右侧是实际提供 WEB 服务的 HTTP Server,它位于内网。显然,左侧浏览器没办法直接访问右侧 WEB 服务,只能借助公网节点作为桥梁。中间的 Pangolin 服务端运行在公网节点上;Pangolin 客户端运行在与 WEB 服务同台机器或者同一网段内。
浏览器发起请求后,请求报文沿着绿色箭头从左到右流动,每个节点都相当于左侧相邻节点的 HTTP Server。唯一的问题出现在 Pangolin 服务端和客户端之间:客户端位于内网,正常情况下 Pangolin 服务端连不上客户端提供的 HTTP Server。
这个问题我用了一个取巧的办法解决:由于 Pangolin 服务端有公网 IP,可以开启 TCP Server,客户端可以通过 IP 和约定的端口与服务端建立 TCP 连接。那么只要稍微改造一下 node-http2 的代码,使它可以基于指定 socket 创建 HTTP/2 Server、发送 HTTP/2 Request,就可以打通所有节点了。这个问题解决后,左侧的请求可以顺利到达右侧,响应数据也可以沿着之前的连接逐级返回。
Pangolin 服务端和客户端内部之间使用 HTTP/2,可以大幅提高性能,降低程序复杂性;对外使用 HTTP/1.1,保证了与已有系统的兼容性。
为了实现内网穿透,Pangolin 需要做以下准备工作:
- Pangolin 服务端开启 TCP Server;
- Pangolin 客户端启动 TCP Client,与 Pangolin 服务端连接,得到 socket 长连接;
- Pangolin 客户端基于这个 socket 连接,开启 HTTP/2 Server;
- Pangolin 服务端开启 HTTP/1.1 Server,等待浏览器来访问;
实际的数据传输流程如下:
- 浏览器向 Pangolin 服务端发起请求(HTTP/1.1);
- Pangolin 服务端基于已有 socket,向 Pangolin 客户端发起请求(HTTP/2);
- Pangolin 客户端向内网 WEB 服务发起请求,得到响应(HTTP/1.1);
- Pangolin 客户端基于已有 socket,将响应返回给 Pangolin 服务端(HTTP/2);
- Pangolin 服务端将响应返回给浏览器(HTTP/1.1);
由于 Pangolin 客户端采用了 HTTP 转发,而不是 TCP 隧道,所以可以轻松实现 ngrok 那样的管理界面,用来查看完整的 Request/Response 信息。目前我还只是简单地打印了请求日志。
HTTP/2 协议本身并没有规定它必须基于 TLS 部署,没有安全层的 HTTP/2 被称之为 h2c(HTTP/2 Cleartext)。目前来看,所有浏览器都不打算支持 h2c,但如果一个系统的某些环节对安全没有那么高的要求,或者已经通过了其它方案确保了安全,部署 h2c 也是一个非常好的选择。现在很多 HTTP/2 工具和类库同时支持 h2 和 h2c,node-http2 也是如此。
实际上,我为了测试方便,在实现 pangolin 时也选择了 h2c。通过 Wireshark 抓包可以看出,HTTP/2 层之下直接就是 TCP 层:
好了,本文就讨论这么多内容,大家有什么问题或想法欢迎给我留言。
本文链接:https://imququ.com/post/tunnel-to-localhost-base-on-http2.html,参与评论 »
--EOF--
发表于 2015-11-23 14:10:41,并被添加「HTTP2」标签。查看本文 Markdown 版本 »
专题「HTTP/2 相关」的其他文章 »
- 谈谈 Nginx 的 HTTP/2 POST Bug (Aug 20, 2016)
- 为什么我们应该尽快支持 ALPN? (May 18, 2016)
- 谈谈 HTTP/2 的协议协商机制 (Apr 14, 2016)
- 使用 nghttp2 调试 HTTP/2 流量 (Mar 07, 2016)
- 从启用 HTTP/2 导致网站无法访问说起 (Jan 17, 2016)
- HTTP/2:新的机遇与挑战 (Nov 22, 2015)
- HTTP/2 头部压缩技术介绍 (Oct 25, 2015)
- 使用 Wireshark 调试 HTTP/2 流量 (Oct 24, 2015)
- H2O 中的 Cache-Aware Server Push 简介 (Oct 21, 2015)
- HTTP/2 资料汇总 (Aug 31, 2015)
Comments
Waline 评论加载中...