AnyProxy

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

Ref: Chinese Doc 中文文档

Github:

Features:

Change Logs since 3.x:

Quick start

install

To Debian and Ubuntu users, you may need to install nodejs-legacy at the same time

sudo apg-get install nodejs-legacy

Then install the AnyProxy

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

launch

anyproxy

other commands

anyproxy --port 1080

Proxy https request

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.

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:

sample

the entire process

how to load rule 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

beforeSendRequest

beforeSendRequest(requestDetail)

beforeSendResponse

beforeSendResponse(requestDetail, responseDetail)

beforeDealHttpsRequest

beforeDealHttpsRequest(requestDetail)

onError

onError(requestDetail, error)

onConnectError

onConnectError(requestDetail, error)

FAQ

Rule Samples

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

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 = {
  summary: 'Rule to modify request data',
  *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.

npm i anyproxy@beta --save # 4.0 is in beta now
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();

About AnyProxy

Appendix

Config root CA in OSX

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

install :

trust root CA in windows

config OSX system proxy

config http proxy server

trust root CA in iOS

trust root CA in iOS >= 10.3

config iOS/Android proxy server

FAQ

The connection is not private

AnyProxy will propmt this message when the certificate of the site you're visiting is not issued by a trusted CA.(e.g. Node uses an hardcoded list of certificate authorities)

You could ignore this kind of error at your own risk. The following is a how-to guide.