写点什么

FunProxy - 使用 Rust 构建跨平台全链路测试抓包代理工具

  • 2025-05-08
    广东
  • 本文字数:6683 字

    阅读完需:约 22 分钟

作者:vivo 互联网大前端团队- Song Jiachao


在软件开发过程中,软件测试对于保障软件质量和用户满意度起着关键作用。为最大程度上提升软件品质,我们积极开展全链路测试实践,打造了用 Rust 语言开发的自研一站式抓包代理工具 FunProxy,基于其跨平台、高性能、易于扩展、安全性高等特性,让全链路抓包和环境代理如丝绸般丝滑。


一、背景介绍

1.1 什么是全链路测试


全链路测试就是“验证整个软件系统在不同组件、服务和模块之间协同工作时的性能、功能和稳定性”。在这里我们举一个非常简单的例子。


比如用户在某商城购买商品。



我们是先打开商城,接着浏览商品,加入购物车,然后提交订单,支付,等待收货,最后完成。在整个购买流程中,我们其实并不是一个功能模块就完成全部步骤,而是调用了很多系统模块。


比如:

全链路测试就是验证一个流程中所有涉及的系统,协同工作时的性能、功能和稳定性。


1.2 全链路测试的现状和痛点


通过上面的购物流程我们可以梳理出全链路测试的现状和痛点:


1. 系统数量众多


全链路测试涉及到多个系统、服务和模块的协同工作,系统的多样性增加了测试的复杂性。


2. 各系统环境配置不一致


各个系统的环境配置可能完全不一样,导致各个系统对接相当麻烦。



就拿我们刚刚所举出用户购买商品的例子。涉及到商品系统、购物车系统、订单系统、支付系统、和物流系统。因为历史或者技术方案的原因,他们选择的环境管理方式各不相同。

  • 其中商品和购物车系统可能是由团队 A 负责,他们团队通过 hosts 方式来管理环境。

  • 订单和支付系统,由团队 B 负责,通过自定义请求头的方式,来进行环境配置。

  • 物流系统由团队 C 负责,通过域名的方式来配置环境。


这只是精简版的购物流程,其真实情况可能远比我们的例子更加复杂。这时候如果想要完整的测试一条购物全流程,环境配置还是相当复杂的。


当前测试流程


为了能够测试所有的流程:

  • 要安装一个抓包软件

  • 安装抓包软件的证书

  • 配置各种抓包的规则

  • 配置抓包的系统代理

  • 配置 hosts

  • 配置终端(安装证书、设置手机代理)


这一套流程下来其实需要配置的非常复杂,并且不能共享,每个人在自己的电脑和手机上都需要重新配置一遍。



二、全链路测试的愿景及目标


因为上述背景,我们对 vivo 的全链路测试提出以下的愿景和目标。

我们的愿景是:让 vivo 的全链路抓包和环境代理,如丝绸般丝滑。

我们的目标是:构建一款跨平台、高性能、易于扩展、安全性高的全链路测试抓包代理工具。



2.1 技术目标详解


跨平台

能够支持几乎所有主流平台,包括但不限于 Windows/macOS/Linux/Android/iOS,为什么要兼容这么多平台,因为我们的业务在这些平台上面几乎都有涉及。


高性能

面对超高并发量的接口请求,能够轻松应对,确保系统在高负载下的稳定运行。


易拓展

提供灵活的架构和工具,支持官方和用户根据个人需要对工具进行拓展。


安全性高

通过全面的安全测试,确保产品在各个层级的安全性,包括数据传输、存储和访问控制,防止潜在的安全威胁和数据泄露。



三、技术选型

3.1 为何选择 Rust


我们首选了 Rust 语言作为我们的基础语言,主要原因如下:

  • Rust 是系统级别的通用编程语言,上至 web 下至 OS 统统能轻松驾驭,并且能够保证内存安全和并发安全;

  • Rust 有丰富的工具链,比如 rustup,cargo 等等,开发体验非常棒;

  • Rust 有非常庞大和活跃的社区,社区贡献了非常多好用的各种库。


并且最近使用 Rust 构建应用的公司越来越多,比如我们公司的 Blue OS ,就是行业首个系统框架由 Rust 语言编写的操作系统,这也是我们坚定不移的选择 Rust 的重要原因之一。



3.2 为何选择 Tauri


选择了 Rust ,我们顺其自然的选择了 Rust 多端开发框架 Tauri。Tauri 是一个基于 Rust 构建的跨平台应用框架。


主要有以下几个优势:

  1. 独立的前端工程:Tauri 支持任何前端框架,所以你不需要改变你的技术栈;

  2. 最小化体积:使用操作系统本地的网页渲染器,Tauri 应用的体积可以达到最小 600KB;

  3. 跨平台:同一套代码可以编译运行到几乎所有的 OS 系统中;

  4. 高安全性:Tauri 团队的首要目标,推动 Tauri 的首要任务和最大的创新。

  5. 进程间通讯:用 JavaScript 编写前端,用 Rust 编写应用程序逻辑,并使用 Swift 和 Kotlin 在系统中深入集成;

  6. 由 Rust 驱动:以性能和安全性为中心,Rust 是下一代应用程序的语言。


使用 Tauri 框架,让我们不仅能获得 Rust 的安全性高效性,还能享受 Web 开发的熟悉感和灵活性。



四、方案介绍


通过刚才的技术方案,我们打造出了 FunProxy 一站式代理工具。FunProxy 是一款用 Rust 开发的纯自研的一站式抓包 & 代理工具,全平台支持,天生更安全,天生更流畅!!!

主打一个方便、快捷和无边界。



4.1 优势及亮点


FunProxy 主要的优势及亮点如下:

  • 全功能抓包能力

  • 全平台独立应用支持

  • 云端 hosts

  • 云端 Rules

  • 协同抓包

  • 极致性能



4.1.1 全功能抓包能力


协议

支持 HTTP/1.x 和 HTTP/2 协议、WebSocket、TLS 1.1-1.3 的加密协议


工具

支持重写、断点、数据对比、模拟超时、全局搜索、筛选、对比


文件

支持导入导出会话、支持 Fun 格式的文件,HAR 格式文件、Charles 格式文件



4.1.2 全平台独立应用支持


我们支持几乎所有的主流操作系统,例如 Windows/macOS/Linux/Android/iOS/还有网页版本,并且多操作系统可以相互配合,FunProxy 让代理打破边界。



4.1.3 云端 hosts


hosts 维护是一个大的痛点,因为各个系统的 hosts 会经常变化,导致有些同学的 hosts 因为没有及时同步变更,而出现问题。所以我们提供了云端 hosts 功能。云端 hosts 可以让项目中的 hosts 由一人设置,人人共享。保证 hosts 能够实时同步到 FunProxy 的各个用户。


云端 hosts 支持静态远程两种 hosts。


静态 hosts:系统中的 hosts 文件格式一样,一一对应。


远程 hosts:可以提供一个链接,我们会远程去拉取这个 hosts,在 FunProxy 运行的时候再去加载这个 Hosts。


顺便说一句:FunProxy 所有的 hosts 都是虚拟 hosts,并不会修改系统 hosts,保证系统 hosts 的干净



4.1.4 云端规则


同云端 hosts 一样,规则也可以配置到云端让所有人共享。



所有的规则都有一些公共的配置。比如规则名称匹配模式匹配链接

我们的匹配模式支持通配符正则表达式。几乎可以适配所有的链接。

同时我们也提供了检测工具,帮助大家检测链接是否被匹配上。



目前规则主要有三个分类:分别是远程映射,本地映射,和解密。


远程映射在远程映射这个功能上,我们可以配置远程主机的相关信息,这样就可以将需要转发的链接转发到对应的服务器上面。

本地映射在本地映射模式中,支持直接修改本地映射的内容,同时我们提供了强大的代码编辑器功能,帮助大家在编写本地映射内容的时候, 能够有比较好的代码提示。

解密功能这个功能对于很多加密传输网站测试尤为重要。因为每个网站的加解密方式可能不一样,所有我们提供了运行时函数。用户可以自己编写 JavaScript 或者 Python 运行函数,动态的根据传入的参数进行对应的加解密计算。

动态函数解析让 FunProxy 几乎能解所有的密文,这样我们在查看接口的时候就非常的方便,不用再去解密工具中查看。

同时也请大家放心,我们的解密工具有着非常高的安全性,保证只有项目管理员授权的人才能查看,大家也不用担心自己的解密方法被别人盗取。



4.1.5 协同抓包


通过 FunProxy 左下角提供的分享功能,可以让当前抓包的所有数据实时同步给所有拥有这个链接的人。所有的操作都能同步到所有端,无需下载,让大家更加方便的进行数据包的共享。



4.1.6 极致性能


我们对比了市场上主流的抓包软件,无论从安装包大小、启动时间、安装空间、使用内存来说,FunProxy 都有非常大的优势。



4.2 技术架构


FunProxy 是基于 Tauri 框架进行跨平台的开发,我们封装了 fun-core 和 fun-mitm 两个核心库。

fun-core 主要是所有端的公共能力,比如配置文件,存储等。

fun-mitm 主要负责所有端的 man in the middle 也就是中间人的功能,提供抓包的核心能力。

tauri-plugin-funproxy 因为有些端的功能可能需要调用系统特定的能力,在移动端我们封装了 tauri-plugin-funproxy 来调用移动端的特定能力,比如 VPN。

其他在各个桌面端我们封装了各个系统的核心 crates 来调用桌面端的特定能力。其他能力则是通过 Rust 直接调用。

为了提供云端能力,我们使用 Go 语言搭建了一个服务。包括:项目管理、用户管理、升级管理、健康检查、云端 Host、云端规则、云端工具等等功能。服务只是一个增值功能,所有的 FunProxy 都可以离线运行。

同时为了保证各端 APP 核心能力的正常,使用了 playwright 搭建了自动化测试能力。重点测试的功能有:登录、代理、抓包、升级、权限、兼容性、性能等。

最后通过 vitepress 搭建 FunProxy 的文档功能。方便用户使用和查看。包括:介绍、使用说明、常见问题、更新日志、API、贡献指南、问题反馈等。



五、核心实现

5.1 MITM


这是 FunProxy 的 MITM(Man-in-the-Middle,中间人)方案。所谓的中间人,就是在服务端和客户端之间加入一个节点,这个节点负责接收客户端发送的内容,通过自己的规则然后再转发到服务端。我们这里用 HTTP 和 HTTPS 举例:



HTTP 中间人方案:

客户端通过发送 HTTP 内容到 FunProxy,FunProxy 拿到 HTTP 内容后,在转发到服务器中。服务器处理完成后再将信息发送给 FunProxy,FunProxy 在转发给客户端。这就完成了一个请求的中间人接收和转发方案。


由于 HTTP 都是明文传输,所以比较简单。HTTPS 就比较复杂了,涉及到 SSL 证书的认证,具体的大家可以看图上的流程。


了解完了中间人的过程。下面我们根据刚才分析的过程,可以设计出 Fun-MITM 的模块需要包含哪些模块。


CA 模块:负责根证书的生成和加载,以及各个网站证书的认证

Client:这个 Client 就是模拟真实用户发送请求的那个客户端

Server:这个 Server 就是拦截用户请求的那个服务端

http_handler:更改请求和响应的内容

socket_handler:处理 socket 请求



let mitm = Proxy::builder()    .with_addr(addr)    .with_client(client)    .with_ca(ca)    .with_http_handler(custom_handler.clone())    .with_websocket_handler(custom_handler.clone())    .with_graceful_shutdown(async {        close_rx.await.unwrap_or_default();    })    .build();
复制代码


pub struct WantsHandlers<C, CA, H, W, F> {  al: AddrOrListener,  client: Client<C, Body>,  ca: CA,  http_handler: H,  websocket_handler: W,  websocket_connector: Option<Connector>,  server: Option<Builder<TokioExecutor>>,  graceful_shutdown: F,}
impl<C, CA, H, W, F> ProxyBuilder<WantsHandlers<C, CA, H, W, F>> { /// Set the HTTP handler. pub fn with_http_handler<H2: HttpHandler>( self, http_handler: H2, ) -> ProxyBuilder<WantsHandlers<C, CA, H2, W, F>> { }
/// Set the WebSocket handler. pub fn with_websocket_handler<W2: WebSocketHandler>( self, websocket_handler: W2, ) -> ProxyBuilder<WantsHandlers<C, CA, H, W2, F>> { }
/// Set the connector to use when connecting to WebSocket servers. pub fn with_websocket_connector(self, connector: Connector) -> Self { }
/// Set a custom server builder to use for the proxy server. pub fn with_server(self, server: Builder<TokioExecutor>) -> Self {
}
/// Set a future that when ready will gracefully shutdown the proxy server. pub fn with_graceful_shutdown<F2: Future<Output = ()> + Send + 'static>( self, graceful_shutdown: F2, ) -> ProxyBuilder<WantsHandlers<C, CA, H, W, F2>> { }
/// Build the proxy. pub fn build(self) -> Proxy<C, CA, H, W, F> { }}
复制代码


5.2 虚拟 hosts


在中间人方案中如何实现虚拟 Hosts 能力?我们在定义 client 的时候,可以定义一个 resolver。这个 resolver 在每次 hosts 解析的时候都会调用。

首先他会去云端获取当前 hosts 的配置的 IP 是哪个,如果有的话,直接返回。如果没有配置,那么调用系统的 loopup_ip 函数来直接解析 hosts。最后将解析好的 hosts 返回。

在定义 HttpConnector 的时候,使用 new_with_resolver 加载这个 dns-resolver。这就完成了一个简单的自定义 dns-resolver。



let resolver = tower::service_fn(move |name: Name| async move {    let ip_option = host::get_ip(name.as_str());    match ip_option {        Some(ip) => {            let ip: IpAddr = ip.parse().unwrap();            host::add(name.as_str(), ip.to_string().as_str());            Ok::<_, Infallible>(iter::once(SocketAddr::from((ip, 80))))        }        None => {            let sys_resolver = Resolver::from_system_conf().unwrap();            let future = spawn_blocking(move || {                let lookup_ip = sys_resolver.lookup_ip(name.as_str()).unwrap();                if let Some(ip) = lookup_ip.iter().next() {                    host::add(name.as_str(), ip.to_string().as_str());                    Ok(SocketAddr::from((ip, 80)))                } else {                    Err("No IP address found")                }            });
let addrs = future.await.unwrap().unwrap(); Ok::<_, Infallible>(iter::once(addrs)) } }});
let mut http_connector = HttpConnector::new_with_resolver(resolver);
复制代码


5.3 自动安装证书


在使用 FunProxy 中可以自动安装并设置证书为信任。具体的是在 macOS 中调用 security add-trusted-cert。在 Windows 中主要调用 certutil 方法具体的命令行和 rust 代码如图片所示:



5.4 流量拦截


接下来给大家分享一下我们是怎么拦截流量的。首先是 PC 系统,比如 Windows/macOS/Linux 这些系统上,APP 授权后可以获取很高的权限,所以我们直接调用系统提供的接口能力。



比如在 macOS 上面就是通过 networksetup 这个命令行工具实现对系统代理的设置,在 Windows 上面可以通过 ProxyServer 设置。




而在移动端 APP 上面,一般 APP 的权限特别低,APP 没有直接设置系统代理的能力,所以我们采用的是通过 VPN 的方式来实现流量的拦截与转发。

我们通过 VpnService 来创建 VPN 服务。然后调用 VpnService 的各种能力。

这里我们用了一个安卓 Vpn 的 setHttpProxy 方法,就可以将所有的流量主动打到我们的 fun-mitm 服务器上面。fun-mitm 在通过自己的规则对所有的流量进行处理,包括 DNS 解析或者请求响应的修改等等。



5.5 调用系统能力


我们以 macOS 应用沉浸式头部为例给大家做个分享。在 macOS 应用设计中,其实更加通用性的设计是沉浸式的头部,而 Tauri 生成的应用,默认都会有一个头部,如屏幕上黄色所示,这个头部其实没多少作用。接下来我将用这个例子,给大家分享一下在 Rust 中如何通过调用系统的原生的能力,来去掉这个头部。



  1. 我们通过 cargo new macos --lib 创建一个 lib 库。在 lib 库中新建一个 window.swift 文件,主要使用 window.titlebarAppearsTransparent 方法来设置透明度。

  2. 接着在 lib 中通过 swift_rs 中的 swift 宏来调用这个函数,并将这个函数设置为 pub 给其他方法调用。

  3. 给这个 lib 添加 build.rs 来编译这个库。可以指定 macOS 的版本等其他内容。最后我们在自己的项目中就可以直接引入这个 lib 库。直接调用里面的 set_titlebar_style 方法。从而通过 swift_rs 调用 macOS 中的原生能力。



5.6 协同抓包


协同抓包能够让一端的抓包数据,通过链接分享的方式,实时同步给其他所有拥有这个链接的人。

主要核心是我们的 fun-core 这个库。通过 fun-core 这个库启动的时候我们会启动两个服务,一个是 api,负责接口服务。

另一个就是会启动 websocket 服务。用户打开分享过来的链接,会自动通浏览器的 websocket 方法来直接对 fun-core 的 websocket 进行通讯。当有新的抓包数据产生的时候,fun-core 会通过 websocket 推送给所有当前连接的用户。从而达到一端抓包,多端同步的功能。



六、规划总结

6.1 规划


接下来我们重点投入的是 workflow 这个功能,支持对请求响应的全生命周期管理。用户可以直观的选择想要的工具,并构建工作流对请求响应进行处理。工作流不仅支持请求响应的处理,还支持逻辑的判断,比如将符合某种条件下的请求高亮显示出来。等等,可玩性更高。



6.2 总结


通过上面我们对 FunProxy 的介绍,相信大家对 FunProxy 有了比较不错的了解。FunProxy 使用 Rust 构建的跨平台全链路测试 &抓包代理工具。通过 FunProxy,我们只需要简单的通过选择,比如选择项目,选择环境,选择规则,就能将全链路测试过程中复杂的配置流程,变得异常的简单。同时通过云端功能让所有人能够共享配置,极大的提升全链路测试的环境配置和抓包效率,让全链路测试变得更加丝滑流畅。

发布于: 2025-05-08阅读数: 82
用户头像

官方公众号:vivo互联网技术,ID:vivoVMIC 2020-07-10 加入

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。

评论

发布
暂无评论
FunProxy - 使用 Rust 构建跨平台全链路测试抓包代理工具_效率_vivo互联网技术_InfoQ写作社区