diff --git a/lib/recorder.js b/lib/recorder.js index 504bc60..5f65a45 100644 --- a/lib/recorder.js +++ b/lib/recorder.js @@ -45,16 +45,8 @@ function Recorder(){ if(!id || !info.resBody) return; //add to body map //do not save image data - if(/image/.test(info.res.headers['content-type'])){ + if(/image/.test(info.resHeader['content-type'])){ 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{ self.recordBodyMap[id] = info.resBody.toString(); } @@ -81,7 +73,7 @@ function normalizeInfo(id,info){ //general singleRecord._id = id; - singleRecord.id = id; + singleRecord.id = id; singleRecord.url = info.url; singleRecord.host = info.host; singleRecord.path = info.path; @@ -92,13 +84,13 @@ function normalizeInfo(id,info){ singleRecord.startTime = info.startTime; //res - if(info.res){ - singleRecord.statusCode= info.res.statusCode; + if(info.endTime){ + singleRecord.statusCode= info.statusCode; singleRecord.endTime = info.endTime; - singleRecord.resHeader = info.res.headers; + singleRecord.resHeader = info.resHeader; singleRecord.length = info.length; - if(info.res.headers['content-type']){ - singleRecord.mime = info.res.headers['content-type'].split(";")[0]; + if(info.resHeader['content-type']){ + singleRecord.mime = info.resHeader['content-type'].split(";")[0]; }else{ singleRecord.mime = ""; } diff --git a/lib/requestHandler.js b/lib/requestHandler.js index 33a1213..16a2d4d 100644 --- a/lib/requestHandler.js +++ b/lib/requestHandler.js @@ -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; } } diff --git a/lib/rule.js b/lib/rule.js deleted file mode 100644 index 45edebd..0000000 --- a/lib/rule.js +++ /dev/null @@ -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); -// } -// } -// } \ No newline at end of file diff --git a/lib/rule_default.js b/lib/rule_default.js new file mode 100644 index 0000000..b80d8c6 --- /dev/null +++ b/lib/rule_default.js @@ -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){ + } +}; \ No newline at end of file diff --git a/postReceiver.js b/postReceiver.js deleted file mode 100644 index d65e25a..0000000 --- a/postReceiver.js +++ /dev/null @@ -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); - diff --git a/proxy.js b/proxy.js index 8775137..7e95430 100644 --- a/proxy.js +++ b/proxy.js @@ -40,7 +40,7 @@ function proxyServer(type, port, hostname,ruleFile){ if(ruleFile){ if(fs.existsSync(ruleFile)){ try{ //for abs path - requestHandler.setRules(require(ruleFile)); //todo : require path + requestHandler.setRules(require(ruleFile)); }catch(e){ //for relative path requestHandler.setRules(require("./" + ruleFile)); } @@ -73,9 +73,10 @@ function proxyServer(type, port, hostname,ruleFile){ } }, - //listen CONNECT method for https over http function(callback){ + //listen CONNECT method for https over http self.httpProxyServer.on('connect',requestHandler.connectReqHandler); + self.httpProxyServer.listen(proxyPort); callback(null); } @@ -135,9 +136,11 @@ function startWebServer(port){ console.log(color.green(tipText)); - //web socket interface var wss = new WebSocketServer({port: DEFAULT_WEBSOCKET_PORT}); + wss.on("connection",function(ws){ + console.log("wss connection"); + }); wss.broadcast = function(data) { for(var i in this.clients){ this.clients[i].send(data); diff --git a/rule_sample.js b/rule_sample.js deleted file mode 100644 index b585c3b..0000000 --- a/rule_sample.js +++ /dev/null @@ -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; \ No newline at end of file diff --git a/rule_sample/README.md b/rule_sample/README.md new file mode 100644 index 0000000..a8bbb2d --- /dev/null +++ b/rule_sample/README.md @@ -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 \ No newline at end of file diff --git a/rule_sample/rule__blank.js b/rule_sample/rule__blank.js new file mode 100644 index 0000000..31617cd --- /dev/null +++ b/rule_sample/rule__blank.js @@ -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; + } + +}; \ No newline at end of file diff --git a/rule_sample/rule_adjust_response_time.js b/rule_sample/rule_adjust_response_time.js new file mode 100644 index 0000000..6ccb6ab --- /dev/null +++ b/rule_sample/rule_adjust_response_time.js @@ -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){ + } +}; \ No newline at end of file diff --git a/rule_sample/rule_allow_CORS.js b/rule_sample/rule_allow_CORS.js new file mode 100644 index 0000000..e1f468f --- /dev/null +++ b/rule_sample/rule_allow_CORS.js @@ -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; +} diff --git a/rule_sample/rule_intercept_some_https_requests.js b/rule_sample/rule_intercept_some_https_requests.js new file mode 100644 index 0000000..3a3b94f --- /dev/null +++ b/rule_sample/rule_intercept_some_https_requests.js @@ -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; + } + } +}; \ No newline at end of file diff --git a/rule_sample/rule_remove_cache_header.js b/rule_sample/rule_remove_cache_header.js new file mode 100644 index 0000000..12bbc34 --- /dev/null +++ b/rule_sample/rule_remove_cache_header.js @@ -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){ + +} \ No newline at end of file diff --git a/rule_sample/rule_replace_request_option.js b/rule_sample/rule_replace_request_option.js new file mode 100644 index 0000000..268541d --- /dev/null +++ b/rule_sample/rule_replace_request_option.js @@ -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){ + } +}; \ No newline at end of file diff --git a/rule_sample/rule_replace_response_data.js b/rule_sample/rule_replace_response_data.js new file mode 100644 index 0000000..daf4d69 --- /dev/null +++ b/rule_sample/rule_replace_response_data.js @@ -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){ + } +}; \ No newline at end of file diff --git a/rule_sample/rule_replace_response_status_code.js b/rule_sample/rule_replace_response_status_code.js new file mode 100644 index 0000000..763bb1f --- /dev/null +++ b/rule_sample/rule_replace_response_status_code.js @@ -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){ + } +}; \ No newline at end of file diff --git a/rule_sample/rule_use_local_data.js b/rule_sample/rule_use_local_data.js new file mode 100644 index 0000000..28a5520 --- /dev/null +++ b/rule_sample/rule_use_local_data.js @@ -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){ + } +}; + diff --git a/testPost.js b/testPost.js deleted file mode 100644 index 8cf401d..0000000 --- a/testPost.js +++ /dev/null @@ -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(); diff --git a/web/css/page.css b/web/css/page.css index 8e11f7b..0db77b4 100644 --- a/web/css/page.css +++ b/web/css/page.css @@ -47,7 +47,7 @@ } .mainTableWrapper .col_method{ - width: 60px; + width: 70px; } .mainTableWrapper .col_host{ diff --git a/web/page.js b/web/page.js index e71660b..ccbb455 100644 --- a/web/page.js +++ b/web/page.js @@ -155,13 +155,16 @@ seajs.use(['$','Underscore' ,'Backbone'], function($, _, Backbone) { //data via web socket var dataSocket = new WebSocket("ws://127.0.0.1:8003"); + dataSocket.onopen = function(){ + console.log("dataSocket open"); + } + dataSocket.onmessage = function(event){ var data = JSON.parse(event.data); var reqDate = new Date(data.startTime); data.startTimeStr = reqDate.toLocaleDateString()+ " " + reqDate.toLocaleTimeString(); - var previous; if(previous = recList.get(data.id)){ previous.set(data);