UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

120 lines (92 loc) 3.34 kB
import Bluebird from 'bluebird' import debugModule from 'debug' import dns from 'dns' import _ from 'lodash' import net from 'net' import tls from 'tls' const debug = debugModule('cypress:network:connect') export function byPortAndAddress (port: number, address: net.Address) { // https://nodejs.org/api/net.html#net_net_connect_port_host_connectlistener return new Bluebird((resolve, reject) => { const onConnect = () => { client.end() resolve(address) } const client = net.connect(port, address.address, onConnect) client.on('error', reject) }) } export function getAddress (port: number, hostname: string) { debug('beginning getAddress %o', { hostname, port }) const fn = byPortAndAddress.bind({}, port) // promisify at the very last second which enables us to // modify dns lookup function (via hosts overrides) const lookupAsync = Bluebird.promisify(dns.lookup, { context: dns }) // this does not go out to the network to figure // out the addresess. in fact it respects the /etc/hosts file // https://github.com/nodejs/node/blob/dbdbdd4998e163deecefbb1d34cda84f749844a4/lib/dns.js#L108 // https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback // @ts-ignore return lookupAsync(hostname, { all: true }) .then((addresses: net.Address[]) => { debug('got addresses %o', { hostname, port, addresses }) // convert to an array if string return Array.prototype.concat.call(addresses).map(fn) }) .tapCatch((err) => { debug('error getting address %o', { hostname, port, err }) }) .any() } export function getDelayForRetry (iteration) { return [0, 100, 200, 200][iteration] } interface RetryingOptions { port: number host: string | undefined useTls: boolean getDelayMsForRetry: (iteration: number, err: Error) => number | undefined } function createSocket (opts: RetryingOptions, onConnect): net.Socket { const netOpts = _.pick(opts, 'host', 'port') if (opts.useTls) { return tls.connect(netOpts, onConnect) } return net.connect(netOpts, onConnect) } export function createRetryingSocket ( opts: RetryingOptions, cb: (err?: Error, sock?: net.Socket, retry?: (err?: Error) => void) => void, ) { if (typeof opts.getDelayMsForRetry === 'undefined') { opts.getDelayMsForRetry = getDelayForRetry } function tryConnect (iteration = 0) { const retry = (err) => { const delay = opts.getDelayMsForRetry(iteration, err) if (typeof delay === 'undefined') { debug('retries exhausted, bubbling up error %o', { iteration, err }) return cb(err) } debug('received error on connect, retrying %o', { iteration, delay, err }) setTimeout(() => { tryConnect(iteration + 1) }, delay) } function onError (err) { sock.on('error', (err) => { debug('second error received on retried socket %o', { opts, iteration, err }) }) retry(err) } function onConnect () { debug('successfully connected %o', { opts, iteration }) // connection successfully established, pass control of errors/retries to consuming function sock.removeListener('error', onError) cb(undefined, sock, retry) } const sock = createSocket(opts, onConnect) sock.once('error', onError) } tryConnect() }