Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions src/utils/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function _checkActivityForHibernation() {
return;
}
if(!client || client.hibernating
|| client.userClosedConnection
|| !client.connectionEstablished // cant hibernate if connection isnt already established/ is being establised
|| ID_TO_RESOLVE_REJECT_MAP.size > 0){ // if there are any pending responses, we cant hibernate
return;
Expand Down Expand Up @@ -91,20 +92,39 @@ function _setupClientAndWaitForClose(connectedCb) {
__receiveMessage(data);
});

let terminated = false;
function _connectionTerminated(reason) {
if (terminated) {
return;
}
terminated = true;

console.log(reason);
// not set bufferRequests = true for unexpected failures.
// Real connection failures → reject immediately, don't buffer
// Hibernation → buffer and reconnect transparently

client.connectionEstablished = false;
for (let [sequenceNumber, handler] of ID_TO_RESOLVE_REJECT_MAP) {
handler.reject(reason);
ID_TO_RESOLVE_REJECT_MAP.delete(sequenceNumber);
}
resolve();
}
client.on('close', function () {
// https://websockets.spec.whatwg.org/#eventdef-websocket-error
// https://stackoverflow.com/questions/40084398/is-onclose-always-called-after-onerror-for-websocket
// we do not need to listen for error event as an error event is immediately followed by a close event.
_connectionTerminated('connection closed');

client.on('error', function (err) {
// ECONNREFUSED, ENOTFOUND, TLS errors, etc. can land here
_connectionTerminated(err);
});

client.on('close', function (code, reasonBuf) {
// reasonBuf may be a Buffer in ws; keep it simple
_connectionTerminated(`connection closed (${code})`);
});

// Optional but useful: HTTP-level failures (proxies, 401, 403, 502...)
client.on('unexpected-response', function (req, res) {
_connectionTerminated(`unexpected-response: ${res.statusCode}`);
});
});
}
Expand All @@ -131,13 +151,14 @@ function _cancelBackoffTimer() {
}
}

async function _setupAndMaintainConnection(firstConnectionCb, neverConnectedCB) {
async function _setupAndMaintainConnection(resolveOnFirstConnect, reject) {
backoffTimer = null;
function connected() {
_resetBackoffTime();
if(firstConnectionCb){
firstConnectionCb("connected");
firstConnectionCb = null;
if(resolveOnFirstConnect){
resolveOnFirstConnect("connected");
resolveOnFirstConnect = null;
reject = null;
// setup hibernate timer on first connection
activityInHibernateInterval = 1;
hibernateTimer = setInterval(_checkActivityForHibernation, INACTIVITY_TIME_FOR_HIBERNATE);
Expand All @@ -160,8 +181,8 @@ async function _setupAndMaintainConnection(firstConnectionCb, neverConnectedCB)
client && client.userClosedConnectionCB && client.userClosedConnectionCB();
client = cocoDBEndPointURL = cocoAuthKey = null;
id = 0;
if(neverConnectedCB){
neverConnectedCB(new Error("user Cancelled"));
if(reject){
reject(new Error("user Cancelled"));
}
}

Expand Down Expand Up @@ -219,7 +240,7 @@ export function close() {
currentClient.userClosedConnection = true;
currentClient.userClosedConnectionCB = function () {
for(let entry of pendingSendMessages){
entry.reject();
entry.reject(new Error('Connection closed'));
}
pendingSendMessages = [];
resolve();
Expand All @@ -239,7 +260,7 @@ export function close() {
* @returns {string} A function that increments the id variable and returns the new value as a hexadecimal string.
*/
function getId() {
id++;
id++; // Will take about 300 years to run out at 1 million sustained tps to db with Number.MAX_SAFE_INTEGER
return id.toString(16);
}

Expand Down Expand Up @@ -284,7 +305,7 @@ function _sendPendingMessages() {
*/
export function sendMessage(message) {
// make a copy as the user may start modifying the object while we are sending it.
message = structuredClone(message);
message = JSON.parse(JSON.stringify(message)); // faster than structured clone for most cases
return new Promise(function (resolve, reject) {
if(bufferRequests){
if(pendingSendMessages.length > MAX_PENDING_SEND_BUFFER_SIZE){
Expand Down
Loading