@applitools/execution-grid-tunnel
Version:
Allows user to run tests with exection-grid and navigate to private hosts and ips
97 lines (84 loc) • 2.89 kB
JavaScript
/**
* Parse PAC's `FindProxyForURL` return string into a chain of entries the
* `@applitools/eg-socks5-proxy-server` `setHttpProxyResolver` API consumes.
*
* PAC syntax:
* "DIRECT"
* "PROXY host:port"
* "HTTPS host:port"
* "PROXY a:1; PROXY b:2; DIRECT" (fallback chain, semicolon-separated)
*
* Mapping:
* - "DIRECT" → sentinel string 'DIRECT'
* - "PROXY host:port" → {address: 'host', port: <number>}
* - "HTTPS host:port" → {address: 'host', port: <number>, tls: true}
* - Anything else → logged as warn (action: 'pac-directive-parse-failed') and skipped
*
* Returns:
* - null if the resulting list is empty OR contains only a single
* 'DIRECT' entry (caller lets the SOCKS5 dispatcher fall through
* to its DIRECT path — cleaner than running the orchestrator)
* - ProxyChainEntry[] otherwise
*
* Per-request — never throws. Logging is via the structured logger
* the caller passes in (same instance threaded through startEgTunnelService).
*/
const DIRECTIVE_REGEX = /^(PROXY|HTTPS|DIRECT)(?:\s+([^\s]+))?$/i
function parsePacDirectives(directiveString, {logger, address, port} = {}) {
if (typeof directiveString !== 'string' || directiveString.trim() === '') {
return null
}
const entries = []
for (const raw of directiveString.split(';')) {
const directive = raw.trim()
if (directive === '') continue // tolerate "a; ;b" patterns
const match = DIRECTIVE_REGEX.exec(directive)
if (!match) {
logger?.warn({
action: 'pac-directive-parse-failed',
directive,
address,
port,
})
continue
}
const kind = match[1].toUpperCase()
if (kind === 'DIRECT') {
entries.push('DIRECT')
continue
}
// PROXY / HTTPS — require a host:port operand
const operand = match[2]
if (!operand || !operand.includes(':')) {
logger?.warn({
action: 'pac-directive-parse-failed',
directive,
address,
port,
})
continue
}
const colonIdx = operand.lastIndexOf(':')
const host = operand.slice(0, colonIdx)
const portNum = Number(operand.slice(colonIdx + 1))
if (!host || !Number.isInteger(portNum) || portNum <= 0 || portNum > 65535) {
logger?.warn({
action: 'pac-directive-parse-failed',
directive,
address,
port,
})
continue
}
const entry = {address: host, port: portNum}
if (kind === 'HTTPS') entry.tls = true
entries.push(entry)
}
if (entries.length === 0) return null
// Lone DIRECT → null so the SOCKS5 dispatcher falls through to its
// existing DIRECT path (avoids spinning up the chain orchestrator for nothing).
if (entries.length === 1 && entries[0] === 'DIRECT') return null
return entries
}
module.exports = {parsePacDirectives}