mirror of
https://github.com/avwo/whistle.git
synced 2026-10-20 07:58:55 +08:00
203 lines
6.0 KiB
JavaScript
203 lines
6.0 KiB
JavaScript
var rules = require('../rules');
|
|
var util = require('../util');
|
|
var pluginMgr = require('../plugins');
|
|
var fileMgr = require('../util/file-mgr');
|
|
var transproto = require('../util/transproto');
|
|
var getEncodeTransform = transproto.getEncodeTransform;
|
|
var getDecodeTransform = transproto.getDecodeTransform;
|
|
var getRawHeaderNames = require('hparser').getRawHeaderNames;
|
|
|
|
var HTTP_RE = /^https?:/;
|
|
var MAX_PAYLOAD_SIZE = 1024 * 256;
|
|
|
|
function resolveRules(req, rules) {
|
|
if (!rules) {
|
|
return;
|
|
}
|
|
req.curUrl = req.fullUrl = util.getFullUrl(req);
|
|
if (rules.initRules) {
|
|
rules.initRules(req);
|
|
} else {
|
|
var _pluginRules = rules.resolveReqRules(req);
|
|
// 插件不支持rulesFile协议
|
|
delete req.rules.rulesFile;
|
|
util.mergeRules(req, _pluginRules);
|
|
}
|
|
}
|
|
|
|
function setupRules(req, next) {
|
|
resolveRules(req, rules);
|
|
rules.resolveRulesFile(req, function () {
|
|
pluginMgr.resolveWhistlePlugins(req);
|
|
pluginMgr.getRules(req, function (pluginRules) {
|
|
req.pluginRules = pluginRules;
|
|
resolveRules(req, pluginRules);
|
|
util.filterWeakRule(req);
|
|
var ruleUrl = util.rule.getUrl(req.rules.rule);
|
|
if (ruleUrl !== req.fullUrl && HTTP_RE.test(ruleUrl)) {
|
|
ruleUrl = util.encodeNonLatin1Char(ruleUrl);
|
|
}
|
|
req.options = util.parseUrl(ruleUrl || req.fullUrl);
|
|
if (
|
|
req.isH2 &&
|
|
ruleUrl &&
|
|
req.options.protocol === 'http:' &&
|
|
ruleUrl !== req.fullUrl
|
|
) {
|
|
req.isH2 = false;
|
|
}
|
|
var rawNames = (req.rawHeaderNames = Array.isArray(req.rawHeaders)
|
|
? getRawHeaderNames(req.rawHeaders)
|
|
: {});
|
|
rawNames.connection = rawNames.connection || 'Connection';
|
|
rawNames['proxy-authorization'] =
|
|
rawNames['proxy-authorization'] || 'Proxy-Authorization';
|
|
next();
|
|
});
|
|
});
|
|
}
|
|
|
|
function getDecoder(obj) {
|
|
return function (socket, callback) {
|
|
var encoding = obj._originEncoding;
|
|
var handleError = function (err) {
|
|
obj.emit('error', err);
|
|
};
|
|
var decoder;
|
|
if (
|
|
obj._needGunzip ||
|
|
socket ||
|
|
encoding !== obj.headers['content-encoding']
|
|
) {
|
|
var stream = encoding && (obj._srcResponse || obj);
|
|
util.readOneChunk(stream, function (chunk) {
|
|
obj._needGunzip = false;
|
|
if (chunk) {
|
|
if (chunk[0] === 31 && (chunk[1] == null || chunk[1] === 139)) {
|
|
decoder = util.getUnzipStream(encoding);
|
|
obj._needGunzip = true;
|
|
} else {
|
|
decoder = util.createTransform();
|
|
obj._originEncoding = null;
|
|
}
|
|
decoder.write(chunk);
|
|
}
|
|
handleDecode(stream);
|
|
});
|
|
} else {
|
|
handleDecode();
|
|
}
|
|
function handleDecode(stream) {
|
|
decoder && decoder.on('error', handleError);
|
|
if (socket) {
|
|
delete obj.headers['content-length'];
|
|
var enTrans = getEncodeTransform();
|
|
var deTrans = getDecodeTransform();
|
|
enTrans.pipe(socket).pipe(deTrans);
|
|
enTrans.on('error', handleError);
|
|
deTrans.on('error', handleError);
|
|
if (decoder) {
|
|
decoder.pipe(enTrans);
|
|
} else {
|
|
decoder = enTrans;
|
|
}
|
|
socket = deTrans;
|
|
}
|
|
callback(decoder, socket, stream);
|
|
}
|
|
};
|
|
}
|
|
|
|
function getEncoder(obj, req) {
|
|
return function (socket, callback) {
|
|
var encoding;
|
|
var enable = req && req.enable;
|
|
if (enable && enable.gzip && (obj._needGunzip || !obj._originEncoding)) {
|
|
encoding = 'gzip';
|
|
} else {
|
|
encoding = obj._needGunzip && obj.headers;
|
|
}
|
|
var encoder = encoding && util.getZipStream(encoding);
|
|
var handleError = function (err) {
|
|
obj.emit('error', err);
|
|
};
|
|
encoder && encoder.on('error', handleError);
|
|
if (socket) {
|
|
delete obj.headers['content-length'];
|
|
var enTrans = getEncodeTransform();
|
|
var deTrans = getDecodeTransform();
|
|
enTrans.on('error', handleError);
|
|
deTrans.on('error', handleError);
|
|
enTrans.pipe(socket).pipe(deTrans);
|
|
socket = enTrans;
|
|
if (encoder) {
|
|
deTrans.pipe(encoder);
|
|
} else {
|
|
encoder = deTrans;
|
|
}
|
|
socket.pipe = function (stream) {
|
|
return encoder.pipe(stream);
|
|
};
|
|
obj.emit('bodyStreamReady', socket);
|
|
}
|
|
callback(socket || encoder);
|
|
};
|
|
}
|
|
|
|
module.exports = function (req, res, next) {
|
|
if (req.isLogRequests !== false) {
|
|
++util.proc.httpRequests;
|
|
++util.proc.totalHttpRequests;
|
|
req.isLogRequests = true;
|
|
}
|
|
req.reqId = util.getReqId();
|
|
req.curUrl = req.fullUrl = util.getFullUrl(req);
|
|
req._originEncoding = req.headers['content-encoding'];
|
|
req.onDecode = function (callback) {
|
|
var decode = getDecoder(req);
|
|
pluginMgr.getReqReadPipe(req, function (socket) {
|
|
decode(socket, callback);
|
|
});
|
|
};
|
|
req.onEncode = function (callback) {
|
|
var encode = getEncoder(req);
|
|
pluginMgr.getReqWritePipe(req, function (socket) {
|
|
encode(socket, callback);
|
|
});
|
|
};
|
|
res.onDecode = function (callback) {
|
|
var decode = getDecoder(res, req);
|
|
pluginMgr.getResReadPipe(req, res, function (socket) {
|
|
decode(socket, callback);
|
|
});
|
|
};
|
|
res.onEncode = function (callback) {
|
|
var encode = getEncoder(res, req);
|
|
pluginMgr.getResWritePipe(req, res, function (socket) {
|
|
encode(socket, callback);
|
|
});
|
|
};
|
|
rules.initHeaderRules(req, true);
|
|
pluginMgr.resolvePipePlugin(req, function () {
|
|
var reqReadPort = req._pipePluginPorts.reqReadPort;
|
|
if (reqReadPort || req._pipePluginPorts.reqWritePort) {
|
|
delete req.headers['content-length'];
|
|
}
|
|
var hasBodyFilter = rules.resolveBodyFilter(req);
|
|
req._bodyFilters = null;
|
|
if (hasBodyFilter || reqReadPort) {
|
|
req._needGunzip = true;
|
|
var payloadSize = MAX_PAYLOAD_SIZE;
|
|
if (!hasBodyFilter) {
|
|
payloadSize = rules.hasReqScript(req) ? 0 : 1;
|
|
}
|
|
req.getPayload(function (err, payload) {
|
|
req._reqBody = fileMgr.decode(payload);
|
|
setupRules(req, next);
|
|
}, payloadSize);
|
|
} else {
|
|
setupRules(req, next);
|
|
}
|
|
});
|
|
};
|