feat: add a wrapper around sockette w/ error retry (#1219)
Some checks are pending
Alpha Build / alpha (macos-latest, aarch64-apple-darwin) (push) Waiting to run
Alpha Build / alpha (macos-latest, x86_64-apple-darwin) (push) Waiting to run
Alpha Build / alpha (windows-latest, aarch64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha (windows-latest, i686-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha (windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, aarch64-unknown-linux-gnu) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, armv7-unknown-linux-gnueabihf) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, i686-unknown-linux-gnu) (push) Waiting to run
Alpha Build / alpha-for-linux (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Waiting to run
Alpha Build / alpha-for-fixed-webview2 (arm64, windows-latest, aarch64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha-for-fixed-webview2 (x64, windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Alpha Build / alpha-for-fixed-webview2 (x86, windows-latest, i686-pc-windows-msvc) (push) Waiting to run
Alpha Build / Update tag (push) Blocked by required conditions

* feat: add a wrapper around sockette w/ error retry

* chore: use import path alias

* perf: reduce retry
This commit is contained in:
Sukka 2024-06-16 18:25:33 +08:00 committed by GitHub
parent a9ef32c32f
commit c8d2410c27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 29 deletions

View File

@ -12,7 +12,7 @@ import { useLogSetup } from "./use-log-setup";
import { useVisibility } from "@/hooks/use-visibility";
import parseTraffic from "@/utils/parse-traffic";
import useSWRSubscription from "swr/subscription";
import Sockette from "sockette";
import { createSockette } from "@/utils/websocket";
interface MemoryUsage {
inuse: number;
@ -42,24 +42,17 @@ export const LayoutTraffic = () => {
(_key, { next }) => {
const { server = "", secret = "" } = clashInfo!;
let errorCount = 10;
const s = new Sockette(
const s = createSockette(
`ws://${server}/traffic?token=${encodeURIComponent(secret)}`,
{
onmessage(event) {
errorCount = 0; // reset counter
const data = JSON.parse(event.data) as ITrafficItem;
trafficRef.current?.appendData(data);
next(null, data);
},
onerror(event) {
errorCount -= 1;
if (errorCount <= 0) {
this.close();
next(event, { up: 0, down: 0 });
}
this.close();
next(event, { up: 0, down: 0 });
},
}
);
@ -86,27 +79,23 @@ export const LayoutTraffic = () => {
clashInfo && pageVisible && displayMemory ? "getRealtimeMemory" : null,
(_key, { next }) => {
const { server = "", secret = "" } = clashInfo!;
const ws = new WebSocket(
`ws://${server}/memory?token=${encodeURIComponent(secret)}`
const s = createSockette(
`ws://${server}/memory?token=${encodeURIComponent(secret)}`,
{
onmessage(event) {
const data = JSON.parse(event.data) as MemoryUsage;
next(null, data);
},
onerror(event) {
this.close();
next(event, { inuse: 0 });
},
}
);
let errorCount = 10;
ws.addEventListener("message", (event) => {
errorCount = 0; // reset counter
next(null, JSON.parse(event.data));
});
ws.addEventListener("error", (event) => {
errorCount -= 1;
if (errorCount <= 0) {
ws.close();
next(event, { inuse: 0 });
}
});
return () => {
ws.close();
s.close();
};
},
{

39
src/utils/websocket.ts Normal file
View File

@ -0,0 +1,39 @@
import Sockette, { type SocketteOptions } from "sockette";
/**
* A wrapper of Sockette that will automatically reconnect up to `maxError` before emitting an error event.
*/
export const createSockette = (
url: string,
opt: SocketteOptions,
maxError = 10
) => {
let remainRetryCount = maxError;
return new Sockette(url, {
...opt,
// Sockette has a built-in reconnect when ECONNREFUSED feature
// Use maxError if opt.maxAttempts is not specified
maxAttempts: opt.maxAttempts ?? maxError,
onmessage(this: Sockette, ev) {
remainRetryCount = maxError; // reset counter
opt.onmessage?.call(this, ev);
},
onerror(this: Sockette, ev) {
remainRetryCount -= 1;
if (remainRetryCount >= 0) {
this.close();
this.reconnect();
} else {
opt.onerror?.call(this, ev);
}
},
onmaximum(this: Sockette, ev) {
opt.onmaximum?.call(this, ev);
// onmaximum will be fired when Sockette reaches built-in reconnect limit,
// We will also set remainRetryCount to 0 to prevent further reconnect.
remainRetryCount = 0;
},
});
};