UNPKG

whistle

Version:

HTTP, HTTP2, HTTPS, Websocket debugging proxy

210 lines (203 loc) 6.33 kB
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); } }); };