whistle
Version:
HTTP, HTTP2, HTTPS, Websocket debugging proxy
210 lines (203 loc) • 6.33 kB
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 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) {
var gRules = req.rules;
resolveRules(req, rules);
if (gRules) {
req.rules.P = gRules.P;
req.rules.G = gRules.G;
}
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 && util.isUrl(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 (util.isZip(encoding, chunk)) {
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 = util.getEnableEncoding(req && req.enable);
if (!encoding || (!obj._needGunzip && obj._originEncoding)) {
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);
var gList = rules.getGRuleList(req);
var hList = req.headerRulesMgr && req.headerRulesMgr.getGRuleList(req);
if (gList || hList) {
rules.resolvePluginVars(req, (gList || []).concat(hList || []));
}
req._resolvedG = 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);
}
});
};