Skip to content

Commit

Permalink
refactor(@embark/utils): use a 'ws' websocket client in pingEndpoint
Browse files Browse the repository at this point in the history
This PR replaces #1166. The "stuck sockets" bug is addressed in #1195 so there
is no longer a need to use timeouts. However a few aspects of the original PR
are still useful, and lessons learned from #1166, #1181, and #1195 can be put
to good use.

Use a websocket client from the `ws` package when pinging websocket endpoints
instead of manually building a request header. The `'upgrade'` event being
listened for was never actually firing; and though a response was received for
those pings, the response messages indicated problems with those requests. It
seems cleaner to use a proper websocket client and callback success upon the
`ws` client's `'open'` event.

Abstract error and success handling across websocket and http pings.

Report network errors other than `ECONNREFUSED`. Only `ECONNREFUSED` is
expected, as that genuinely indicates an endpoint isn't accepting connections
at the specified host and port. If other kinds of network errors are occurring,
it will be helpful to have a visual indicator to prompt investigation.

After success or the first error, cleanup the ping's request/connection
immediately since we're not awaiting `'data'` events on an http request and we
don't want to leave a websocket connection open. Don't callback any additional
`'error'` events that might fire after the first `'error'` event, but do report
them.
  • Loading branch information
michaelsbradleyjr committed Dec 14, 2018
1 parent 6a03a8f commit 7b01a03
Showing 1 changed file with 48 additions and 35 deletions.
83 changes: 48 additions & 35 deletions src/lib/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,44 +90,57 @@ function getJson(url, cb) {
}

function pingEndpoint(host, port, type, protocol, origin, callback) {
const options = {
protocolVersion: 13,
perMessageDeflate: true,
origin: origin,
host: host,
port: port
// remove trailing api key from infura, e.g. rinkeby.infura.io/nmY8WtT4QfEwz2S7wTbl
const _host = host.indexOf('/') > -1 ? host.split('/')[0] : host;

let closed = false;
const close = (req, closeMethod) => {
if (!closed) {
closed = true;
req[closeMethod]();
}
};
if (type === 'ws') {
options.headers = {
'Sec-WebSocket-Version': 13,
Connection: 'Upgrade',
Upgrade: 'websocket',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
Origin: origin
};
}
let req;
// remove trailing api key from infura, ie rinkeby.infura.io/nmY8WtT4QfEwz2S7wTbl
if (options.host.indexOf('/') > -1) {
options.host = options.host.split('/')[0];
}
if (protocol === 'https') {
req = require('https').get(options);
} else {
req = require('http').get(options);
}

req.on('error', (err) => {
callback(err);
});
const handleEvent = (req, closeMethod, ...args) => {
close(req, closeMethod);
setImmediate(() => { callback(...args); });
};

req.on('response', (_response) => {
callback();
});
const handleError = (req, closeMethod) => {
req.on('error', (err) => {
if (err.code !== 'ECONNREFUSED') {
console.error(
`Ping: Network error` +
(err.message ? ` '${err.message}'` : '') +
(err.code ? ` (code: ${err.code})` : '')
);
}
// when closed additional error events will not callback
if (!closed) { handleEvent(req, closeMethod, err); }
});
};

req.on('upgrade', (_res, _socket, _head) => {
callback();
});
const handleSuccess = (req, closeMethod, event) => {
req.once(event, () => { handleEvent(req, closeMethod); });
};

const handleRequest = (req, closeMethod, event) => {
handleError(req, closeMethod);
handleSuccess(req, closeMethod, event);
};

if (type === 'ws') {
const req = new (require('ws'))(
`${protocol === 'https' ? 'wss' : 'ws'}://${_host}:${port}/`,
{origin}
);
handleRequest(req, 'close', 'open');
} else {
const req = (protocol === 'https' ? require('https') : require('http')).get(
{host: _host, origin, port}
);
handleRequest(req, 'abort', 'response');
}
}

function runCmd(cmd, options, callback) {
Expand Down Expand Up @@ -554,7 +567,7 @@ function isNotFolder(node){
}

function byName(a, b) {
return a.name.localeCompare(b.name);
return a.name.localeCompare(b.name);
}

function fileTreeSort(nodes){
Expand Down

0 comments on commit 7b01a03

Please sign in to comment.