playsocketjs 1.1.9 → 1.1.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/dist/playsocket-client.js +2 -1
- package/dist/playsocket-server.js +36 -26
- package/package.json +1 -1
|
@@ -2333,7 +2333,8 @@ class PlaySocket {
|
|
|
2333
2333
|
* @private
|
|
2334
2334
|
*/
|
|
2335
2335
|
async #attemptReconnect() {
|
|
2336
|
-
|
|
2336
|
+
this.#reconnectCount++;
|
|
2337
|
+
if (this.#reconnectCount > 9) {
|
|
2337
2338
|
this.#triggerEvent("error", "Disconnected from server.");
|
|
2338
2339
|
return this.destroy();
|
|
2339
2340
|
}
|
|
@@ -2226,6 +2226,7 @@ function requireServer () {
|
|
|
2226
2226
|
|
|
2227
2227
|
// Set up ws event handlers
|
|
2228
2228
|
this.#wss.on('connection', ws => {
|
|
2229
|
+
ws.connectionId = crypto.randomUUID();
|
|
2229
2230
|
ws.isAlive = true;
|
|
2230
2231
|
ws.on('pong', () => { ws.isAlive = true; });
|
|
2231
2232
|
ws.on('message', msg => this.#handleMessage(ws, msg));
|
|
@@ -2255,12 +2256,19 @@ function requireServer () {
|
|
|
2255
2256
|
* @private
|
|
2256
2257
|
*/
|
|
2257
2258
|
#handleMessage(ws, message) {
|
|
2259
|
+
if (ws.isTerminating) return;
|
|
2258
2260
|
try {
|
|
2259
2261
|
const data = decode(message);
|
|
2260
2262
|
|
|
2261
|
-
// Apply rate limiting
|
|
2262
|
-
if (
|
|
2263
|
-
|
|
2263
|
+
// Apply rate limiting to all connections (including unregistered)
|
|
2264
|
+
if (!this.#checkRateLimit(ws.connectionId, data.type)) {
|
|
2265
|
+
ws.rateLimitViolations = (ws.rateLimitViolations || 0) + 1;
|
|
2266
|
+
if (ws.rateLimitViolations > 5 && !ws.isTerminating) {
|
|
2267
|
+
ws.isTerminating = true; // Prevent multiple terminate calls (it is async)
|
|
2268
|
+
ws.terminate();
|
|
2269
|
+
return console.error(`Connection ${ws.connectionId} terminated due to repeated rate limit violations.`);
|
|
2270
|
+
}
|
|
2271
|
+
return console.error(`Connection ${ws.connectionId} rate limit exceeded.`);
|
|
2264
2272
|
}
|
|
2265
2273
|
|
|
2266
2274
|
switch (data.type) {
|
|
@@ -2465,24 +2473,24 @@ function requireServer () {
|
|
|
2465
2473
|
* Check rate limit using token bucket algorithm
|
|
2466
2474
|
* @private
|
|
2467
2475
|
*/
|
|
2468
|
-
#checkRateLimit(
|
|
2469
|
-
const MAX_POINTS =
|
|
2476
|
+
#checkRateLimit(connectionId, operationType) {
|
|
2477
|
+
const MAX_POINTS = 20;
|
|
2470
2478
|
const now = Date.now();
|
|
2471
2479
|
|
|
2472
|
-
if (!this.#rateLimits.has(
|
|
2473
|
-
this.#rateLimits.set(
|
|
2480
|
+
if (!this.#rateLimits.has(connectionId)) {
|
|
2481
|
+
this.#rateLimits.set(connectionId, { points: MAX_POINTS, lastReset: now });
|
|
2474
2482
|
return true;
|
|
2475
2483
|
}
|
|
2476
2484
|
|
|
2477
|
-
const limit = this.#rateLimits.get(
|
|
2485
|
+
const limit = this.#rateLimits.get(connectionId);
|
|
2478
2486
|
|
|
2479
|
-
// Reset points if interval has passed (
|
|
2480
|
-
if (now - limit.lastReset >
|
|
2487
|
+
// Reset points if interval has passed (1s)
|
|
2488
|
+
if (now - limit.lastReset > 1000) {
|
|
2481
2489
|
limit.points = MAX_POINTS;
|
|
2482
2490
|
limit.lastReset = now;
|
|
2483
2491
|
}
|
|
2484
2492
|
|
|
2485
|
-
const pointCost = ['create_room', 'join_room'].includes(operationType) ?
|
|
2493
|
+
const pointCost = ['create_room', 'join_room'].includes(operationType) ? 5 : 1;
|
|
2486
2494
|
if (limit.points < pointCost) return false;
|
|
2487
2495
|
|
|
2488
2496
|
limit.points -= pointCost;
|
|
@@ -2494,22 +2502,25 @@ function requireServer () {
|
|
|
2494
2502
|
* @private
|
|
2495
2503
|
*/
|
|
2496
2504
|
#handleDisconnection(ws) {
|
|
2497
|
-
|
|
2498
|
-
this.#clients.delete(ws.clientId); // Immediately remove from active clients (otherwise, server would try to message this client)
|
|
2505
|
+
this.#rateLimits.delete(ws.connectionId);
|
|
2499
2506
|
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
if (roomId != null) this.#changeHostIfDisconnected(roomId, ws.clientId);
|
|
2507
|
+
if (ws.clientId) {
|
|
2508
|
+
this.#clients.delete(ws.clientId); // Immediately remove from active clients (otherwise, server would try to message this client)
|
|
2503
2509
|
|
|
2504
|
-
|
|
2505
|
-
this.#
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2510
|
+
// If client was in a room, check if they were the host & migrate
|
|
2511
|
+
const roomId = this.#clientRooms.get(ws.clientId);
|
|
2512
|
+
if (roomId != null) this.#changeHostIfDisconnected(roomId, ws.clientId);
|
|
2513
|
+
|
|
2514
|
+
if (ws.willfulDisconnect) {
|
|
2515
|
+
this.#disconnectClient(ws); // Immediate disconnection
|
|
2516
|
+
} else {
|
|
2517
|
+
// Pending complete disconnection with 5s grace period to allow for reconnections
|
|
2518
|
+
this.#pendingDisconnects.set(ws.clientId, {
|
|
2519
|
+
timeout: setTimeout(() => {
|
|
2520
|
+
this.#disconnectClient(ws);
|
|
2521
|
+
}, 5000)
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2513
2524
|
}
|
|
2514
2525
|
}
|
|
2515
2526
|
|
|
@@ -2520,7 +2531,6 @@ function requireServer () {
|
|
|
2520
2531
|
*/
|
|
2521
2532
|
#disconnectClient(ws) {
|
|
2522
2533
|
this.#pendingDisconnects.delete(ws.clientId);
|
|
2523
|
-
this.#rateLimits.delete(ws.clientId);
|
|
2524
2534
|
this.#clientTokens.delete(ws.clientId);
|
|
2525
2535
|
const roomId = this.#clientRooms.get(ws.clientId);
|
|
2526
2536
|
const room = this.#rooms[roomId];
|