写点什么

用 Pipy 做个 HTTP 隧道

作者:Flomesh
  • 2023-01-16
    北京
  • 本文字数:1884 字

    阅读完需:约 6 分钟

用 Pipy 做个 HTTP 隧道

在现实世界中隧道经常见,是一种跨越通常无法跨越的地形或者边界的方法。在网络世界中,隧道技术是通过对数据进行封装,将私有网络数据和协议信息通过公网传输,在网络中使用不支持或者无法支持的协议来传输数据的。


HTTP 隧道(HTTP tunnel) 用于在被限制的网络连接(包括防火墙、NAT 和 ACL)以及其他限制的情况下在两台计算机之间建立网络链接。该隧道通常由位于 DMZ 中的代理服务器中介创建。

-- 来自 Wikipedia


简单来讲,HTTP 隧道是使用高层的协议(HTTP)来传输低层协议(TCP)的一种技术。


HTTP 隧道是 Pipy 在今年(2022)初的 0.20.0-31 版本中加入的支持,今天我们就用 Pipy 来写一个 HTTP 隧道。

方案


假设我们有两个设备之间使用基于 TCP 的私有协议通信,而防火墙只允许 HTTP 通信,同时服务端监听内部 IP 以及 8081 端口。这里我们使用 Pipy 模拟一个 TCP 服务端,收到请求后响应 Hi, TCP!(模拟私有协议):

pipy()
.listen('127.0.0.1:8081') .replaceData( () => new Data('Hi, TCP!\n') )
复制代码


接着我们需要在 DMZ 一侧的代理提供 HTTP 隧道功能。客户端与代理建立 HTTP 连接(使用 HTTP CONNECT 方法),连接成功后代理按照客户端的要求与目的服务器建立连接,将客户端和服务端“连接”起来。随后代理会将客户端发送的 TCP 流转发给服务端,并将服务端的响应发回客户端。


PipyJS 编码

编码的部分很简单,我们有个代理监听在 80 端口,接收到请求后检查请求头是否是 HTTP CONNECT,如果是,就从请求头的 path 中获取目的地服务器的地址和端口,并与目的服务器建立连接。如果不是 HTTP CONNECT,当做普通的 HTTP 请求进行处理,这里我们不做实现直接返回 404 Not Found!


pipy({  _isTunnel: false,  _target: null,})
//http tunnel .listen(80) .demuxHTTP().to($ => $ .handleMessageStart( msg => msg.head.method === 'CONNECT' && (_isTunnel = true) ) .branch( () => _isTunnel, ( $ => $.acceptHTTPTunnel( msg => ( _target = msg.head.path, new Message({ status: 200 }) ) ).to( $ => $.connect(() => _target) ) ), ( //common HTTP request $ => $.replaceMessage(new Message({ status: 404 }, 'Not Found!\n')) ) ) )
复制代码


这里我们了个很特别的 过滤器 acceptHTTPTunnelacceptHTTPTunnel 在服务端一侧实现了 HTTP 隧道(在客户端一侧有另一个 过滤器 connectHTTPTunnel ,后面我们讲一下使用),它会将 HTTP CONNECT 之后的 TCP 流转到子管道中(上面代码中 过滤器 to )。


与普通的 HTTP 代理相比,就只是多了个 acceptHTTPTunnel 过滤器而已。

测试

在主机 192.168.1.110 上运行我们的代理以及服务端,客户端 curl 运行在主机 192.168.1.11 上。


curl -p -x http://192.168.1.110:80 telnet://127.0.0.1:8081
复制代码


curl 使用参数 -x--proxy)指定代理服务器,参数 -p--proxytunnel)表示使用代理 HTTP 隧道。



总结

我们使用差不多 10 行的代码为 HTTP 代理增加了隧道的功能,本文仅是简单介绍了隧道的实现方法,然而隧道的作用不仅仅是在解决连通性的问题,基于高层 HTTP 隧道还可以提供底层协议无法提供功能,比如可以加入各种安全认证来提供安全性的提升。


在服务网格 osm-edge 中的出口网关就提供了 HTTP 隧道的功能,与 sidecar 之间通过 HTTP 隧道提供高层协议的治理功能。我们在介绍出口网关的时候,对其进行说明。

进阶

这次由于我们的客户端 curl 正好可以支持使用 HTTP 隧道,假如客户端不支持呢?那就要用到我们前面提到的 connectHTTPTunnel 了,这个过滤器在客户端一侧提供 HTTP 隧道功能:先发送 HTTP CONNECT 请求建立隧道,然后将 TCP 流使用该隧道进行发送,如下图。


有兴趣的小伙伴,可以尝试实现一下。成功实现的小伙伴可以给我们投稿,有好礼相送。



引用链接

[1] HTTP 隧道(HTTP tunnel): https://en.wikipedia.org/wiki/HTTP_tunnel

[2] 0.20.0-31: https://github.com/flomesh-io/pipy/releases/tag/0.22.0-31

[3] HTTP CONNECT: 

https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods

[4] 过滤器 acceptHTTPTunnel

https://flomesh.io/pipy/docs/en/reference/api/Configuration/acceptHTTPTunnel

[5] 过滤器 connectHTTPTunnel:

https://flomesh.io/pipy/docs/en/reference/api/Configuration/connectHTTPTunnel

[6] 过滤器 to

https://flomesh.io/pipy/docs/en/reference/api/Configuration/to

[7] osm-edge: https://flomesh.io/osm-edge

发布于: 刚刚阅读数: 4
用户头像

Flomesh

关注

微信订阅号:flomesh 2022-04-07 加入

一站式云原生应用流量管理供应商 官网:https://flomesh.io

评论

发布
暂无评论
用 Pipy 做个 HTTP 隧道_HTTP_Flomesh_InfoQ写作社区