Skip to content

Commit

Permalink
Merge pull request #7 from nwaughachukwuma/refactor-and-optimize
Browse files Browse the repository at this point in the history
  • Loading branch information
nwaughachukwuma authored Jan 21, 2024
2 parents d6931f9 + 31d136c commit a917190
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 68 deletions.
26 changes: 9 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ import WSReqonet from "ws-reqonet";
const url = `ws://localhost:3001/`;
const protocols = [];
const options = {
maxReconnectAttempts: 5,
maxRetryAttempts: 3,
maxRetryAttempts: 10,
queueMessage: true,
};

Expand Down Expand Up @@ -81,41 +80,34 @@ Either a single protocol string or an array of protocol strings. These strings a

Type: `object`

#### maxReconnectAttempts

Type: `number`\
Default: 5

Number of times it attempts to reconnect within a retry

#### maxRetryAttempts

The maximum number of retries - how many attempts at reconnecting

Type: `number`\
Default: 5

The maximum number of retries - how many attempts at reconnecting

#### queueMessage

Whether to store 'send' data when the connection is broken, which is to be relayed when connection is restored.

Type: `boolean`\
Default: true

Whether to store 'send' data when the connection is broken, which is to be relayed when connection is restored.

#### disableReconnect

Whether to disable reconnection

Type: `boolean`\
Default: false

Whether to disable reconnection

#### debug

Whether to run in debug mode which enables logging to dev tools

Type: `boolean`\
Default: false

Whether to run in debug mode which enables logging to dev tools

### events

#### open, close, message, error
Expand Down
16 changes: 10 additions & 6 deletions app/src/routes/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,26 @@
let wsResponse = '';
onMount(() => {
wsClient = webSocket();
wsClient.on('message', (event: any) => {
wsResponse = String(event.data);
wsClient.on('message', (event) => {
wsResponse = event.data;
});
wsClient.on('error', (error: any) => {
wsClient.on('error', (error) => {
console.log('websocket error', error);
});
wsClient.on('open', () => {
console.log('websocket connection established');
});
wsClient.on('close', () => {
console.log('websocket connection closed');
});
});
const sendMessage = (message: string) => {
const payload = { message };
wsClient.send(JSON.stringify(payload));
wsClient.send(JSON.stringify({ message }));
};
</script>

Expand Down Expand Up @@ -56,7 +60,7 @@
<div class="response-area">
<h1>WebSocket response</h1>
<span>
{JSON.stringify(wsResponse, null, 2)}
{wsResponse}
</span>
</div>
</message-container>
Expand Down
68 changes: 34 additions & 34 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { EventEmitter } from "events";

const MAX_RECONNECT_ATTEMPTS = 10;
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
export interface WSReqonetOptions {
/** # of times to reconnect within a retry */
maxReconnectAttempts?: number;
/** # of attempts at reconnecting */
/** # attempts to reconnect */
maxRetryAttempts?: number;
/** Whether to store 'send' data when connection is broken */
queueMessage?: boolean;
Expand All @@ -16,45 +15,47 @@ export interface WSReqonetOptions {
// websocket with reconnection on exponential back-off
export default class WSReqonet extends EventEmitter {
private ws: WebSocket;
private reconnectAttempts = 0;
private maxReconnectAttempts: number;
private retryAttempts = 0;
private maxRetryAttempts: number;
private intervalRef = 0;
private queueMessage: boolean;
private messageQueue: Array<string | Blob | ArrayBuffer | ArrayBufferView> =
[];
private queueMessage: boolean;

private reconnectAttempts = 0;
private intervalRef = 0;
private forcedClose = false;
private disableReconnect: boolean;

constructor(
url: string | URL,
private protocols: string | string[] = [],
options: WSReqonetOptions = {}
) {
super();
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
this.maxRetryAttempts = options.maxRetryAttempts ?? 5;

this.maxRetryAttempts = options.maxRetryAttempts ?? 10;
this.queueMessage = options.queueMessage ?? true;
this.disableReconnect = options.disableReconnect ?? false;

if (!options?.debug) {
console.log = () => {};
}

this.ws = new window.WebSocket(url, protocols);
this.connect();
}
private onOpen = () => {
private onopen = () => {
this.emit("open");
this.forcedClose = false;
};
private onMessage = (event: MessageEvent<any>) => {
private onmessage = (event: MessageEvent<any>) => {
this.emit("message", event);
};
private onError = (error: Event) => {
this.emit("error", error);
this.reconnect();
};
private onClose = () => {
private onclose = () => {
if (!this.forcedClose) {
this.emit("close");
this.reconnect();
Expand All @@ -63,7 +64,7 @@ export default class WSReqonet extends EventEmitter {
public send: WebSocket["send"] = (
data: string | Blob | ArrayBuffer | ArrayBufferView
) => {
if (this.isOpen()) {
if (this.isopen()) {
this.ws.send(data);
} else if (this.queueMessage) {
this.messageQueue.push(data);
Expand All @@ -74,10 +75,10 @@ export default class WSReqonet extends EventEmitter {
this.ws.close(code, reason);
};
private connect = () => {
this.ws.onclose = this.onClose;
this.ws.onclose = this.onclose;
this.ws.onerror = this.onError;
this.ws.onopen = this.onOpen;
this.ws.onmessage = this.onMessage;
this.ws.onopen = this.onopen;
this.ws.onmessage = this.onmessage;

if (this.queueMessage) {
this.relayQueuedMessages();
Expand All @@ -87,7 +88,7 @@ export default class WSReqonet extends EventEmitter {
private relayQueuedMessages = async () => {
const messageQueue = [...this.messageQueue];
for (const msg of messageQueue) {
await wait(100);
await wait(10);
this.ws.send(msg);
}
this.messageQueue.splice(0, messageQueue.length);
Expand All @@ -101,28 +102,28 @@ export default class WSReqonet extends EventEmitter {
}

const TIMEOUT = Math.pow(2, this.retryAttempts + 1) * 1000;
this.intervalRef = window.setInterval(() => {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
const reconnectHandler = () => {
if (this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
this.reconnectAttempts++;
console.log("ws: reconnecting - attempt: ", this.reconnectAttempts);

this.ws = new window.WebSocket(this.ws.url, this.protocols);
this.ws.onopen = this.onRestore;
} else {
if (this.retryAttempts < this.maxRetryAttempts) {
this.retryAttempts++;
console.log("ws: retrying - attempt: ", this.retryAttempts);
this.ws.onopen = this.onrestore;
} else if (this.retryAttempts < this.maxRetryAttempts) {
this.retryAttempts++;
console.log("ws: retrying - attempt: ", this.retryAttempts);

this.reconnectAttempts = 0;
this.reconnect();
} else {
this.emit("reconnection_timeout");
window.clearInterval(this.intervalRef);
}
this.reconnectAttempts = 0;
this.reconnect();
} else {
this.emit("reconnection_timeout");
window.clearInterval(this.intervalRef);
}
}, TIMEOUT);
};

this.intervalRef = window.setInterval(reconnectHandler, TIMEOUT);
};
private onRestore = () => {
private onrestore = () => {
console.log("ws: connection restored!");
this.reconnectAttempts = 0;
window.clearInterval(this.intervalRef);
Expand All @@ -131,6 +132,5 @@ export default class WSReqonet extends EventEmitter {
this.emit("reconnected");
this.connect();
};
/** check if connection is open */
public isOpen = () => this.ws.readyState === this.ws.OPEN;
public isopen = () => this.ws.readyState === this.ws.OPEN;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"prepare": "husky install",
"tsc": "tsc",
"build": "tsup",
"test": "tsd && ava",
"test": "tsc && tsd && ava",
"pretty-quick": "pretty-quick --staged"
},
"sideEffects": false,
Expand Down
10 changes: 6 additions & 4 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import cors from "@fastify/cors";
import fastifyWS from "@fastify/websocket";

const PORT = 8080;
const ADDRESS = "0.0.0.0";
const HOST = "0.0.0.0";
const getEnvDetails = () => ({
VERSION: process.env.VERSION || "local",
PORT,
Expand All @@ -25,16 +25,18 @@ fastify()

app.get("/ws/*", { websocket: true }, ({ socket }, req) => {
socket.on("message", (msg) => {
console.log("from client", msg.toString(), req.url);
const msgStr = String(msg);
console.log(JSON.parse(msgStr), req.url);

const res = JSON.stringify({
pong: `acked: ${Date.now()}`,
msg: `${msg}`,
msg: msgStr,
});
socket.send(res);
});
});
})
.listen({ port: PORT, host: ADDRESS })
.listen({ port: PORT, host: HOST })
.then((v) => {
console.log(`ws server started on ${v}`);
});
12 changes: 6 additions & 6 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ test.serial("WSReqonet can force close client connection", async (t) => {
const app = new TestApp(FAKE_URL);

await sleep(SLEEP_DURATION);
t.is(app.ws.isOpen(), true);
t.is(app.ws.isopen(), true);

app.ws.close();

await sleep(SLEEP_DURATION);
t.not(app.ws.isOpen(), true);
t.not(app.ws.isopen(), true);

mockServer.stop();
});
Expand Down Expand Up @@ -133,7 +133,7 @@ test.serial("WSReqonet can handle reconnect to server", async (t) => {

await sleep(SLEEP_DURATION);
mockServer.close();
t.not(app.ws.isOpen(), true);
t.not(app.ws.isopen(), true);
// --------------------------------------------------
// attempt to reconnect to server
// --------------------------------------------------
Expand All @@ -144,7 +144,7 @@ test.serial("WSReqonet can handle reconnect to server", async (t) => {
});
// wait enough time for reconnection attempts to kick in
await sleep(SLEEP_DURATION * 30);
t.is(app.ws.isOpen(), true);
t.is(app.ws.isopen(), true);
mockServer.stop();
});

Expand All @@ -157,7 +157,7 @@ test.serial("WSReqonet client can relay queued sent messages", async (t) => {

mockServer.close();

t.not(app.ws.isOpen(), true);
t.not(app.ws.isopen(), true);

app.ws.send("test message 1");
app.ws.send("test message 2");
Expand All @@ -176,7 +176,7 @@ test.serial("WSReqonet client can relay queued sent messages", async (t) => {
// wait enough time for reconnection attempts to kick in
await sleep(SLEEP_DURATION * 30);

t.is(app.ws.isOpen(), true);
t.is(app.ws.isopen(), true);
t.is(app.messages.length, 3, "confirm that three message were received");
mockServer.stop();
});
Expand Down

0 comments on commit a917190

Please sign in to comment.