git-watchtower 2.1.12 → 2.1.14
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/package.json +1 -1
- package/src/server/coordinator.js +31 -1
- package/src/server/web.js +8 -1
package/package.json
CHANGED
|
@@ -34,6 +34,18 @@ const WATCHTOWER_DIR = path.join(os.homedir(), '.watchtower');
|
|
|
34
34
|
*/
|
|
35
35
|
const MAX_IPC_BUFFER = 1024 * 1024;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* How long the coordinator gives an accepted connection to send a
|
|
39
|
+
* `register` frame before destroying the socket. Legitimate workers
|
|
40
|
+
* send register immediately on connect (sub-100 ms in practice), so
|
|
41
|
+
* 5 s is generous. Without this, a peer that opens a connection and
|
|
42
|
+
* sits idle without registering consumes one of the
|
|
43
|
+
* MAX_WORKER_CONNECTIONS slots indefinitely — so a runaway peer that
|
|
44
|
+
* never sends data could lock out legitimate workers even though it
|
|
45
|
+
* is below the connection cap.
|
|
46
|
+
*/
|
|
47
|
+
const WORKER_REGISTER_TIMEOUT_MS = 5000;
|
|
48
|
+
|
|
37
49
|
/**
|
|
38
50
|
* Maximum number of concurrent worker connections the coordinator will
|
|
39
51
|
* accept. The legitimate ceiling is "one git-watchtower instance per
|
|
@@ -379,6 +391,21 @@ class Coordinator {
|
|
|
379
391
|
let workerId = null;
|
|
380
392
|
let buffer = '';
|
|
381
393
|
|
|
394
|
+
// Drop the socket if the peer doesn't complete the register handshake
|
|
395
|
+
// in time. Cleared by setWorkerId on a successful 'register' frame
|
|
396
|
+
// and on close/error to avoid acting on a destroyed socket.
|
|
397
|
+
const registerTimer = setTimeout(() => {
|
|
398
|
+
if (!workerId) {
|
|
399
|
+
socket.destroy();
|
|
400
|
+
}
|
|
401
|
+
}, WORKER_REGISTER_TIMEOUT_MS);
|
|
402
|
+
if (registerTimer.unref) registerTimer.unref();
|
|
403
|
+
|
|
404
|
+
const setWorkerId = (id) => {
|
|
405
|
+
workerId = id;
|
|
406
|
+
clearTimeout(registerTimer);
|
|
407
|
+
};
|
|
408
|
+
|
|
382
409
|
socket.on('data', (data) => {
|
|
383
410
|
buffer += data.toString();
|
|
384
411
|
if (buffer.length > MAX_IPC_BUFFER) {
|
|
@@ -392,7 +419,7 @@ class Coordinator {
|
|
|
392
419
|
if (line.trim()) {
|
|
393
420
|
try {
|
|
394
421
|
const msg = JSON.parse(line);
|
|
395
|
-
this._handleWorkerMessage(socket, msg,
|
|
422
|
+
this._handleWorkerMessage(socket, msg, setWorkerId, () => workerId);
|
|
396
423
|
} catch (e) {
|
|
397
424
|
// Both sides of this socket are our own code, so a JSON-parse
|
|
398
425
|
// failure indicates a protocol/version bug worth diagnosing.
|
|
@@ -404,6 +431,7 @@ class Coordinator {
|
|
|
404
431
|
});
|
|
405
432
|
|
|
406
433
|
socket.on('close', () => {
|
|
434
|
+
clearTimeout(registerTimer);
|
|
407
435
|
if (workerId) {
|
|
408
436
|
this.projects.delete(workerId);
|
|
409
437
|
this.workerSockets.delete(workerId);
|
|
@@ -412,6 +440,7 @@ class Coordinator {
|
|
|
412
440
|
});
|
|
413
441
|
|
|
414
442
|
socket.on('error', () => {
|
|
443
|
+
clearTimeout(registerTimer);
|
|
415
444
|
if (workerId) {
|
|
416
445
|
this.projects.delete(workerId);
|
|
417
446
|
this.workerSockets.delete(workerId);
|
|
@@ -741,4 +770,5 @@ module.exports = {
|
|
|
741
770
|
SOCKET_PATH,
|
|
742
771
|
MAX_IPC_BUFFER,
|
|
743
772
|
MAX_WORKER_CONNECTIONS,
|
|
773
|
+
WORKER_REGISTER_TIMEOUT_MS,
|
|
744
774
|
};
|
package/src/server/web.js
CHANGED
|
@@ -564,7 +564,14 @@ class WebDashboardServer {
|
|
|
564
564
|
try {
|
|
565
565
|
client.write(message);
|
|
566
566
|
} catch (e) {
|
|
567
|
-
//
|
|
567
|
+
// Write failed — proactively prune the dead client instead of
|
|
568
|
+
// waiting for req.on('close') to fire. On abrupt socket resets
|
|
569
|
+
// 'close' can be delayed long enough that subsequent frames
|
|
570
|
+
// hit the same failed write, accumulating exception work and
|
|
571
|
+
// keeping clientCount misleadingly high. Set.delete during
|
|
572
|
+
// iteration is safe; the for-of iterator handles it.
|
|
573
|
+
try { client.end(); } catch (_) { /* already torn down */ }
|
|
574
|
+
this.clients.delete(client);
|
|
568
575
|
}
|
|
569
576
|
}
|
|
570
577
|
}
|