feature: 拦截配置功能,允许匹配到多个 ‘域名匹配符’ 下的拦截配置了,只要域名符合 ‘域名匹配符’。 (#286)
parent
3ade87d07b
commit
74c14736b1
|
@ -65,7 +65,7 @@ function doDiff (oldObj, newObj) {
|
||||||
|
|
||||||
function deleteNullItems (target) {
|
function deleteNullItems (target) {
|
||||||
lodash.forEach(target, (item, key) => {
|
lodash.forEach(target, (item, key) => {
|
||||||
if (item == null) {
|
if (item == null || item === '[delete]') {
|
||||||
delete target[key]
|
delete target[key]
|
||||||
}
|
}
|
||||||
if (lodash.isObject(item)) {
|
if (lodash.isObject(item)) {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
:style="{ height: '100%' }"
|
:style="{ height: '100%' }"
|
||||||
>
|
>
|
||||||
<a-tab-pane tab="拦截设置" key="1" >
|
<a-tab-pane tab="拦截设置" key="1" >
|
||||||
<vue-json-editor style="height:100%;" ref="editor" v-model="targetConfig.intercepts" mode="code" :show-btns="false" :expandedOnStart="true" @json-change="onJsonChange" ></vue-json-editor>
|
<vue-json-editor style="height:100%;" ref="editor" v-model="targetConfig.intercepts" mode="code" :show-btns="false" :expandedOnStart="true" @json-change="onJsonChange"></vue-json-editor>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane tab="DNS设置" key="2">
|
<a-tab-pane tab="DNS设置" key="2">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -138,7 +138,6 @@
|
||||||
<!-- <a-button type="danger" icon="minus" @click="deleteSniList(item,index)"/>-->
|
<!-- <a-button type="danger" icon="minus" @click="deleteSniList(item,index)"/>-->
|
||||||
<!-- </a-col>-->
|
<!-- </a-col>-->
|
||||||
<!-- </a-row>-->
|
<!-- </a-row>-->
|
||||||
|
|
||||||
<!-- </a-tab-pane>-->
|
<!-- </a-tab-pane>-->
|
||||||
<a-tab-pane tab="IP测速" key="6">
|
<a-tab-pane tab="IP测速" key="6">
|
||||||
<div>
|
<div>
|
||||||
|
@ -201,7 +200,6 @@
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
|
|
|
@ -13,6 +13,8 @@ function readConfig (config, defaultConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'options',
|
||||||
|
priority: 1,
|
||||||
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ module.exports = {
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
// 允许跨域
|
// 允许跨域
|
||||||
'Dev-Sidecar-Interceptor': 'options',
|
'DS-Interceptor': 'options',
|
||||||
'Access-Control-Allow-Origin': rOptions.headers.origin,
|
'Access-Control-Allow-Origin': rOptions.headers.origin,
|
||||||
'Access-Control-Allow-Headers': allowHeaders,
|
'Access-Control-Allow-Headers': allowHeaders,
|
||||||
'Access-Control-Allow-Methods': allowMethods,
|
'Access-Control-Allow-Methods': allowMethods,
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
requestIntercept (context, interceptOpts, req, res, ssl, next) {
|
name: 'abort',
|
||||||
|
priority: 103,
|
||||||
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
log.info('abort:', rOptions.hostname, req.url)
|
|
||||||
res.writeHead(403)
|
res.writeHead(403, {
|
||||||
res.write('DevSidecar 403: \n\n request abort, this request is matched by abort intercept.\n\n 因配置abort拦截器,本请求将取消')
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
|
'DS-Interceptor': 'abort'
|
||||||
|
})
|
||||||
|
res.write(
|
||||||
|
'DevSidecar 403: Request abort.\r\n\r\n' +
|
||||||
|
' This request is matched by abort intercept.\r\n' +
|
||||||
|
' 因配置abort拦截器,本请求直接返回403禁止访问。'
|
||||||
|
)
|
||||||
res.end()
|
res.end()
|
||||||
return true// 是否结束
|
|
||||||
|
const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
|
||||||
|
log.info('abort intercept:', url)
|
||||||
|
return true // true代表请求结束
|
||||||
},
|
},
|
||||||
is (interceptOpt) {
|
is (interceptOpt) {
|
||||||
return !!interceptOpt.abort
|
return !!interceptOpt.abort
|
||||||
|
|
|
@ -55,6 +55,8 @@ function getLastModifiedTimeFromIfModifiedSince (rOptions, log) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'cacheReq',
|
||||||
|
priority: 111,
|
||||||
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
|
|
||||||
|
@ -65,12 +67,12 @@ module.exports = {
|
||||||
// 获取 Cache-Control 用于判断是否禁用缓存
|
// 获取 Cache-Control 用于判断是否禁用缓存
|
||||||
const cacheControl = rOptions.headers['cache-control']
|
const cacheControl = rOptions.headers['cache-control']
|
||||||
if (cacheControl && (cacheControl.indexOf('no-cache') >= 0 || cacheControl.indexOf('no-store') >= 0)) {
|
if (cacheControl && (cacheControl.indexOf('no-cache') >= 0 || cacheControl.indexOf('no-store') >= 0)) {
|
||||||
return // 禁用缓存,跳过当前拦截器
|
return // 当前请求指定要禁用缓存,跳过当前拦截器
|
||||||
}
|
}
|
||||||
// 获取 Pragma 用于判断是否禁用缓存
|
// 获取 Pragma 用于判断是否禁用缓存
|
||||||
const pragma = rOptions.headers.pragma
|
const pragma = rOptions.headers.pragma
|
||||||
if (pragma && (pragma.indexOf('no-cache') >= 0 || pragma.indexOf('no-store') >= 0)) {
|
if (pragma && (pragma.indexOf('no-cache') >= 0 || pragma.indexOf('no-store') >= 0)) {
|
||||||
return // 禁用缓存,跳过当前拦截
|
return // 当前请求指定要禁用缓存,跳过当前拦截器
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最近编辑时间
|
// 最近编辑时间
|
||||||
|
@ -89,10 +91,12 @@ module.exports = {
|
||||||
|
|
||||||
// 缓存未过期,直接拦截请求并响应304
|
// 缓存未过期,直接拦截请求并响应304
|
||||||
res.writeHead(304, {
|
res.writeHead(304, {
|
||||||
'Dev-Sidecar-Interceptor': 'cacheReq'
|
'DS-Interceptor': 'cache: ' + maxAge
|
||||||
})
|
})
|
||||||
res.end()
|
res.end()
|
||||||
|
|
||||||
|
const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
|
||||||
|
log.info('cache intercept:', url)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
is (interceptOpt) {
|
is (interceptOpt) {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
const url = require('url')
|
const url = require('url')
|
||||||
|
const lodash = require('lodash')
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'proxy',
|
||||||
|
priority: 121,
|
||||||
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log, RequestCounter } = context
|
const { rOptions, log, RequestCounter } = context
|
||||||
|
|
||||||
|
@ -8,17 +11,17 @@ module.exports = {
|
||||||
let proxyConf = interceptOpt.proxy
|
let proxyConf = interceptOpt.proxy
|
||||||
if (RequestCounter && interceptOpt.backup && interceptOpt.backup.length > 0) {
|
if (RequestCounter && interceptOpt.backup && interceptOpt.backup.length > 0) {
|
||||||
// 优选逻辑
|
// 优选逻辑
|
||||||
const backup = [proxyConf]
|
const backupList = [proxyConf]
|
||||||
for (const bk of interceptOpt.backup) {
|
for (const bk of interceptOpt.backup) {
|
||||||
backup.push(bk)
|
backupList.push(bk)
|
||||||
}
|
}
|
||||||
const key = rOptions.hostname + '/' + interceptOpt.key
|
const key = rOptions.hostname + '/' + interceptOpt.key
|
||||||
const count = RequestCounter.getOrCreate(key, backup)
|
const count = RequestCounter.getOrCreate(key, backupList)
|
||||||
if (count.value == null) {
|
if (count.value == null) {
|
||||||
count.doRank()
|
count.doRank()
|
||||||
}
|
}
|
||||||
if (count.value == null) {
|
if (count.value == null) {
|
||||||
log.error('count value is null', count)
|
log.error('`count.value` is null, the count:', count)
|
||||||
} else {
|
} else {
|
||||||
count.doCount(count.value)
|
count.doCount(count.value)
|
||||||
proxyConf = count.value
|
proxyConf = count.value
|
||||||
|
@ -30,27 +33,34 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取代理目标地址
|
||||||
|
let proxyTarget
|
||||||
|
if (interceptOpt.replace) {
|
||||||
|
const regexp = new RegExp(interceptOpt.replace)
|
||||||
|
proxyTarget = req.url.replace(regexp, proxyConf)
|
||||||
|
} else if (proxyConf.indexOf('http:') === 0 || proxyConf.indexOf('https:') === 0) {
|
||||||
|
proxyTarget = proxyConf
|
||||||
|
} else {
|
||||||
let uri = req.url
|
let uri = req.url
|
||||||
if (uri.indexOf('http') === 0) {
|
if (uri.indexOf('http') === 0) {
|
||||||
// eslint-disable-next-line node/no-deprecated-api
|
// eslint-disable-next-line node/no-deprecated-api
|
||||||
const URL = url.parse(uri)
|
const URL = url.parse(uri)
|
||||||
uri = URL.path
|
uri = URL.path
|
||||||
}
|
}
|
||||||
let proxyTarget = proxyConf + uri
|
proxyTarget = proxyConf + uri
|
||||||
if (interceptOpt.replace) {
|
|
||||||
const regexp = new RegExp(interceptOpt.replace)
|
|
||||||
proxyTarget = req.url.replace(regexp, proxyConf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// no-template-curly-in-string
|
// no-template-curly-in-string
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
proxyTarget = proxyTarget.replace('${host}', rOptions.hostname)
|
proxyTarget = proxyTarget.replace('${host}', rOptions.hostname)
|
||||||
|
|
||||||
log.info('拦截【proxy】: original:', rOptions.hostname, ',target:', proxyTarget)
|
|
||||||
// const backup = interceptOpt.backup
|
|
||||||
const proxy = proxyTarget.indexOf('http') === 0 ? proxyTarget : rOptions.protocol + '//' + proxyTarget
|
const proxy = proxyTarget.indexOf('http') === 0 ? proxyTarget : rOptions.protocol + '//' + proxyTarget
|
||||||
// eslint-disable-next-line node/no-deprecated-api
|
// eslint-disable-next-line node/no-deprecated-api
|
||||||
const URL = url.parse(proxy)
|
const URL = url.parse(proxy)
|
||||||
|
rOptions.origional = lodash.cloneDeep(rOptions) // 备份原始请求参数
|
||||||
|
delete rOptions.origional.agent
|
||||||
|
delete rOptions.origional.headers
|
||||||
rOptions.protocol = URL.protocol
|
rOptions.protocol = URL.protocol
|
||||||
rOptions.hostname = URL.host
|
rOptions.hostname = URL.host
|
||||||
rOptions.host = URL.host
|
rOptions.host = URL.host
|
||||||
|
@ -69,8 +79,10 @@ module.exports = {
|
||||||
if (rOptions.agent && rOptions.agent.options) {
|
if (rOptions.agent && rOptions.agent.options) {
|
||||||
rOptions.agent.options.rejectUnauthorized = false
|
rOptions.agent.options.rejectUnauthorized = false
|
||||||
}
|
}
|
||||||
|
res.setHeader('DS-Interceptor', `proxy: ${proxyTarget}, sni: ${interceptOpt.sni}`)
|
||||||
log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget, ', sni replace servername:', rOptions.servername)
|
log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget, ', sni replace servername:', rOptions.servername)
|
||||||
} else {
|
} else {
|
||||||
|
res.setHeader('DS-Interceptor', `proxy: ${proxyTarget}`)
|
||||||
log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget)
|
log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,34 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'redirect',
|
||||||
|
priority: 102,
|
||||||
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
const url = req.url
|
|
||||||
let redirect
|
let redirect
|
||||||
if (typeof interceptOpt.redirect === 'string') {
|
if (typeof interceptOpt.redirect === 'string') {
|
||||||
redirect = rOptions.protocol + '//' + interceptOpt.redirect + url
|
if (interceptOpt.redirect.indexOf('http:') === 0 || interceptOpt.redirect.indexOf('https:') === 0) {
|
||||||
|
redirect = interceptOpt.redirect
|
||||||
} else {
|
} else {
|
||||||
redirect = interceptOpt.redirect(url)
|
redirect = rOptions.protocol + '//' + interceptOpt.redirect + req.url
|
||||||
}
|
}
|
||||||
log.info('请求重定向:', rOptions.hostname, url, redirect)
|
} else {
|
||||||
res.writeHead(302, { Location: redirect })
|
redirect = interceptOpt.redirect(req.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// no-template-curly-in-string
|
||||||
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
redirect = redirect.replace('${host}', rOptions.hostname)
|
||||||
|
|
||||||
|
res.writeHead(302, {
|
||||||
|
Location: redirect,
|
||||||
|
'DS-Interceptor': 'redirect'
|
||||||
|
})
|
||||||
res.end()
|
res.end()
|
||||||
return true// 是否结束
|
|
||||||
|
const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
|
||||||
|
log.info(`redirect intercept: ${url} ➜ ${redirect}`)
|
||||||
|
return true // true代表请求结束
|
||||||
},
|
},
|
||||||
is (interceptOpt) {
|
is (interceptOpt) {
|
||||||
return interceptOpt.redirect // 如果配置中有redirect,那么这个配置是需要redirect拦截的
|
return interceptOpt.redirect // 如果配置中有redirect,那么这个配置是需要redirect拦截的
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
requestIntercept (context, interceptOpt) {
|
name: 'sni',
|
||||||
|
priority: 122,
|
||||||
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
if (interceptOpt.sni != null) {
|
|
||||||
rOptions.servername = interceptOpt.sni
|
rOptions.servername = interceptOpt.sni
|
||||||
if (rOptions.agent && rOptions.agent.options) {
|
if (rOptions.agent && rOptions.agent.options) {
|
||||||
rOptions.agent.options.rejectUnauthorized = false
|
rOptions.agent.options.rejectUnauthorized = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res.setHeader('DS-Interceptor', 'sni: ' + interceptOpt.sni)
|
||||||
|
|
||||||
log.info('sni intercept: sni replace servername:', rOptions.hostname, '➜', rOptions.servername)
|
log.info('sni intercept: sni replace servername:', rOptions.hostname, '➜', rOptions.servername)
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
is (interceptOpt) {
|
is (interceptOpt) {
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
requestIntercept (context, interceptOpts, req, res, ssl, next) {
|
name: 'success',
|
||||||
|
priority: 101,
|
||||||
|
requestIntercept (context, interceptOpt, req, res, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
log.info('success:', rOptions.hostname, req.url)
|
|
||||||
res.writeHead(200)
|
res.writeHead(200, {
|
||||||
res.write('DevSidecar 200: \n\n request success, this request is matched by success intercept.\n\n 因配置success拦截器,本请求将直接返回成功')
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
|
'DS-Interceptor': 'success'
|
||||||
|
})
|
||||||
|
res.write(
|
||||||
|
'DevSidecar 200: Request success.\n\n' +
|
||||||
|
' This request is matched by success intercept.\n\n' +
|
||||||
|
' 因配置success拦截器,本请求直接返回200成功。'
|
||||||
|
)
|
||||||
res.end()
|
res.end()
|
||||||
return true// 是否结束
|
|
||||||
|
const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
|
||||||
|
log.info('success intercept:', url)
|
||||||
|
return true // true代表请求结束
|
||||||
},
|
},
|
||||||
is (interceptOpt) {
|
is (interceptOpt) {
|
||||||
return !!interceptOpt.success
|
return !!interceptOpt.success
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
const cacheReq = require('../req/cacheReq')
|
const cacheReq = require('../req/cacheReq')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'cacheRes',
|
||||||
|
priority: 201,
|
||||||
responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
|
responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
|
||||||
const { rOptions, log } = context
|
const { rOptions, log } = context
|
||||||
|
|
||||||
// 只有GET请求,且响应码为2xx时才进行缓存
|
// 只有GET请求,且响应码为2xx时才进行缓存
|
||||||
if (rOptions.method !== 'GET' || proxyRes.statusCode < 200 || proxyRes.statusCode >= 300) {
|
if (rOptions.method !== 'GET' || proxyRes.statusCode < 200 || proxyRes.statusCode >= 300) {
|
||||||
|
// res.setHeader('DS-Cache-Interceptor', `skip: 'method' or 'status' not match`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +51,9 @@ module.exports = {
|
||||||
if (interceptOpt.cacheImmutable !== false && originalHeaders.cacheControl.value.indexOf('immutable') < 0) {
|
if (interceptOpt.cacheImmutable !== false && originalHeaders.cacheControl.value.indexOf('immutable') < 0) {
|
||||||
maxAge = maxAgeMatch[1]
|
maxAge = maxAgeMatch[1]
|
||||||
} else {
|
} else {
|
||||||
|
const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
|
||||||
|
res.setHeader('DS-Cache-Interceptor', `skip: ${maxAgeMatch[1]} > ${maxAge}`)
|
||||||
|
log.info(`cache response intercept: skip: ${maxAgeMatch[1]} > ${maxAge}, url: ${url}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +86,7 @@ module.exports = {
|
||||||
res.setHeader('Expires', replaceHeaders.expires)
|
res.setHeader('Expires', replaceHeaders.expires)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader('Dev-Sidecar-Cache-Response-Interceptor', 'cacheRes:maxAge=' + maxAge)
|
res.setHeader('DS-Cache-Response-Interceptor', maxAge)
|
||||||
},
|
},
|
||||||
is (interceptOpt) {
|
is (interceptOpt) {
|
||||||
const maxAge = cacheReq.getMaxAge(interceptOpt)
|
const maxAge = cacheReq.getMaxAge(interceptOpt)
|
||||||
|
|
|
@ -13,6 +13,8 @@ function getScript (key, script) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'script',
|
||||||
|
priority: 202,
|
||||||
responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
|
responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
|
||||||
const { rOptions, log, setting } = context
|
const { rOptions, log, setting } = context
|
||||||
let keys = interceptOpt.script
|
let keys = interceptOpt.script
|
||||||
|
@ -20,20 +22,23 @@ module.exports = {
|
||||||
keys = [keys]
|
keys = [keys]
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let tags = getScript('global', monkey.get(setting.script.dirAbsolutePath).global.script)
|
const scripts = monkey.get(setting.script.dirAbsolutePath)
|
||||||
|
let tags = getScript('global', scripts.global.script)
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const script = monkey.get(setting.script.dirAbsolutePath)[key]
|
const script = scripts[key]
|
||||||
if (script == null) {
|
if (script == null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const scriptTag = getScript(key, script.script)
|
const scriptTag = getScript(key, script.script)
|
||||||
tags += '\r\n' + scriptTag
|
tags += '\r\n' + scriptTag
|
||||||
}
|
}
|
||||||
log.info('responseIntercept: insert script', rOptions.hostname, rOptions.path)
|
res.setHeader('DS-Script-Interceptor', 'true')
|
||||||
|
log.info('script response intercept: insert script', rOptions.hostname, rOptions.path, ', head:', tags)
|
||||||
return {
|
return {
|
||||||
head: tags
|
head: tags
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
res.setHeader('DS-Script-Interceptor', 'error')
|
||||||
log.error('load monkey script error', err)
|
log.error('load monkey script error', err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,7 +18,7 @@ function matched (hostname, overWallTargetMap) {
|
||||||
log.info(`matchHostname: matched overwall: '${hostname}' -> '${ret}' in pac.txt`)
|
log.info(`matchHostname: matched overwall: '${hostname}' -> '${ret}' in pac.txt`)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
// log.debug(`matchHostname: matched overwall: Not-Matched '${hostname}' -> '${ret}' in pac.txt`)
|
log.debug(`matchHostname: matched overwall: Not-Matched '${hostname}' -> '${ret}' in pac.txt`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const interceptors = require('./lib/interceptor')
|
const interceptorImpls = require('./lib/interceptor')
|
||||||
const dnsUtil = require('./lib/dns')
|
const dnsUtil = require('./lib/dns')
|
||||||
const log = require('./utils/util.log')
|
const log = require('./utils/util.log')
|
||||||
const matchUtil = require('./utils/util.match')
|
const matchUtil = require('./utils/util.match')
|
||||||
|
@ -49,7 +49,7 @@ module.exports = (config) => {
|
||||||
const hostname = req.url.split(':')[0]
|
const hostname = req.url.split(':')[0]
|
||||||
const inWhiteList = matchUtil.matchHostname(whiteList, hostname, 'in whiteList') != null
|
const inWhiteList = matchUtil.matchHostname(whiteList, hostname, 'in whiteList') != null
|
||||||
if (inWhiteList) {
|
if (inWhiteList) {
|
||||||
log.info('白名单域名,不拦截', hostname)
|
log.info('为白名单域名,不拦截:', hostname)
|
||||||
return false // 所有都不拦截
|
return false // 所有都不拦截
|
||||||
}
|
}
|
||||||
// 配置了拦截的域名,将会被代理
|
// 配置了拦截的域名,将会被代理
|
||||||
|
@ -57,29 +57,42 @@ module.exports = (config) => {
|
||||||
if (matched === true) {
|
if (matched === true) {
|
||||||
return matched // 拦截
|
return matched // 拦截
|
||||||
}
|
}
|
||||||
return null // 由下一个拦截器判断
|
return null // 未匹配到任何拦截配置,由下一个拦截器判断
|
||||||
},
|
},
|
||||||
createIntercepts: (context) => {
|
createIntercepts: (context) => {
|
||||||
const rOptions = context.rOptions
|
const rOptions = context.rOptions
|
||||||
const hostname = rOptions.hostname
|
const interceptOpts = matchUtil.matchHostnameAll(intercepts, rOptions.hostname, 'get interceptOpts')
|
||||||
const interceptOpts = matchUtil.matchHostname(intercepts, hostname, 'get interceptOpts')
|
|
||||||
if (!interceptOpts) { // 该域名没有配置拦截器,直接过
|
if (!interceptOpts) { // 该域名没有配置拦截器,直接过
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchIntercepts = []
|
const matchIntercepts = []
|
||||||
|
const matchInterceptsOpts = {}
|
||||||
for (const regexp in interceptOpts) { // 遍历拦截配置
|
for (const regexp in interceptOpts) { // 遍历拦截配置
|
||||||
const interceptOpt = interceptOpts[regexp]
|
const interceptOpt = interceptOpts[regexp]
|
||||||
interceptOpt.key = regexp
|
// interceptOpt.key = regexp
|
||||||
if (regexp !== true) {
|
if (regexp !== true && regexp !== 'true') {
|
||||||
if (!matchUtil.isMatched(rOptions.path, regexp)) {
|
if (!matchUtil.isMatched(rOptions.path, regexp)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const impl of interceptors) {
|
log.info(`interceptor matched, regexp: '${regexp}' =>`, JSON.stringify(interceptOpt), ', path:', rOptions.path)
|
||||||
|
for (const impl of interceptorImpls) {
|
||||||
// 根据拦截配置挑选合适的拦截器来处理
|
// 根据拦截配置挑选合适的拦截器来处理
|
||||||
if (impl.is && impl.is(interceptOpt)) {
|
if (impl.is && impl.is(interceptOpt)) {
|
||||||
const interceptor = {}
|
let action = 'add'
|
||||||
|
|
||||||
|
// 如果存在同名拦截器,则order值越大,优先级越高
|
||||||
|
const matchedInterceptOpt = matchInterceptsOpts[impl.name]
|
||||||
|
if (matchedInterceptOpt) {
|
||||||
|
if (matchedInterceptOpt.order >= interceptOpt.order) {
|
||||||
|
log.warn(`duplicate interceptor: ${impl.name}, hostname: ${rOptions.hostname}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
action = 'replace'
|
||||||
|
}
|
||||||
|
|
||||||
|
const interceptor = { name: impl.name, priority: impl.priority }
|
||||||
if (impl.requestIntercept) {
|
if (impl.requestIntercept) {
|
||||||
// req拦截器
|
// req拦截器
|
||||||
interceptor.requestIntercept = (context, req, res, ssl, next) => {
|
interceptor.requestIntercept = (context, req, res, ssl, next) => {
|
||||||
|
@ -91,10 +104,26 @@ module.exports = (config) => {
|
||||||
return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next)
|
return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log.info(`${action} interceptor: ${impl.name}, hostname: ${rOptions.hostname}, regexp: ${regexp}`)
|
||||||
|
if (action === 'add') {
|
||||||
matchIntercepts.push(interceptor)
|
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
|
return matchIntercepts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,16 @@ function merge (oldObj, newObj) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
function deleteNullItems (target) {
|
||||||
|
lodash.forEach(target, (item, key) => {
|
||||||
|
if (item == null || item === '[delete]') {
|
||||||
|
delete target[key]
|
||||||
|
}
|
||||||
|
if (lodash.isObject(item)) {
|
||||||
|
deleteNullItems(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function matchHostnameAll (hostMap, hostname, action) {
|
function matchHostnameAll (hostMap, hostname, action) {
|
||||||
// log.debug('matchHostnameAll:', action, hostMap)
|
// log.debug('matchHostnameAll:', action, hostMap)
|
||||||
|
@ -108,29 +118,9 @@ function matchHostnameAll (hostMap, hostname, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let values = {}
|
let values = {}
|
||||||
let hasValue = false
|
let value
|
||||||
|
|
||||||
// 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
|
// 通配符匹配 或 正则表达式匹配(优先级:1,最低)
|
||||||
let value = hostMap.origin[hostname]
|
|
||||||
if (value) {
|
|
||||||
log.info(`matchHostnameAll: ${action}: '${hostname}' -> '${hostname}': ${JSON.stringify(value)}`)
|
|
||||||
values = merge(values, value)
|
|
||||||
hasValue = true
|
|
||||||
}
|
|
||||||
value = hostMap.origin['*' + hostname]
|
|
||||||
if (value) {
|
|
||||||
log.info(`matchHostnameAll: ${action}: '${hostname}' -> '*${hostname}': ${JSON.stringify(value)}`)
|
|
||||||
values = merge(values, value)
|
|
||||||
hasValue = true
|
|
||||||
}
|
|
||||||
value = hostMap.origin['*.' + hostname]
|
|
||||||
if (value) {
|
|
||||||
log.info(`matchHostnameAll: ${action}: '${hostname}' -> '*.${hostname}': ${JSON.stringify(value)}`)
|
|
||||||
values = merge(values, value)
|
|
||||||
hasValue = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通配符匹配 或 正则表达式匹配
|
|
||||||
for (const target in hostMap) {
|
for (const target in hostMap) {
|
||||||
if (target === 'origin') {
|
if (target === 'origin') {
|
||||||
continue
|
continue
|
||||||
|
@ -149,17 +139,37 @@ function matchHostnameAll (hostMap, hostname, action) {
|
||||||
// 正则表达式匹配
|
// 正则表达式匹配
|
||||||
if (hostname.match(regexp)) {
|
if (hostname.match(regexp)) {
|
||||||
value = hostMap[target]
|
value = hostMap[target]
|
||||||
// log.info(`matchHostname: ${action}: '${hostname}' -> '${target}': ${JSON.stringify(value)}`)
|
log.info(`matchHostnameOne: ${action}: '${hostname}' -> '${target}': ${JSON.stringify(value)}`)
|
||||||
values = merge(values, value)
|
values = merge(values, value)
|
||||||
hasValue = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasValue) {
|
// 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
|
||||||
log.info(`*matchHostnameAll*: ${action}: '${hostname}':`, JSON.stringify(values))
|
// 优先级:2
|
||||||
|
value = hostMap.origin['*' + hostname]
|
||||||
|
if (value) {
|
||||||
|
log.info(`matchHostnameOne: ${action}: '${hostname}' -> '*${hostname}': ${JSON.stringify(value)}`)
|
||||||
|
values = merge(values, value)
|
||||||
|
}
|
||||||
|
// 优先级:3
|
||||||
|
value = hostMap.origin['*.' + hostname]
|
||||||
|
if (value) {
|
||||||
|
log.info(`matchHostnameOne: ${action}: '${hostname}' -> '*.${hostname}': ${JSON.stringify(value)}`)
|
||||||
|
values = merge(values, value)
|
||||||
|
}
|
||||||
|
// 优先级:4,最高(注:优先级高的配置,可以覆盖优先级低的配置,甚至有空配置时,可以移除已有配置)
|
||||||
|
value = hostMap.origin[hostname]
|
||||||
|
if (value) {
|
||||||
|
log.info(`matchHostnameOne: ${action}: '${hostname}' -> '${hostname}': ${JSON.stringify(value)}`)
|
||||||
|
values = merge(values, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lodash.isEmpty(values)) {
|
||||||
|
deleteNullItems(values)
|
||||||
|
log.info(`matchHostnameAll: ${action}: '${hostname}':`, JSON.stringify(values))
|
||||||
return values
|
return values
|
||||||
} else {
|
} else {
|
||||||
log.debug(`*matchHostnameAll*: ${action}: '${hostname}' Not-Matched`)
|
log.debug(`matchHostnameAll: ${action}: '${hostname}' Not-Matched`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue