catproxy
Version:
a node proxy or host change tools
410 lines (374 loc) • 13.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.pipeRequest = exports.beforeRes = exports.afterRes = exports.beforeReq = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; // 事件触发中心
var _log = require('./log');
var _log2 = _interopRequireDefault(_log);
var _rule = require('./config/rule');
var _config = require('./config/config');
var config = _interopRequireWildcard(_config);
var _mime = require('mime');
var _mime2 = _interopRequireDefault(_mime);
var _iconvLite = require('iconv-lite');
var _iconvLite2 = _interopRequireDefault(_iconvLite);
var _zlib = require('zlib');
var _zlib2 = _interopRequireDefault(_zlib);
var _buffer = require('buffer');
var _promise = require('promise');
var _promise2 = _interopRequireDefault(_promise);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// <meta charset="gb2312">
// <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
var checkMetaCharset = /<meta(?:\s)+.*charset(?:\s)*=(?:[\s'"])*([^"']+)/i;
// 自动解析类型,其他类型一律保存的是 Buffer
var autoDecodeRegs = /text\/.+|(?:application\/(?:json.*|.*javascript))/i;
// 不解码的后缀格式
var excludeDecodeExt = ['ttf', 'eot', 'svg', 'woff'];
// 解压数据
var decodeCompress = function decodeCompress(bodyData, encode) {
return new _promise2.default(function (resolve, reject) {
// 成功的取到bodyData
if (bodyData) {
var isZip = /gzip/i.test(encode);
var isDeflate = /deflate/i.test(encode);
if (isZip) {
_zlib2.default.gunzip(bodyData, function (err, buff) {
if (err) {
reject(bodyData);
_log2.default.error('decompress err: ', err.message);
} else {
resolve(buff);
}
});
} else if (isDeflate) {
_zlib2.default.inflateRaw(bodyData, function (err, buff) {
if (err) {
reject(bodyData);
_log2.default.error('decompress err: ', err.message);
} else {
resolve(buff);
}
});
} else {
reject(bodyData);
}
} else {
reject(bodyData);
}
});
};
/**
* 代理请求发出前
* 该方法主要是处理在响应前的所有事情,可以用来替换header,替换头部数据等操作
* 可以直接像res中写数据结束请求
* 如果是异步请返回promise对象
* @param reqInfo 请求信息 可以修改请求代理的form数据和 请求代理的头部数据
* @param {resInfo} 响应投信息可以修改响应投的header
* @param res 响应对象
* @returns {*}
* reqInfo 包含的信息
* {
* headers: "请求头"
* host: "请求地址,包括端口,默认端口省略"
* method: "请求方法"
* protocol: "请求协议"
* originalFullUrl: "原始请求地址,包括参数"
* req: "请求对象,请勿删除"
* port: "请求端口"
* startTime: "请求开始时间"
* path: "请求路径,包括参数"
* originalUrl: "原始的请求地址,不包括参数,请不要修改",
* bodyData: "buffer 数据,body参数,可以修改"
* }
*
* 举例说明,可以请求的修改的地方
* 修改请求头,注意只有请求远程服务器的时候管用
* reqInfo.headers['test-cjx'] = 111;
* //修改请求域名
* reqInfo.host = '114.113.198.187';
* //修改请求协议
* reqInfo.protocol = '114.113.198.187';
* //修改请求端口
* reqInfo.port="8080"
* //修改请求路径--包括get参数
* reqInfo.path= "/ccc?aaa"
* //修改方法
* reqInfo.method= "post" //注意post方法要有对应的content-type数据才能过去
* //修改请求数据
* reqInfo.bodyData = "请求数据",
* //直接定位到某个文件 --如果返回某个文件,有这个,就会忽略远程的调用即host设置之类的都无效
* reqInfo.sendToFile
* //重定向到某个url,--有这个,就会忽略远程的调用即host设置之类的都无效
* reqInfo.redirect
*/
var beforeReq = function beforeReq(reqInfo) {
// reqInfo.headers['test-cjx'] = 111;
// reqInfo.path = '/hyg/mobile/common/base/base.34b37a3c0b.js';
// reqInfo.port = 9090;
// reqInfo.protocol = "https";
// reqInfo.path = "/a/b?c=d";
// reqInfo.method = "post";
// reqInfo.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
// reqInfo.bodyData = new Buffer('a=b&c=d');
// reqInfo.sendToFile = "D:/project/gitWork/catproxy/bin.js";
// log.debug(reqInfo.headers);
// log.debug(reqInfo.bodyData.toString());
// if (reqInfo.host.indexOf('pimg1.126.net') > -1) {
// reqInfo.host = '114.113.198.187';
// }
var catProxy = this.catProxy;
var com = this;
return (0, _rule.parseRule)(reqInfo).then(function (result) {
return result || reqInfo;
}).then(function (reqInfo) {
return reqInfo;
}).then(function (reqInfo) {
var arr = catProxy._beforeReqEvt;
if (!arr.length) {
return reqInfo;
}
var p = _promise2.default.resolve(arr[0].call(com, reqInfo));
var _loop = function _loop(i) {
p.then(function () {
return arr[i].call(com, reqInfo);
});
};
for (var i = 1; i < arr.length; i++) {
_loop(i);
}
return p;
}).then(function (result) {
return result || reqInfo;
}, function (err) {
_log2.default.error(err);
return reqInfo;
});
};
// 如果不是合法的类型,就不进行decode
var decodeContent = function decodeContent(resInfo, isDecode) {
var contentEncoding = resInfo.headers['content-encoding'];
var bodyData = resInfo.bodyData;
var contentType = resInfo.headers['content-type'] || "";
// 先看看是否需要解压数据
return decodeCompress(bodyData, contentEncoding).then(function (bodyData) {
delete resInfo.headers['content-encoding'];
delete resInfo.headers['content-length'];
return bodyData;
})
// 解压后在通过编码去解码数据
.then(function (bodyData) {
// 默认编码是utf8
var charset = 'UTF-8',
tmp = void 0;
var ext = _mime2.default.extension(contentType);
if (!bodyData || !isDecode) {
return { bodyData: bodyData, charset: charset };
}
if (contentType) {
// 如果contenttype上又编码,则重新设置编码
tmp = contentType.match(/charset=([^;]+)/);
if (tmp && tmp.length > 0) {
charset = tmp[1].toUpperCase();
}
}
if (_buffer.Buffer.isBuffer(bodyData)) {
// 其他编码先尝试用 iconv去解码
if (charset !== 'UTF-8' && _iconvLite2.default.encodingExists(charset)) {
bodyData = _iconvLite2.default.decode(bodyData, charset);
} else if (contentType && (ext === 'html' || ext === 'htm')) {
// 如果是一个文档,在取一次编码
var strBodyData = bodyData.toString();
// 在内容中再次找寻编码
var _tmp = strBodyData.match(checkMetaCharset);
if (_tmp && _tmp[1]) {
_tmp[1] = _tmp[1].toUpperCase();
if (_tmp[1] !== "UTF-8" && _iconvLite2.default.encodingExists(_tmp[1])) {
charset = _tmp[1];
bodyData = _iconvLite2.default.decode(bodyData, _tmp[1]);
} else {
bodyData = strBodyData;
}
} else {
bodyData = strBodyData;
}
} else {
bodyData = bodyData.toString();
}
}
// 再次加编码传递到页面
return {
bodyData: bodyData,
charset: charset
};
}, function (bodyData) {
// 出错,编码错误,直接返回
// 默认编码是utf8
var charset = 'UTF-8',
tmp = void 0;
if (!bodyData || !isDecode) {
return { bodyData: bodyData, charset: charset };
}
if (contentType) {
// 如果contenttype上又编码,则重新设置编码
tmp = contentType.match(/charset=([^;]+)/);
if (tmp && tmp.length > 0) {
charset = tmp[1].toUpperCase();
}
}
return {
bodyData: bodyData,
charset: charset
};
});
};
/**
* 准备响应请求前
* @param {[type]} resInfo [响应信息]
* * resInfo包含的信息
* {
* statusCode: "响应状态码, 可以修改"
* headers: "请求头,可修改"
* ---注意如果有bodyData则会直接用bodyData的数据返回
* bodyData: "buffer 数据",
* bodyDataErr: "请求出错,目前如果是大文件会触发这个,这个时候bodyData为空,且不可以设置"
* //这个时候 resInfo,bodyData无效
* originalFullUrl 原始请求url
* originalUrl 原始请求url
* }
*
* 举例说明可以修改响应的地方/
* resInfo.headers['test-cjx'] = 111;
* @return {[type]} [description]
*/
var beforeRes = function beforeRes(resInfo) {
var catProxy = this.catProxy;
var com = this;
return _promise2.default.resolve(resInfo).then(function (resInfo) {
// 禁用缓存则删掉缓存相关的header
var disCache = config.get('disCache');
if (disCache) {
resInfo.headers['cache-control'] = "no-store";
resInfo.headers.expires = "0";
delete resInfo.headers.etag;
delete resInfo.headers['last-modifed'];
}
return resInfo;
}).then(function (resInfo) {
// 禁用缓存或者用户这是监听,则需哟啊解码内容返回给用户
var disCache = config.get('disCache');
// 用户监听
var useListener = false;
if (disCache || useListener) {
var _ret2 = function () {
var contentType = resInfo.headers['content-type'] || "";
// 按照指定编码解码内容
var ext = _path2.default.extname(resInfo.path.split('?')[0].split('#')[0]) || "";
ext = (ext.split('.')[1] || "").toLowerCase();
if (excludeDecodeExt.some(function (cur) {
return cur === ext;
})) {
ext = "";
}
return {
v: decodeContent(resInfo, autoDecodeRegs.test(contentType) && !!ext).then(function (_ref) {
var charset = _ref.charset;
var bodyData = _ref.bodyData;
var extension = _mime2.default.extension(contentType);
var isType = typeof bodyData === 'string';
// 如果访问的是一个html,并且成功截取到这个html的内容
if (disCache && contentType && (extension === 'html' || extension === 'htm') && isType) {
bodyData = bodyData.replace("<head>", '<head>\n\t\t\t\t\t\t<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />\n\t\t\t\t\t\t<meta http-equiv="Pragma" content="no-cache" />\n\t\t\t\t\t\t<meta http-equiv="Expires" content="0" />');
}
resInfo.charset = charset;
resInfo.bodyData = bodyData;
return resInfo;
})
};
}();
if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === "object") return _ret2.v;
}
return _promise2.default.resolve(resInfo);
}).then(function (resInfo) {
// 为什么要变成buffer主要是为了让浏览器认识,根据浏览器当前的编码解析
if (typeof resInfo.bodyData === 'string') {
resInfo.bodyData = _iconvLite2.default.encode(resInfo.bodyData, resInfo.charset || "UTF-8");
}
return resInfo;
}).then(function (resInfo) {
var arr = catProxy._beforeResEvt;
if (!arr.length) {
return resInfo;
}
var p = _promise2.default.resolve(arr[0].call(com, resInfo));
var _loop2 = function _loop2(i) {
p.then(function () {
return arr[i].call(com, resInfo);
});
};
for (var i = 1; i < arr.length; i++) {
_loop2(i);
}
return p;
}).then(function (result) {
return result || resInfo;
}, function (err) {
_log2.default.debug('err', err);
return resInfo;
});
// resInfo.statusCode = 302;
// resInfo.headers['test-cjx'] = 111;
// bodyData = "test";
};
/**
* 请求响应后
* 该方法主要是请求响应后的处理操作,主要是可以查看请求数据,
* 注意这时候请求已经结束了,无法在做其他的处理
* @param result
* 所有字段不可以修改,只可以查看
* * result包含的信息
* {
* statusCode: "响应状态码"
* headers: "请求头"
* host: "请求地址"
* method: "请求方法"
* protocol: "请求协议"
* originalFullUrl: "原始请求地址,包括参数"
* port: "请求端口"
* endTime: "请求开始时间"
* path: "请求路径,包括参数"
* originalUrl: "原始的请求地址,不包括参数",
* bodyData: "buffer 数据,body参数",
* bodyDataErr: null,
* bodyDataFile: null //如果资源定位到本地就显示这个字段
* }
* @returns {*}
*/
var afterRes = function afterRes(result) {
var _this = this;
var catProxy = this.catProxy;
if (catProxy && catProxy._afterResEvt.length) {
catProxy._afterResEvt.forEach(function (current) {
return current.call(_this, result);
});
}
return result;
};
// 中转请求
var pipeRequest = function pipeRequest(result) {
var _this2 = this;
var catProxy = this.catProxy;
if (catProxy && catProxy._pipeRequestEvt.length) {
catProxy._pipeRequestEvt.forEach(function (current) {
return current.call(_this2, result);
});
}
};
exports.beforeReq = beforeReq;
exports.afterRes = afterRes;
exports.beforeRes = beforeRes;
exports.pipeRequest = pipeRequest;