anyproxy
Version:
A fully configurable HTTP/HTTPS proxy in Node.js
1,262 lines (821 loc) • 80.1 kB
HTML
<!DOCTYPE HTML>
<html lang="cn" >
<head>
<meta charset="UTF-8">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>简介 · AnyProxy</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" content="">
<meta name="generator" content="GitBook 3.2.2">
<meta name="author" content="AnyProxy">
<link rel="stylesheet" href="../gitbook/style.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-highlight/website.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-search/search.css">
<link rel="stylesheet" href="../gitbook/gitbook-plugin-fontsettings/website.css">
<link rel="shortcut icon" href="/assets/favicon.png" type="image/png">
<link rel="stylesheet" href="/assets/website.css">
<script src="/assets/main.js" ></script>
</head>
<body>
<div>
</div>
<div class="book">
<div class="book-summary">
<div id="book-search-input" role="search">
<input type="text" placeholder="Type to search" />
</div>
<nav role="navigation">
<ul class="summary">
<li class="chapter active" data-level="1.1" data-path="./">
<a href="./">
<div class="summary-title-span 简介">
简介
</div>
</a>
</li>
<li class="chapter " data-level="1.2" data-path="./">
<a href="./#快速开始">
<div class="summary-title-span 快速开始">
快速开始
</div>
</a>
<ul class="articles">
<li class="chapter " data-level="1.2.1" data-path="./">
<a href="./#安装">
<div class="summary-title-span 安装">
安装
</div>
</a>
</li>
<li class="chapter " data-level="1.2.2" data-path="./">
<a href="./#启动">
<div class="summary-title-span 启动">
启动
</div>
</a>
</li>
<li class="chapter " data-level="1.2.3" data-path="./">
<a href="./#其他命令">
<div class="summary-title-span 其他命令">
其他命令
</div>
</a>
</li>
<li class="chapter " data-level="1.2.4" data-path="./">
<a href="./#作为npm模块使用">
<div class="summary-title-span 作为npm模块启动">
作为npm模块启动
</div>
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.3" data-path="./">
<a href="./#代理https">
<div class="summary-title-span 代理HTTPS">
代理HTTPS
</div>
</a>
</li>
<li class="chapter " data-level="1.4" data-path="./">
<a href="./#代理websocket">
<div class="summary-title-span 代理WebSocket">
代理WebSocket
</div>
</a>
</li>
<li class="chapter " data-level="1.5" data-path="./">
<a href="./#rule模块">
<div class="summary-title-span rule模块">
rule模块
</div>
</a>
<ul class="articles">
<li class="chapter " data-level="1.5.1" data-path="./">
<a href="./#开发示例">
<div class="summary-title-span 开发示例">
开发示例
</div>
</a>
</li>
<li class="chapter " data-level="1.5.2" data-path="./">
<a href="./#处理流程">
<div class="summary-title-span 处理流程">
处理流程
</div>
</a>
</li>
<li class="chapter " data-level="1.5.3" data-path="./">
<a href="./#如何引用">
<div class="summary-title-span 如何引用">
如何引用
</div>
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.6" data-path="./">
<a href="./#rule接口文档">
<div class="summary-title-span rule接口文档">
rule接口文档
</div>
</a>
<ul class="articles">
<li class="chapter " data-level="1.6.1" data-path="./">
<a href="./#summary">
<div class="summary-title-span rule-title">
summary
</div>
</a>
</li>
<li class="chapter " data-level="1.6.2" data-path="./">
<a href="./#beforesendrequest">
<div class="summary-title-span rule-title">
beforeSendRequest
</div>
</a>
</li>
<li class="chapter " data-level="1.6.3" data-path="./">
<a href="./#beforesendresponse">
<div class="summary-title-span rule-title">
beforeSendResponse
</div>
</a>
</li>
<li class="chapter " data-level="1.6.4" data-path="./">
<a href="./#beforedealhttpsrequest">
<div class="summary-title-span rule-title">
beforeDealHttpsRequest
</div>
</a>
</li>
<li class="chapter " data-level="1.6.5" data-path="./">
<a href="./#onerror">
<div class="summary-title-span rule-title">
onError
</div>
</a>
</li>
<li class="chapter " data-level="1.6.6" data-path="./">
<a href="./#onconnecterror">
<div class="summary-title-span rule-title">
onConnectError
</div>
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.7" data-path="./">
<a href="./#rule样例">
<div class="summary-title-span rule样例">
rule样例
</div>
</a>
<ul class="articles">
<li class="chapter " data-level="1.7.1" data-path="./">
<a href="./#使用本地数据">
<div class="summary-title-span sample-title">
使用本地数据
</div>
</a>
</li>
<li class="chapter " data-level="1.7.2" data-path="./">
<a href="./#修改请求头">
<div class="summary-title-span sample-title">
修改请求头
</div>
</a>
</li>
<li class="chapter " data-level="1.7.3" data-path="./">
<a href="./#修改请求数据">
<div class="summary-title-span sample-title">
修改请求数据
</div>
</a>
</li>
<li class="chapter " data-level="1.7.4" data-path="./">
<a href="./#修改请求的目标地址">
<div class="summary-title-span sample-title">
修改请求的目标地址
</div>
</a>
</li>
<li class="chapter " data-level="1.7.5" data-path="./">
<a href="./#修改请求协议">
<div class="summary-title-span sample-title">
修改请求协议
</div>
</a>
</li>
<li class="chapter " data-level="1.7.6" data-path="./">
<a href="./#修改返回状态码">
<div class="summary-title-span sample-title">
修改返回状态码
</div>
</a>
</li>
<li class="chapter " data-level="1.7.7" data-path="./">
<a href="./#修改返回头">
<div class="summary-title-span sample-title">
修改返回头
</div>
</a>
</li>
<li class="chapter " data-level="1.7.8" data-path="./">
<a href="./#修改返回内容并延迟">
<div class="summary-title-span sample-title">
修改返回内容并延迟
</div>
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.8" data-path="./">
<a href="./#证书配置">
<div class="summary-title-span 证书配置">
证书配置
</div>
</a>
<ul class="articles">
<li class="chapter " data-level="1.8.1" data-path="./">
<a href="./#osx系统信任ca证书">
<div class="summary-title-span OSX系统信任CA证书">
OSX系统信任CA证书
</div>
</a>
</li>
<li class="chapter " data-level="1.8.2" data-path="./">
<a href="./#windows系统信任ca证书">
<div class="summary-title-span Windows系统信任CA证书">
Windows系统信任CA证书
</div>
</a>
</li>
<li class="chapter " data-level="1.8.3" data-path="./">
<a href="./#配置osx系统代理">
<div class="summary-title-span 配置OSX系统代理">
配置OSX系统代理
</div>
</a>
</li>
<li class="chapter " data-level="1.8.4" data-path="./">
<a href="./#配置浏览器http代理">
<div class="summary-title-span 配置浏览器HTTP代理">
配置浏览器HTTP代理
</div>
</a>
</li>
<li class="chapter " data-level="1.8.5" data-path="./">
<a href="./#ios系统信任ca证书">
<div class="summary-title-span iOS系统信任CA证书">
iOS系统信任CA证书
</div>
</a>
</li>
<li class="chapter " data-level="1.8.6" data-path="./">
<a href="./#ios--103信任ca证书">
<div class="summary-title-span iOS >= 10.3信任CA证书">
iOS >= 10.3信任CA证书
</div>
</a>
</li>
<li class="chapter " data-level="1.8.7" data-path="./">
<a href="./#安卓系统信任ca证书">
<div class="summary-title-span 安卓系统信任CA证书">
安卓系统信任CA证书
</div>
</a>
</li>
<li class="chapter " data-level="1.8.8" data-path="./">
<a href="./#配置iosandroid系统代理">
<div class="summary-title-span 配置iOS/Android系统代理">
配置iOS/Android系统代理
</div>
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.9" data-path="./">
<a href="./#faq">
<div class="summary-title-span FAQ">
FAQ
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul>
</nav>
</div>
<div class="book-body">
<div class="body-inner">
<div class="book-header" role="navigation">
<!-- Title -->
<h1>
<i class="fa fa-circle-o-notch fa-spin"></i>
<a href="." >简介</a>
</h1>
</div>
<div class="page-wrapper" tabindex="-1" role="main">
<div class="page-inner">
<div id="book-search-results">
<div class="search-noresults">
<section class="normal markdown-section">
<h1 id="anyproxy">AnyProxy</h1>
<blockquote>
<p>本文档的适用范围是AnyProxy 4.0,欢迎提供反馈</p>
</blockquote>
<p>Ref: <a href="../en">English Doc</a></p>
<p>AnyProxy是一个开放式的HTTP代理服务器。</p>
<p>Github主页:<a href="https://github.com/alibaba/anyproxy" target="_blank">https://github.com/alibaba/anyproxy</a></p>
<p>主要特性包括:</p>
<ul>
<li>基于Node.js,开放二次开发能力,允许自定义请求处理逻辑</li>
<li>支持Https的解析</li>
<li>提供GUI界面,用以观察请求</li>
</ul>
<p>相比3.x版本,AnyProxy 4.0的主要变化:</p>
<ul>
<li>规则文件(Rule)全面支持Promise和Generator</li>
<li>简化了规则文件内的接口</li>
<li>Web版界面重构</li>
</ul>
<p><img src="https://gw.alipayobjects.com/zos/rmsportal/JoxHUbVhXNedsPUUilnj.gif" width="1275px"></p>
<h1 id="快速开始">快速开始</h1>
<h2 id="作为全局模块">作为全局模块</h2>
<h3 id="安装">安装</h3>
<p>对于Debian或者Ubuntu系统,在安装AnyProxy之前,可能还需要安装 <code>nodejs-legacy</code></p>
<pre><code class="lang-bash">sudo apt-get install nodejs-legacy
</code></pre>
<p>然后,安装AnyProxy</p>
<pre><code class="lang-bash">npm install -g anyproxy
</code></pre>
<h3 id="启动">启动</h3>
<ul>
<li>命令行启动AnyProxy,默认端口号8001</li>
</ul>
<pre><code class="lang-bash">anyproxy
</code></pre>
<ul>
<li>启动后将终端http代理服务器配置为127.0.0.1:8001即可</li>
<li>访问<a href="http://127.0.0.1:8002" target="_blank">http://127.0.0.1:8002</a> ,web界面上能看到所有的请求信息</li>
</ul>
<h3 id="其他命令">其他命令</h3>
<ul>
<li>配置启动端口,如1080端口启动</li>
</ul>
<pre><code class="lang-bash">anyproxy --port 1080
</code></pre>
<h2 id="作为npm模块使用">作为npm模块使用</h2>
<p>AnyProxy可以作为一个npm模块使用,整合进其他工具。</p>
<blockquote>
<p>如要启用https解析,请在代理服务器启动前自行调用<code>AnyProxy.utils.certMgr</code>相关方法生成证书,并引导用户信任安装。或引导用户使用<code>anyproxy-ca</code>方法。</p>
</blockquote>
<ul>
<li>引入</li>
</ul>
<pre><code class="lang-bash">npm i anyproxy --save
</code></pre>
<ul>
<li>使用举例</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> AnyProxy = <span class="hljs-built_in">require</span>(<span class="hljs-string">'anyproxy'</span>);
<span class="hljs-keyword">const</span> options = {
port: <span class="hljs-number">8001</span>,
rule: <span class="hljs-built_in">require</span>(<span class="hljs-string">'myRuleModule'</span>),
webInterface: {
enable: <span class="hljs-literal">true</span>,
webPort: <span class="hljs-number">8002</span>
},
throttle: <span class="hljs-number">10000</span>,
forceProxyHttps: <span class="hljs-literal">false</span>,
wsIntercept: <span class="hljs-literal">false</span>, <span class="hljs-comment">// 不开启websocket代理</span>
silent: <span class="hljs-literal">false</span>
};
<span class="hljs-keyword">const</span> proxyServer = <span class="hljs-keyword">new</span> AnyProxy.ProxyServer(options);
proxyServer.on(<span class="hljs-string">'ready'</span>, () => { <span class="hljs-comment">/* */</span> });
proxyServer.on(<span class="hljs-string">'error'</span>, (e) => { <span class="hljs-comment">/* */</span> });
proxyServer.start();
<span class="hljs-comment">//when finished</span>
proxyServer.close();
</code></pre>
<ul>
<li><p>Class: AnyProxy.proxyServer</p>
<ul>
<li><p>创建代理服务器</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> proxy = <span class="hljs-keyword">new</span> AnyProxy.proxyServer(options)
</code></pre>
</li>
<li><p><code>options</code></p>
<ul>
<li><code>port</code> {number} 必选,代理服务器端口</li>
<li><code>rule</code> {object} 自定义规则模块</li>
<li><code>throttle</code> {number} 限速值,单位kb/s,默认不限速</li>
<li><code>forceProxyHttps</code> {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认<code>false</code></li>
<li><code>silent</code> {boolean} 是否屏蔽所有console输出,默认<code>false</code></li>
<li><code>dangerouslyIgnoreUnauthorized</code> {boolean} 是否忽略请求中的证书错误,默认<code>false</code></li>
<li><code>wsIntercept</code> {boolean} 是否开启websocket代理,默认<code>false</code></li>
<li><code>webInterface</code> {object} web版界面配置<ul>
<li><code>enable</code> {boolean} 是否启用web版界面,默认<code>false</code></li>
<li><code>webPort</code> {number} web版界面端口号,默认<code>8002</code></li>
</ul>
</li>
</ul>
</li>
<li><p>Event: <code>ready</code></p>
<ul>
<li>代理服务器启动完成</li>
<li>示例</li>
</ul>
<pre><code class="lang-js">proxy.on(<span class="hljs-string">'ready'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ })
</code></pre>
</li>
<li><p>Event: <code>error</code></p>
<ul>
<li>代理服务器发生错误</li>
<li>示例</li>
</ul>
<pre><code class="lang-js">proxy.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ })
</code></pre>
</li>
<li><p>Method: <code>start</code></p>
<ul>
<li>启动代理服务器</li>
<li>示例</li>
</ul>
<pre><code class="lang-js">proxy.start();
</code></pre>
</li>
<li><p>Method: <code>close</code></p>
<ul>
<li>关闭代理服务器</li>
<li>示例</li>
</ul>
<pre><code class="lang-js">proxy.close();
</code></pre>
</li>
</ul>
</li>
<li><p>AnyProxy.utils.systemProxyMgr</p>
<ul>
<li>管理系统的全局代理配置,方法调用时可能会弹出密码框</li>
<li>使用示例</li>
</ul>
<pre><code class="lang-js"><span class="hljs-comment">// 配置127.0.0.1:8001为全局http代理服务器</span>
AnyProxy.utils.systemProxyMgr.enableGlobalProxy(<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-string">'8001'</span>);
<span class="hljs-comment">// 关闭全局代理服务器</span>
AnyProxy.utils.systemProxyMgr.disableGlobalProxy();
</code></pre>
</li>
<li><p>AnyProxy.utils.certMgr</p>
<ul>
<li>管理AnyProxy的证书</li>
<li><code>AnyProxy.utils.certMgr.ifRootCAFileExists()</code><ul>
<li>校验系统内是否存在AnyProxy的根证书</li>
</ul>
</li>
<li><code>AnyProxy.utils.certMgr.generateRootCA(callback)</code><ul>
<li>生成AnyProxy的rootCA,完成后请引导用户信任.crt文件</li>
</ul>
</li>
<li>样例</li>
</ul>
<pre><code class="lang-js"> <span class="hljs-keyword">const</span> AnyProxy = <span class="hljs-built_in">require</span>(<span class="hljs-string">'anyproxy'</span>);
<span class="hljs-keyword">const</span> exec = <span class="hljs-built_in">require</span>(<span class="hljs-string">'child_process'</span>).exec;
<span class="hljs-keyword">if</span> (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {
AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {
<span class="hljs-comment">// let users to trust this CA before using proxy</span>
<span class="hljs-keyword">if</span> (!error) {
<span class="hljs-keyword">const</span> certDir = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>).dirname(keyPath);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The cert is generated at'</span>, certDir);
<span class="hljs-keyword">const</span> isWin = <span class="hljs-regexp">/^win/</span>.test(process.platform);
<span class="hljs-keyword">if</span> (isWin) {
exec(<span class="hljs-string">'start .'</span>, { cwd: certDir });
} <span class="hljs-keyword">else</span> {
exec(<span class="hljs-string">'open .'</span>, { cwd: certDir });
}
} <span class="hljs-keyword">else</span> {
<span class="hljs-built_in">console</span>.error(<span class="hljs-string">'error when generating rootCA'</span>, error);
}
});
}
</code></pre>
</li>
</ul>
<h1 id="代理https">代理HTTPS</h1>
<ul>
<li>AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书</li>
</ul>
<blockquote>
<p>解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程</p>
</blockquote>
<ul>
<li>生成证书并解析所有https请求</li>
</ul>
<pre><code class="lang-bash">anyproxy-ca <span class="hljs-comment">#生成rootCA证书,生成后需要手动信任</span>
anyproxy --intercept <span class="hljs-comment">#启动AnyProxy,并解析所有https请求</span>
</code></pre>
<ul>
<li><a href="#证书配置">附录:如何信任CA证书</a></li>
</ul>
<h1 id="代理websocket">代理WebSocket</h1>
<pre><code class="lang-bash">anyproxy --ws-intercept
</code></pre>
<blockquote>
<p>当启用<code>HTTPS</code>代理时,<code>wss</code>也会被代理,但是不会被AnyProxy记录。需要开启<code>--ws-intercept</code>后才会从界面上看到相应内容。</p>
</blockquote>
<h1 id="rule模块">rule模块</h1>
<p>AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。</p>
<blockquote>
<p>注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题</p>
</blockquote>
<p>规则模块的能力范围包括:</p>
<ul>
<li>拦截并修改正在发送的请求<ul>
<li>可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等</li>
</ul>
</li>
<li>拦截并修改服务端响应<ul>
<li>可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等</li>
</ul>
</li>
<li>拦截https请求,对内容做修改<ul>
<li>本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA</li>
</ul>
</li>
</ul>
<h3 id="开发示例">开发示例</h3>
<ul>
<li><p>举例</p>
<ul>
<li>需要编写一个规则模块,在 GET <a href="http://httpbin.org/user-agent" target="_blank">http://httpbin.org/user-agent</a> 的返回值里加上测试信息,并延迟5秒返回</li>
</ul>
</li>
<li><p>Step 1,编写规则</p>
<pre><code class="lang-js"><span class="hljs-comment">// file: sample.js</span>
<span class="hljs-built_in">module</span>.exports = {
summary: <span class="hljs-string">'a rule to hack response'</span>,
*beforeSendResponse(requestDetail, responseDetail) {
<span class="hljs-keyword">if</span> (requestDetail.url === <span class="hljs-string">'http://httpbin.org/user-agent'</span>) {
<span class="hljs-keyword">const</span> newResponse = responseDetail.response;
newResponse.body += <span class="hljs-string">'- AnyProxy Hacked!'</span>;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>((resolve, reject) => {
setTimeout(() => { <span class="hljs-comment">// delay</span>
resolve({ response: newResponse });
}, <span class="hljs-number">5000</span>);
});
}
},
};
</code></pre>
</li>
<li><p>Step 2, 启动AnyProxy,加载规则</p>
<ul>
<li>运行 <code>anyproxy --rule sample.js</code></li>
</ul>
</li>
<li><p>Step 3, 测试规则</p>
<ul>
<li><p>用curl测试</p>
<pre><code class="lang-bash">curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
</code></pre>
</li>
<li><p>用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 <a href="http://httpbin.org/user-agent" target="_blank">http://httpbin.org/user-agent</a></p>
</li>
<li><p>经过代理服务器后,期望的返回如下</p>
</li>
</ul>
<pre><code>{
"user-agent": "curl/7.43.0"
}
- AnyProxy Hacked!
</code></pre></li>
<li><p>Step 4, 查看请求信息</p>
<ul>
<li>浏览器访问<a href="http://127.0.0.1:8002" target="_blank">http://127.0.0.1:8002</a> ,界面上能看到刚才的请求信息</li>
</ul>
</li>
</ul>
<h3 id="处理流程">处理流程</h3>
<ul>
<li>处理流程图如下</li>
</ul>
<p><img src="https://zos.alipayobjects.com/rmsportal/TWyNuSJtEZBdrdcOMRjE.png" width="550"></p>
<ul>
<li><p>当http请求经过代理服务器时,具体处理过程是:</p>
<ul>
<li>收集请求所有请求参数,包括method, header, body等</li>
<li>AnyProxy调用规则模块<code>beforeSendRequest</code>方法,由模块做处理,返回新的请求参数,或返回响应内容</li>
<li>如果<code>beforeSendRequest</code>返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。</li>
<li>根据请求参数,向服务端发出请求,接收服务端响应。</li>
<li>调用规则模块<code>beforeSendResponse</code>方法,由模块对响应内容进行处理</li>
<li>把响应信息返回给客户端</li>
</ul>
</li>
<li><p>当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。</p>
<ul>
<li>调用规则模块<code>beforeDealHttpsRequest</code>方法,如果返回<code>true</code>,会明文解析这个请求,其他请求不处理</li>
<li>被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。</li>
</ul>
</li>
</ul>
<h3 id="如何引用">如何引用</h3>
<p>如下几种方案都可以用来引用规则模块:</p>
<ul>
<li>使用本地路径<pre><code class="lang-bash">anyproxy --rule ./rule.js
</code></pre>
</li>
<li><p>使用在线地址</p>
<pre><code class="lang-bash">anyproxy --rule https://sample.com/rule.js
</code></pre>
</li>
<li><p>使用npm包</p>
<ul>
<li>AnyProxy使用<code>require()</code>加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包</li>
</ul>
<pre><code class="lang-bash">anyproxy --rule ./myRulePkg/ <span class="hljs-comment">#本地包</span>
npm i -g myRulePkg && anyproxy --rule myRulePkg <span class="hljs-comment">#全局包</span>
</code></pre>
</li>
</ul>
<h1 id="rule接口文档">rule接口文档</h1>
<p>规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-comment">// 模块介绍</span>
summary: <span class="hljs-string">'my customized rule for AnyProxy'</span>,
<span class="hljs-comment">// 发送请求前拦截处理</span>
*beforeSendRequest(requestDetail) { <span class="hljs-comment">/* ... */</span> },
<span class="hljs-comment">// 发送响应前处理</span>
*beforeSendResponse(requestDetail, responseDetail) { <span class="hljs-comment">/* ... */</span> },
<span class="hljs-comment">// 是否处理https请求</span>
*beforeDealHttpsRequest(requestDetail) { <span class="hljs-comment">/* ... */</span> },
<span class="hljs-comment">// 请求出错的事件</span>
*onError(requestDetail, error) { <span class="hljs-comment">/* ... */</span> },
<span class="hljs-comment">// https连接服务器出错</span>
*onConnectError(requestDetail, error) { <span class="hljs-comment">/* ... */</span> }
};
</code></pre>
<blockquote>
<p>规则文件中,除了summary,都是由 <a href="https://www.npmjs.com/package/co" target="_blank">co</a> 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。</p>
</blockquote>
<h3 id="summary">summary</h3>
<h4 id="summary-string--summarystring">summary(): string | summary:string</h4>
<ul>
<li>规则模块的介绍文案,用于AnyProxy提示用户, 可以是一个函数,也可以是一个普通的字符串</li>
</ul>
<h3 id="beforesendrequest">beforeSendRequest</h3>
<h4 id="beforesendrequestrequestdetail">beforeSendRequest(requestDetail)</h4>
<ul>
<li>AnyProxy向服务端发送请求前,会调用<code>beforeSendRequest</code>,并带上参数<code>requestDetail</code></li>
<li><code>requestDetail</code><ul>
<li><code>protocol</code> {string} 请求使用的协议,http或者https</li>
<li><code>requestOptions</code> {object} 即将发送的请求配置,供require('http').request作为使用。详见:<a href="https://nodejs.org/api/http.html#http_http_request_options_callback" target="_blank">https://nodejs.org/api/http.html#http_http_request_options_callback</a></li>
<li><code>requestData</code> {object} 请求Body</li>
<li><code>url</code> {string} 请求url</li>
<li><code>_req</code> {object} 请求的原始request</li>
</ul>
</li>
<li><p>举例:请求 <em>anyproxy.io</em> 时,<code>requestDetail</code>参数内容大致如下</p>
<pre><code class="lang-js">{
protocol: <span class="hljs-string">'http'</span>,
url: <span class="hljs-string">'http://anyproxy.io/'</span>,
requestOptions: {
hostname: <span class="hljs-string">'anyproxy.io'</span>,
port: <span class="hljs-number">80</span>,
path: <span class="hljs-string">'/'</span>,
method: <span class="hljs-string">'GET'</span>,
headers: {
Host: <span class="hljs-string">'anyproxy.io'</span>,
<span class="hljs-string">'Proxy-Connection'</span>: <span class="hljs-string">'keep-alive'</span>,
<span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">'...'</span>
}
},
requestData: <span class="hljs-string">'...'</span>,
_req: { <span class="hljs-comment">/* ... */</span>}
}
</code></pre>
</li>
<li><p>以下几种返回都是合法的</p>
<ul>
<li>不做任何处理,返回null</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
</code></pre>
<ul>
<li>修改请求协议,如强制改用https发起请求</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> {
protocol: <span class="hljs-string">'https'</span>
};
</code></pre>
<ul>
<li>修改请求参数</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> newOption = <span class="hljs-built_in">Object</span>.assign({}, requestDetail.requestOptions);
newOption.path = <span class="hljs-string">'/redirect/to/another/path'</span>;
<span class="hljs-keyword">return</span> {
requestOptions: newOption
};
</code></pre>
<ul>
<li>修改请求body</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> {
requestData: <span class="hljs-string">'my new request data'</span>
<span class="hljs-comment">//这里也可以同时加上requestOptions</span>
};
</code></pre>
<ul>
<li>直接返回客户端,不再发起请求,其中<code>statusCode</code> <code>header</code> 是必选字段</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> {
response: {
statusCode: <span class="hljs-number">200</span>,
header: { <span class="hljs-string">'content-type'</span>: <span class="hljs-string">'text/html'</span> },
body: <span class="hljs-string">'this could be a <string> or <buffer>'</span>
}
};
</code></pre>
</li>
</ul>
<h3 id="beforesendresponse">beforeSendResponse</h3>
<h4 id="beforesendresponserequestdetail-responsedetail">beforeSendResponse(requestDetail, responseDetail)</h4>
<ul>
<li>AnyProxy向客户端发送请求前,会调用<code>beforeSendResponse</code>,并带上参数<code>requestDetail</code> <code>responseDetail</code></li>
<li><code>requestDetail</code> 同<code>beforeSendRequest</code>中的参数</li>
<li><code>responseDetail</code><ul>
<li><code>response</code> {object} 服务端的返回信息,包括<code>statusCode</code> <code>header</code> <code>body</code>三个字段</li>
<li><code>_res</code> {object} 原始的服务端返回对象</li>
</ul>
</li>
<li><p>举例,请求 <em>anyproxy.io</em> 时,<code>responseDetail</code>参数内容大致如下</p>
<pre><code class="lang-js">{
response: {
statusCode: <span class="hljs-number">200</span>,
header: {
<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'image/gif'</span>,
Connection: <span class="hljs-string">'close'</span>,
<span class="hljs-string">'Cache-Control'</span>: <span class="hljs-string">'...'</span>
},
body: <span class="hljs-string">'...'</span>
},
_res: { <span class="hljs-comment">/* ... */</span> }
}
</code></pre>
</li>
<li><p>以下几种返回都是合法的</p>
<ul>
<li>不做任何处理,返回null</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
</code></pre>
<ul>
<li>修改返回的状态码</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> newResponse = <span class="hljs-built_in">Object</span>.assign({}, responseDetail.response);
newResponse.statusCode = <span class="hljs-number">404</span>;
<span class="hljs-keyword">return</span> {
response: newResponse
};
</code></pre>
<ul>
<li>修改返回的内容</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> newResponse = <span class="hljs-built_in">Object</span>.assign({}, responseDetail.response);
newResponse.body += <span class="hljs-string">'--from anyproxy--'</span>;
<span class="hljs-keyword">return</span> {
response: newResponse
};
</code></pre>
</li>
</ul>
<h3 id="beforedealhttpsrequest">beforeDealHttpsRequest</h3>
<h4 id="beforedealhttpsrequestrequestdetail">beforeDealHttpsRequest(requestDetail)</h4>
<ul>
<li>AnyProxy收到https请求时,会调用<code>beforeDealHttpsRequest</code>,并带上参数<code>requestDetail</code></li>
<li>如果配置了全局解析https的参数,则AnyProxy会略过这个调用</li>
<li>只有返回<code>true</code>时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。</li>
<li>注意:https over http的代理模式中,这里的request是CONNECT请求</li>
<li><code>requestDetail</code><ul>
<li><code>host</code> {string} 请求目标的Host,受制于协议,这里无法获取完整url</li>
<li><code>_req</code> {object} 请求的原始request</li>
</ul>
</li>
<li>返回值<ul>
<li><code>true</code>或者<code>false</code>,表示是否需要AnyProxy替换证书并解析https</li>
</ul>
</li>
</ul>
<h3 id="onerror">onError</h3>
<h4 id="onerrorrequestdetail-error">onError(requestDetail, error)</h4>
<ul>
<li>在请求处理过程中发生错误时,AnyProxy会调用<code>onError</code>方法,并提供对应的错误信息</li>
<li>多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等</li>
<li><code>requestDetail</code> 同<code>beforeSendRequest</code>中的参数</li>
<li><p>以下几种返回都是合法的</p>
<ul>
<li>不做任何处理。此时AnyProxy会返回一个默认的错误页&#