Table of Contents
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
是的,我大支付宝的前端攻城师们又写了一个代理服务器,基于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用户使用,实时观测网络请求
- 开放接口,允许用户进行充分的自定义(后文详述)
开放式代理服务器设计
在无止境的需求面前,任何预定义的功能模板都会随着时间的流逝而落伍。想要扩大使用范围,不妨做成开放式的服务,在使用时进行二次开发。
这里,我们把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下的表现
- 可以导出所有请求记录,供后期数据分析使用
- 可以进行模块化调用,做二次封装,合并到现有的前端集成开发环境中,个性化搭建自己的调试环境
文档和支持
- HTTPS相关配置的中文文档
- 完整的文档和介绍:https://github.com/alibaba/anyproxy, 喜欢的话可以给我们加个star。
- 有任何问题,欢迎提issue,或是PR,或者加入anyproxy用户旺旺群答疑:1203077233。