feature: proxy、redirect拦截器配置,支持域名正则匹配占位符替换。(之前仅支持path匹配替换)

develop
王良 2025-05-20 10:19:44 +08:00
parent 217ce9d972
commit e47cf82d34
5 changed files with 49 additions and 22 deletions

View File

@ -111,7 +111,7 @@ function checkIsLimitConfig (id, api) {
module.exports = {
name: 'baiduOcr',
priority: 131,
requestIntercept (context, interceptOpt, req, res, ssl, next, matched) {
requestIntercept (context, interceptOpt, req, res, ssl, next) {
const { rOptions, log } = context
const headers = {

View File

@ -1,21 +1,29 @@
const url = require('node:url')
const lodash = require('lodash')
function replacePlaceholder0 (url, matched, pre) {
if (matched) {
for (let i = 0; i < matched.length; i++) {
url = url.replace(`\${${pre}[${i}]}`, matched[i] || '')
}
if (matched.groups) {
for (const key in matched.groups) {
url = url.replace(`\${${key}}`, matched.groups[key] || '')
}
}
}
return url
}
// 替换占位符
function replacePlaceholder (url, rOptions, matched) {
function replacePlaceholder (url, rOptions, pathMatched, hostnameMatched) {
if (url.includes('${')) {
// eslint-disable-next-line no-template-curly-in-string
url = url.replace('${host}', rOptions.hostname)
if (matched && url.includes('${')) {
for (let i = 0; i < matched.length; i++) {
url = url.replace(`\${m[${i}]}`, matched[i] == null ? '' : matched[i])
}
if (matched.groups) {
for (const key in matched.groups) {
url = url.replace(`\${${key}}`, matched.groups[key] == null ? '' : matched.groups[key])
}
}
if (url.includes('${')) {
url = replacePlaceholder0(url, pathMatched, 'p')
url = replacePlaceholder0(url, hostnameMatched, 'h')
}
// 移除多余的占位符
@ -27,7 +35,7 @@ function replacePlaceholder (url, rOptions, matched) {
return url
}
function buildTargetUrl (rOptions, urlConf, interceptOpt, matched) {
function buildTargetUrl (rOptions, urlConf, interceptOpt, matched, hostnameMatched) {
let targetUrl
if (interceptOpt && interceptOpt.replace) {
const regexp = new RegExp(interceptOpt.replace)
@ -45,7 +53,7 @@ function buildTargetUrl (rOptions, urlConf, interceptOpt, matched) {
}
// 替换占位符
targetUrl = replacePlaceholder(targetUrl, rOptions, matched)
targetUrl = replacePlaceholder(targetUrl, rOptions, matched, hostnameMatched)
// 拼接协议
targetUrl = targetUrl.indexOf('http:') === 0 || targetUrl.indexOf('https:') === 0 ? targetUrl : `${rOptions.protocol}//${targetUrl}`
@ -53,9 +61,9 @@ function buildTargetUrl (rOptions, urlConf, interceptOpt, matched) {
return targetUrl
}
function doProxy (proxyConf, rOptions, req, interceptOpt, matched) {
function doProxy (proxyConf, rOptions, req, interceptOpt, matched, hostnameMatched) {
// 获取代理目标地址
const proxyTarget = buildTargetUrl(rOptions, proxyConf, interceptOpt, matched)
const proxyTarget = buildTargetUrl(rOptions, proxyConf, interceptOpt, matched, hostnameMatched)
// 替换rOptions的属性
// eslint-disable-next-line node/no-deprecated-api
@ -81,7 +89,7 @@ module.exports = {
replacePlaceholder,
buildTargetUrl,
doProxy,
requestIntercept (context, interceptOpt, req, res, ssl, next, matched) {
requestIntercept (context, interceptOpt, req, res, ssl, next, matched, hostnameMatched) {
const { rOptions, log, RequestCounter } = context
const originHostname = rOptions.hostname
@ -112,7 +120,7 @@ module.exports = {
}
// 替换 rOptions 中的地址,并返回代理目标地址
const proxyTarget = doProxy(proxyConf, rOptions, req, interceptOpt, matched)
const proxyTarget = doProxy(proxyConf, rOptions, req, interceptOpt, matched, hostnameMatched)
if (context.requestCount) {
log.info('proxy choice:', JSON.stringify(context.requestCount))

View File

@ -3,11 +3,11 @@ const proxyApi = require('./proxy')
module.exports = {
name: 'redirect',
priority: 105,
requestIntercept (context, interceptOpt, req, res, ssl, next, matched) {
requestIntercept (context, interceptOpt, req, res, ssl, next, matched, hostnameMatched) {
const { rOptions, log } = context
// 获取重定向目标地址
const redirect = proxyApi.buildTargetUrl(rOptions, interceptOpt.redirect, interceptOpt, matched)
const redirect = proxyApi.buildTargetUrl(rOptions, interceptOpt.redirect, interceptOpt, matched, hostnameMatched)
const headers = {
'Location': redirect,

View File

@ -186,12 +186,12 @@ module.exports = (serverConfig) => {
if (impl.requestIntercept) {
// req拦截器
interceptor.requestIntercept = (context, req, res, ssl, next) => {
return impl.requestIntercept(context, interceptOpt, req, res, ssl, next, matched)
return impl.requestIntercept(context, interceptOpt, req, res, ssl, next, matched, interceptOpts.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)
return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next, matched, interceptOpts.matched)
}
}

View File

@ -141,10 +141,29 @@ function matchHostnameAll (hostMap, hostname, action) {
// }
// 正则表达式匹配
if (hostname.match(regexp)) {
const matched = hostname.match(regexp)
if (matched) {
value = hostMap[regexp]
log.debug(`matchHostname-one: ${action}: '${hostname}' -> { "${regexp}": ${JSON.stringify(value)} }`)
values = merge(values, value)
// 设置matched
if (matched.length > 1) {
if (values.matched) {
// 合并array
matched.shift()
values.matched = [...values.matched, ...matched] // 拼接上多个matched
// 合并groups
if (matched.groups) {
values.matched.groups = merge(values.matched.groups, matched.groups)
} else {
values.matched.groups = matched.groups
}
} else {
values.matched = matched
}
}
}
}