1.fix record issue with https 2.rewrite rule scheme

This commit is contained in:
加里
2014-09-01 16:38:43 +08:00
parent 0f4c1ac017
commit 48fb1cbe40
20 changed files with 633 additions and 373 deletions

View File

@@ -8,43 +8,28 @@ var http = require("http"),
async = require('async'),
color = require("colorful"),
Buffer = require('buffer').Buffer,
httpsServerMgr = require("./httpsServerMgr"),
userRule = require("./rule.js"); //TODO - to be configurable
httpsServerMgr = require("./httpsServerMgr");
var httpsServerMgrInstance = new httpsServerMgr();
//default rule
var handleRule = {
map :[
// {
// host :".",
// path :"/path/test",
// localFile :"",
// localDir :"~/"
// }
]
,httpsConfig:{
bypassAll : true,
interceptDomains:["^.*alibaba-inc\.com$"]
}
};
var httpsServerMgrInstance = new httpsServerMgr(),
userRule = require("./rule_default.js"); //default rule file
function userRequestHandler(req,userRes){
var host = req.headers.host,
var host = req.headers.host,
urlPattern = url.parse(req.url),
path = urlPattern.path,
callback = null, //TODO : remove callback
protocol = (!!req.connection.encrypted && !/http:/.test(req.url)) ? "https" : "http",
resourceInfo = {},
resourceInfo,
resourceInfoId = -1;
//record
resourceInfo.host = host;
resourceInfo.method = req.method;
resourceInfo.path = path;
resourceInfo.url = protocol + "://" + host + path;
resourceInfo.req = req;
resourceInfo.startTime = new Date().getTime();
resourceInfo = {
host : host,
method : req.method,
path : path,
url : protocol + "://" + host + path,
req : req,
startTime : new Date().getTime()
};
try{
resourceInfoId = GLOBAL.recorder.appendRecord(resourceInfo);
@@ -101,14 +86,17 @@ function userRequestHandler(req,userRes){
var statusCode = res.statusCode;
statusCode = userRule.replaceResponseStatusCode(req,res,statusCode) || statusCode;
var resHeader = res.headers;
resHeader = userRule.replaceResponseHeader(req,res,resHeader) || resHeader;
var resHeader = userRule.replaceResponseHeader(req,res,res.headers) || res.headers;
resHeader = lower_keys(resHeader);
//remove content-encoding
// delete resHeader['content-encoding'];
/* remove gzip related header, and ungzip the content */
var ifServerGzipped = /gzip/i.test(resHeader['content-encoding']);
delete resHeader['content-encoding'];
delete resHeader['content-length'];
userRes.writeHead(statusCode, resHeader);
//waiting for data
var resData = [],
length;
@@ -122,12 +110,11 @@ function userRequestHandler(req,userRes){
userCustomResData;
async.series([
//TODO : manage gzip
//unzip server res
//ungzip server res
function(callback){
serverResData = Buffer.concat(resData);
if(/gzip/i.test(res.headers['content-encoding'])){
if(ifServerGzipped ){
zlib.gunzip(serverResData,function(err,buff){
serverResData = buff;
callback();
@@ -140,20 +127,6 @@ function userRequestHandler(req,userRes){
},function(callback){
userCustomResData = userRule.replaceServerResData(req,res,serverResData);
//gzip users' string if necessary
if(typeof userCustomResData == "string" && /gzip/i.test(res.headers['content-encoding']) ){
zlib.gzip(userCustomResData,function(err,data){
userCustomResData = data;
console.log(data);
callback();
});
}else{
callback();
}
//generate response data
},function(callback){
serverResData = userCustomResData || serverResData;
callback();
@@ -168,18 +141,17 @@ function userRequestHandler(req,userRes){
//send response
},function(callback){
userRes.write(serverResData);
userRes.end();
userRes.end(serverResData);
callback();
//udpate record info
},function(callback){
resourceInfo.endTime = new Date().getTime();
resourceInfo.res = res; //TODO : replace res header / statusCode ?
resourceInfo.resBody = serverResData;
resourceInfo.length = serverResData.length;
resourceInfo.endTime = new Date().getTime();
resourceInfo.statusCode = statusCode;
resourceInfo.resHeader = resHeader;
resourceInfo.resBody = serverResData;
resourceInfo.length = serverResData.length;
try{
GLOBAL.recorder.updateRecord(resourceInfoId,resourceInfo);
}catch(e){}
@@ -208,80 +180,112 @@ function userRequestHandler(req,userRes){
}
function connectReqHandler(req, socket, head){
var hostname = req.url.split(":")[0],
var host = req.url.split(":")[0],
targetPort= req.url.split(":")[1],
httpsRule = handleRule.httpsConfig;
resourceInfo,
resourceInfoId;
var shouldBypass = !!httpsRule.bypassAll;
if(!shouldBypass){ //read rules
shouldBypass = true;
for(var index in httpsRule.interceptDomains){
var reg = new RegExp(httpsRule.interceptDomains[index]);
if( reg.test(hostname) ){
shouldBypass = false;
break;
}
}
}
console.log(color.green("\nreceived https CONNECT request " + hostname));
if(shouldBypass){
console.log("==>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
}
var shouldIntercept = userRule.shouldInterceptHttpsReq(req);
console.log(color.green("\nreceived https CONNECT request " + host));
if(shouldIntercept){
console.log("==>will forward to local https server");
}else{
//TODO : remote port other than 433
console.log("==>meet the rules, will forward to local https server");
//forward the https-request to local https server
httpsServerMgrInstance.fetchPort(hostname,userRequestHandler,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);
}
});
console.log("==>will bypass the man-in-the-middle proxy");
}
//record
resourceInfo = {
host : host,
method : req.method,
path : "",
url : "https://" + host,
req : req,
startTime : new Date().getTime()
};
resourceInfoId = GLOBAL.recorder.appendRecord(resourceInfo);
var proxyPort, proxyHost;
async.series([
//find port
function(callback){
if(shouldIntercept){
//TODO : remote port other than 433
httpsServerMgrInstance.fetchPort(host,userRequestHandler,function(err,port){
if(!err && port){
proxyPort = port;
proxyHost = "127.0.0.1";
callback();
}else{
callback(err);
}
});
}else{
proxyPort = targetPort;
proxyHost = host;
callback();
}
//connect
},function(callback){
try{
var conn = net.connect(proxyPort, proxyHost, function(){
socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8', function() {
conn.pipe(socket);
socket.pipe(conn);
callback();
});
});
conn.on("error",function(e){
console.log("err when connect to __host".replace(/__host/,host));
});
}catch(e){
console.log("err when connect to remote https server (__host)".replace(/__host/,host));
}
//update record
},function(callback){
resourceInfo.endTime = new Date().getTime();
resourceInfo.statusCode = "200";
resourceInfo.resHeader = {};
resourceInfo.resBody = "";
resourceInfo.length = 0;
try{
GLOBAL.recorder.updateRecord(resourceInfoId,resourceInfo);
}catch(e){}
callback();
}
],function(err,result){
if(err){
console.log("err " + err);
throw err;
}
});
}
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
function lower_keys(obj){
for(var key in obj){
var val = obj[key];
delete obj[key];
obj[key.toLowerCase()] = val;
}
return obj;
}
//TODO : reactive this function
function setRules(newRule){
if(!newRule){
return;
}
if(!newRule.map || !newRule.httpsConfig){
throw(new Error("invalid rule schema"));
}else{
handleRule = newRule;
userRule = newRule;
}
}