@arcblock/ws 1.30.9 → 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.
@@ -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;
@@ -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,
@@ -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,
@@ -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.9",
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.9",
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",