Skip to content

Commit

Permalink
fix: retry socket on all errors
Browse files Browse the repository at this point in the history
  • Loading branch information
b-ma committed Sep 5, 2024
1 parent 0187ca6 commit 207ea30
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 46 deletions.
49 changes: 14 additions & 35 deletions src/client/Socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class Socket {
}

webSocketOptions = {
// handshakeTimeout: 2000,
// do not reject self-signed certificates
rejectUnauthorized: false,
};
}
Expand All @@ -107,30 +109,15 @@ class Socket {
// Init socket
// ----------------------------------------------------------
return new Promise(resolve => {
let connectionRefusedLogged = false;
let hangingTimeoutDuration = 5; // 5, 10, 20, 40

const trySocket = async () => {
const ws = new WebSocket(url, webSocketOptions);

// If after a given delay, we receive neither an 'open' nor an 'error'
// message (e.g. hardware is not ready), let's just drop and recreate a new socket
// cf. https://github.com/collective-soundworks/soundworks/issues/97
const hangingTimeoutId = setTimeout(() => {
ws.terminate ? ws.terminate() : ws.close();
trySocket();
}, hangingTimeoutDuration * 1000);
// exponentialy increase hangingTimeoutDuration on each try and clamp at 40sec
hangingTimeoutDuration = Math.min(hangingTimeoutDuration * 2, 40);

ws.addEventListener('open', openEvent => {
clearTimeout(hangingTimeoutId);
// parse incoming messages for pubsub
this.#socket = ws;

this.#socket.addEventListener('message', e => {
if (e.data === PING_MESSAGE) {
// heartbeat();
this.#socket.send(PONG_MESSAGE);
// do not propagate ping / pong messages
return;
Expand Down Expand Up @@ -173,28 +160,20 @@ class Socket {

// cf. https://github.com/collective-soundworks/soundworks/issues/17
ws.addEventListener('error', e => {
clearTimeout(hangingTimeoutId);

if (e.type === 'error') {
if (ws.terminate) {
ws.terminate();
} else {
ws.close();
}
if (ws.terminate) {
ws.terminate();
} else {
ws.close();
}

// for node clients, retry connection
if (e.error && e.error.code === 'ECONNREFUSED') {
// we want to log the warning just once
if (!connectionRefusedLogged) {
logger.log('[soundworks.Socket] Connection refused, waiting for the server to start');
// console.log(e.error);
connectionRefusedLogged = true;
}

// retry in 1 second
setTimeout(trySocket, 1000);
}
if (e.error) {
const msg = `[Socket Error] code: ${e.error.code}, message: ${e.error.message}`;
logger.log(msg);
}

// Try reconnect in all cases, note that if the socket has been connected the
// close event will be propagated and the launcher will restart the process.
setTimeout(trySocket, 1000);
});
};

Expand Down
45 changes: 34 additions & 11 deletions tests/essentials/Socket.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ import { kSocketTerminate } from '../../src/client/Socket.js';
import config from '../utils/config.js';

describe('# client::Socket', () => {
let server;
describe(`## "close" event`, () => {
let server;

beforeEach(async () => {
server = new Server(config);
await server.start();
});
beforeEach(async () => {
server = new Server(config);
await server.start();
});

afterEach(async () => {
if (server.status !== 'stopped') {
await server.stop();
}
});
afterEach(async () => {
if (server.status !== 'stopped') {
await server.stop();
}
});

describe(`## "close" event`, () => {
it('should be triggered when calling socket[kSocketTerminate]()', async () => {
const client = new Client({ role: 'test', ...config });
await client.start();
Expand Down Expand Up @@ -65,6 +65,29 @@ describe('# client::Socket', () => {
assert.equal(closeCalled, 1);
});
});

describe('connect retry', () => {
it(`should connect when server is started later than client`, async function() {
this.timeout(4 * 1000);

let connected = false;
const client = new Client({ role: 'test', ...config });
client.start();
client.socket.addListener('open', () => {
connected = true;
});

await delay(2 * 1000);

const server = new Server(config);
await server.start();
// socket connection retry timeout is 1 second
await delay(1 * 1000);
await server.stop();

assert.isTrue(connected);
});
});
});


0 comments on commit 207ea30

Please sign in to comment.