# Web 网页自集成 HTTP 代理方案

# 前言

大部分程序员,想必都有会一个常用的抓包代理工具;
但在座的各位,可曾见过这样一款集成在 Web 应用中的代理工具?

它是明显区别于传统代理工具的,有以下特性:

  • 零安装,零配置,Web 点击即用、APP 扫码即用;(不是开盖即食,而是直接喂到嘴里
  • 分享 URL 链接(或二维码),即可远程调试 HTTP,协助他人或调试移动设备
  • 用户可远程相互共享代理配置,无需手动导入导出

以一个场景举例

  1. 你是一个前端开发,当后端接口还没准备好(或有 Bug 时),产品或设计师想体验效果
  2. 传统办法,你配好接口 Mock,让产品使用你的设备体验
  3. 当前方案,你配好接口 Mock,发一个 URL 链接给产品即可远程体验

它的原理是将代理服务与应用(Web 网页或 APP)集成,从而大幅降低用户(研测产运)的使用成本。 Overview
应用集成代理服务对应的 SDK,自动转发请求到代理服务

传统代理工具的主要成本

  • 下载安装代理程序,甚至收费
  • 配置系统代理(移动端更烦)、https 证书
  • 同事之间共享代理规则(手动导入导出文件)

这些成本带来各种烦恼,也将非技术岗位拒之门外。
场景:中午去吃饭的时候,手机突然断网;原来是刚刚网络调试,配置了系统代理!

笔者曾尝试过多款代理工具,始终有不满意的地方,多年前在自己的开源项目 Erra (opens new window) 中探索新的代理方案,仍然不满意,遂放弃。
现将 Erra 的代理服务与工作中的应用集成,借鉴 whistle (opens new window) 的规则,再重新实现 UI 交互,终觉尚可。

Charles LightProxy FF-Proxy
下载安装 APP
配置操作系统代理
安装信任 HTTPS 证书
Mock 方式 UI 文本规则 UI + 文本规则
Mock 规则共享 导出规则文件 复制规则文本 在线分享
远程调试
通用 HTTP 请求代理
安装部署服务

该方案改进了工作中高频对特定应用(公司产品的域名、APP) 进行网络调试的体验和效率;
该方案是一个增强型功能,与通用代理工具(Charles、Fiddler 等没有任何冲突,不会影响习惯使用通用代理工具的同学。

接下来介绍该方案的技术实现思路。

# 方案原理概览

Overview

总结分为四个部分

  1. 与应用集成,将请求转发到代理服务,由代理服务转发请求到目标服务
  2. 代理服务提供网络调试界面(UI),允许用户查看网络请求、配置 Mock 规则
  3. 代理服务记录请求,并同步到 UI 界面
  4. 代理服务在转发请求前、收到响应后,按 Mock 规则篡改请求内容

# 应用集成

与应用集成是当前方案区别与代理工具的根本所在,也是能实现零安装、零配置便捷性的原因。

集成是将代理服务对应的 SDK 注入到业务系统(网页或 APP)中,SDK 会拦截并转发请求到代理服务。

集成 SDK 的方式介绍以下两种,更多拦截注入技巧请阅读 Web 终极拦截技巧(全是骚操作) (opens new window)

# 自动注入 SDK

  1. 配置 DNS 使 ff-* (混合泛解析)指向代理服务,代理转发 html 请求时自动注入 SDK
    • 注入脚本示例 resp.body.replace('<head>', '<head><script src="/ff-sdk.js"></script>')
  2. 假设目标域名为 live.bilibili.com,使用 ff-live.bilibili.com 访问代理服务即可自动注入

该方式无需业务项目对接,适合多个域名(系统)需要接入的场景。

# 手动注入 SDK

手动往前端项目的 HTML 中写入 <script src="/ff-sdk.js"></script>,或打包构建时自动注入。

接入项目较少,或没有 DNS 配置权限,则可采用该方式接入。

# 拦截转发请求

向页面注入 SDK(ff-sdk.js)后, 根据 URL Query 参数决定是否开启拦截转发请求。
若 URL 参数包含 ff-proxy-id 即启动拦截转发,参数值是随机生成 id,用于隔离不同用户数据。
用户数据指抓包记录、Mock 规则

拦截转发的实现原理是重写 fetch, xhr, WebSocket 等对象,将请求转到代理服务,如何转发请看下文请求变换协议
另外还需要重写 open, a, iframe 的目标链接,确保页面跳转(或嵌入)场景也能继续保持 URL 前缀(ff-)和 Query 参数(ff-proxy-id) 的状态。

// 接口性能监控,打开 https://example.com/, 在控制台执行以下代码
const _fetch = window.fetch;
window.fetch = (...args) => {
  const startTime = performance.now();
  return _fetch(...args).finally(() => {
    console.info('接口耗时:', Math.round(performance.now() - startTime), 'ms');
  });
};

await fetch('//example.com');

重写系统 API 以实现拦截的更多技巧,请请阅读 Web 终极拦截技巧(全是骚操作) (opens new window)

# 移动端 APP 支持

  1. 向网络库注册一个拦截器
  2. 扫描特制的二维码后,拦截器开始转发请求
  3. 扫码后 APP 还需要修改 WebView 容器的 URL,添加前缀(ff-)和 Query 参数
    • 前缀和参数的内容在二维码中获取

# HTTP 请求变换协议

SDK 拦截 HTTP 请求转到代理服务,然后还原再转到目标服务,其关键在于变换与还原请求的协议

原始请求,直接指向目标服务 (以 curl 风格描述请求

curl -v https://api.live.bilibili.com/test

URL 添加前缀 ff- 请求会转到代理服务,因为代理服务注册了 DNS 混合泛解析规则ff-*
再添加 HTTP Header x-ff-proxy-id 用于隔离不同用户的数据。

curl -v 'http://ff-api.live.bilibili.com/test' -H 'x-ff-proxy-id: xxx'

代理服务移除 URL 中的前缀与 Header,即还原成原始请求,再发送给目标服务;
同时,根据 x-ff-proxy-id 将该请求同步给对应 UI 界面。

# 代理服务与 UI 交互

proxy-server-client

代理服务作为远程中心节点,跟客户端(UI 界面)使用 WebSocket 保持通信,代理服务会按照 ff-proxy-id 只同步当前用户的数据,数据包括抓包记录、Mock 规则、资源。
资源是用来管理内容较大的 Request、Response body,方便用于 Mock 替换 HTTP 内容。

通过 URL ff-proxy.bilibili.com/ui/<ff-proxy-id> 即可访问 UI 界面。

http list

# 抓包

代理服务每次代理请求都会根据 ff-proxy-id 的值,将请求内容 copy 一份发送给客户端,这就实现了远程调试。
所以该方案实现远程调试是非常自然的,传统代理工具相对麻烦是因为它工作在用户的设备上,远程调试必须通过手动设置系统代理来实现连接。

比如,手机上访问接口
curl -v 'http://ff-api.live.bilibili.com/test' -H 'x-ff-proxy-id: xxx'

任何用户在任何设备上打开了 ff-proxy.bilibili.com/ui/xxx 页面, 即可远程看到请求的详细内容。

http detail

# Mock 规则

代理服务还能接受客户端提交的 Mock 规则,在转发请求前、收到响应后,按规则篡改请求内容。

支持对 HTTP 请求内容的基本修改(添加、删除、替换),即可满足绝大部分 Mock 诉求;
HTTP 请求内容指

  • Request: URL、Header、Body,
  • Response: StatusCode、Header、Body

mock rules

规则借鉴了 whistle (opens new window) 并做了大量简化,同时也提供 UI 配置以降低使用门槛。

mock ui

规则解析

  1. 定义规则语法
  2. 使用 peggy (opens new window) 按语法将字符串解析为 AST(抽象语法树)
  3. 将 AST 转换为 js 函数实现
  4. 函数会检测代理的请求是否命中规则,命中后按规则描述篡权请求内容

规则相对简单,使用语法解析是为了保持扩展性,字符串处理多写几个条件判断也能实现。

数据存储

数据(Mock 规则、资源)存储在浏览器的 indexedDB 中,每次连接时同步到代理服务进行解析;
所以,代理服务是一个干净的、只维持临时状态的服务,非常方便部署与扩展。

# 相对传统方案的缺点

方案的优点写在了文章开头,这里有必要总结一下缺点,感兴趣的同学自行斟酌。

  • 需要部署、维护代理服务
    • 该方案将以系统维护者的一次性部署成本,交换用户安装、配置软件的成本
  • 有一定侵入性
    • 该方案不是一个透明的中间节点,会修改业务系统的行为(注入 SDK 拦截转发)
    • PS:DNS 集成方式,可让业务系统无感自动接入,最小化影响
  • 不具备通用性
    • 该方案无法调试系统网络请求,无法调试未接入的业务系统、APP

# 总结

该方案的原理是将代理服务与应用集成,从而改进了工作中高频对特定应用进行网络调试的体验和效率。

所以,实施的前提是:一定的团队规模让(效率体验改进)收益大于付出(代理系统部署维护成本)。

# 附录

💗 博主正处于裸辞待业状态,欢迎 商务合作 💗

相关文章

从 React 看前端 UI 代码范式革命

alt text 前言 本来打算写的主题是“我为什么讨厌 React Hooks API”,展开聊聊“小甜甜”是如何变成“牛夫人”的,没想到越写越严肃:) React 是两次前端范式革命的引领者,至今仍有繁荣的社区和旺盛的创造力; React 多次天才又激进的创新,一些想法被借鉴改良、一些引发广泛质疑,大部分是被认同和接受的; ...

WebCodecs 性能表现及优化思路

笔者开源 WebAV 已经一年半,还写了系列文章帮助初学者入门 Web 音视频。 之前一直隐隐担心在 Web 平台处理音视频与 Native APP 会有明显性能差距,因为 WebCodecs API 毕竟被浏览器代理了一层,且一些数据处理需要 js 配合,不确定有多大的性能损耗。 相信刚接触 WebCodecs 的读者也非常关心它的性能表现如何。 ...

Web 文件系统(OPFS 及工具)介绍

文件系统是往往是构建大型软件的基石之一,很长一段时间 Web 平台因缺失成熟的文件系统成为构建大型软件的阻碍,如今 OPFS 可弥补这一缺憾。 本文介绍 OPFS 背景和基本使用方法、使用过程中的注意事项,及如何配合笔者开源的 opfs-tools、opfs-tools-explorer 两个项目,充分发挥 OPFS 的性能与开发效率。 Web 存储 A ...

opfs-tools (文件系统 API) 项目介绍

文件系统是许多领域程序的基石,所有通用编程语言都会内置完备的文件系统 API。 Web 很长一段时间没有提供完善的访问文件系统的规范,使得需要高频读写文件、大文件处理软件在 Web 端都会受到一些限制,比如音视频剪辑、游戏、数据库等等。 之前在浏览器中实现视频裁剪、截帧等相关功能时,发现缺少基本的操作文件的 API,比如读写、移动、复制文件。 而 [OPFS] ...

Web 终极拦截技巧(全是骚操作)

拦截的价值 > 计算机科学领域的任何问题都可以通过增加一个中间层来解决。 —— Butler Lampson 如果系统的控制权、代码完全被掌控,很容易添加中间层; 现实情况我们往往无法控制系统的所有环节,所以需要使用一些 “非常规”(拦截) 手段来增加中间层。 拦截的方法 拦截/覆写 浏览器 API 最常见的场景有通过拦截 console ...

Web 音视频(六)图像素材处理

Web 音视频目录 前序章节介绍了如何在浏览器中解析、创建视频,以及给视频添加一些自定义素材(图片、音频、文字...); 本章介绍如何给图像素材加特效、加动画,实现转场、移动水印、图像滤镜美化等功能。 你可以跳过原理介绍,直接查看 WebAV 示例 素材动画 在视频制作中实现动画跟其他场景略有不同,因为视频 ...

Web Audio & Video (7) Mid-Series Review

Web Audio & Video Series Table of Contents Personal Reflection Over the past month since this series began, we've covered the parsing, processing, and composition of ...