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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "2.1.12",
3
+ "version": "2.1.14",
4
4
  "description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
5
5
  "main": "bin/git-watchtower.js",
6
6
  "bin": {
@@ -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, (id) => { workerId = id; }, () => workerId);
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
- // Dead clientwill be cleaned up on 'close'
567
+ // Write failedproactively 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
  }