catproxy
Version:
a node proxy or host change tools
244 lines (231 loc) • 8.21 kB
JavaScript
import log from '../log';
import fse from 'fs-extra';
import fs from 'fs';
import Promise from 'promise';
import merge from 'merge';
import isbinaryfile from 'isbinaryfile';
import crypto from 'crypto';
import {cacheFile} from './cacheFile';
import {isBinary, getReqType, isImage} from '../dataHelper';
import {addMonitor, updateMonitor} from '../ws/sendMsg';
import {weinreId} from '../tools';
import * as config from '../config/config';
import {WEINRE_PATH} from '../config/defCfg';
// 当前监控数据-- 记录文件的url和 resBodyData的文件生成的md5值
var monitorList = {};
const resBodyName = "_res_body_";
// 处理mulitpartData
/** 数据格式
* multipart/form-data; boundary=----WebKitFormBoundaryAxMpx9qwQiovE99R 659
content-- ------WebKitFormBoundaryAxMpx9qwQiovE99R
Content-Disposition: form-data; name="fileName"
说明.txt
------WebKitFormBoundaryAxMpx9qwQiovE99R
Content-Disposition: form-data; name="gameId"
2010110218YX22859517
------WebKitFormBoundaryAxMpx9qwQiovE99R
Content-Disposition: form-data; name="formatType"
1
------WebKitFormBoundaryAxMpx9qwQiovE99R
Content-Disposition: form-data; name="period"
61202
------WebKitFormBoundaryAxMpx9qwQiovE99R
Content-Disposition: form-data; name="uploadFile"; filename="说明.txt"
Content-Type: text/plain
xbmcRemote Զ�̿��� kodi������
homido ����ͷ������
------WebKitFormBoundaryAxMpx9qwQiovE99R--
*/
let detailMultipartData = function(contentType, bodyData) {
contentType = contentType || "";
var key = contentType.toLowerCase().split("boundary=");
if (key && key.length > 0) {
let reg = /Content-Disposition\s*:\s*form-data;.+;\s*filename=.*/gi;
let isContentType = /^Content-Type.*/i;
key = key[1];
let data = bodyData.toString().split("\n");
let newData = [];
let l = data.length;
let s = null;
let j = null;
if (l) {
for (let i = 0; i < l; i++) {
let current = data[i] || "";
if (reg.test(current)) {
s = true;
}
if (s) {
if (!j) {
newData.push(current);
}
if (isContentType.test(current)) {
j = true;
}
if (current.indexOf(key) > -1) {
j = null;
s = null;
newData.push(current);
}
} else {
newData.push(current);
}
}
}
return newData.join('\n');
}
};
export default function(catproxy) {
if (!catproxy || !catproxy.onBeforeReq) {
throw new Error("catproxy是必须得");
}
var monitorBeforeReq,
monitorBeforeRes,
monitorAfterRes;
// 检测是不是本地的一个服务器
// 只要ip是localhost ui服务器 或者是weinre的请求就忽略
let checkIsInnerServer = (originalUrl) => {
return catproxy.localUiReg.test(originalUrl) || originalUrl.toLowerCase().indexOf(WEINRE_PATH + "/" + weinreId) >= 0;
};
// 请求发送前
catproxy.onBeforeReq(monitorBeforeReq = (result) => {
if (result && result.id && config.get('monitor:monitorStatus') && !checkIsInnerServer(result.originalUrl)) {
/**
*
* 目前没有把请求数据直接保存到文件,后期可以考虑
请求头部数据一共三种,一种是 post提交数据 一种是 get提交数据, 还有一种是表单提交数据,表单提交数据如果带 二进制就存储,否则直接返回
contentType 的三种情况
application/x-www-form-urlencoded 在发送前编码所有字符(默认)// 转换成 form data & 符号链接的数据, <xml 数据 等等, 直接返回json ???
text/plain 空格转换为 "+" 加号,但不对特殊字符编码。 // 转换成form data 任意不是二进制的数据 -- 可以检查 是不是{}或者 []开始判断是不是json
上面2种类似
multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。 去掉二进制数据转换 multipart request payload
**/
let contentType = result.headers['content-type'];
let addMontiorData = {
id: result.id,
name: result.originalFullUrl,
protocol: result.protocol,
method: result.method,
reqHeaders: result.headers,
startTime: result.startTime
};
if (result.ruleInfo) {
addMontiorData.reqRuleInfo = result.ruleInfo;
}
let bodyData = result.bodyData;
if (result.bodyData) {
let isb = isBinary(result.bodyData);
addMontiorData.isReqBinary = isb;
if (isb) {
if (contentType) {
// 混合流,里面有二进制的文件数据也有 字段数据,需要解析下
if (contentType.indexOf('boundary=') > -1) {
bodyData = detailMultipartData(contentType, bodyData);
} else {
// 不认识的二进制数据忽略
bodyData = "二进制数据!!!";
}
} else {
bodyData = "二进制数据!!!";
}
}
addMontiorData.reqBodyData = (bodyData || "").toString();
// log.debug(contentType, "************\n" ,result.bodyData.length, "**************\n", result.originalFullUrl);
// log.debug("isBinary", isb);
// log.debug('content--', addMontiorData.bodyData);
// 先记录到缓存中
monitorList[result.id] = addMontiorData;
}
}
});
// 准备发送请求
catproxy.onBeforeRes(monitorBeforeRes = result => {
if (result && result.id && config.get('monitor:monitorStatus') && !checkIsInnerServer(result.originalUrl)) {
let addMontiorData = merge(monitorList[result.id], {
ext: result.ext,
resHeaders: result.headers,
serverIp: result.serverIp
});
let type = getReqType(addMontiorData, result.ext) || "other";
addMontiorData.type = type;
addMontiorData.isResinary = result.isBinary;
// 修改缓存数据
monitorList[result.id] = {
startTime: addMontiorData.startTime
};
// startTime不需要传递到前端
delete addMontiorData.startTime;
// 调用数据增加
addMonitor(addMontiorData);
}
});
// 请求发送后
catproxy.onAfterRes(monitorAfterRes = result => {
if (result && +result.id && config.get('monitor:monitorStatus') && !checkIsInnerServer(result.originalUrl)) {
if (monitorList[result.id]) {
let startTime = monitorList[result.id].startTime;
let {bodyData} = result;
let fileName;
let resBodyData;
if (bodyData && bodyData.length) {
let contentType = result.headers['content-type'];
// 是二进制数据并且是图片,或者不是二进制数据
if (!result.isBinary || (result.isBinary && isImage.test(contentType))) {
let md5 = crypto.createHash('md5');
md5.update(bodyData);
fileName = md5.digest('hex');
fileName = resBodyName + fileName;
// 缓存文件
cacheFile(fileName, bodyData);
} else {
resBodyData = "二进制数据!!!";
}
} else {
if (result.bodyDataErr) {
resBodyData = result.bodyDataErr;
} else {
// 可能是 302,等请求没有响应内容
resBodyData = "";
}
}
let updateData = {
id: result.id,
time: result.endTime - startTime,
status: result.statusCode,
size: bodyData ? bodyData.length : 0,
};
if (result.charset) {
updateData.resCharset = result.charset;
}
if (fileName) {
updateData.resBodyDataId = fileName;
}
if (resBodyData !== undefined) {
updateData.resBodyData = resBodyData;
}
delete monitorList[result.id];
updateMonitor(updateData);
}
}
});
catproxy.__monitorBeforeReq = monitorBeforeReq;
catproxy.__monitorBeforeRes = monitorBeforeRes;
catproxy.__monitorAfterRes = monitorAfterRes;
// 管道调用
catproxy.onPipeRequest(result => {
// 后面判断带得协议不准确,但是仅仅是为了通过正则,测试,正则中并不关系,请求的类型是ws还是wss
if (result && result.id && config.get('monitor:monitorStatus') && !checkIsInnerServer(`ws://${result.host}`)) {
let addMontiorData = {
id: result.id,
name: (result.host || "").split(":")[0],
protocol: result.protocol,
method: "CONNECT",
time: "-",
status: 200,
size: 0,
type: result.protocol === 'ws' || result.protocol === 'wss' ? "ws" : "other"
};
// 调用数据增加
addMonitor(addMontiorData);
}
});
};