dev-sidecar/packages/mitmproxy/src/options.js

194 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const interceptorImpls = require('./lib/interceptor')
const dnsUtil = require('./lib/dns')
const log = require('./utils/util.log')
const matchUtil = require('./utils/util.match')
const path = require('path')
const fs = require('fs')
const scriptInterceptor = require('./lib/interceptor/impl/res/script')
const { getTmpPacFilePath, downloadPacAsync, createOverwallMiddleware } = require('./lib/proxy/middleware/overwall')
// 处理拦截配置
function buildIntercepts (intercepts) {
// 自动生成script拦截器所需的辅助配置降低使用`script拦截器`配置绝对地址和相对地址时的门槛
scriptInterceptor.handleScriptInterceptConfig(intercepts)
return intercepts
}
module.exports = (serverConfig) => {
const intercepts = matchUtil.domainMapRegexply(buildIntercepts(serverConfig.intercepts))
const whiteList = matchUtil.domainMapRegexply(serverConfig.whiteList)
const timeoutMapping = matchUtil.domainMapRegexply(serverConfig.setting.timeoutMapping)
const dnsMapping = serverConfig.dns.mapping
const setting = serverConfig.setting
if (!setting.script.dirAbsolutePath) {
setting.script.dirAbsolutePath = path.join(setting.rootDir, setting.script.defaultDir)
}
if (setting.verifySsl !== false) {
setting.verifySsl = true
}
setting.timeoutMapping = timeoutMapping
const overWallConfig = serverConfig.plugin.overwall
if (overWallConfig.pac && overWallConfig.pac.enabled) {
const pacConfig = overWallConfig.pac
// 自动更新 pac.txt
if (!pacConfig.pacFileAbsolutePath && pacConfig.autoUpdate) {
// 异步下载远程 pac.txt 文件,并保存到本地;下载成功后,需要重启代理服务才会生效
downloadPacAsync(pacConfig)
}
// 优先使用本地已下载的 pac.txt 文件
if (!pacConfig.pacFileAbsolutePath && fs.existsSync(getTmpPacFilePath())) {
pacConfig.pacFileAbsolutePath = getTmpPacFilePath()
log.info('读取已下载的 pac.txt 文件:', pacConfig.pacFileAbsolutePath)
}
if (!pacConfig.pacFileAbsolutePath) {
pacConfig.pacFileAbsolutePath = path.join(setting.rootDir, pacConfig.pacFilePath)
if (pacConfig.autoUpdate) {
log.warn('远程 pac.txt 文件下载失败或还在下载中,现使用内置 pac.txt 文件:', pacConfig.pacFileAbsolutePath)
}
}
}
// 插件列表
const middlewares = []
// 梯子插件:如果启用了,则添加到插件列表中
const overwallMiddleware = createOverwallMiddleware(overWallConfig)
if (overwallMiddleware) {
middlewares.push(overwallMiddleware)
}
const preSetIpList = matchUtil.domainMapRegexply(serverConfig.preSetIpList)
const options = {
host: serverConfig.host,
port: serverConfig.port,
dnsConfig: {
preSetIpList,
providers: dnsUtil.initDNS(serverConfig.dns.providers, preSetIpList),
mapping: matchUtil.domainMapRegexply(dnsMapping),
speedTest: serverConfig.dns.speedTest
},
setting,
sniConfig: serverConfig.sniList,
middlewares,
sslConnectInterceptor: (req, cltSocket, head) => {
const hostname = req.url.split(':')[0]
const inWhiteList = matchUtil.matchHostname(whiteList, hostname, 'in whiteList') != null
if (inWhiteList) {
log.info('为白名单域名,不拦截:', hostname)
return false // 所有都不拦截
}
// 配置了拦截的域名,将会被代理
const matched = !!matchUtil.matchHostname(intercepts, hostname, 'matched intercepts')
if (matched === true) {
return matched // 拦截
}
return null // 未匹配到任何拦截配置,由下一个拦截器判断
},
createIntercepts: (context) => {
const rOptions = context.rOptions
const interceptOpts = matchUtil.matchHostnameAll(intercepts, rOptions.hostname, 'get interceptOpts')
if (!interceptOpts) { // 该域名没有配置拦截器,直接过
return
}
const matchIntercepts = []
const matchInterceptsOpts = {}
for (const regexp in interceptOpts) { // 遍历拦截配置
// 判断是否匹配拦截器
let matched
if (regexp !== true && regexp !== 'true') {
matched = matchUtil.isMatched(rOptions.path, regexp)
if (matched == null) { // 拦截器匹配失败
continue
}
}
// 获取拦截器
const interceptOpt = interceptOpts[regexp]
// interceptOpt.key = regexp
// 添加exclusions字段用于排除某些路径
// @since 1.8.5
if (Array.isArray(interceptOpt.exclusions) && interceptOpt.exclusions.length > 0) {
let isExcluded = false
for (const exclusion of interceptOpt.exclusions) {
if (matchUtil.isMatched(rOptions.path, exclusion)) {
log.debug(`拦截器配置排除了path${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}, exclusion: '${exclusion}', interceptOpt:`, interceptOpt)
isExcluded = true
}
}
if (isExcluded) {
continue
}
}
log.debug(`拦截器匹配path成功${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}, regexp: ${regexp}, interceptOpt:`, interceptOpt)
// log.info(`interceptor matched, regexp: '${regexp}' =>`, JSON.stringify(interceptOpt), ', url:', url)
for (const impl of interceptorImpls) {
// 根据拦截配置挑选合适的拦截器来处理
if (impl.is && impl.is(interceptOpt)) {
let action = 'add'
// 如果存在同名拦截器则order值越大优先级越高
const matchedInterceptOpt = matchInterceptsOpts[impl.name]
if (matchedInterceptOpt) {
if (matchedInterceptOpt.order >= (interceptOpt.order || 0)) {
log.warn(`duplicate interceptor: ${impl.name}, hostname: ${rOptions.hostname}`)
continue
}
action = 'replace'
}
const interceptor = { name: impl.name, priority: impl.priority }
if (impl.requestIntercept) {
// req拦截器
interceptor.requestIntercept = (context, req, res, ssl, next) => {
return impl.requestIntercept(context, interceptOpt, req, res, ssl, next, matched)
}
} else if (impl.responseIntercept) {
// res拦截器
interceptor.responseIntercept = (context, req, res, proxyReq, proxyRes, ssl, next) => {
return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next, matched)
}
}
// log.info(`${action} interceptor: ${impl.name}, hostname: ${rOptions.hostname}, regexp: ${regexp}`)
if (action === 'add') {
matchIntercepts.push(interceptor)
} else {
matchIntercepts[matchedInterceptOpt.index] = interceptor
}
matchInterceptsOpts[impl.name] = {
order: interceptOpt.order || 0,
index: matchIntercepts.length - 1
}
}
}
}
matchIntercepts.sort((a, b) => { return a.priority - b.priority })
// for (const interceptor of matchIntercepts) {
// log.info('interceptor:', interceptor.name, 'priority:', interceptor.priority)
// }
return matchIntercepts
}
}
if (setting.rootCaFile) {
options.caCertPath = setting.rootCaFile.certPath
options.caKeyPath = setting.rootCaFile.keyPath
}
return options
}