199 lines
5.7 KiB
JavaScript
199 lines
5.7 KiB
JavaScript
const lodash = require('lodash')
|
||
const log = require('./util.log')
|
||
|
||
function isMatched (url, regexp) {
|
||
if (regexp === '.*' || regexp === '*' || regexp === 'true' || regexp === true) {
|
||
return [url]
|
||
}
|
||
|
||
try {
|
||
let urlRegexp = regexp
|
||
if (regexp[0] === '*' || regexp[0] === '?' || regexp[0] === '+') {
|
||
urlRegexp = '.' + regexp
|
||
}
|
||
return url.match(urlRegexp)
|
||
} catch (e) {
|
||
log.error('匹配串有问题:', regexp)
|
||
return null
|
||
}
|
||
}
|
||
|
||
function domainRegexply (target) {
|
||
if (target === '.*' || target === '*' || target === 'true' || target === true) {
|
||
return '^.*$'
|
||
}
|
||
return '^' + target.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$'
|
||
}
|
||
|
||
function domainMapRegexply (hostMap) {
|
||
const regexpMap = {}
|
||
const origin = {} // 用于快速匹配,见matchHostname、matchHostnameAll方法
|
||
if (hostMap == null) {
|
||
return regexpMap
|
||
}
|
||
lodash.each(hostMap, (value, domain) => {
|
||
if (domain.indexOf('*') >= 0 || domain[0] === '^') {
|
||
const regDomain = domain[0] !== '^' ? domainRegexply(domain) : domain
|
||
regexpMap[regDomain] = value
|
||
|
||
if (domain.indexOf('*') === 0 && domain.lastIndexOf('*') === 0) {
|
||
origin[domain] = value
|
||
}
|
||
} else {
|
||
origin[domain] = value
|
||
}
|
||
})
|
||
regexpMap.origin = origin
|
||
return regexpMap
|
||
}
|
||
|
||
function matchHostname (hostMap, hostname, action) {
|
||
// log.error('matchHostname:', action, hostMap)
|
||
|
||
if (hostMap == null) {
|
||
log.warn(`matchHostname: ${action}: '${hostname}' Not-Matched, hostMap is null`)
|
||
return null
|
||
}
|
||
if (hostMap.origin == null) {
|
||
log.warn(`matchHostname: ${action}: '${hostname}' Not-Matched, hostMap.origin is null`)
|
||
return null
|
||
}
|
||
|
||
// 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
|
||
let value = hostMap.origin[hostname]
|
||
if (value) {
|
||
log.info(`matchHostname: ${action}: '${hostname}' -> '${hostname}': ${JSON.stringify(value)}`)
|
||
return value // 快速匹配成功
|
||
}
|
||
value = hostMap.origin['*' + hostname]
|
||
if (value) {
|
||
log.info(`matchHostname: ${action}: '${hostname}' -> '*${hostname}': ${JSON.stringify(value)}`)
|
||
return value // 快速匹配成功
|
||
}
|
||
value = hostMap.origin['*.' + hostname]
|
||
if (value) {
|
||
log.info(`matchHostname: ${action}: '${hostname}' -> '*.${hostname}': ${JSON.stringify(value)}`)
|
||
return value // 快速匹配成功
|
||
}
|
||
|
||
// 通配符匹配 或 正则表达式匹配
|
||
for (const target in hostMap) {
|
||
if (target === 'origin') {
|
||
continue
|
||
}
|
||
|
||
// if (target.indexOf('*') < 0 && target[0] !== '^') {
|
||
// continue // 不是通配符匹配串,也不是正则表达式,跳过
|
||
// }
|
||
|
||
// 如果是通配符匹配串,转换为正则表达式
|
||
let regexp = target
|
||
// if (target[0] !== '^') {
|
||
// regexp = domainRegexply(regexp)
|
||
// }
|
||
|
||
// 正则表达式匹配
|
||
if (hostname.match(regexp)) {
|
||
value = hostMap[target]
|
||
log.info(`matchHostname: ${action}: '${hostname}' -> '${target}': ${JSON.stringify(value)}`)
|
||
return value
|
||
}
|
||
}
|
||
|
||
log.debug(`matchHostname: ${action}: '${hostname}' Not-Matched`)
|
||
}
|
||
|
||
function merge (oldObj, newObj) {
|
||
return lodash.mergeWith(oldObj, newObj, function (objValue, srcValue) {
|
||
if (lodash.isArray(objValue)) {
|
||
return srcValue
|
||
}
|
||
})
|
||
}
|
||
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) {
|
||
// log.debug('matchHostname-all:', action, hostMap)
|
||
|
||
if (hostMap == null) {
|
||
log.warn(`matchHostname-all: ${action}: '${hostname}', hostMap is null`)
|
||
return null
|
||
}
|
||
if (hostMap.origin == null) {
|
||
log.warn(`matchHostname-all: ${action}: '${hostname}', hostMap.origin is null`)
|
||
return null
|
||
}
|
||
|
||
let values = {}
|
||
let value
|
||
|
||
// 通配符匹配 或 正则表达式匹配(优先级:1,最低)
|
||
for (const target in hostMap) {
|
||
if (target === 'origin') {
|
||
continue
|
||
}
|
||
|
||
// if (target.indexOf('*') < 0 && target[0] !== '^') {
|
||
// continue // 不是通配符匹配串,也不是正则表达式,跳过
|
||
// }
|
||
|
||
// 如果是通配符匹配串,转换为正则表达式
|
||
let regexp = target
|
||
// if (target[0] !== '^') {
|
||
// regexp = domainRegexply(regexp)
|
||
// }
|
||
|
||
// 正则表达式匹配
|
||
if (hostname.match(regexp)) {
|
||
value = hostMap[target]
|
||
log.debug(`matchHostname-one: ${action}: '${hostname}' -> '${target}': ${JSON.stringify(value)}`)
|
||
values = merge(values, value)
|
||
}
|
||
}
|
||
|
||
// 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
|
||
// 优先级:2
|
||
value = hostMap.origin['*' + hostname]
|
||
if (value) {
|
||
log.debug(`matchHostname-one: ${action}: '${hostname}' -> '*${hostname}': ${JSON.stringify(value)}`)
|
||
values = merge(values, value)
|
||
}
|
||
// 优先级:3
|
||
value = hostMap.origin['*.' + hostname]
|
||
if (value) {
|
||
log.debug(`matchHostname-one: ${action}: '${hostname}' -> '*.${hostname}': ${JSON.stringify(value)}`)
|
||
values = merge(values, value)
|
||
}
|
||
// 优先级:4,最高(注:优先级高的配置,可以覆盖优先级低的配置,甚至有空配置时,可以移除已有配置)
|
||
value = hostMap.origin[hostname]
|
||
if (value) {
|
||
log.debug(`matchHostname-one: ${action}: '${hostname}' -> '${hostname}': ${JSON.stringify(value)}`)
|
||
values = merge(values, value)
|
||
}
|
||
|
||
if (!lodash.isEmpty(values)) {
|
||
deleteNullItems(values)
|
||
log.info(`matchHostname-all: ${action}: '${hostname}':`, JSON.stringify(values))
|
||
return values
|
||
} else {
|
||
log.debug(`matchHostname-all: ${action}: '${hostname}' Not-Matched`)
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
isMatched,
|
||
domainRegexply,
|
||
domainMapRegexply,
|
||
matchHostname,
|
||
matchHostnameAll
|
||
}
|