From 535ed00ea175b0d96d4f84b259572c90e61b4482 Mon Sep 17 00:00:00 2001 From: OttoMao Date: Wed, 5 Apr 2017 16:22:17 +0800 Subject: [PATCH] commit missing html --- 4.x/cn.html | 825 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4.x/en.html | 820 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1645 insertions(+) create mode 100644 4.x/cn.html create mode 100644 4.x/en.html diff --git a/4.x/cn.html b/4.x/cn.html new file mode 100644 index 0000000..28cea0b --- /dev/null +++ b/4.x/cn.html @@ -0,0 +1,825 @@ + + + + + + + AnyProxy + + + + + + + + + + + + + Fork me on GitHub + + +
+ +

AnyProxy

+
+

本文档的适用范围是AnyProxy 4.0,此版本当前正在beta中,欢迎提供反馈

+
+

Ref: English Doc

+

AnyProxy是一个开放式的HTTP代理服务器。

+

Github主页:https://github.com/alibaba/anyproxy/tree/4.x

+

主要特性包括:

+
    +
  • 基于Node.js,开放二次开发能力,允许自定义请求处理逻辑
  • +
  • 支持Https的解析
  • +
  • 提供GUI界面,用以观察请求
  • +
+

相比3.x版本,AnyProxy 4.0的主要变化:

+
    +
  • 规则文件(Rule)全面支持Promise和Generator
  • +
  • 简化了规则文件内的接口
  • +
  • Web版界面重构
  • +
+

+

快速上手

+

安装

+
npm install -g anyproxy@beta #本文档对应的AnyProxy为4.0Beta版

启动

+
    +
  • 命令行启动AnyProxy,默认端口号8001
  • +
+
anyproxy
    +
  • 启动后将终端http代理服务器配置为127.0.0.1:8001即可
  • +
  • 访问http://127.0.0.1:8002 ,web界面上能看到所有的请求信息
  • +
+

其他命令

+
    +
  • 配置启动端口,如1080端口启动
  • +
+
anyproxy --port 1080

代理https请求

+
    +
  • AnyProxy默认不对https请求做处理,如需看到明文信息,需要配置CA证书
  • +
+
+

解析https请求的原理是中间人攻击(man-in-the-middle),用户必须信任AnyProxy生成的CA证书,才能进行后续流程

+
+
    +
  • 生成证书并解析所有https请求
  • +
+
anyproxy-ca #生成rootCA证书,生成后需要手动信任
+anyproxy --intercept #启动AnyProxy,并解析所有https请求
+

规则模块(Rule)

+

AnyProxy提供了二次开发的能力,你可以用js编写自己的规则模块(rule),来自定义网络请求的处理逻辑。

+
+

注意:引用规则前,请务必确保文件来源可靠,以免发生安全问题

+
+

规则模块的能力范围包括:

+
    +
  • 拦截并修改正在发送的请求
      +
    • 可修改内容包括请求头(request header),请求体(request body),甚至是请求的目标地址等
    • +
    +
  • +
  • 拦截并修改服务端响应
      +
    • 可修改的内容包括http状态码(status code)、响应头(response header)、响应内容等
    • +
    +
  • +
  • 拦截https请求,对内容做修改
      +
    • 本质是中间人攻击(man-in-the-middle attack),需要客户端提前信任AnyProxy生成的CA
    • +
    +
  • +
+

开发示例

+
    +
  • 举例

    + +
  • +
  • Step 1,编写规则

    +
    // file: sample.js
    +module.exports = {
    +  summary: 'a rule to modify response',
    +  *beforeSendResponse(requestDetail, responseDetail) {
    +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
    +      const newResponse = responseDetail.response;
    +      newResponse.body += '-- AnyProxy Hacked! --';
    +      return new Promise((resolve, reject) => {
    +        setTimeout(() => { // delay
    +          resolve({ response: newResponse });
    +        }, 5000);
    +      });
    +    }
    +  },
    +};
  • +
  • Step 2, 启动AnyProxy,加载规则

    +
      +
    • 运行 anyproxy --rule sample.js
    • +
    +
  • +
  • Step 3, 测试规则

    +
      +
    • 用curl测试

      +
      curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
    • +
    • 用浏览器测试:配置浏览器http代理为 127.0.0.1:8001,访问 http://httpbin.org/user-agent

      +
    • +
    • 经过代理服务器后,期望的返回如下

      +
    • +
    +
    {
    +  "user-agent": "curl/7.43.0"
    +}
    +- AnyProxy Hacked!
  • +
  • Step 4, 查看请求信息

    + +
  • +
+

处理流程

+
    +
  • 处理流程图如下
  • +
+

+
    +
  • 当http请求经过代理服务器时,具体处理过程是:

    +
      +
    • 收集请求所有请求参数,包括method, header, body等
    • +
    • AnyProxy调用规则模块beforeSendRequest方法,由模块做处理,返回新的请求参数,或返回响应内容
    • +
    • 如果beforeSendRequest返回了响应内容,则立即把此响应返回到客户端(而不再发送到真正的服务端),流程结束。
    • +
    • 根据请求参数,向服务端发出请求,接收服务端响应。
    • +
    • 调用规则模块beforeSendResponse方法,由模块对响应内容进行处理
    • +
    • 把响应信息返回给客户端
    • +
    +
  • +
  • 当代理服务器收到https请求时,AnyProxy可以替换证书,对请求做明文解析。

    +
      +
    • 调用规则模块beforeDealHttpsRequest方法,如果返回true,会明文解析这个请求,其他请求不处理
    • +
    • 被明文解析后的https请求,处理流程同http一致。未明文解析请求不会再进入规则模块做处理。
    • +
    +
  • +
+

如何引用

+

如下几种方案都可以用来引用规则模块:

+
    +
  • 使用本地路径
    anyproxy --rule ./rule.js
  • +
  • 使用在线地址

    +
    anyproxy --rule https://sample.com/rule.js
  • +
  • 使用npm包

    +
      +
    • AnyProxy使用require()加载本地规则,你可以在参数里传入一个本地的npm包路径,或是某个全局安装的npm包
    • +
    +
    anyproxy --rule ./myRulePkg/ #本地包
    +npm i -g myRulePkg && anyproxy --rule myRulePkg #全局包
  • +
+

规则接口文档

+

规则模块应该符合cmd规范,一个典型的规则模块代码结构如下。模块中所有方法都是可选的,只需实现业务感兴趣的部分即可。

+
module.exports = {
+  // 模块介绍
+  summary: 'my customized rule for AnyProxy', 
+  // 发送请求前拦截处理
+  *beforeSendRequest(requestDetail) { /* ... */ }, 
+  // 发送响应前处理
+  *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, 
+  // 是否处理https请求
+  *beforeDealHttpsRequest(requestDetail) { /* ... */ }, 
+  // 请求出错的事件
+  *onError(requestDetail, error) { /* ... */ }, 
+  // https连接服务器出错
+  *onConnectError(requestDetail, error) { /* ... */ } 
+};
+

规则文件中,除了summary,都是由 co 驱动的,函数需要满足yieldable。可以返回promise或使用generator函数。

+
+

summary

+

summary

+
    +
  • 规则模块的介绍文案,用于AnyProxy提示用户
  • +
+

beforeSendRequest

+

beforeSendRequest(requestDetail)

+
    +
  • AnyProxy向服务端发送请求前,会调用beforeSendRequest,并带上参数requestDetail
  • +
  • requestDetail +
  • +
  • 举例:请求 anyproxy.io 时,requestDetail参数内容大致如下

    +
    {
    +  protocol: 'http',
    +  url: 'http://anyproxy.io/',
    +  requestOptions: {
    +    hostname: 'anyproxy.io',
    +    port: 80,
    +    path: '/',
    +    method: 'GET',
    +    headers: {
    +      Host: 'anyproxy.io',
    +      'Proxy-Connection': 'keep-alive',
    +      'User-Agent': '...'
    +    }
    +  },
    +  requestData: '...',
    +  _req: { /* ... */}
    +}
  • +
  • 以下几种返回都是合法的

    +
      +
    • 不做任何处理,返回null
    • +
    +
    return null;
      +
    • 修改请求协议,如强制改用https发起请求
    • +
    +
    return {
    +  protocol: 'https'
    +};
      +
    • 修改请求参数
    • +
    +
    var newOption = Object.assign({}, requestDetail.requestOptions);
    +newOption.path = '/redirect/to/another/path';
    +return {
    +  requestOptions: newOption
    +};
      +
    • 修改请求body
    • +
    +
    return {
    +  requestData: 'my new request data'
    +  //这里也可以同时加上requestOptions
    +};
      +
    • 直接返回客户端,不再发起请求,其中statusCode header 是必选字段
    • +
    +
    return {
    +  response: {
    +    statusCode: 200,
    +    header: { 'content-type': 'text/html' },
    +    body: 'this could be a <string> or <buffer>'
    +  }
    +};
  • +
+

beforeSendResponse

+

beforeSendResponse(requestDetail, responseDetail)

+
    +
  • AnyProxy向客户端发送请求前,会调用beforeSendResponse,并带上参数requestDetail responseDetail
  • +
  • requestDetailbeforeSendRequest中的参数
  • +
  • responseDetail
      +
    • response {object} 服务端的返回信息,包括statusCode header body三个字段
    • +
    • _res {object} 原始的服务端返回对象
    • +
    +
  • +
  • 举例,请求 anyproxy.io 时,responseDetail参数内容大致如下

    +
    { 
    +  response: { 
    +    statusCode: 200,
    +    header: { 
    +      'Content-Type': 'image/gif',
    +      Connection: 'close',
    +      'Cache-Control': '...'
    +    },
    +    body: '...'
    +  },
    +  _res: { /* ... */ }
    +}
  • +
  • 以下几种返回都是合法的

    +
      +
    • 不做任何处理,返回null
    • +
    +
    return null;
      +
    • 修改返回的状态码
    • +
    +
    var newResponse = Object.assign({}, responseDetail.reponse);
    +newResponse.statusCode = 404;
    +return {
    +  response: newResponse
    +};
      +
    • 修改返回的内容
    • +
    +
    var newResponse = Object.assign({}, responseDetail.reponse);
    +newResponse.body += '--from anyproxy--';
    +return {
    +  response: newResponse
    +};
  • +
+

beforeDealHttpsRequest

+

beforeDealHttpsRequest(requestDetail)

+
    +
  • AnyProxy收到https请求时,会调用beforeDealHttpsRequest,并带上参数requestDetail
  • +
  • 如果配置了全局解析https的参数,则AnyProxy会略过这个调用
  • +
  • 只有返回true时,AnyProxy才会尝试替换证书、解析https。否则只做数据流转发,无法看到明文数据。
  • +
  • 注意:https over http的代理模式中,这里的request是CONNECT请求
  • +
  • requestDetail
      +
    • host {string} 请求目标的Host,受制于协议,这里无法获取完整url
    • +
    • _req {object} 请求的原始request
    • +
    +
  • +
  • 返回值
      +
    • true或者false,表示是否需要AnyProxy替换证书并解析https
    • +
    +
  • +
+

onError

+

onError(requestDetail, error)

+
    +
  • 在请求处理过程中发生错误时,AnyProxy会调用onError方法,并提供对应的错误信息
  • +
  • 多数场景下,错误会在请求目标服务器的时候发生,比如DNS解析失败、请求超时等
  • +
  • requestDetailbeforeSendRequest中的参数
  • +
  • 以下几种返回都是合法的

    +
      +
    • 不做任何处理。此时AnyProxy会返回一个默认的错误页。
    • +
    +
    return null;
      +
    • 返回自定义错误页
    • +
    +
    return {
    +  response: {
    +    statusCode: 200,
    +    header: { 'content-type': 'text/html' },
    +    body: 'this could be a <string> or <buffer>'
    +  }
    +};
  • +
+

onConnectError

+

onConnectError(requestDetail, error)

+
    +
  • AnyProxy在与目标HTTPS服务器建立连接的过程中,如果发生错误,AnyProxy会调用这个方法
  • +
  • requestDetailbeforeDealHttpsRequest中的参数
  • +
  • 此处无法控制向客户端的返回信息,无需返回值。
  • +
+

FAQ

+
    +
  • Q: 为什么https请求不能进入处理函数?
  • +
  • A: 以下任意一项都能用来改变https的处理特性:

    +
      +
    1. 命令行启动AnyProxy时配置--intercept参数,按npm模块启动时配置forceProxyHttps参数,所有Https请求都会被替换证书并解析
    2. +
    3. 规则文件内提供beforeDealHttpsRequest方法,返回 true 的https请求会被解析
    4. +
    +
  • +
  • Q: 提示 function is not yieldable

    +
  • +
  • A: 规则模块是用 co 驱动的,函数需要满足yieldable。可以使用generator方法或是返回Promise。
  • +
+

规则模块样例

+
    +
  • 这里提供一些样例,来讲解规则模块的常见用法
  • +
  • 你可以通过 anyproxy --rule http://....js 来加载模块并体验
  • +
  • 用curl发请求测试的方法如下
      +
    • 直接请求服务器:curl http://httpbin.org/
    • +
    • 通过代理服务器请求:curl http://httpbin.org/ --proxy http://127.0.0.1:8001
    • +
    +
  • +
+

使用本地数据

+
    +
  • 拦截发送到 http://httpbin.org 的请求,使用本地数据代替服务端返回
  • +
+
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_use_local_response.js
/* 
+  sample: 
+    intercept all requests toward httpbin.org, use a local response
+  test:
+    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    const localResponse = {
+      statusCode: 200,
+      header: { 'Content-Type': 'application/json' },
+      body: '{"hello": "this is local response"}'
+    };
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      return {
+        response: localResponse
+      };
+    }
+  },
+};

修改请求头

+
    +
  • 修改发送到 httpbin.org 的user-agent
  • +
+
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_header.js
/* 
+  sample: 
+    modify the user-agent in requests toward httpbin.org
+  test:
+    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newRequestOptions = requestDetail.requestOptions;
+      newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';
+      return {
+        requestOptions: newRequestOptions
+      };
+    }
+  },
+};

修改请求数据

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_data.js
/* 
+  sample: 
+    modify the post data towards http://httpbin.org/post
+  test:
+    curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001
+  expected response:
+    { "data": "i-am-anyproxy-modified-post-data" }
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {
+      return {
+        requestData: 'i-am-anyproxy-modified-post-data'
+      };
+    }
+  },
+};

修改请求的目标地址

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_path.js
/* 
+  sample: 
+    redirect all httpbin.org requests to http://httpbin.org/user-agent
+  test:
+    curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001
+  expected response:
+    { "user-agent": "curl/7.43.0" }
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newRequestOptions = requestDetail.requestOptions;
+      newRequestOptions.path = '/user-agent';
+      newRequestOptions.method = 'GET';
+      return {
+        requestOptions: newRequestOptions
+      };
+    }
+  },
+};

修改请求协议

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_protocol.js
/* 
+  sample: 
+    redirect all http requests of httpbin.org to https
+  test:
+    curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001
+  expected response:
+    { "X-Forwarded-Protocol": "https" }
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newOption = requestDetail.requestOptions;
+      newOption.port = 443;
+      return {
+        protocol: 'https',
+        requestOptions: newOption
+      };
+    }
+  }
+};

修改返回状态码

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_statuscode.js
/* 
+  sample: 
+    modify all status code of http://httpbin.org/ to 404
+  test:
+    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
+  expected response:
+    HTTP/1.1 404 Not Found
+*/
+module.exports = {
+  *beforeSendResponse(requestDetail, responseDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newResponse = responseDetail.response;
+      newResponse.statusCode = 404;
+      return {
+        response: newResponse
+      };
+    }
+  }
+};

修改返回头

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_header.js
/* 
+  sample: 
+    modify response header of http://httpbin.org/user-agent
+  test:
+    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
+  expected response:
+    X-Proxy-By: AnyProxy
+*/
+module.exports = {
+  *beforeSendResponse(requestDetail, responseDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {
+      const newResponse = responseDetail.response;
+      newResponse.header['X-Proxy-By'] = 'AnyProxy';
+      return {
+        response: newResponse
+      };
+    }
+  }
+};

修改返回内容并延迟

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_data.js
/* 
+  sample: 
+    modify response data of http://httpbin.org/user-agent
+  test:
+    curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
+  expected response:
+    { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! --
+*/
+
+module.exports = {
+  *beforeSendResponse(requestDetail, responseDetail) {
+    if (requestDetail.url === 'http://httpbin.org/user-agent') {
+      const newResponse = responseDetail.response;
+      newResponse.body += '-- AnyProxy Hacked! --';
+      return new Promise((resolve, reject) => {
+        setTimeout(() => { // delay the response for 5s
+          resolve({ response: newResponse });
+        }, 5000);
+      });
+    }
+  },
+};

作为npm模块使用

+

AnyProxy可以作为一个npm模块使用,整合进其他工具。

+
+

如要启用https解析,请在代理服务器启动前自行调用AnyProxy.utils.certMgr相关方法生成证书,并引导用户信任安装。或引导用户使用anyproxy-ca方法。

+
+
    +
  • 引入
  • +
+
npm i anyproxy@beta --save # 4.0版正在beta中
    +
  • 使用举例
  • +
+
const AnyProxy = require('anyproxy');
+const options = {
+  port: 8001,
+  rule: require('myRuleModule'),
+  webInterface: {
+    enable: true,
+    webPort: 8002,
+    wsPort: 8003,
+  },
+  throttle: 10000,
+  forceProxyHttps: false,
+  silent: false
+};
+const proxyServer = new AnyProxy.ProxyServer(options);
+
+proxyServer.on('ready', () => { /* */ });
+proxyServer.on('error', (e) => { /* */ });
+proxyServer.start();
+
+//when finished
+proxyServer.close();
    +
  • Class: AnyProxy.proxyServer

    +
      +
    • 创建代理服务器

      +
      const proxy = new AnyProxy.proxyServer(options)
    • +
    • options

      +
        +
      • port {number} 必选,代理服务器端口
      • +
      • rule {object} 自定义规则模块
      • +
      • throttle {number} 限速值,单位kb/s,默认不限速
      • +
      • forceProxyHttps {boolean} 是否强制拦截所有的https,忽略规则模块的返回,默认false
      • +
      • silent {boolean} 是否屏蔽所有console输出,默认false
      • +
      • dangerouslyIgnoreUnauthorized {boolean} 是否忽略请求中的证书错误,默认false
      • +
      • webInterface {object} web版界面配置
          +
        • enable {boolean} 是否启用web版界面,默认false
        • +
        • webPort {number} web版界面端口号,默认8002
        • +
        +
      • +
      +
    • +
    • Event: ready

      +
        +
      • 代理服务器启动完成
      • +
      • 示例
      • +
      +
      proxy.on('ready', function() { })
    • +
    • Event: error

      +
        +
      • 代理服务器发生错误
      • +
      • 示例
      • +
      +
      proxy.on('error', function() { })
    • +
    • Method: start

      +
        +
      • 启动代理服务器
      • +
      • 示例
      • +
      +
      proxy.start();
    • +
    • Method: close

      +
        +
      • 关闭代理服务器
      • +
      • 示例
      • +
      +
      proxy.close();
    • +
    +
  • +
  • AnyProxy.utils.systemProxyMgr

    +
      +
    • 管理系统的全局代理配置,方法调用时可能会弹出密码框
    • +
    • 使用示例
    • +
    +
    // 配置127.0.0.1:8001为全局http代理服务器
    +AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');    
    +
    +// 关闭全局代理服务器
    +AnyProxy.utils.systemProxyMgr.disableGlobalProxy();
  • +
  • AnyProxy.utils.certMgr

    +
      +
    • 管理AnyProxy的证书
    • +
    • AnyProxy.utils.certMgr.ifRootCAFileExists()
        +
      • 校验系统内是否存在AnyProxy的根证书
      • +
      +
    • +
    • AnyProxy.utils.certMgr.generateRootCA(callback)
        +
      • 生成AnyProxy的rootCA,完成后请引导用户信任.crt文件
      • +
      +
    • +
    • 样例
    • +
    +
      const AnyProxy = require('AnyProxy');
    +  const exec = require('child_process').exec;
    +
    +  if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {
    +    AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {
    +      // let users to trust this CA before using proxy
    +      if (!error) {
    +        const certDir = require('path').dirname(keyPath);
    +        console.log('The cert is generated at', certDir);
    +        const isWin = /^win/.test(process.platform);
    +        if (isWin) {
    +          exec('start .', { cwd: certDir });
    +        } else {
    +          exec('open .', { cwd: certDir });
    +        }
    +      } else {
    +        console.error('error when generating rootCA', error);
    +      }
    +    });
    +  }
  • +
+

关于AnyProxy

+ +

配置帮助

+

OSX系统信任CA证书

+
    +
  • 类似这种报错都是因为系统没有信任AnyProxy生成的CA所造成的
  • +
+

+
+

警告:CA证书和系统安全息息相关,建议亲自生成,并妥善保管

+
+

安装CA:

+
    +
  • 双击打开rootCA.crt

    +
  • +
  • 确认将证书添加到login或system

    +
  • +
+

+
    +
  • 找到刚刚导入的AnyProxy证书,配置为信任(Always Trust)
  • +
+

+

Windows系统信任CA证书

+

+

配置OSX系统代理

+
    +
  • 在wifi高级设置中,配置http代理即可
  • +
+

+

配置浏览器HTTP代理

+ +

+

配置iOS/Android系统代理

+
    +
  • 代理服务器都在wifi设置中配置

    +
  • +
  • iOS HTTP代理配置

    +
  • +
+

+
    +
  • Android HTTP代理配置
  • +
+

+
+
+ + + + + + \ No newline at end of file diff --git a/4.x/en.html b/4.x/en.html new file mode 100644 index 0000000..fee6b10 --- /dev/null +++ b/4.x/en.html @@ -0,0 +1,820 @@ + + + + + + + AnyProxy + + + + + + + + + + + + + Fork me on GitHub + + +
+ +

AnyProxy

+

AnyProxy is a fully configurable http/https proxy in NodeJS. Version 4.0 is in beta now.

+

Ref: Chinese Doc 中文文档

+

Github:

+ +

Features:

+
    +
  • Offer you the ablity to handle http traffic by invoking a js module
  • +
  • Intercept https
  • +
  • GUI webinterface
  • +
+

Change Logs since 3.x:

+
    +
  • Support Promise and Generator in rule module
  • +
  • Simplified interface in rule module
  • +
  • A newly designed web interface
  • +
+

+

Quick start

+

install

+
npm install -g anyproxy@beta # 4.x is in beta now

launch

+
    +
  • start AnyProxy in command line, with default port 8001
  • +
+
anyproxy
    +
  • now you can use http proxy server by 127.0.0.1:8001
  • +
  • visit http://127.0.0.1:8002 to see the http requests
  • +
+

other commands

+
    +
  • specify the port of http proxy
  • +
+
anyproxy --port 1080

Proxy https request

+
    +
  • AnyProxy does NOT intercept https requests by default. To view decrypted info, you have to config the CA certificate.
  • +
+
+

Under the hood, AnyProxy decryptes https requests by man-in-the-middle attack. Users have to trust the CA cert in advance. Otherwise, client side will issue errors about unsecure network.

+
+
    +
  • generate certifycates and intercept
  • +
+
anyproxy-ca #generate root CA. manually trust it after that.
+anyproxy --intercept #launch anyproxy and intercept all https traffic
+

Use rule module

+

AnyProxy provides the ability to load your own rules written in javascript. With rule module, you could customize the logic to handle requests.

+
+

Make sure your rule file is got from a trusted source. Otherwise, you may face some unknown security risk.

+
+

Rule module could do the following stuff:

+
    +
  • intercept and modify the request which is being sent
      +
    • editable fields include request header, body, target address
    • +
    +
  • +
  • intercept and modify the response from server
      +
    • editable fields include response status code, header, body
    • +
    +
  • +
  • intercept https requests, modify request and response
  • +
+

sample

+
    +
  • Target

    + +
  • +
  • Step 1,Write the rule file, save as sample.js

    +
    // file: sample.js
    +module.exports = {
    +  summary: 'a rule to modify response',
    +  *beforeSendResponse(requestDetail, responseDetail) {
    +    if (requestDetail.url === 'http://httpbin.org/user-agent') {
    +      const newResponse = responseDetail.response;
    +      newResponse.body += '-- AnyProxy Hacked! --';
    +      return new Promise((resolve, reject) => {
    +        setTimeout(() => { // delay
    +          resolve({ response: newResponse });
    +        }, 5000);
    +      });
    +    }
    +  },
    +};
  • +
  • Step 2, start AnyProxy and load the rule file

    +
      +
    • run anyproxy --rule sample.js
    • +
    +
  • +
  • Step 3, test

    +
      +
    • use curl

      +
      curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
    • +
    • use browser. Point the http proxy of browser to 127.0.0.1:8001, then visit http://httpbin.org/user-agent

      +
    • +
    • the expected response from proxy is

      +
    • +
    +
    {
    +  "user-agent": "curl/7.43.0"
    +}
    +- AnyProxy Hacked!
  • +
  • Step 4, view the request log

    + +
  • +
+

the entire process

+
    +
  • The flow chart is as follows
  • +
+

+
    +
  • When got an http request, the entire process of proxy server is

    +
      +
    • AnyProxy collects all the quest info, include method, header, body
    • +
    • AnyProxy calls beforeSendRequest of the rule module. Rule module deal the request, return new request param or response content
    • +
    • If beforeSendRequest returns the response content, AnyProxy will send the response to client without sending to target server. The process ends here.
    • +
    • Send request to target server, collect response
    • +
    • Call beforeSendResponse of the rule module. Rule module deal the response data
    • +
    • Send response to client
    • +
    +
  • +
  • When AnyProxy get https request, it could replace the certificate and decrypt the request data

    +
      +
    • AnyProxy calls beforeDealHttpsRequest of the rule module
    • +
    • If the function returns true, AnyProxy will do the man-in-the-middle attack to it. Otherwise, the request will not be dealed.
    • +
    +
  • +
+

how to load rule module

+
    +
  • use local file

    +
    anyproxy --rule ./rule.js
  • +
  • use an online rule file

    +
    anyproxy --rule https://sample.com/rule.js
  • +
  • use an npm module

    +
      +
    • AnyProxy uses require() to load rule module. You could either load a local npm module or a global-installed one.
    • +
    +
    anyproxy --rule ./myRulePkg/ #local module
    +npm i -g myRulePkg && anyproxy --rule myRulePkg #global-installed module
  • +
+

Rule module interface

+

A typical rule module is as follows. All the functions are optional, just write the part you are interested in.

+
module.exports = {
+  // introduction
+  summary: 'my customized rule for AnyProxy', 
+  // intercept before send request to server
+  *beforeSendRequest(requestDetail) { /* ... */ }, 
+  // deal response before send to client 
+  *beforeSendResponse(requestDetail, responseDetail) { /* ... */ }, 
+  // if deal https request
+  *beforeDealHttpsRequest(requestDetail) { /* ... */ }, 
+  // error happened when dealing requests
+  *onError(requestDetail, error) { /* ... */ }, 
+  // error happened when connect to https server
+  *onConnectError(requestDetail, error) { /* ... */ } 
+};
+

All functions in your rule file, except summary, are all driven by co . They should be yieldable, i.e. return a promise or be a generator function.

+
+

summary

+

summary

+
    +
  • Introduction of this rule file. AnyProxy will read this field and give some tip to user.
  • +
+

beforeSendRequest

+

beforeSendRequest(requestDetail)

+
    +
  • Before sending request to server, AnyProxy will call beforeSendRequest with param requestDetail
  • +
  • requestDetail
      +
    • protocol {string} the protocol to use, http or https
    • +
    • requestOptions {object} the options of the request-to-go, a param of require('http').request . ref: https://nodejs.org/api/http.html#http_http_request_options_callback
    • +
    • requestData {object} request body
    • +
    • url {string} request url
    • +
    • _req {object} the native node.js request object
    • +
    +
  • +
  • e.g. When requesting anyproxy.io, requestDetail is something like the following

    +
    {
    +  protocol: 'http',
    +  url: 'http://anyproxy.io/',
    +  requestOptions: {
    +    hostname: 'anyproxy.io',
    +    port: 80,
    +    path: '/',
    +    method: 'GET',
    +    headers: {
    +      Host: 'anyproxy.io',
    +      'Proxy-Connection': 'keep-alive',
    +      'User-Agent': '...'
    +    }
    +  },
    +  requestData: '...',
    +  _req: { /* ... */}
    +}
  • +
  • Any of these return values are valid

    +
      +
    • do nothing, and return null
    • +
    +
    return null;
      +
    • modify the request protocol,i.e. force use https
    • +
    +
    return {
    +  protocol: 'https'
    +};
      +
    • modify request param
    • +
    +
    var newOption = Object.assign({}, requestDetail.requestOptions);
    +newOption.path = '/redirect/to/another/path';
    +return {
    +  requestOptions: newOption
    +};
      +
    • modify request body
    • +
    +
    return {
    +  requestData: 'my new request data'
    +  // requestOptions can also be used here
    +};
      +
    • give response to the client, not sending request any longer. statusCode headersare required is this situation.
    • +
    +
    return {
    +  response: {
    +    statusCode: 200,
    +    header: { 'content-type': 'text/html' },
    +    body: 'this could be a <string> or <buffer>'
    +  }
    +};
  • +
+

beforeSendResponse

+

beforeSendResponse(requestDetail, responseDetail)

+
    +
  • Before sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail
  • +
  • requestDetail is the same param as in beforeSendRequest
  • +
  • responseDetail
      +
    • response {object} the response from server, includes statusCode header body
    • +
    • _res {object} the native node.js response object
    • +
    +
  • +
  • e.g. When requesting anyproxy.io, responseDetail is something like the following

    +
    { 
    +  response: { 
    +    statusCode: 200,
    +    header: { 
    +      'Content-Type': 'image/gif',
    +      Connection: 'close',
    +      'Cache-Control': '...'
    +    },
    +    body: '...'
    +  },
    +  _res: { /* ... */ }
    +}
  • +
  • Any of these return values are valid

    +
      +
    • do nothing, and return null
    • +
    +
    return null;
      +
    • modify the response status code
    • +
    +
    var newResponse = Object.assign({}, responseDetail.reponse);
    +newResponse.statusCode = 404;
    +return {
    +  response: newResponse
    +};
      +
    • modify the response content
    • +
    +
    var newResponse = Object.assign({}, responseDetail.reponse);
    +newResponse.body += '--from anyproxy--';
    +return {
    +  response: newResponse
    +};
  • +
+

beforeDealHttpsRequest

+

beforeDealHttpsRequest(requestDetail)

+
    +
  • When receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail
  • +
  • If configed with forceProxyHttps in launching, AnyProxy will skip calling this method
  • +
  • Only by returning true, AnyProxy will try to replace the certificate and intercept the https request.
  • +
  • requestDetail
      +
    • host {string} the target host to request. Due to the request protocol, full url couldn't be got here
    • +
    • _req {object} the native node.js request object. The _req here refers to the CONNECT request.
    • +
    +
  • +
  • return value
      +
    • true or false, whether AnyProxy should intercept the https request
    • +
    +
  • +
+

onError

+

onError(requestDetail, error)

+
    +
  • AnyProxy will call this method when an error happened in request handling.
  • +
  • Errors usually are issued during requesting, e.g. DNS failure, request timeout
  • +
  • requestDetail is the same one as in beforeSendRequest
  • +
  • Any of these return values are valid

    +
      +
    • do nothing, and AnyProxy will response a default error page
    • +
    +
    return null;
      +
    • return a customized error page
    • +
    +
    return {
    +  response: {
    +    statusCode: 200,
    +    header: { 'content-type': 'text/html' },
    +    body: 'this could be a <string> or <buffer>'
    +  }
    +};
  • +
+

onConnectError

+

onConnectError(requestDetail, error)

+
    +
  • AnyProxy will call this method when failed to connect target server in https request
  • +
  • requestDetail is the same one as in beforeDealHttpsRequest
  • +
  • no return value is required
  • +
+

FAQ

+
    +
  • Q: can not deal https request in rule module.
  • +
  • A: Any of these options could be used to change the way AnyProxy deall https requests

    +
      +
    1. config --intercept when luanching AnyProxy via cli, or use forceProxyHttps when using as an npm module
    2. +
    3. place a beforeDealHttpsRequest function in your rule file and determine which request to intercept by your own.
    4. +
    +
  • +
  • Q: get an error says function is not yieldable

    +
  • +
  • A: Rule module is driven by co. The functions inside should be yieldable, i.e. return a promise or be a generator function.
  • +
+

Rule Samples

+
    +
  • here are some samples about frequently used rule file
  • +
  • try these samples by anyproxy --rule http://....js
  • +
  • how to test with curl:
      +
    • request the server directly curl http://httpbin.org/
    • +
    • request the server via proxy curl http://httpbin.org/ --proxy http://127.0.0.1:8001
    • +
    +
  • +
+

use local response

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_use_local_response.js
/* 
+  sample: 
+    intercept all requests toward httpbin.org, use a local response
+  test:
+    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    const localResponse = {
+      statusCode: 200,
+      header: { 'Content-Type': 'application/json' },
+      body: '{"hello": "this is local response"}'
+    };
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      return {
+        response: localResponse
+      };
+    }
+  },
+};

modify request header

+
    +
  • modify the user-agent sent to httpbin.org
  • +
+
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_header.js
/* 
+  sample: 
+    modify the user-agent in requests toward httpbin.org
+  test:
+    curl http://httpbin.org/user-agent --proxy http://127.0.0.1:8001
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newRequestOptions = requestDetail.requestOptions;
+      newRequestOptions.headers['User-Agent'] = 'AnyProxy/0.0.0';
+      return {
+        requestOptions: newRequestOptions
+      };
+    }
+  },
+};

modify request body

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_data.js
/* 
+  sample: 
+    modify the post data towards http://httpbin.org/post
+  test:
+    curl -H "Content-Type: text/plain" -X POST -d 'original post data' http://httpbin.org/post --proxy http://127.0.0.1:8001
+  expected response:
+    { "data": "i-am-anyproxy-modified-post-data" }
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org/post') === 0) {
+      return {
+        requestData: 'i-am-anyproxy-modified-post-data'
+      };
+    }
+  },
+};

modify the request target

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_path.js
/* 
+  sample: 
+    redirect all httpbin.org requests to http://httpbin.org/user-agent
+  test:
+    curl http://httpbin.org/any-path --proxy http://127.0.0.1:8001
+  expected response:
+    { "user-agent": "curl/7.43.0" }
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newRequestOptions = requestDetail.requestOptions;
+      newRequestOptions.path = '/user-agent';
+      newRequestOptions.method = 'GET';
+      return {
+        requestOptions: newRequestOptions
+      };
+    }
+  },
+};

modify request protocol

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_request_protocol.js
/* 
+  sample: 
+    redirect all http requests of httpbin.org to https
+  test:
+    curl 'http://httpbin.org/get?show_env=1' --proxy http://127.0.0.1:8001
+  expected response:
+    { "X-Forwarded-Protocol": "https" }
+*/
+module.exports = {
+  *beforeSendRequest(requestDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newOption = requestDetail.requestOptions;
+      newOption.port = 443;
+      return {
+        protocol: 'https',
+        requestOptions: newOption
+      };
+    }
+  }
+};

modify response status code

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_statuscode.js
/* 
+  sample: 
+    modify all status code of http://httpbin.org/ to 404
+  test:
+    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
+  expected response:
+    HTTP/1.1 404 Not Found
+*/
+module.exports = {
+  *beforeSendResponse(requestDetail, responseDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org') === 0) {
+      const newResponse = responseDetail.response;
+      newResponse.statusCode = 404;
+      return {
+        response: newResponse
+      };
+    }
+  }
+};

modify the response header

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_header.js
/* 
+  sample: 
+    modify response header of http://httpbin.org/user-agent
+  test:
+    curl -I 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
+  expected response:
+    X-Proxy-By: AnyProxy
+*/
+module.exports = {
+  *beforeSendResponse(requestDetail, responseDetail) {
+    if (requestDetail.url.indexOf('http://httpbin.org/user-agent') === 0) {
+      const newResponse = responseDetail.response;
+      newResponse.header['X-Proxy-By'] = 'AnyProxy';
+      return {
+        response: newResponse
+      };
+    }
+  }
+};

modify response data and delay

+ +
anyproxy --rule https://raw.githubusercontent.com/alibaba/anyproxy/4.x/rule_sample/sample_modify_response_data.js
/* 
+  sample: 
+    modify response data of http://httpbin.org/user-agent
+  test:
+    curl 'http://httpbin.org/user-agent' --proxy http://127.0.0.1:8001
+  expected response:
+    { "user-agent": "curl/7.43.0" } -- AnyProxy Hacked! --
+*/
+
+module.exports = {
+  *beforeSendResponse(requestDetail, responseDetail) {
+    if (requestDetail.url === 'http://httpbin.org/user-agent') {
+      const newResponse = responseDetail.response;
+      newResponse.body += '-- AnyProxy Hacked! --';
+      return new Promise((resolve, reject) => {
+        setTimeout(() => { // delay the response for 5s
+          resolve({ response: newResponse });
+        }, 5000);
+      });
+    }
+  },
+};

Use AnyProxy as an npm module

+

AnyProxy can be used as an npm module

+
+

To enable https feature, please guide users to use anyproxy-ca in cli. Or use methods under AnyProxy.utils.certMgr to generate certificates.

+
+
    +
  • install
  • +
+
npm i anyproxy@beta --save # 4.0 is in beta now
    +
  • sample
  • +
+
const AnyProxy = require('anyproxy');
+const options = {
+  port: 8001,
+  rule: require('myRuleModule'),
+  webInterface: {
+    enable: true,
+    webPort: 8002,
+    wsPort: 8003,
+  },
+  throttle: 10000,
+  forceProxyHttps: false,
+  silent: false
+};
+const proxyServer = new AnyProxy.ProxyServer(options);
+
+proxyServer.on('ready', () => { /* */ });
+proxyServer.on('error', (e) => { /* */ });
+proxyServer.start();
+
+//when finished
+proxyServer.close();
    +
  • Class: AnyProxy.proxyServer

    +
      +
    • create a proxy server

      +
      const proxy = new AnyProxy.proxyServer(options)
    • +
    • options

      +
        +
      • port {number} required, port number of proxy server
      • +
      • rule {object} your rule module
      • +
      • throttle {number} throttle in kb/s, unlimited for default
      • +
      • forceProxyHttps {boolean} in force intercept all https request, false for default
      • +
      • silent {boolean} if keep silent in console, false for defaultfalse
      • +
      • dangerouslyIgnoreUnauthorized {boolean} if ignore certificate error in request, false for default
      • +
      • webInterface {object} config for web interface
          +
        • enable {boolean} if enable web interface, false for default
        • +
        • webPort {number} port number for web interface
        • +
        +
      • +
      +
    • +
    • Event: ready

      +
        +
      • emit when proxy server is ready
      • +
      • sample
      • +
      +
      proxy.on('ready', function() { })
    • +
    • Event: error

      +
        +
      • emit when error happened inside proxy server
      • +
      • sample
      • +
      +
      proxy.on('error', function() { })
    • +
    • Method: start

      +
        +
      • start proxy server
      • +
      • sample
      • +
      +
      proxy.start();
    • +
    • Method: close

      +
        +
      • close proxy server
      • +
      • sample
      • +
      +
      proxy.close();
    • +
    +
  • +
  • AnyProxy.utils.systemProxyMgr

    +
      +
    • manage the system proxy config. sudo password may be required
    • +
    • sample
    • +
    +
    // set 127.0.0.1:8001 as system http server
    +AnyProxy.utils.systemProxyMgr.enableGlobalProxy('127.0.0.1', '8001');    
    +
    +// disable global proxy server
    +AnyProxy.utils.systemProxyMgr.disableGlobalProxy();
  • +
  • AnyProxy.utils.certMgr

    +
      +
    • Manage certificates of AnyProxy
    • +
    • AnyProxy.utils.certMgr.ifRootCAFileExists()
        +
      • detect if AnyProx rootCA exists
      • +
      +
    • +
    • AnyProxy.utils.certMgr.generateRootCA(callback)
        +
      • generate a rootCA
      • +
      +
    • +
    • Sample
    • +
    +
      const AnyProxy = require('AnyProxy');
    +  const exec = require('child_process').exec;
    +
    +  if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) {
    +    AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => {
    +      // let users to trust this CA before using proxy
    +      if (!error) {
    +        const certDir = require('path').dirname(keyPath);
    +        console.log('The cert is generated at', certDir);
    +        const isWin = /^win/.test(process.platform);
    +        if (isWin) {
    +          exec('start .', { cwd: certDir });
    +        } else {
    +          exec('open .', { cwd: certDir });
    +        }
    +      } else {
    +        console.error('error when generating rootCA', error);
    +      }
    +    });
    +  }
  • +
+

About AnyProxy

+ +

Appendix

+

Config root CA in OSX

+
    +
  • this kind of errors is usually caused by untrusted root CA
  • +
+

+
+

Warning: please keep your root CA safe since it may influence your system security.

+
+

install :

+
    +
  • double click rootCA.crt

    +
  • +
  • add cert into login or system

    +
  • +
+

+
    +
  • find the newly imported AnyProxy certificates, configured as Always Trust
  • +
+

+

trust root CA in windows

+

+

config OSX system proxy

+
    +
  • the config is in wifi - advanced
  • +
+

+

config http proxy server

+ +

+

config iOS/Android proxy server

+
    +
  • proxy settings are placed in wifi setting

    +
  • +
  • iOS

    +
  • +
+

+
    +
  • Android
  • +
+

+
+
+ + + + + + \ No newline at end of file