|
1 | 1 | const fs = require('node:fs') |
2 | 2 | const path = require('node:path') |
3 | 3 | const lodash = require('lodash') |
| 4 | +const { LRUCache } = require('lru-cache') |
4 | 5 | const dnsUtil = require('./lib/dns') |
5 | 6 | const interceptorImpls = require('./lib/interceptor') |
6 | 7 | const scriptInterceptor = require('./lib/interceptor/impl/res/script') |
7 | 8 | const { getTmpPacFilePath, downloadPacAsync, createOverwallMiddleware } = require('./lib/proxy/middleware/overwall') |
8 | 9 | const log = require('./utils/util.log.server') |
9 | 10 | const matchUtil = require('./utils/util.match') |
10 | 11 |
|
| 12 | +// 每个域名的路径级拦截器缓存的最大条数。 |
| 13 | +// 对于使用 .* 路径模式的域名(如 api.github.com),每个唯一 URL(含不同 query string)都会生成独立的缓存条目。 |
| 14 | +// 设置上限,超出后清空最久未使用的缓存,防止长期运行时因 API 分页/唯一 token 等导致内存无界增长。 |
| 15 | +const PATH_CACHE_MAX_SIZE = 512 |
| 16 | + |
11 | 17 | // 处理拦截配置 |
12 | 18 | function buildIntercepts (intercepts) { |
13 | 19 | // 自动生成script拦截器所需的辅助配置,降低使用`script拦截器`配置绝对地址和相对地址时的门槛 |
@@ -153,6 +159,24 @@ module.exports = (serverConfig) => { |
153 | 159 | return |
154 | 160 | } |
155 | 161 |
|
| 162 | + // 获取缓存:同一 hostname+path 的拦截器列表是固定的,不必每次重新构建 |
| 163 | + // 注:缓存 key 使用完整路径(含 query string),以保证正则捕获组(matched)的正确性 |
| 164 | + // 注:采用 LRU 淘汰策略,上限为 PATH_CACHE_MAX_SIZE 条;利用 Map 按插入顺序迭代的特性,命中时删除后重新插入以更新位置 |
| 165 | + if (!interceptOpts._pathCache) { |
| 166 | + const cache = new LRUCache({ |
| 167 | + maxSize: PATH_CACHE_MAX_SIZE, |
| 168 | + sizeCalculation: () => { |
| 169 | + return 1 |
| 170 | + }, |
| 171 | + }) |
| 172 | + Object.defineProperty(interceptOpts, '_pathCache', { value: cache, enumerable: false, configurable: true }) |
| 173 | + } else { |
| 174 | + const cached = interceptOpts._pathCache.get(rOptions.path) |
| 175 | + if (cached) { |
| 176 | + return cached |
| 177 | + } |
| 178 | + } |
| 179 | + |
156 | 180 | const matchIntercepts = [] |
157 | 181 | const matchInterceptsOpts = {} |
158 | 182 | for (const regexp in interceptOpts) { // 遍历拦截配置 |
@@ -244,6 +268,9 @@ module.exports = (serverConfig) => { |
244 | 268 | // log.info('interceptor:', interceptor.name, 'priority:', interceptor.priority) |
245 | 269 | // } |
246 | 270 |
|
| 271 | + // 设置缓存 |
| 272 | + interceptOpts._pathCache.set(rOptions.path, matchIntercepts) |
| 273 | + |
247 | 274 | return matchIntercepts |
248 | 275 | }, |
249 | 276 | } |
|
0 commit comments