Skip to content

v3.x 代理服务器的新轮子:anyproxy

Otto Mao edited this page Dec 1, 2017 · 1 revision

是的,我大支付宝的前端攻城师们又写了一个代理服务器,基于Node.js,取名 AnyProxy

引子

业界的http代理工具已经不少了,windows有fiddler,mac有charles,跨平台的有liver pool。满足特定功能的民间产品更是层出不穷,如代理淘宝线上combo文件的flex-combo,clam中的doji等等。

即使有如此之多的产品,我们仍感觉力不从心,因为前端攻城师们在各种网络环境中的奇葩需求太多了。举几个例子:

  • 调试线上页面,需要注入某个脚本,或是替换一些html数据。比如常用的移动端调试工具weinre就依赖这种方法
  • 不改url,把请求发送到某台指定的服务器,移动端的dns结果有缓存,改host又效率太低
  • 查看页面首次打开时的表现,需要阻止CDN提供304这样的响应
  • 服务器不支持跨域头Access-Control-Allow-*,调试时却需要发送跨域请求。本地服务器调试app中嵌入的离线页面时常面对这种需求
  • 替换cookie,快速实现多账户切换
  • 模拟各类网速环境
  • 接管某个API的响应,进行数据mock

既然现有产品无法覆盖如此复杂的调试需求,我们决定再做一个轮子,做一个适合前端攻城师调试环境的新工具。

代理服务器的战略意义

站在http协议的角度,前端攻城师们在网络通信层面的需求都可以归纳成:

  • 修改请求参数,如防止资源304
  • 修改响应头和内容,如脚本植入、数据mock、支持跨域请求
  • 调整网络传输环境,如网速模拟、指定服务器

此时,代理服务器就是不二之选。

代理服务器是个中间人,站在了客户端和服务端中间,双方通信的每个比特,都会滴水不漏地经过它。它控制了完整的请求头、请求体、响应头、响应体,可以在客户端与服务端都无感知的情况下介入处理所有的流程。只要代理服务器可以实现灵活配置,前端工程师们的奇葩需求就有出头之日了。

这里,我们全新开发了一个代理服务器,取名AnyProxy,其基本特性为:

  • 基于nodeJS,降低前端工程师学习成本,更好地耦合现有开发环境
  • 实现代理服务器底层结构和基本功能,包括转发、HTTPS明文解析、数据统计
  • 提供web版界面,供mac/windows用户使用,实时观测网络请求
  • 开放接口,允许用户进行充分的自定义(后文详述)

UI

开放式代理服务器设计

在无止境的需求面前,任何预定义的功能模板都会随着时间的流逝而落伍。想要扩大使用范围,不妨做成开放式的服务,在使用时进行二次开发。

这里,我们把http通信过程中的各个阶段进行抽离,分解成三个阶段:

  • 收到来自客户端请求之后,允许开发者直接从本地提供返回
  • 在转发请求到服务器前,允许开发者对发送的请求进行修改
  • 在收到服务器响应之后,允许开发者对响应内容进行修改,再返回给客户端

对于上述每个阶段,我们都提供了API接口,引入开发者编写自己的规则代码,实时干预通信过程,以此满足各类自定义需求。

具体地,我们提供的接口包括:

  • 收到用户请求之后
    • shouldUseLocalResponse ,是否在本地直接发送响应(不再向服务器发出请求)
    • dealLocalResponse 如果shouldUseLocalResponse返回true,会调用这个函数来获取本地响应内容(异步接口)
  • 向服务端发出请求之前
    • replaceRequestProtocol 替换向服务器发出的请求协议,支持http和https的替换
    • replaceRequestOption 替换向服务器发出的请求参数,即nodeJS中的 request option
    • replaceRequestData 替换请求的body
  • 向用户返回服务端的响应之前
    • replaceResponseStatusCode 替换服务器响应的http状态码
    • replaceResponseHeader 替换服务器响应的http头
    • replaceServerResDataAsync 替换服务器响应的数据(异步接口)
    • pauseBeforeSendingResponse 在请求返回给用户前的延迟时间

AnyProxy规则文件样例

以“防止CDN返回304”这个需求为例,最直接的方案是拦截请求,在发送到CDN前删除header中的if-modified-since字段。在AnyProxy中,配置replaceRequestOption接口,3行代码就能实现这个自定义功能:

//rule file
module.exports = {
	//在向服务器发出请求前,AnyProxy会调用这个接口,可以在此时修改发送请求的参数
	replaceRequestOption : function(req,option){
	    var newOption = option;
	    delete newOption.headers['if-modified-since'];
	    return newOption;
	}
};

再举个例子,如果你想修改响应数据,在所有html文件最后加个"Hello World",就需要调用replaceServerResDataAsync接口,并结合content-type字段来进行修改,大约需要8行代码。

//rule file
module.exports = {
    replaceServerResDataAsync: function(req,res,serverResData,callback){
        //append "hello world" to all web pages
        if(/html/i.test(res.headers['content-type'])){
            var newDataStr = serverResData.toString();
            newDataStr += "hello world!";
            callback(newDataStr);
        }else{
            callback(serverResData);
        }
    }
};

为了帮助用户更快地编写规则(rule)文件,我们还提供了很多清晰易懂的sample,如:

  • 把所有的响应延迟1500毫秒
  • 为ajax请求增加跨域头
  • 截获github.com的https请求,再在最后加点文字
  • 去除响应头里缓存相关的头
  • 在请求发送到服务端前对参数做一些调整
  • 修改响应数据
  • 改变服务端响应的http状态码
  • 把响应映射到本地

AnyProxy其他特性

  • 支持Https的中间人(man-in-the-middle)代理,同时提供便捷的根证书安装路径,方便移动端导入证书
  • 低网速网速模拟,协助调试2G/3G下的表现
  • 可以导出所有请求记录,供后期数据分析使用
  • 可以进行模块化调用,做二次封装,合并到现有的前端集成开发环境中,个性化搭建自己的调试环境

文档和支持