-
Notifications
You must be signed in to change notification settings - Fork 79
Expand file tree
/
Copy pathhost-merge-ruler.ts
More file actions
106 lines (92 loc) · 3.12 KB
/
Copy pathhost-merge-ruler.ts
File metadata and controls
106 lines (92 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* Copyright (c) 2021-present Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
import { getPsl } from '@bg/psl'
import FIFOCache from '@util/fifo-cache'
import { isIpAndPort, judgeVirtualFast } from "@util/pattern"
/**
* @param origin origin host
* @param dotCount the count of dots to remain
*/
const getTheSuffix = (origin: string, dotCount: number) => {
if (isIpAndPort(origin)) return origin
let result: string[] = []
while (true) {
if (dotCount-- < 0) {
break
}
if (!origin.includes('.')) {
result.push(origin)
break
}
const index = origin.lastIndexOf('.')
result.push(origin.substring(index + 1))
origin = origin.substring(0, index)
}
return result.reverse().join('.')
}
type RegRuleItem = {
reg: RegExp
result: string | number
}
const processRegStr = (regStr: string) => regStr
.split('.').join('\\.')
.split('**').join('.+')
.split('*').join('[^\\.]+')
function convert(dbItem: tt4b.site.MergeRule): RegRuleItem | [string, string | number] {
const { origin, merged } = dbItem
if (origin.includes('*')) {
const regStr = processRegStr(origin)
const reg = new RegExp('^' + regStr + '$')
return { reg, result: merged } as RegRuleItem
} else {
return [origin, merged]
}
}
export default class CustomizedHostMergeRuler {
private noRegMergeRules: { [origin: string]: string | number } = {}
private regulars: RegRuleItem[] = []
private cache: FIFOCache<string> = new FIFOCache(500)
constructor(rules: tt4b.site.MergeRule[]) {
rules.map(item => convert(item))
.forEach(rule => Array.isArray(rule)
? (this.noRegMergeRules[rule[0]] = rule[1] || rule[0])
: (this.regulars.push(rule)))
}
merge(origin: string): string {
let result = this.cache.get(origin)
if (result) return result
result = this.mergeInner(origin)
this.cache.set(origin, result)
return result
}
/**
* @param origin origin host
* @returns merged host
*/
private mergeInner(origin: string): string {
let host: string | undefined = origin
if (judgeVirtualFast(origin)) {
host = origin.split('/')?.[0]
if (!host) return origin
}
// First check the static rules
let merged = this.noRegMergeRules[host]
// Then check the regular rules
let matchResult: undefined | RegRuleItem = undefined
merged === undefined && (matchResult = this.regulars.find(item => item.reg.test(host)))
matchResult && (merged = matchResult.result)
if (merged === undefined) {
// No rule matched
return isIpAndPort(host) ? host : (getPsl(host) ?? this.merge0(2, host))
} else {
return this.merge0(merged, host)
}
}
private merge0(merged: string | number, origin: string): string {
return typeof merged === 'string' ? (merged === '' ? origin : merged) : getTheSuffix(origin, merged)
}
}