catproxy
Version:
a node proxy or host change tools
242 lines (232 loc) • 8.34 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;
};
// 请求发送前
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;
}
}
};
// 准备发送请求
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;
// 当所有用户调用结束在看是否是二进制数据
result.isBinary = isBinary(result.bodyData);
addMontiorData.isResbinary = result.isBinary;
// 修改缓存数据
monitorList[result.id] = {
startTime: addMontiorData.startTime,
};
// startTime不需要传递到前端
delete addMontiorData.startTime;
// 调用数据增加
addMonitor(addMontiorData);
}
};
// 请求发送后
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);
}
}
};
// 动态添加到数组中,因为这些方法在用户调用 on事件后才能被调用
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);
}
});
}