@applitools/execution-grid-tunnel
Version:
Allows user to run tests with exection-grid and navigate to private hosts and ips
145 lines (129 loc) • 4.36 kB
JavaScript
const {createSocks5ProxyServer, EVENTS} = require('@applitools/eg-socks5-proxy-server')
const {parsePacDirectives} = require('./parse-pac-directives')
module.exports = async function ({port, logger, findProxyForURL}) {
const socks5Proxy = createSocks5ProxyServer()
socks5Proxy.on(
EVENTS.REMOTE_CONNECTION_TIMEOUT_ERROR,
({originInfo, destinationInfo: {address, port}}) =>
logger.error({
action: 'remote-connection-timeout-error',
address,
port,
originInfo,
}),
)
socks5Proxy.on(EVENTS.ORIGIN_SOCKET_ERROR, ({err}) =>
logger.warn({
action: 'origin-socket-error',
error: err,
}),
)
socks5Proxy.on(
EVENTS.REMOTE_SOCKET_ERROR,
({err, destination, args, originInfo, destinationInfo: {address, port}}) =>
logger.warn({
action: 'remote-socket-error',
error: err,
address,
port,
}),
)
socks5Proxy.on(EVENTS.ACCEPT_NEW_REQUEST, ({destinationInfo: {address, port}}) =>
logger.info({
action: 'socks5-accept-new-request',
address,
port,
}),
)
socks5Proxy.on(EVENTS.CONNECTED_TO_REMOTE_ADDRESS, ({destinationInfo: {address, port}}) =>
logger.info({
action: 'connected-to-remote-address',
address,
port,
}),
)
// HTTP CONNECT upstream path lifecycle (client-side PAC routing). These
// events come from @applitools/eg-socks5-proxy-server ≥ 1.1.0 when a
// resolver is registered via `setHttpProxyResolver(...)` below. Subscribing
// unconditionally is safe — they never fire when no resolver is set, so
// existing DIRECT-only / SOCKS5-chain consumers see no new log lines.
socks5Proxy.on(EVENTS.CONNECTED_TO_HTTP_PROXY, ({destinationInfo, proxyInfo}) =>
logger.info({
action: 'connected-to-http-proxy',
address: destinationInfo && destinationInfo.address,
port: destinationInfo && destinationInfo.port,
proxyInfo,
}),
)
socks5Proxy.on(
EVENTS.HTTP_PROXY_CONNECT_RESPONDED,
({destinationInfo, proxyInfo, statusCode}) => {
const isOk = statusCode >= 200 && statusCode < 300
logger[isOk ? 'info' : 'warn']({
action: 'http-proxy-connect-responded',
address: destinationInfo && destinationInfo.address,
port: destinationInfo && destinationInfo.port,
proxyInfo,
statusCode,
})
},
)
socks5Proxy.on(
EVENTS.HTTP_PROXY_CONNECT_FAILED,
({destinationInfo, proxyInfo, rfc1928Reply, error}) =>
logger.warn({
action: 'http-proxy-connect-failed',
address: destinationInfo && destinationInfo.address,
port: destinationInfo && destinationInfo.port,
proxyInfo,
rfc1928Reply,
error,
}),
)
await socks5Proxy.listenAsync(port)
// Per-request PAC routing adapter — only wired when a compiled
// `findProxyForURL` was supplied by the caller (i.e. `pacFile` / `pacScript`
// / `pacUrl` was set on `startEgTunnelService`). When unset, the SOCKS5
// server runs in its existing DIRECT-only mode — zero new code path.
//
// Runtime PAC errors (eval throws, directive parse failures) do NOT crash
// the service or the request — they log and fall through to DIRECT.
if (findProxyForURL) {
socks5Proxy.setHttpProxyResolver(async ({destinationInfo}) => {
const address = destinationInfo.address
const port = destinationInfo.port
let directive
try {
directive = await findProxyForURL(
`http://${address}:${port}/`,
address,
)
} catch (err) {
logger.warn({
action: 'pac-eval-failed',
error: err,
address,
port,
})
return null
}
return parsePacDirectives(directive, {logger, address, port})
})
}
const checkConnectionsLeakInterval = setInterval(
() =>
socks5Proxy.getConnections((error, count) => {
if (error) return
if (count < socks5Proxy.destinationSockets.length) {
logger.warn({
action: 'socks5-connections-leak',
expectedSockets: count,
actualSockets: socks5Proxy.destinationSockets.length,
})
}
}),
10000,
)
socks5Proxy.on('close', () => clearInterval(checkConnectionsLeakInterval))
return socks5Proxy
}