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

@ -45,16 +45,8 @@ function Recorder(){
if(!id || !info.resBody) return; if(!id || !info.resBody) return;
//add to body map //add to body map
//do not save image data //do not save image data
if(/image/.test(info.res.headers['content-type'])){ if(/image/.test(info.resHeader['content-type'])){
self.recordBodyMap[id] = "(image)"; self.recordBodyMap[id] = "(image)";
}else if(/gzip/.test(info.res.headers['content-encoding'])){
zlib.unzip(info.resBody,function(err,buffer){
if(err){
self.recordBodyMap[id] = "(err when unzip response buffer)";
}else{
self.recordBodyMap[id] = buffer.toString();
}
});
}else{ }else{
self.recordBodyMap[id] = info.resBody.toString(); self.recordBodyMap[id] = info.resBody.toString();
} }
@ -81,7 +73,7 @@ function normalizeInfo(id,info){
//general //general
singleRecord._id = id; singleRecord._id = id;
singleRecord.id = id; singleRecord.id = id;
singleRecord.url = info.url; singleRecord.url = info.url;
singleRecord.host = info.host; singleRecord.host = info.host;
singleRecord.path = info.path; singleRecord.path = info.path;
@ -92,13 +84,13 @@ function normalizeInfo(id,info){
singleRecord.startTime = info.startTime; singleRecord.startTime = info.startTime;
//res //res
if(info.res){ if(info.endTime){
singleRecord.statusCode= info.res.statusCode; singleRecord.statusCode= info.statusCode;
singleRecord.endTime = info.endTime; singleRecord.endTime = info.endTime;
singleRecord.resHeader = info.res.headers; singleRecord.resHeader = info.resHeader;
singleRecord.length = info.length; singleRecord.length = info.length;
if(info.res.headers['content-type']){ if(info.resHeader['content-type']){
singleRecord.mime = info.res.headers['content-type'].split(";")[0]; singleRecord.mime = info.resHeader['content-type'].split(";")[0];
}else{ }else{
singleRecord.mime = ""; singleRecord.mime = "";
} }

View File

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

View File

@ -1,148 +0,0 @@
module.exports = {
/*
thess functions are required
you may leave their bodies blank if necessary
*/
//whether to intercept this request by local logic
//if the return value is true, anyproxy will call dealLocalResponse to get response data and will not send request to remote server anymore
shouldUseLocalResponse : function(req){
if(req.method == "OPTIONS"){
return true;
}else{
return false;
}
},
//response to user via local logic, be called when shouldUseLocalResponse returns true
//you should call callback(statusCode,resHeader,responseData)
//e.g. callback(200,{"content-type":"text/html"},"hello world")
dealLocalResponse : function(req,callback){
if(req.method == "OPTIONS"){
callback(200,mergeCORSHeader(req.headers),"");
}
},
//req is user's request sent to the proxy server
// option is how the proxy server will send request to the real server. i.e. require("http").request(option,function(){...})
//you may return a customized option to replace the original option
replaceRequestOption : function(req,option){
var newOption = option;
// newOption = {
// hostname : "www.example.com",
// port : "80",
// path : '/',
// method : "GET",
// headers : {}
// };
return newOption;
},
//replace the request protocol when sending to the real server
//protocol : "http" or "https"
replaceRequestProtocol:function(req,protocol){
var newProtocol = protocol;
return newProtocol;
},
//replace the statusCode before it's sent to the user
replaceResponseStatusCode: function(req,res,statusCode){
var newStatusCode = statusCode;
return newStatusCode;
},
//replace the httpHeader before it's sent to the user
replaceResponseHeader: function(req,res,header){
var newHeader = header;
newHeader = mergeCORSHeader(req.headers, newHeader);
newHeader = disableCacheHeader(newHeader);
return newHeader;
},
//replace the response from the server before it's sent to the user
//you may return either a Buffer or a string
//serverResData is a Buffer, you may get its content by calling serverResData.toString()
replaceServerResData: function(req,res,serverResData){
if(/html/i.test(res.headers['content-type'])){
var newDataStr = serverResData.toString(); //TODO : failed to decode data
// newDataStr += "hello world!";
return newDataStr;
}else{
return serverResData;
}
},
//add a pause before sending response to user
pauseBeforeSendingResponse : function(req,res){
var timeInMS = 100; //delay all requests for 0.1s
return timeInMS;
}
};
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
function mergeCORSHeader(reqHeader,originHeader){
var targetObj = originHeader || {};
delete targetObj["Access-Control-Allow-Credentials"];
delete targetObj["Access-Control-Allow-Origin"];
delete targetObj["Access-Control-Allow-Methods"];
delete targetObj["Access-Control-Allow-Headers"];
targetObj["access-control-allow-credentials"] = "true";
targetObj["access-control-allow-origin"] = reqHeader['origin'] || "-___-||";
targetObj["access-control-allow-methods"] = "GET, POST, PUT";
targetObj["access-control-allow-headers"] = reqHeader['access-control-request-headers'] || "-___-||";
return targetObj;
}
function disableCacheHeader(header){
header = header || {};
header["Cache-Control"] = "no-cache, no-store, must-revalidate";
header["Pragma"] = "no-cache";
header["Expires"] = 0;
header["server"] = "anyproxy server";
header["x-powered-by"] = "Anyproxy";
return header;
}
//try to mactch rule file
// 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;
// //localfile not set, map to dir
// if(!targetLocalfile){ //find file in dir, /a/b/file.html -> dir + b/file.html
// var remotePathWithoutPrefix = path.replace(new RegExp(rule.path),""); //remove prefix
// targetLocalfile = pathUtil.join(rule.localDir,remotePathWithoutPrefix);
// }
// console.log("==>local file: " + targetLocalfile);
// if(fs.existsSync(targetLocalfile)){
// try{
// var fsStream = fs.createReadStream(targetLocalfile);
// userRes.writeHead(200,mergeCORSHeader( req.headers,{}) ); //CORS for localfiles
// fsStream.pipe(userRes);
// ifLocalruleMatched = true;
// break;
// }catch(e){
// console.log(e.message);
// }
// }else{
// console.log("file not exist : " + targetLocalfile);
// }
// }
// }

28
lib/rule_default.js Normal file
View File

@ -0,0 +1,28 @@
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};

View File

@ -1,18 +0,0 @@
var http= require("http");
var s = http.createServer(function(req,res) {
var total = "";
req.on("data",function(chunk){
total += chunk;
});
req.on("end",function(){
console.log(total);
});
console.log(req);
// body...
});
s.listen(80);

View File

@ -40,7 +40,7 @@ function proxyServer(type, port, hostname,ruleFile){
if(ruleFile){ if(ruleFile){
if(fs.existsSync(ruleFile)){ if(fs.existsSync(ruleFile)){
try{ //for abs path try{ //for abs path
requestHandler.setRules(require(ruleFile)); //todo : require path requestHandler.setRules(require(ruleFile));
}catch(e){ //for relative path }catch(e){ //for relative path
requestHandler.setRules(require("./" + ruleFile)); requestHandler.setRules(require("./" + ruleFile));
} }
@ -73,9 +73,10 @@ function proxyServer(type, port, hostname,ruleFile){
} }
}, },
//listen CONNECT method for https over http
function(callback){ function(callback){
//listen CONNECT method for https over http
self.httpProxyServer.on('connect',requestHandler.connectReqHandler); self.httpProxyServer.on('connect',requestHandler.connectReqHandler);
self.httpProxyServer.listen(proxyPort); self.httpProxyServer.listen(proxyPort);
callback(null); callback(null);
} }
@ -135,9 +136,11 @@ function startWebServer(port){
console.log(color.green(tipText)); console.log(color.green(tipText));
//web socket interface //web socket interface
var wss = new WebSocketServer({port: DEFAULT_WEBSOCKET_PORT}); var wss = new WebSocketServer({port: DEFAULT_WEBSOCKET_PORT});
wss.on("connection",function(ws){
console.log("wss connection");
});
wss.broadcast = function(data) { wss.broadcast = function(data) {
for(var i in this.clients){ for(var i in this.clients){
this.clients[i].send(data); this.clients[i].send(data);

View File

@ -1,39 +0,0 @@
var rules = {
"map" :[
{
"host" :/./, //regExp
"path" :/\/path\/test/, //regExp
"localFile" :"", //this file will be returned to user when host and path pattern both meets the request
"localDir" :"~/" //find the file of same name in localdir. anyproxy will not read localDir settings unless localFile is falsy
}
// ,{
// "host" :/./,
// "path" :/\.(png|gif|jpg|jpeg)/,
// "localFile" :"/Users/Stella/tmp/test.png",
// "localDir" :"~/"
// }
,{
"host" :/./,
"path" :/tps/,
"localFile" :"",
"localDir" :"/Users/Stella/tmp/"
},{
"host" :/./,
"path" :/response\.(json)/
},{
"host" :/./,
"path" :/html/,
"callback" :function(res){
//remoty.js will be inject into response via callback
res.write("<script type=\"text\/javascript\" src=\"http:\/\/localhost:3001\/remoty\.js\"><\/script>");
res.write("<script type=\"text\/javascript\" src=\"http:\/\/localhost:8080\/target\/target\-script\-min\.js\#anonymous\"><\/script>");
}
}
]
,"httpsConfig":{
"bypassAll" : false, //by setting this to true, anyproxy will not intercept any https request
"interceptDomains":[/www\.example\.com/] //by setting bypassAll:false, requests towards these domains will be intercepted, and try to meet the map rules above
}
}
module.exports = rules;

12
rule_sample/README.md Normal file
View File

@ -0,0 +1,12 @@
## rule files sample
* **rule__blank.js**, blank rule file with some comments. You may read this before writing your own rule file.
* **rule_adjust_response_time.js**, delay all the response for 1500ms
* **rule_allow_CORS.js**, add CORS headers to allow cross-domain ajax request
* **rule_intercept_some_https_requests.js**, intercept https requests toward github.com
* **rule_remove_cache_header.js**, remove all cache-related headers from server
* **rule_replace_request_option.js**, replace request parameters before sending to the server
* **rule_replace_response_data.js**, modify response data
* **rule_replace_response_status_code.js**, replace server's status code
* **rule_use_local_data.js**, map some requests to local file

View File

@ -0,0 +1,66 @@
module.exports = {
/*
thess functions are required
you may leave their bodies blank if necessary
*/
//whether to intercept this request by local logic
//if the return value is true, anyproxy will call dealLocalResponse to get response data and will not send request to remote server anymore
shouldUseLocalResponse : function(req){
return false;
},
//response to user via local logic, be called when shouldUseLocalResponse returns true
//callback(statusCode,resHeader,responseData)
//e.g. callback(200,{"content-type":"text/html"},"hello world")
dealLocalResponse : function(req,callback){
//callback(statusCode,resHeader,responseData)
},
//req is user's request sent to the proxy server
// option is how the proxy server will send request to the real server. i.e. require("http").request(option,function(){...})
//you may return a customized option to replace the original option
replaceRequestOption : function(req,option){
var newOption = option;
return newOption;
},
//replace the request protocol when sending to the real server
//protocol : "http" or "https"
replaceRequestProtocol:function(req,protocol){
var newProtocol = protocol;
return newProtocol;
},
//replace the statusCode before it's sent to the user
replaceResponseStatusCode: function(req,res,statusCode){
var newStatusCode = statusCode;
return newStatusCode;
},
//replace the httpHeader before it's sent to the user
//Here header == res.headers
replaceResponseHeader: function(req,res,header){
var newHeader = header;
return newHeader;
},
//replace the response from the server before it's sent to the user
//you may return either a Buffer or a string
//serverResData is a Buffer, you may get its content by calling serverResData.toString()
replaceServerResData: function(req,res,serverResData){
return serverResData;
},
//add a pause before sending response to user
pauseBeforeSendingResponse : function(req,res){
var timeInMS = 1; //delay all requests for 1ms
return timeInMS;
},
//should intercept https request, or it will be forwarded to real server
shouldInterceptHttpsReq :function(req){
return false;
}
};

View File

@ -0,0 +1,34 @@
//rule scheme :
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
//delay all the response for 1500ms
return 1500;
},
shouldInterceptHttpsReq :function(req){
}
};

View File

@ -0,0 +1,57 @@
//rule scheme :
// Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
module.exports = {
shouldUseLocalResponse : function(req){
//intercept all options request
if(req.method == "OPTIONS"){
return true;
}else{
return false;
}
},
dealLocalResponse : function(req,callback){
if(req.method == "OPTIONS"){
callback(200,mergeCORSHeader(req.headers),"");
}
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
return mergeCORSHeader(req.headers, header);
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};
function mergeCORSHeader(reqHeader,originHeader){
var targetObj = originHeader || {};
delete targetObj["Access-Control-Allow-Credentials"];
delete targetObj["Access-Control-Allow-Origin"];
delete targetObj["Access-Control-Allow-Methods"];
delete targetObj["Access-Control-Allow-Headers"];
targetObj["access-control-allow-credentials"] = "true";
targetObj["access-control-allow-origin"] = reqHeader['origin'] || "-___-||";
targetObj["access-control-allow-methods"] = "GET, POST, PUT";
targetObj["access-control-allow-headers"] = reqHeader['access-control-request-headers'] || "-___-||";
return targetObj;
}

View File

@ -0,0 +1,44 @@
//rule scheme :
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
},
replaceServerResData: function(req,res,serverResData){
//add "hello github" to all github pages
if(req.headers.host == "github.com"){
serverResData += "hello github";
}
return serverResData;
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
//intercept https://github.com/
//otherwise, all the https traffic will not go through this proxy
if(req.headers.host == "github.com"){
return true;
}else{
return false;
}
}
};

View File

@ -0,0 +1,41 @@
//rule scheme :
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
header = header || {};
header["Cache-Control"] = "no-cache, no-store, must-revalidate";
header["Pragma"] = "no-cache";
header["Expires"] = 0;
return header;
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};
function disableCacheHeader(header){
}

View File

@ -0,0 +1,48 @@
//rule scheme :
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
//replace request towards http://www.taobao.com
// to http://www.taobao.com/about/
/*
option scheme:
{
hostname : "www.taobao.com"
port : 80
path : "/"
method : "GET"
headers : {cookie:""}
}
*/
if(option.hostname == "www.taobao.com" && option.path == "/"){
option.path = "/about/";
}
console.log(option);
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};

View File

@ -0,0 +1,41 @@
//rule scheme :
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
},
replaceServerResData: function(req,res,serverResData){
//append "hello world" to all web pages
if(/html/i.test(res.headers['content-type'])){
var newDataStr = serverResData.toString();
newDataStr += "hello world!";
return newDataStr;
}else{
return serverResData;
}
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};

View File

@ -0,0 +1,45 @@
//rule scheme :
module.exports = {
shouldUseLocalResponse : function(req){
},
dealLocalResponse : function(req,callback){
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
//redirect requests toward http://www.taobao.com/*
// to http://www.etao.com
//using 302
if(req.headers.host == "www.taobao.com"){
statusCode = 302;
}
return statusCode;
},
replaceResponseHeader: function(req,res,header){
if(req.headers.host == "www.taobao.com"){
header.location = "http://www.etao.com";
}
return header;
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};

View File

@ -0,0 +1,75 @@
//replace all the images with local one
var url = require("url"),
path = require("path"),
fs = require("fs"),
buffer = require("buffer");
var map = [
{
"host" :/./,
"path" :/\.(png|gif|jpg|jpeg)/,
"localFile" :"/Users/Stella/tmp/test.png",
"localDir" :"~/"
}
];
module.exports = {
shouldUseLocalResponse : function(req){
var host = req.headers.host,
urlPattern = url.parse(req.url),
path = urlPattern.path;
for(var index in map){
var rule = map[index];
var hostTest = new RegExp(rule.host).test(host),
pathTest = new RegExp(rule.path).test(path);
if(hostTest && pathTest && (rule.localFile || rule.localDir) ){
var targetLocalfile = rule.localFile;
//localfile not set, map to dir
if(!targetLocalfile){ //find file in dir, /a/b/file.html -> dir + b/file.html
var remotePathWithoutPrefix = path.replace(new RegExp(rule.path),""); //remove prefix
targetLocalfile = pathUtil.join(rule.localDir,remotePathWithoutPrefix);
}
if(fs.existsSync(targetLocalfile)){
console.log("==>local file: " + targetLocalfile);
req.replaceLocalFile = targetLocalfile; //add a flag to req object
return true;
}
}
}
return false;
},
dealLocalResponse : function(req,callback){
if(req.replaceLocalFile){
callback(200, {"content-type":"image/png"}, fs.readFileSync(req.replaceLocalFile) );
}
},
replaceRequestOption : function(req,option){
},
replaceRequestProtocol:function(req,protocol){
},
replaceResponseStatusCode: function(req,res,statusCode){
},
replaceResponseHeader: function(req,res,header){
},
replaceServerResData: function(req,res,serverResData){
},
pauseBeforeSendingResponse : function(req,res){
},
shouldInterceptHttpsReq :function(req){
}
};

View File

@ -1,28 +0,0 @@
// We need this to build our post string
var http = require('http');
var fs = require('fs');
// An object of options to indicate where to post to
var post_options = {
host: 'localhost',
port: '8004',
path: '/',
method: 'POST',
headers: {
Host: "127.0.0.1"
}
};
// Set up the request
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
// post the data
post_req.write("hello world hello world hello world hello world hello world ");
post_req.end();

View File

@ -47,7 +47,7 @@
} }
.mainTableWrapper .col_method{ .mainTableWrapper .col_method{
width: 60px; width: 70px;
} }
.mainTableWrapper .col_host{ .mainTableWrapper .col_host{

View File

@ -155,13 +155,16 @@ seajs.use(['$','Underscore' ,'Backbone'], function($, _, Backbone) {
//data via web socket //data via web socket
var dataSocket = new WebSocket("ws://127.0.0.1:8003"); var dataSocket = new WebSocket("ws://127.0.0.1:8003");
dataSocket.onopen = function(){
console.log("dataSocket open");
}
dataSocket.onmessage = function(event){ dataSocket.onmessage = function(event){
var data = JSON.parse(event.data); var data = JSON.parse(event.data);
var reqDate = new Date(data.startTime); var reqDate = new Date(data.startTime);
data.startTimeStr = reqDate.toLocaleDateString()+ " " + reqDate.toLocaleTimeString(); data.startTimeStr = reqDate.toLocaleDateString()+ " " + reqDate.toLocaleTimeString();
var previous; var previous;
if(previous = recList.get(data.id)){ if(previous = recList.get(data.id)){
previous.set(data); previous.set(data);