@applitools/execution-grid-tunnel
Version:
Allows user to run tests with exection-grid and navigate to private hosts and ips
124 lines (106 loc) • 3.64 kB
JavaScript
const tls = require('tls')
const net = require('net')
const {KeepaliveMessageFilter} = require('./keepalive-filter-message')
const ATTACHING_REQUEST = 'attaching-request'
const CLOSING_CONNECTION = 'closing-connection'
class TunnelConnection {
constructor({keepAliveMessage, connectedMessage, logger, connectionId}) {
this._logger = logger
this._filter = new KeepaliveMessageFilter({keepAliveMessage})
this._connectedMessage = connectedMessage
this._filter.on('error', (error) =>
logger.warn({action: 'filter-error', error: error.stack || error.message}),
)
this._connectionId = connectionId
}
connect({tunnelId, host, port, token, protocol = 'tcp', localProxyOptions}) {
return new Promise((resolve, reject) => {
let isResolved = false
this._logger.debug({
action: 'connection-info',
connectionId: this._connectionId,
port,
host,
token,
protocol,
})
const remoteConnection = protocol == 'tcp' ? net.connect(port, host) : tls.connect(port, host)
this._remoteConnection = remoteConnection
remoteConnection.once('connect', () => {
this._localConnection = net.connect(localProxyOptions)
this._localConnection.on('error', (e) => {
this._logger.warn({
action: 'local-connection-error',
connectionId: this._connectionId,
error: e.stack || e.message,
})
})
this._localConnection.once('connect', () => {
this._localConnection.on('end', () => {
this._logger.debug({
action: 'local-connection-end',
connectionId: this._connectionId,
tunnelId,
})
remoteConnection.end()
})
remoteConnection.write(JSON.stringify({tunnelId, token}))
remoteConnection.once('data', (chunk) => {
const result = chunk.toString()
if (result !== this._connectedMessage) {
this._logger.warn({
action: 'connection-refused',
connectionId: this._connectionId,
error: result,
})
const error = new Error(`Server Error: ${result}`)
this._localConnection.end()
return reject(error)
}
remoteConnection.pipe(this._filter).pipe(this._localConnection).pipe(remoteConnection)
isResolved = true
resolve()
})
})
})
remoteConnection.on('error', (e) => {
this._logger.warn({
action: 'remote-connection-error',
connectionId: this._connectionId,
error: e.stack || e.message,
})
if (!isResolved) {
reject(new Error(`Connection Closed: ${e.message || 'Unexpected'}`))
}
remoteConnection.end()
})
})
}
waitForAttachingRequest() {
return this._filter.waitForChunk().then(() => ATTACHING_REQUEST)
}
waitForRemoteConnectionClosing() {
return new Promise((resolve) => {
this._remoteConnection.once('end', () => {
resolve(CLOSING_CONNECTION)
})
})
}
end() {
if (!this._localConnection?.closed) {
this._localConnection.end()
}
if (!this._remoteConnection.closed) {
this._remoteConnection.end()
}
}
destroy() {
if (!this._localConnection?.destroyed) {
this._localConnection.destroy()
}
if (!this._remoteConnection.destroyed) {
this._remoteConnection.destroy()
}
}
}
module.exports = {TunnelConnection, ATTACHING_REQUEST, CLOSING_CONNECTION}