anyproxy/proxy.js
2014-10-21 13:12:47 +08:00

256 lines
8.9 KiB
JavaScript

//mix some modules to global.util
try{
GLOBAL.util = require('./lib/util');
GLOBAL.util['iconv-lite'] = require("iconv-lite");
GLOBAL.util['colorful'] = require("colorful");
GLOBAL.util['path'] = require("path");
GLOBAL.util['jsdom'] = require('jsdom');
GLOBAL.util['jquery'] = require('jquery');
GLOBAL.util['Socks5ClientHttpAgent'] = require('socks5-http-client/lib/Agent');
GLOBAL.util['Socks5ClientHttpsAgent'] = require('socks5-https-client/lib/Agent');
}catch(e){}
var http = require('http'),
https = require('https'),
fs = require('fs'),
async = require("async"),
url = require('url'),
program = require('commander'),
color = require('colorful'),
certMgr = require("./lib/certMgr"),
getPort = require("./lib/getPort"),
requestHandler = require("./lib/requestHandler"),
Recorder = require("./lib/recorder"),
inherits = require("util").inherits,
util = require("./lib/util"),
path = require("path"),
juicer = require('juicer'),
events = require("events"),
express = require("express"),
ip = require("ip"),
fork = require("child_process").fork;
GLOBAL.recorder = new Recorder();
var T_TYPE_HTTP = 0,
T_TYPE_HTTPS = 1,
DEFAULT_PORT = 8001,
DEFAULT_WEB_PORT = 8002,
DEFAULT_WEBSOCKET_PORT = 8003,
DEFAULT_CONFIG_PORT = 8088,
DEFAULT_HOST = "localhost",
DEFAULT_TYPE = T_TYPE_HTTP;
var default_rule = require('./lib/rule_default');
var anyproxyHome = path.join(util.getUserHome(),"/.anyproxy/");
if(!fs.existsSync(anyproxyHome)){
fs.mkdirSync(anyproxyHome);
}
if(fs.existsSync(path.join(anyproxyHome,"rule_default.js"))){
default_rule = require(path.join(anyproxyHome,"rule_default"));
}
if(fs.existsSync(process.cwd() + '/rule.js')){
default_rule = require(process.cwd() + '/rule');
}
//option
//option.type : 'http'(default) or 'https'
//option.port : 8001(default)
//option.hostname : localhost(default)
//option.rule : ruleModule
//option.webPort : 8002(default)
//option.socketPort : 8003(default)
//option.webConfigPort : 8088(default)
function proxyServer(option){
option = option || {};
var self = this,
proxyType = /https/i.test(option.type || DEFAULT_TYPE) ? T_TYPE_HTTPS : T_TYPE_HTTP ,
proxyPort = option.port || DEFAULT_PORT,
proxyHost = option.hostname || DEFAULT_HOST,
proxyRules = option.rule || default_rule,
proxyWebPort = option.webPort || DEFAULT_WEB_PORT, //port for web interface
socketPort = option.socketPort || DEFAULT_WEBSOCKET_PORT, //port for websocket
proxyConfigPort = option.webConfigPort || DEFAULT_CONFIG_PORT; //port to ui config server
requestHandler.setRules(proxyRules); //TODO : optimize calling for set rule
self.httpProxyServer = null;
async.series(
[
//creat proxy server
function(callback){
if(proxyType == T_TYPE_HTTPS){
certMgr.getCertificate(proxyHost,function(err,keyContent,crtContent){
if(err){
callback(err);
}else{
self.httpProxyServer = https.createServer({
key : keyContent,
cert: crtContent
},requestHandler.userRequestHandler);
callback(null);
}
});
}else{
self.httpProxyServer = http.createServer(requestHandler.userRequestHandler);
callback(null);
}
},
//listen CONNECT method for https over http
function(callback){
self.httpProxyServer.on('connect',requestHandler.connectReqHandler);
self.httpProxyServer.listen(proxyPort);
callback(null);
},
//start web interface
function(callback){
//web interface
var args = [proxyWebPort, socketPort, proxyConfigPort, requestHandler.getRuleSummary(), ip.address()];
var child_webServer = fork(path.join(__dirname,"./webServer.js"),args);
child_webServer.on("message",function(data){
if(data.type == "reqBody" && data.id){
child_webServer.send({
type : "body",
id : data.id,
body : GLOBAL.recorder.getBody(data.id)
});
}
});
//kill web server when father process exits
process.on("exit",function(){
child_webServer.kill();
});
GLOBAL.recorder.on("update",function(data){
child_webServer.send({
type: "update",
body: data
});
});
var configServer = new UIConfigServer(proxyConfigPort);
configServer.on("rule_changed",function() {
// console.log(arguments);
})
var tipText,webUrl;
webUrl = "http://" + ip.address() + ":" + proxyWebPort +"/";
tipText = "web interface started at : " + webUrl;
console.log(color.green(tipText));
tipText = "[alpha]qr code to for iOS client: " + webUrl + "qr";
console.log(color.green(tipText));
callback(null);
}
],
//final callback
function(err,result){
if(!err){
var tipText = (proxyType == T_TYPE_HTTP ? "Http" : "Https") + " proxy started at port " + proxyPort;
console.log(color.green(tipText));
}else{
var tipText = "err when start proxy server :(";
console.log(color.red(tipText));
console.log(err);
}
}
);
self.close = function(){
self.httpProxyServer && self.httpProxyServer.close();
console.log(color.green("server closed :" + proxyHost + ":" + proxyPort));
}
}
// BETA : UIConfigServer
function UIConfigServer(port){
var self = this;
var app = express(),
customerRule = {
summary: function(){
console.log("replace some response with local response");
return "replace some response with local response";
}
},
userKey;
port = port || DEFAULT_CONFIG_PORT;
customerRule.shouldUseLocalResponse = function(req,reqBody){
var url = req.url;
if(userKey){
var ifMatch = false;
userKey.map(function(item){
if(ifMatch) return;
var matchCount = 0;
if( !item.urlKey && !item.reqBodyKey){
ifMatch = false;
return;
}else{
if(!item.urlKey || (item.urlKey && url.indexOf(item.urlKey) >= 0 ) ){
++matchCount;
}
if(!item.reqBodyKey || (item.reqBodyKey && reqBody.toString().indexOf(item.reqBodyKey) >= 0) ){
++matchCount;
}
ifMatch = (matchCount==2);
if(ifMatch){
req.willResponse = item.localResponse;
}
}
});
return ifMatch;
}else{
return false;
}
};
customerRule.dealLocalResponse = function(req,reqBody,callback){
callback(200,{"content-type":"text/html"},req.willResponse)
return req.willResponse;
};
app.post("/update",function(req,res){
var data = "";
req.on("data",function(chunk){
data += chunk;
});
req.on("end",function(){
userKey = JSON.parse(data);
res.statusCode = 200;
res.setHeader("Content-Type", "application/json;charset=UTF-8");
res.end(JSON.stringify({success : true}));
requestHandler.setRules(customerRule);
self.emit("rule_changed");
});
});
app.use(express.static(__dirname + "/web_uiconfig"));
app.listen(port);
self.app = app;
}
inherits(UIConfigServer, events.EventEmitter);
module.exports.proxyServer = proxyServer;
module.exports.generateRootCA = certMgr.generateRootCA;
module.exports.isRootCAFileExists = certMgr.isRootCAFileExists;