@arcblock/ws 1.30.10 → 1.30.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/server/index.d.mts +14 -0
- package/esm/server/index.mjs +17 -1
- package/lib/server/index.cjs +17 -1
- package/lib/server/index.d.cts +14 -0
- package/package.json +2 -2
package/esm/server/index.d.mts
CHANGED
|
@@ -40,6 +40,19 @@ interface WsServerOptions {
|
|
|
40
40
|
skipLogOnHookError?: boolean;
|
|
41
41
|
broadcastEventName?: string;
|
|
42
42
|
heartbeatTimeout?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Maximum size of a single inbound WebSocket frame in bytes. The `ws`
|
|
45
|
+
* library rejects oversized frames with close code 1009. Defaults to 64 KiB
|
|
46
|
+
* which is far above any legitimate Phoenix-protocol client message.
|
|
47
|
+
*/
|
|
48
|
+
maxPayload?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Maximum distinct `phx_join` topics a single socket can hold. Each join
|
|
51
|
+
* inserts a Set into `this.topics`, so an unbounded value lets a hostile
|
|
52
|
+
* client force unbounded server-side memory growth via random topic strings.
|
|
53
|
+
* Defaults to 64. Set to 0 to disable the cap.
|
|
54
|
+
*/
|
|
55
|
+
maxTopicsPerSocket?: number;
|
|
43
56
|
}
|
|
44
57
|
interface BroadcastOptions {
|
|
45
58
|
enableLog?: boolean;
|
|
@@ -81,6 +94,7 @@ declare class WsServer extends EventEmitter {
|
|
|
81
94
|
logger: Logger;
|
|
82
95
|
skipLogOnHookError: boolean;
|
|
83
96
|
heartbeatTimeout: number;
|
|
97
|
+
maxTopicsPerSocket: number;
|
|
84
98
|
wss: WebSocketServer;
|
|
85
99
|
topics: Record<string, Set<WsSocket>>;
|
|
86
100
|
broadcastEventName: string;
|
package/esm/server/index.mjs
CHANGED
|
@@ -41,6 +41,8 @@ const refreshHeartbeat = (socket) => {
|
|
|
41
41
|
socket.heartbeatAt = Date.now();
|
|
42
42
|
};
|
|
43
43
|
const HEARTBEAT_TIMEOUT = 300 * 1e3;
|
|
44
|
+
const MAX_PAYLOAD = 64 * 1024;
|
|
45
|
+
const MAX_TOPICS_PER_SOCKET = 64;
|
|
44
46
|
/**
|
|
45
47
|
* Create a websocket server
|
|
46
48
|
*
|
|
@@ -63,9 +65,11 @@ var WsServer = class extends EventEmitter {
|
|
|
63
65
|
this.logger = opts.logger || logger_default("server", opts.silent);
|
|
64
66
|
this.skipLogOnHookError = opts.skipLogOnHookError || false;
|
|
65
67
|
this.heartbeatTimeout = opts.heartbeatTimeout || HEARTBEAT_TIMEOUT;
|
|
68
|
+
this.maxTopicsPerSocket = opts.maxTopicsPerSocket ?? MAX_TOPICS_PER_SOCKET;
|
|
66
69
|
this.wss = new WebSocketServer({
|
|
67
70
|
noServer: true,
|
|
68
|
-
clientTracking: false
|
|
71
|
+
clientTracking: false,
|
|
72
|
+
maxPayload: opts.maxPayload ?? MAX_PAYLOAD
|
|
69
73
|
});
|
|
70
74
|
this.wss.on("connection", this.onWssConnection.bind(this));
|
|
71
75
|
this.wss.on("close", this.onWssClose.bind(this));
|
|
@@ -295,6 +299,18 @@ var WsServer = class extends EventEmitter {
|
|
|
295
299
|
return;
|
|
296
300
|
}
|
|
297
301
|
if (event === "phx_join") {
|
|
302
|
+
if (this.maxTopicsPerSocket > 0 && !(topic in wsSocket.channel) && Object.keys(wsSocket.channel).length >= this.maxTopicsPerSocket) {
|
|
303
|
+
reply({
|
|
304
|
+
socket: wsSocket,
|
|
305
|
+
topic,
|
|
306
|
+
event: `chan_reply_${ref}`,
|
|
307
|
+
data: { message: `topic limit reached (max ${this.maxTopicsPerSocket} per socket)` },
|
|
308
|
+
status: "error",
|
|
309
|
+
ref,
|
|
310
|
+
joinRef
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
298
314
|
try {
|
|
299
315
|
const authInfo = await this.hooks.authenticateJoinChannel({
|
|
300
316
|
socket: wsSocket,
|
package/lib/server/index.cjs
CHANGED
|
@@ -46,6 +46,8 @@ const refreshHeartbeat = (socket) => {
|
|
|
46
46
|
socket.heartbeatAt = Date.now();
|
|
47
47
|
};
|
|
48
48
|
const HEARTBEAT_TIMEOUT = 300 * 1e3;
|
|
49
|
+
const MAX_PAYLOAD = 64 * 1024;
|
|
50
|
+
const MAX_TOPICS_PER_SOCKET = 64;
|
|
49
51
|
/**
|
|
50
52
|
* Create a websocket server
|
|
51
53
|
*
|
|
@@ -68,9 +70,11 @@ var WsServer = class extends node_events.default {
|
|
|
68
70
|
this.logger = opts.logger || require_logger.default("server", opts.silent);
|
|
69
71
|
this.skipLogOnHookError = opts.skipLogOnHookError || false;
|
|
70
72
|
this.heartbeatTimeout = opts.heartbeatTimeout || HEARTBEAT_TIMEOUT;
|
|
73
|
+
this.maxTopicsPerSocket = opts.maxTopicsPerSocket ?? MAX_TOPICS_PER_SOCKET;
|
|
71
74
|
this.wss = new ws.WebSocketServer({
|
|
72
75
|
noServer: true,
|
|
73
|
-
clientTracking: false
|
|
76
|
+
clientTracking: false,
|
|
77
|
+
maxPayload: opts.maxPayload ?? MAX_PAYLOAD
|
|
74
78
|
});
|
|
75
79
|
this.wss.on("connection", this.onWssConnection.bind(this));
|
|
76
80
|
this.wss.on("close", this.onWssClose.bind(this));
|
|
@@ -300,6 +304,18 @@ var WsServer = class extends node_events.default {
|
|
|
300
304
|
return;
|
|
301
305
|
}
|
|
302
306
|
if (event === "phx_join") {
|
|
307
|
+
if (this.maxTopicsPerSocket > 0 && !(topic in wsSocket.channel) && Object.keys(wsSocket.channel).length >= this.maxTopicsPerSocket) {
|
|
308
|
+
reply({
|
|
309
|
+
socket: wsSocket,
|
|
310
|
+
topic,
|
|
311
|
+
event: `chan_reply_${ref}`,
|
|
312
|
+
data: { message: `topic limit reached (max ${this.maxTopicsPerSocket} per socket)` },
|
|
313
|
+
status: "error",
|
|
314
|
+
ref,
|
|
315
|
+
joinRef
|
|
316
|
+
});
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
303
319
|
try {
|
|
304
320
|
const authInfo = await this.hooks.authenticateJoinChannel({
|
|
305
321
|
socket: wsSocket,
|
package/lib/server/index.d.cts
CHANGED
|
@@ -40,6 +40,19 @@ interface WsServerOptions {
|
|
|
40
40
|
skipLogOnHookError?: boolean;
|
|
41
41
|
broadcastEventName?: string;
|
|
42
42
|
heartbeatTimeout?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Maximum size of a single inbound WebSocket frame in bytes. The `ws`
|
|
45
|
+
* library rejects oversized frames with close code 1009. Defaults to 64 KiB
|
|
46
|
+
* which is far above any legitimate Phoenix-protocol client message.
|
|
47
|
+
*/
|
|
48
|
+
maxPayload?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Maximum distinct `phx_join` topics a single socket can hold. Each join
|
|
51
|
+
* inserts a Set into `this.topics`, so an unbounded value lets a hostile
|
|
52
|
+
* client force unbounded server-side memory growth via random topic strings.
|
|
53
|
+
* Defaults to 64. Set to 0 to disable the cap.
|
|
54
|
+
*/
|
|
55
|
+
maxTopicsPerSocket?: number;
|
|
43
56
|
}
|
|
44
57
|
interface BroadcastOptions {
|
|
45
58
|
enableLog?: boolean;
|
|
@@ -81,6 +94,7 @@ declare class WsServer extends EventEmitter {
|
|
|
81
94
|
logger: Logger;
|
|
82
95
|
skipLogOnHookError: boolean;
|
|
83
96
|
heartbeatTimeout: number;
|
|
97
|
+
maxTopicsPerSocket: number;
|
|
84
98
|
wss: WebSocketServer;
|
|
85
99
|
topics: Record<string, Set<WsSocket>>;
|
|
86
100
|
broadcastEventName: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/ws",
|
|
3
|
-
"version": "1.30.
|
|
3
|
+
"version": "1.30.11",
|
|
4
4
|
"description": "OCAP Chain websocket server and client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"websocket"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"url": "https://github.com/ArcBlock/blockchain/issues"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@arcblock/event-hub": "1.30.
|
|
78
|
+
"@arcblock/event-hub": "1.30.11",
|
|
79
79
|
"debug": "^4.4.3",
|
|
80
80
|
"eventemitter3": "^4.0.7",
|
|
81
81
|
"lodash": "^4.17.23",
|