mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-08-04 21:39:04 +00:00
add bin.js
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
var exec = require('child_process').exec,
|
||||
spawn = require('child_process').spawn,
|
||||
path = require("path"),
|
||||
fs = require("fs"),
|
||||
os = require("os"),
|
||||
asyncTaskMgr = require("./asyncTaskMgr");
|
||||
color = require('colorful'),
|
||||
asyncTask = require("./asyncTaskMgr");
|
||||
|
||||
var certDir = path.join(getUserHome(),"/.anyproxy_certs/"),
|
||||
asyncTaskMgr = new asyncTaskMgr();
|
||||
cmdDir = "./cert/",
|
||||
asyncTaskMgr = new asyncTask();
|
||||
|
||||
if(!fs.existsSync(certDir)){
|
||||
fs.mkdirSync(certDir);
|
||||
@@ -38,14 +41,16 @@ function getUserHome() {
|
||||
}
|
||||
|
||||
function createCert(hostname,callback){
|
||||
console.log("creating cert for :" + hostname);
|
||||
console.log(hostname);
|
||||
checkRootCA();
|
||||
|
||||
var cmd = "./gen-cer __host __path".replace(/__host/,hostname).replace(/__path/,certDir);
|
||||
exec(cmd,{cwd:"./cert/"},function(err,stdout,stderr){
|
||||
exec(cmd,{ cwd : cmdDir },function(err,stdout,stderr){
|
||||
if(err){
|
||||
callback && callback(new Error("error when generating certificate"),null);
|
||||
}else{
|
||||
console.log("certificate created for __HOST".replace(/__HOST/,hostname));
|
||||
var tipText = "certificate created for __HOST".replace(/__HOST/,hostname);
|
||||
console.log(color.yellow(color.bold("[internal https]")) + color.yellow(tipText));
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
@@ -55,6 +60,34 @@ function clearCerts(cb){
|
||||
exec("rm *.key *.csr *.crt",{cwd : certDir},cb);
|
||||
}
|
||||
|
||||
function checkRootCA(){
|
||||
var crtFile = path.join(cmdDir,"rootCA.crt"),
|
||||
keyFile = path.join(cmdDir,"rootCA.key");
|
||||
|
||||
if(!fs.existsSync(crtFile) || !fs.existsSync(keyFile)){
|
||||
console.log(color.red("can not find rootCA.crt or rootCA.key"));
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
function generateRootCA(){
|
||||
var spawnSteam = spawn("./gen-rootCA",['.'],{cwd:cmdDir,stdio: 'inherit'});
|
||||
|
||||
spawnSteam.on('close', function (code) {
|
||||
if(code == 0){
|
||||
console.log(color.green("rootCA generated"));
|
||||
console.log("now clearing temp certs...");
|
||||
clearCerts(function(){
|
||||
console.log(color.green("done"));
|
||||
process.exit(0);
|
||||
});
|
||||
}else{
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.generateRootCA = generateRootCA;
|
||||
module.exports.getCertificate = getCertificate;
|
||||
module.exports.createCert = createCert;
|
||||
module.exports.clearCerts = clearCerts;
|
||||
@@ -6,11 +6,16 @@ var getPort = require('./getPort'),
|
||||
fs = require('fs'),
|
||||
net = require('net'),
|
||||
url = require('url'),
|
||||
color = require('colorful'),
|
||||
certMgr = require("./certMgr"),
|
||||
requestHandler = require("./requestHandler");
|
||||
requestHandler = require("./requestHandler"),
|
||||
asyncTask = require("./asyncTaskMgr");
|
||||
|
||||
|
||||
var DEFAULT_RELEASE_TIME = 120*1000;
|
||||
|
||||
var asyncTaskMgr = new asyncTask();
|
||||
|
||||
module.exports =function(){
|
||||
var self = this;
|
||||
self.serverList = {
|
||||
@@ -30,40 +35,49 @@ module.exports =function(){
|
||||
|
||||
//server exists
|
||||
if(serverInfo){
|
||||
console.log("exists :" + hostname );
|
||||
serverInfo.lastestUse = new Date().getTime();
|
||||
port = serverInfo.port;
|
||||
userCB && userCB(null,port);
|
||||
|
||||
//create server with corresponding CA
|
||||
}else{
|
||||
console.log("creating :" + hostname );
|
||||
async.series([
|
||||
//find a clean port
|
||||
function(callback){
|
||||
getPort(function(cleanPort){
|
||||
port = cleanPort;
|
||||
callback(null,port);
|
||||
});
|
||||
}
|
||||
|
||||
//create server
|
||||
,function(callback){
|
||||
certMgr.getCertificate(hostname,function(err,keyContent,crtContent){
|
||||
var server = createHttpsServer(port,keyContent,crtContent);
|
||||
self.serverList[hostname] = {
|
||||
port : port,
|
||||
server : server,
|
||||
lastestUse : new Date().getTime()
|
||||
};
|
||||
console.log("https server @port __portNum for __HOST established".replace(/__portNum/,port).replace(/__HOST/,hostname));
|
||||
callback && callback(null,port);
|
||||
|
||||
});
|
||||
}
|
||||
],function(err,result){
|
||||
userCB && userCB(err,result[result.length - 1]);
|
||||
|
||||
asyncTaskMgr.addTask(hostname ,userCB,function(cb){
|
||||
createServer(cb);
|
||||
});
|
||||
|
||||
function createServer(cb){
|
||||
async.series([
|
||||
//find a clean port
|
||||
function(callback){
|
||||
getPort(function(cleanPort){
|
||||
port = cleanPort;
|
||||
callback(null,port);
|
||||
});
|
||||
}
|
||||
|
||||
//create server
|
||||
,function(callback){
|
||||
|
||||
certMgr.getCertificate(hostname,function(err,keyContent,crtContent){
|
||||
var server = createHttpsServer(port,keyContent,crtContent);
|
||||
self.serverList[hostname] = {
|
||||
port : port,
|
||||
server : server,
|
||||
lastestUse : new Date().getTime()
|
||||
};
|
||||
|
||||
var tipText = "https server @port __portNum for __HOST established".replace(/__portNum/,port).replace(/__HOST/,hostname);
|
||||
console.log(color.yellow(color.bold("[internal https]")) + color.yellow(tipText));
|
||||
callback && callback(null,port);
|
||||
|
||||
});
|
||||
}
|
||||
],function(err,result){
|
||||
cb && cb(err,result[result.length - 1]);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +89,7 @@ module.exports =function(){
|
||||
if( (timeNow - item.lastestUse) > DEFAULT_RELEASE_TIME){
|
||||
item.server.close();
|
||||
delete self.serverList[serverName];
|
||||
console.log("https server released : " + serverName);
|
||||
console.log(color.yellow(color.bold("[internal https]")) + color.yellow("https server released : " + serverName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +100,7 @@ function createHttpsServer(port,keyContent,crtContent){
|
||||
return https.createServer({
|
||||
key : keyContent,
|
||||
cert: crtContent
|
||||
},requestHandler).listen(port);
|
||||
},requestHandler.userRequestHandler).listen(port);
|
||||
|
||||
}
|
||||
|
||||
|
||||
8
lib/httpsServerMgr_test.js
Normal file
8
lib/httpsServerMgr_test.js
Normal file
@@ -0,0 +1,8 @@
|
||||
var httpsServerMgr = require("./httpsServerMgr");
|
||||
|
||||
|
||||
var instance = new httpsServerMgr();
|
||||
|
||||
instance.fetchPort("localhost",function(err,port){
|
||||
console.log(port);
|
||||
});
|
||||
@@ -1,29 +1,181 @@
|
||||
var http = require("http"),
|
||||
https = require("https");
|
||||
https = require("https"),
|
||||
net = require("net"),
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
pathUtil = require("path"),
|
||||
color = require("colorful"),
|
||||
httpsServerMgr = require("./httpsServerMgr");
|
||||
|
||||
function handler(req,userRes){
|
||||
console.log(req);
|
||||
var httpsServerMgrInstance = new httpsServerMgr();
|
||||
|
||||
var ifHttps = !!req.connection.encrypted && !/http:/.test(req.url);
|
||||
//default rule
|
||||
var handleRule = {
|
||||
map :[
|
||||
// {
|
||||
// host :".",
|
||||
// path :"/path/test",
|
||||
// localFile :"",
|
||||
// localDir :"~/"
|
||||
// }
|
||||
]
|
||||
,httpsConfig:{
|
||||
bypassAll : false,
|
||||
interceptDomains:["^.*alibaba-inc\.com$"]
|
||||
}
|
||||
};
|
||||
|
||||
var options = {
|
||||
hostname : req.headers.host,
|
||||
port : req.port || (ifHttps ? 443 : 80),
|
||||
path : req.url,
|
||||
method : req.method,
|
||||
headers : req.headers
|
||||
};
|
||||
function userRequestHandler(req,userRes){
|
||||
var host = req.headers.host,
|
||||
path = url.parse(req.url).path,
|
||||
ifLocalruleMatched = false;
|
||||
|
||||
var proxyReq = (ifHttps ? https : http).request(options, function(res) {
|
||||
userRes.writeHead(res.statusCode,res.headers);
|
||||
res.pipe(userRes);
|
||||
});
|
||||
console.log(color.green("\nreceived request to : " + host + path));
|
||||
/*
|
||||
req.url is wired
|
||||
in http server : http://www.baidu.com/a/b/c
|
||||
in https server : /work/alibaba
|
||||
*/
|
||||
|
||||
proxyReq.on("error",function(e){
|
||||
console.log("err with request :" + req.url);
|
||||
userRes.end();
|
||||
});
|
||||
proxyReq.end();
|
||||
for(var index in handleRule.map){
|
||||
var rule = handleRule.map[index];
|
||||
|
||||
var hostTest = new RegExp(rule.host).test(host),
|
||||
pathTest = new RegExp(rule.path).test(path);
|
||||
|
||||
if(hostTest && pathTest && (rule.localFile || rule.localDir) ){
|
||||
console.log("==>meet the rules, will map to local file");
|
||||
|
||||
var targetLocalfile = rule.localFile;
|
||||
if(!targetLocalfile){ //find file in dir //TODO : /a/b/c -> b/c
|
||||
var basename = pathUtil.basename(path);
|
||||
basename = basename.slice(0,basename.indexOf("?")); //remove chars after question mark
|
||||
targetLocalfile = pathUtil.join(rule.localDir,basename);
|
||||
}
|
||||
|
||||
console.log("==>local file: " + targetLocalfile);
|
||||
if(fs.existsSync(targetLocalfile)){
|
||||
try{
|
||||
var fsStream = fs.createReadStream(targetLocalfile);
|
||||
userRes.writeHead(200,{"cache-control":"max-age=0"}); //remove any cache related header
|
||||
fsStream.pipe(userRes);
|
||||
ifLocalruleMatched = true;
|
||||
break;
|
||||
}catch(e){
|
||||
console.log(e.message);
|
||||
}
|
||||
}else{
|
||||
console.log("file not exist : " + targetLocalfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ifLocalruleMatched){
|
||||
return;
|
||||
|
||||
}else{
|
||||
console.log("==>will forward to real server by proxy");
|
||||
var ifHttps = !!req.connection.encrypted && !/http:/.test(req.url);
|
||||
|
||||
var options = {
|
||||
hostname : req.headers.host,
|
||||
port : req.port || (ifHttps ? 443 : 80),
|
||||
path : path,
|
||||
method : req.method,
|
||||
headers : req.headers
|
||||
};
|
||||
|
||||
var proxyReq = (ifHttps ? https : http).request(options, function(res) {
|
||||
userRes.writeHead(res.statusCode,res.headers);
|
||||
res.pipe(userRes);
|
||||
});
|
||||
|
||||
proxyReq.on("error",function(e){
|
||||
console.log("err with request :" + req.url);
|
||||
userRes.end();
|
||||
});
|
||||
proxyReq.end();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = handler;
|
||||
function connectReqHandler(req, socket, head){
|
||||
var hostname = req.url.split(":")[0],
|
||||
targetPort= req.url.split(":")[1],
|
||||
httpsRule = handleRule.httpsConfig;
|
||||
|
||||
var shouldBypass = httpsRule.bypassAll;
|
||||
if(!shouldBypass){ //read rules
|
||||
for(var index in httpsRule.interceptDomains){
|
||||
var reg = new RegExp(httpsRule.interceptDomains[index]);
|
||||
if( reg.test(hostname) ){
|
||||
shouldBypass = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(color.green("\nreceived https CONNECT request " + hostname));
|
||||
|
||||
if(shouldBypass){
|
||||
console.log("==>meet the rules, will bypass the man-in-the-middle proxy");
|
||||
try{
|
||||
var conn = net.connect(targetPort, hostname, function(){
|
||||
socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8', function() {
|
||||
conn.pipe(socket);
|
||||
socket.pipe(conn);
|
||||
});
|
||||
});
|
||||
|
||||
conn.on("error",function(e){
|
||||
console.log("err when connect to __host".replace(/__host/,hostname));
|
||||
});
|
||||
}catch(e){
|
||||
console.log("err when connect to remote https server (__hostname)".replace(/__hostname/,hostname));//TODO
|
||||
}
|
||||
|
||||
}else{
|
||||
//TODO : remote port other than 433
|
||||
console.log("==>will forward to local https server");
|
||||
|
||||
//forward the https-request to local https server
|
||||
httpsServerMgrInstance.fetchPort(hostname,function(err,port){
|
||||
if(!err && port){
|
||||
try{
|
||||
var conn = net.connect(port, 'localhost', function(){ //TODO : localhost -> server
|
||||
socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8', function() {
|
||||
conn.pipe(socket);
|
||||
socket.pipe(conn);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
conn.on("error",function(e){
|
||||
console.log("err when connect to __host".replace(/__host/,hostname));
|
||||
});
|
||||
}catch(e){
|
||||
console.log("err when connect to local https server (__hostname)".replace(/__hostname/,hostname));//TODO
|
||||
}
|
||||
|
||||
}else{
|
||||
console.log("err fetch HTTPS server for host:" + hostname);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setRules(newRule){
|
||||
if(!newRule){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!newRule.map || !newRule.httpsConfig){
|
||||
throw(new Error("invalid rule schema"));
|
||||
}else{
|
||||
handleRule = newRule;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.userRequestHandler = userRequestHandler;
|
||||
module.exports.connectReqHandler = connectReqHandler;
|
||||
module.exports.setRules = setRules;
|
||||
|
||||
Reference in New Issue
Block a user