UNPKG

whistle

Version:

HTTP, HTTP2, HTTPS, Websocket debugging proxy

166 lines (161 loc) 5.96 kB
var net = require('net'); var rules = require('../lib/rules'); var util = require('../lib/util'); var handleUIReq = require('./webui/lib').handleRequest; var handleWeinreReq = require('./weinre'); var config = require('../lib/config'); var common = require('../lib/util/common'); var localIpCache = util.localIpCache; var WEBUI_PATH = config.WEBUI_PATH; var CUSTOM_WEBUI_PATH = /\/[\w.-]*\.whistle-path\.5b6af7b9884e1165[\w.-]*\/+/; var CUSTOM_WEBUI_PATH_RE = /^\/[\w.-]*\.whistle-path\.5b6af7b9884e1165[\w.-]*\/+/; var PREVIEW_PATH_RE = config.PREVIEW_PATH_RE; var WEBUI_PATH_RE = util.escapeRegExp(WEBUI_PATH, true); var REAL_WEBUI_HOST = new RegExp('^' + WEBUI_PATH_RE + '(__([a-z\\d.-]+)(?:__(\\d{1,5}))?__/)'); var INTERNAL_APP = new RegExp('^' + WEBUI_PATH_RE + '(log|weinre|cgi)(?:\\.(\\d{1,5}))?/'); var PLUGIN_RE = new RegExp('^' + WEBUI_PATH_RE + 'whistle\\.([a-z\\d_-]+)/'); var CUSTOM_REAL_WEBUI_HOST = new RegExp('^/[\\w.-]*\\.whistle-path\\.5b6af7b9884e1165[\\w.-]*/+(__([a-z\\d.-]+)(?:__(\\d{1,5}))?__/)'); var CUSTOM_INTERNAL_APP = new RegExp('^/[\\w.-]*\\.whistle-path\\.5b6af7b9884e1165[\\w.-]*/+(log|weinre|cgi)(?:\\.(\\d{1,5}))?/'); var CUSTOM_PLUGIN_RE = new RegExp('^/[\\w.-]*\\.whistle-path\\.5b6af7b9884e1165[\\w.-]*/+whistle\\.([a-z\\d_-]+)/'); var REAL_WEBUI_HOST_PARAM = /_whistleInternalHost_=(__([a-z\d.-]+)(?:__(\d{1,5}))?__)/; var OUTER_PLUGIN_RE = /^(?:\/whistle)?\/((?:whistle|plugin)\.[a-z\\d_-]+)::(\d{1,5})\//; function transformUI(req, res) { if (config.customUIHost && !config.keepProxyUI) { return res.status(404).end(); } return handleUIReq(req, res); } module.exports = function(req, res, next) { var config = this.config; var pluginMgr = this.pluginMgr; var fullUrl = req.fullUrl = util.getFullUrl(req); // format request var headers = req.headers; var host = util.parseHost(headers.host); var port = host[1] || (req.isHttps ? 443 : 80); var bypass; host = host[0]; req._w2hostname = host; var transformPort, isProxyReq, isWeinre, isOthers; var webUI = WEBUI_PATH; var realHostRe = REAL_WEBUI_HOST; var internalAppRe = INTERNAL_APP; var pluginRe = PLUGIN_RE; var isWebUI = req.path.indexOf(WEBUI_PATH) === 0; var isOld; if (!isWebUI && CUSTOM_WEBUI_PATH_RE.test(req.path)) { isWebUI = true; isOld = true; webUI = CUSTOM_WEBUI_PATH; realHostRe = CUSTOM_REAL_WEBUI_HOST; internalAppRe = CUSTOM_INTERNAL_APP; pluginRe = CUSTOM_PLUGIN_RE; } if (isWebUI) { isWebUI = !config.pureProxy; var realHost; if (isWebUI) { if (realHostRe.test(req.path) || REAL_WEBUI_HOST_PARAM.test(req.url)) { var realPath = RegExp.$1; var realPort = RegExp.$3; realHost = RegExp.$2 + (realPort ? ':' + realPort : ''); headers[util.REAL_HOST_HEADER] = realHost; req.url = req.url.replace(realPath, ''); } else { req.curUrl = fullUrl; if (realHost = rules.resolveInternalHost(req)) { headers[util.REAL_HOST_HEADER] = realHost; } } if (internalAppRe.test(req.path)) { transformPort = RegExp.$2; isWeinre = RegExp.$1 === 'weinre'; if (transformPort) { isOthers = isProxyReq = transformPort != config.port; } else { isProxyReq = false; transformPort = config.port; } isProxyReq = isProxyReq || isOld; } else if (pluginRe.test(req.path)) { isProxyReq = !pluginMgr.getPlugin(RegExp.$1 + ':'); } else if (!headers[config.WEBUI_HEAD]) { isWebUI = false; } if (!config.proxyServer && isProxyReq && !config.isLocalUIUrl(host)) { isWebUI = false; req.isPluginReq = true; req._isProxyReq = true; } if (isWebUI) { req.fromInternalPath = true; var hostname = (req._fwdHost && util.parseHost(req._fwdHost)[0]) || host; headers[common.ORIGIN_HOST_HEADER] = hostname || '*'; } } } else { isWebUI = headers[config.WEBUI_HEAD]; if (!isWebUI) { if (!(isWebUI = localIpCache.get(host))) { isWebUI = config.isLocalUIUrl(host); if (isWebUI ? net.isIP(host) : util.isLocalHost(host)) { isWebUI = util.isProxyPort(port); } } } else if (util.isProxyPort(port) && net.isIP(host)) { localIpCache.set(host, 1); } if (PREVIEW_PATH_RE.test(req.url)) { headers[util.INTERNAL_ID_HEADER] = util.INTERNAL_ID; req.url = '/preview.html?charset=' + RegExp.$1; isWebUI = true; } else if (isWebUI) { if (req.path.indexOf('/_/') === 0) { bypass = '/_/'; } else if (req.path.indexOf('/-/') === 0) { bypass = '/-/'; } if (bypass) { req.url = req.url.replace(bypass, '/'); } delete headers[util.INTERNAL_ID_HEADER]; } } // 后续有用到 fullUrl = req.fullUrl = util.getFullUrl(req); if (bypass) { return next(); } var localRule; req.curUrl = fullUrl; if (isWebUI) { if (isOthers) { util.transformReq(req, res, transformPort); } else { req.url = req.url.replace(transformPort ? internalAppRe : webUI, '/'); if (OUTER_PLUGIN_RE.test(req.path)) { var outerPort = RegExp.$2; req.url = req.url.replace(RegExp['$&'], '/' + RegExp.$1 + '/'); if (outerPort > 0 && outerPort < 65536 && outerPort != config.port) { headers.host = '127.0.0.1:' + outerPort; return util.transformReq(req, res, outerPort); } } req._hasRespond = true; if (isWeinre) { handleWeinreReq(req, res); } else { transformUI(req, res); } } } else if (localRule = rules.resolveLocalRule(req)) { req.url = localRule.url; if (localRule.realPort) { headers.host = '127.0.0.1:' + localRule.realPort; util.transformReq(req, res, localRule.realPort); } else { transformUI(req, res); } } else { next(); } };