refactor(ProxyManager): enhance bypass rule matching and logging (#9546)

* refactor(ProxyManager): enhance bypass rule matching and logging

- Updated the `isByPass` function to improve hostname and port matching against bypass rules.
- Refactored the dispatcher logic to utilize the updated `isByPass` function for better clarity.
- Enhanced logging to include bypass rules in system proxy change notifications.
- Simplified URL handling in the dispatcher to ensure consistent behavior.

* delete file

* refactor(ProxyManager): improve bypass rule handling and error logging

- Enhanced the `isByPass` function to check for bypass rules more efficiently, including improved error handling and logging for rule parsing failures.
- Added error logging for exceptions during URL processing.
- Cleaned up the logic to ensure consistent return values and better readability.
- Removed unnecessary environment variable deletions in the proxy manager cleanup process.

* feat(ProxyManager): add no_proxy environment variable support

- Introduced the `no_proxy` environment variable to allow bypassing specific hosts in proxy settings.
- The `no_proxy` value is constructed from the existing bypass rules, enhancing flexibility in proxy management.
This commit is contained in:
beyondkmp 2025-08-26 20:54:27 +08:00 committed by GitHub
parent eac71f1f43
commit 5bbc35695a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -11,14 +11,42 @@ import { Dispatcher, EnvHttpProxyAgent, getGlobalDispatcher, setGlobalDispatcher
const logger = loggerService.withContext('ProxyManager')
let byPassRules: string[] = []
const isByPass = (hostname: string) => {
const isByPass = (url: string) => {
if (byPassRules.length === 0) {
return false
}
return byPassRules.includes(hostname)
}
try {
const subjectUrlTokens = new URL(url)
for (const rule of byPassRules) {
const ruleMatch = rule.replace(/^(?<leadingDot>\.)/, '*').match(/^(?<hostname>.+?)(?::(?<port>\d+))?$/)
if (!ruleMatch || !ruleMatch.groups) {
logger.warn('Failed to parse bypass rule:', { rule })
continue
}
if (!ruleMatch.groups.hostname) {
continue
}
const hostnameIsMatch = subjectUrlTokens.hostname === ruleMatch.groups.hostname
if (
hostnameIsMatch &&
(!ruleMatch.groups ||
!ruleMatch.groups.port ||
(subjectUrlTokens.port && subjectUrlTokens.port === ruleMatch.groups.port))
) {
return true
}
}
return false
} catch (error) {
logger.error('Failed to check bypass:', error as Error)
return false
}
}
class SelectiveDispatcher extends Dispatcher {
private proxyDispatcher: Dispatcher
private directDispatcher: Dispatcher
@ -31,9 +59,7 @@ class SelectiveDispatcher extends Dispatcher {
dispatch(opts: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers) {
if (opts.origin) {
const url = new URL(opts.origin)
// 检查是否为 localhost 或本地地址
if (isByPass(url.hostname)) {
if (isByPass(opts.origin.toString())) {
return this.directDispatcher.dispatch(opts, handler)
}
}
@ -93,15 +119,20 @@ export class ProxyManager {
// Set new interval
this.systemProxyInterval = setInterval(async () => {
const currentProxy = await getSystemProxy()
if (currentProxy?.proxyUrl.toLowerCase() === this.config?.proxyRules) {
if (
currentProxy?.proxyUrl.toLowerCase() === this.config?.proxyRules &&
currentProxy?.noProxy.join(',').toLowerCase() === this.config?.proxyBypassRules?.toLowerCase()
) {
return
}
logger.info(`system proxy changed: ${currentProxy?.proxyUrl}, this.config.proxyRules: ${this.config.proxyRules}`)
logger.info(
`system proxy changed: ${currentProxy?.proxyUrl}, this.config.proxyRules: ${this.config.proxyRules}, this.config.proxyBypassRules: ${this.config.proxyBypassRules}`
)
await this.configureProxy({
mode: 'system',
proxyRules: currentProxy?.proxyUrl.toLowerCase(),
proxyBypassRules: undefined
proxyBypassRules: currentProxy?.noProxy.join(',')
})
}, 1000 * 60)
}
@ -151,6 +182,7 @@ export class ProxyManager {
delete process.env.grpc_proxy
delete process.env.http_proxy
delete process.env.https_proxy
delete process.env.no_proxy
delete process.env.SOCKS_PROXY
delete process.env.ALL_PROXY
@ -162,6 +194,7 @@ export class ProxyManager {
process.env.HTTPS_PROXY = url
process.env.http_proxy = url
process.env.https_proxy = url
process.env.no_proxy = byPassRules.join(',')
if (url.startsWith('socks')) {
process.env.SOCKS_PROXY = url
@ -229,8 +262,7 @@ export class ProxyManager {
// filter localhost
if (url) {
const hostname = typeof url === 'string' ? new URL(url).hostname : url.hostname
if (isByPass(hostname)) {
if (isByPass(url.toString())) {
return originalMethod(url, options, callback)
}
}