Files
2023-05-08 22:04:22 +08:00

442 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
var qs = require('querystring');
var iconv = require('iconv-lite');
var util = require('../util');
var extend = require('extend');
var hparser = require('hparser');
var pluginMgr = require('../plugins');
var config = require('../config');
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var toMultiparts = hparser.toMultiparts;
var MultipartParser = hparser.MultipartParser;
var JSON_RE = /{[\w\W]*}|\[[\w\W]*\]/;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
urlencoded: 'application/x-www-form-urlencoded',
form: 'application/x-www-form-urlencoded',
json: 'application/json',
xml: 'text/xml',
text: 'text/plain',
upload: 'multipart/form-data',
multipart: 'multipart/form-data',
defaultType: 'application/octet-stream'
};
/**
* 处理请求数据
*
* @param req:method、body、headers,top,bottom,speed、delay,charset,timeout
* @param data
*/
function handleReq(req, data) {
extend(req.headers, data.headers);
if (typeof data.charset == 'string') {
var type = req.headers['content-type'];
var charset = '; charset=' + data.charset;
if (typeof type == 'string') {
req.headers['content-type'] = type.split(';')[0] + charset;
} else {
req.headers['content-type'] = charset;
}
} else {
delete data.charset;
}
if (!util.hasRequestBody(req.method)) {
delete data.top;
delete data.bottom;
delete data.body;
delete req.headers['content-length'];
} else {
util.removeReqBody(req, data);
if (data.top || data.bottom || data.body) {
delete req.headers['content-length'];
req._hasInjectBody = true;
}
}
util.isWhistleTransformData(data) &&
req.addZipTransform(new WhistleTransform(data));
var opList = util.parseHeaderReplace(req.rules.headerReplace).req;
if (opList) {
var host = req.headers.host;
var xff = req.headers[config.CLIENT_IP_HEAD];
var clientId = req.headers[config.CLIENT_ID_HEAD];
util.handleHeaderReplace(req.headers, opList);
if (req.headers.host !== host) {
req._customHost = req.headers.host;
}
if (xff !== req.headers[config.CLIENT_IP_HEAD]) {
req._customXFF = req.headers[config.CLIENT_IP_HEAD];
}
if (clientId !== req.headers[config.CLIENT_ID_HEAD]) {
req._customClientId = req.headers[config.CLIENT_ID_HEAD];
}
}
}
function handleAuth(data, auth) {
auth = util.getAuthBasic(auth);
auth && util.setHeader(data, 'authorization', auth);
}
function handleParams(req, params, urlParams) {
var originParams = params;
var hasBody;
if ((params = params && qs.stringify(params))) {
var transform;
var headers = req.headers;
var isJson = util.isJSONContent(req);
if (isJson || util.isUrlEncoded(req)) {
delete headers['content-length'];
transform = new Transform();
var buffer, interrupt;
transform._transform = function (chunk, encoding, callback) {
if (chunk) {
if (!interrupt) {
buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
chunk = null;
if (buffer.length > MAX_REQ_SIZE) {
interrupt = true;
chunk = buffer;
buffer = null;
}
}
} else if (buffer && buffer.length) {
var body;
var isGBK = !util.isUtf8(buffer);
if (isGBK) {
try {
body = iconv.decode(buffer, 'GB18030');
} catch (e) {}
}
if (!body) {
body = buffer + '';
}
if (isJson) {
body = body.replace(JSON_RE, function (json) {
var obj = util.parseRawJson(json);
return obj
? JSON.stringify(extend(true, obj, originParams))
: json;
});
} else {
body = util.replaceQueryString(body, params);
}
if (isGBK) {
try {
body = iconv.encode(body, 'GB18030');
} catch (e) {}
} else {
chunk = util.toBuffer(body);
}
buffer = null;
} else {
var data = params;
if (isJson) {
try {
data = JSON.stringify(originParams);
} catch (e) {}
}
chunk = util.toBuffer(data);
}
callback(null, chunk);
};
req.addZipTransform(transform);
hasBody = true;
} else if (
util.isMultipart(req) &&
/boundary=(?:"([^"]+)"|([^;]+))/i.test(headers['content-type'])
) {
delete headers['content-length'];
var boundaryStr = '--' + (RegExp.$1 || RegExp.$2);
var startBoundary = util.toBuffer(boundaryStr + '\r\n');
var boundary = util.toBuffer('\r\n' + boundaryStr);
var sepBoundary = util.toBuffer('\r\n' + boundaryStr + '\r\n');
var endBoudary = util.toBuffer('\r\n' + boundaryStr + '--');
var length = startBoundary.length;
var sepLength = sepBoundary.length;
transform = new Transform();
transform.parse = function (chunk) {
var index, result, sep, data;
while (
(index = util.indexOfList(chunk, boundary)) != -1 &&
((sep = util.startWithList(chunk, sepBoundary, index)) ||
util.startWithList(chunk, endBoudary, index))
) {
data = this.parser.transform(chunk.slice(0, index));
result =
result && data ? Buffer.concat([result, data]) : result || data;
if (!sep) {
data = toMultiparts(originParams, boundaryStr);
result = result ? Buffer.concat([result, data]) : data;
}
sep = sep ? sepBoundary : endBoudary;
result = result ? Buffer.concat([result, sep]) : sep;
chunk = chunk.slice(index + sepLength);
this.parser = new MultipartParser(originParams);
}
var len = chunk.length;
if (len >= sepLength) {
var lastIndex = len - sepLength + 1;
data = this.parser.transform(chunk.slice(0, lastIndex));
chunk = chunk.slice(lastIndex);
result =
result && data ? Buffer.concat([result, data]) : result || data;
}
this.buffer = chunk;
return result;
};
transform._transform = function (chunk, encoding, callback) {
if (this.badMultipart) {
return callback(null, chunk);
}
var end;
if (chunk) {
chunk = this.buffer ? Buffer.concat([this.buffer, chunk]) : chunk;
} else {
end = true;
chunk = this.buffer;
}
if (chunk) {
this.buffer = null;
if (!this.parser) {
if (util.startWithList(chunk, startBoundary)) {
this.parser = new MultipartParser(originParams);
chunk = this.parse(chunk.slice(length));
chunk = chunk
? Buffer.concat([startBoundary, chunk])
: startBoundary;
} else if (end || chunk.length >= length) {
this.badMultipart = true;
} else {
this.buffer = chunk;
chunk = null;
}
} else {
chunk = this.parse(chunk);
}
}
if (end && this.buffer) {
chunk = chunk ? Buffer.concat([chunk, this.buffer]) : this.buffer;
}
callback(null, chunk);
};
req.addZipTransform(transform);
hasBody = true;
}
}
var _params = hasBody ? null : originParams;
if (_params || urlParams) {
req._urlParams =
urlParams && _params ? extend(_params, urlParams) : _params || urlParams;
}
}
function handleReplace(req, replacement) {
if (!util.hasRequestBody(req) || !replacement) {
return;
}
var type = req.headers['content-type'];
type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
if (!type || type == 'IMG') {
return;
}
Object.keys(replacement).forEach(function (pattern) {
var value = replacement[pattern];
if (
util.isOriginalRegExp(pattern) &&
(pattern = util.toOriginalRegExp(pattern))
) {
req.addTextTransform(new ReplacePatternTransform(pattern, value));
} else if (pattern) {
req.addTextTransform(new ReplaceStringTransform(pattern, value));
}
});
}
module.exports = function (req, res, next) {
var reqRules = req.rules;
var authObj = util.getAuthByRules(reqRules);
util.parseRuleJson(
[
reqRules.reqHeaders,
reqRules.reqCookies,
authObj ? null : reqRules.auth,
reqRules.params,
reqRules.reqCors,
reqRules.reqReplace,
reqRules.urlReplace,
reqRules.urlParams
],
function (
headers,
cookies,
auth,
params,
cors,
replacement,
urlReplace,
urlParams
) {
var data = {};
if (headers) {
data.headers = extend(data.headers || {}, headers);
}
if (data.body && typeof data.body !== 'string') {
try {
data.body = JSON.stringify(data.body);
} catch (e) {}
}
if (data.headers) {
data.headers = util.lowerCaseify(data.headers, req.rawHeaderNames);
req._customHost = data.headers.host;
req._customXFF = data.headers[config.CLIENT_IP_HEAD];
req._customClientId = data.headers[config.CLIENT_ID_HEAD];
}
if (reqRules.method) {
var method = util.getMatcherValue(reqRules.method);
data.method = method;
}
if (reqRules.reqType) {
var newType = util.getMatcherValue(reqRules.reqType).split(';');
var type = newType[0];
newType[0] =
!type || type.indexOf('/') != -1
? type
: REQ_TYPE[type] || REQ_TYPE.defaultType;
type = newType.join(';');
if (type.indexOf(';') == -1) {
var origType = req.headers['content-type'];
if (typeof origType == 'string' && origType.indexOf(';') != -1) {
origType = origType.split(';');
origType[0] = type;
type = origType.join(';');
}
}
util.setHeader(data, 'content-type', type);
}
if (reqRules.reqCharset) {
data.charset = util.getMatcherValue(reqRules.reqCharset);
}
if (reqRules.referer) {
var referer = util.getMatcherValue(reqRules.referer);
util.setHeader(data, 'referer', referer);
}
if (reqRules.ua) {
var ua = util.getMatcherValue(reqRules.ua);
util.setHeader(data, 'user-agent', ua);
}
var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
reqSpeed = reqSpeed && parseFloat(reqSpeed);
if (reqSpeed > 0) {
data.speed = reqSpeed;
}
var cookie = data.headers && data.headers.cookie;
if (typeof cookie !== 'string') {
if (Array.isArray(cookie)) {
cookie = cookie.join('; ');
} else {
cookie = req.headers.cookie;
}
}
util.setReqCookies(data, cookies, cookie, req);
handleAuth(data, auth || authObj);
util.setReqCors(data, cors);
req.method = util.getMethod(data.method || req.method);
var bodyList = [
reqRules.reqBody,
reqRules.reqPrepend,
reqRules.reqAppend
];
util.getRuleValue(
bodyList,
function (reqBody, reqPrepend, reqAppend) {
if (reqBody != null) {
data.body = reqBody || util.EMPTY_BUFFER;
}
if (reqPrepend) {
data.top = reqPrepend;
}
if (reqAppend) {
data.bottom = reqAppend;
}
handleReq(req, data);
handleParams(req, params, urlParams);
if (req._urlParams || urlReplace) {
var options = req.options;
var isUrl = util.isUrl(options.href);
var newUrl = isUrl ? options.href : req.fullUrl;
if (req._urlParams) {
newUrl = util.replaceUrlQueryString(newUrl, req._urlParams);
}
if (urlReplace) {
newUrl = util.parsePathReplace(newUrl, urlReplace);
}
if (newUrl !== options.href) {
if (isUrl) {
req.options = util.parseUrl(newUrl);
} else {
req._realUrl = newUrl;
}
}
}
util.removeUnsupportsHeaders(req.headers);
util.disableReqProps(req);
handleReplace(req, replacement);
var reqWriter = util.hasRequestBody(req)
? util.getRuleFile(reqRules.reqWrite)
: null;
util.getFileWriters(
[reqWriter, util.getRuleFile(reqRules.reqWriteRaw)],
function (writer, rawWriter) {
if (writer) {
req.addZipTransform(
new FileWriterTransform(writer, req, false, false, true)
);
}
if (rawWriter) {
req.addZipTransform(
new FileWriterTransform(rawWriter, req, true, false, true)
);
}
pluginMgr.postStats(req);
next();
},
util.isEnable(req, 'forceReqWrite')
);
},
!util.hasRequestBody(req.method),
null,
null,
req
);
},
req
);
};