@torrent-tv/proxy 2.83.2 → 2.83.3
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/CHANGELOG.md +5 -0
- package/package.json +1 -1
- package/services/torrent-pool.js +83 -94
- package/test/swarm-follows-readers.test.js +36 -112
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## 2.83.3
|
|
2
|
+
|
|
3
|
+
- **Fix**: LEAVING A SWARM IS NOW A CONSEQUENCE OF A DEPARTURE, not something a pass over every torrent decides. 2.83.1 asked every five seconds whether anybody was reading, and a torrent added three seconds earlier answered no — its edges still being read, its playback plan still being built — so it left its swarm with 741 connections let go and nothing rejoined it: rejoining waits for a reader, and the reader was waiting for the header the swarm had been fetching. 2.83.2 covered that with a state, "read at least once", which was a patch over the poll rather than a removal of it. The pair is now acted on where the facts happen: the last file claim being released lets the swarm go, and a claim being taken brings it back. A torrent nobody has read yet cannot leave one at all, because there is no longer anything that asks it to.
|
|
4
|
+
- **Chore**: `leaveSwarm` and `rejoinSwarm` are functions of a torrent, like `decideUploadLimit` and `torrentsForUploadPolicy` beside them, so both can be exercised without building a pool — the previous shape could only be checked by faking one, and a fake pool is what hid the missing state in the first place.
|
|
5
|
+
|
|
1
6
|
## 2.83.2
|
|
2
7
|
|
|
3
8
|
- **Fix**: 2.83.1 COULD NOT START PLAYBACK AT ALL, and the cause was its own new rule. A torrent nobody has stated anything about leaves its swarm — and a torrent that has just been added has stated nothing yet, because the read of the file's edges is still in flight and the playback plan is still being built. Field 2026-09-11: added at 20:57:17, first peer at 20:57:18, out of the swarm at 20:57:21 with 741 connections let go. Nothing ever rejoined it: rejoining waits for a reader, and the reader was waiting for the header the swarm had been fetching. `[stats]` then read `peers=0 connected of 0 known (94 queued, 5 tracker(s) offered up to 683 seeders)` for as long as the viewer was willing to wait.
|
package/package.json
CHANGED
package/services/torrent-pool.js
CHANGED
|
@@ -428,6 +428,83 @@ export function decideUploadLimit(activeTorrents, opts = {}) {
|
|
|
428
428
|
return { bytesPerSec: floor, reason: "active readers, not choke-starved" };
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Let go of the swarm of a torrent whose last reader has just left.
|
|
433
|
+
*
|
|
434
|
+
* A PROXY THAT NEEDS NOTHING FROM A SWARM SHOULD NOT BE IN THAT SWARM. While a
|
|
435
|
+
* file is being read every connection is worth keeping — the one that has
|
|
436
|
+
* delivered nothing yet may deliver next — so this is not a limit on
|
|
437
|
+
* connections and never fires during playback.
|
|
438
|
+
*
|
|
439
|
+
* What that state cost until now: WebTorrent enforces `maxConns` only on peers
|
|
440
|
+
* it dials (`_drain`), while `_addIncomingPeer` checks that the torrent is
|
|
441
|
+
* neither destroyed nor paused and registers the peer. A proxy with its port
|
|
442
|
+
* mapped is reachable, so connections arrive and are never turned away — field
|
|
443
|
+
* 2026-09-11, 249 connected at the start of one viewing and 596 at the end,
|
|
444
|
+
* 15 862 more queued, on a file complete for three quarters of an hour.
|
|
445
|
+
*
|
|
446
|
+
* CALLED FROM THE DEPARTURE ITSELF, not from a pass over every torrent. The
|
|
447
|
+
* first version asked every five seconds whether anybody was reading, and a
|
|
448
|
+
* torrent added three seconds earlier — its edges still being read, its
|
|
449
|
+
* playback plan still being built — answered no. It was taken out of its swarm
|
|
450
|
+
* with 741 connections let go, and nothing rejoined it: rejoining waits for a
|
|
451
|
+
* reader, and the reader was waiting for the header the swarm had been
|
|
452
|
+
* fetching. Playback did not start at all (2.83.1).
|
|
453
|
+
*
|
|
454
|
+
* Asked at the moment the last claim is released, leaving cannot happen
|
|
455
|
+
* spontaneously: it is a consequence of a departure, and a torrent nobody has
|
|
456
|
+
* read yet has nothing to depart from.
|
|
457
|
+
*
|
|
458
|
+
* `paused` is the library's own word for this and the path it already checks,
|
|
459
|
+
* so nothing here fights it. The pause alone does not close what is already
|
|
460
|
+
* open, so the peers are let go by hand; the data stays exactly where it is.
|
|
461
|
+
*
|
|
462
|
+
* @param {import("webtorrent").Torrent} torrent
|
|
463
|
+
* @returns {number} Connections let go.
|
|
464
|
+
*/
|
|
465
|
+
export function leaveSwarm(torrent) {
|
|
466
|
+
if (!torrent || torrent.destroyed || torrent.paused === true) {
|
|
467
|
+
return 0;
|
|
468
|
+
}
|
|
469
|
+
torrent.pause();
|
|
470
|
+
const open = Array.isArray(torrent.wires) ? torrent.wires.length : 0;
|
|
471
|
+
let closed = 0;
|
|
472
|
+
for (const peer of [...(torrent._peers?.values?.() ?? [])]) {
|
|
473
|
+
try {
|
|
474
|
+
peer.destroy();
|
|
475
|
+
closed += 1;
|
|
476
|
+
} catch {
|
|
477
|
+
// A peer already going: nothing to do, and nothing worth failing for.
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
logger.info(
|
|
481
|
+
`torrent-pool: [${String(torrent.infoHash ?? "?").slice(0, 8)}] left the swarm — its last reader went, ` +
|
|
482
|
+
`${open} connection(s) open, ${closed} let go; the data stays and the next reader rejoins`
|
|
483
|
+
);
|
|
484
|
+
return closed;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Take the swarm back for a torrent a reader has just arrived at.
|
|
489
|
+
*
|
|
490
|
+
* The other half of the pair: a departure lets a swarm go, an arrival takes it
|
|
491
|
+
* back. Both are acted on at the moment they happen, and neither is discovered
|
|
492
|
+
* by asking.
|
|
493
|
+
*
|
|
494
|
+
* @param {import("webtorrent").Torrent} torrent
|
|
495
|
+
* @returns {boolean} Whether it had to be taken back.
|
|
496
|
+
*/
|
|
497
|
+
export function rejoinSwarm(torrent) {
|
|
498
|
+
if (!torrent || torrent.destroyed || torrent.paused !== true) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
torrent.resume();
|
|
502
|
+
logger.info(
|
|
503
|
+
`torrent-pool: [${String(torrent.infoHash ?? "?").slice(0, 8)}] rejoined the swarm — a reader arrived`
|
|
504
|
+
);
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
|
|
431
508
|
/**
|
|
432
509
|
* Compute the default disk cap: the smaller of a fixed 10 GB and half of the
|
|
433
510
|
* currently free space on the store's filesystem (so a tiny host is never
|
|
@@ -938,88 +1015,6 @@ export class TorrentPool {
|
|
|
938
1015
|
this.#uploadAdjustTimer.unref?.();
|
|
939
1016
|
}
|
|
940
1017
|
|
|
941
|
-
/**
|
|
942
|
-
* Leave the swarm of a torrent nobody is reading, and rejoin it when
|
|
943
|
-
* somebody is.
|
|
944
|
-
*
|
|
945
|
-
* A PROXY THAT NEEDS NOTHING FROM A SWARM SHOULD NOT BE IN THAT SWARM. While
|
|
946
|
-
* a file is being watched every connection is worth keeping — the one that
|
|
947
|
-
* has delivered nothing yet may deliver next — so this is not a limit on
|
|
948
|
-
* connections and never fires during playback. It fires when NOTHING is
|
|
949
|
-
* stated about this torrent at all: no window from any reader, no file held.
|
|
950
|
-
*
|
|
951
|
-
* What that state cost until now: WebTorrent enforces `maxConns` only on
|
|
952
|
-
* peers it dials (`_drain`), while `_addIncomingPeer` checks that the torrent
|
|
953
|
-
* is neither destroyed nor paused and registers the peer. A proxy with its
|
|
954
|
-
* port mapped is reachable, so connections arrive and are never turned away —
|
|
955
|
-
* field 2026-09-11, 249 connected at the start of one viewing and 596 at the
|
|
956
|
-
* end, 15 862 more queued, on a file complete for three quarters of an hour,
|
|
957
|
-
* each of them served by reading a 4 MB piece off the disk for every 16 KB
|
|
958
|
-
* sent.
|
|
959
|
-
*
|
|
960
|
-
* `paused` is the library's own word for this and the path it already checks,
|
|
961
|
-
* so nothing here fights it. The pause alone does not close what is already
|
|
962
|
-
* open, so the peers are let go by hand; the data stays exactly where it is,
|
|
963
|
-
* and the next reader resumes the torrent rather than fetching it again.
|
|
964
|
-
*
|
|
965
|
-
* @param {import("webtorrent").Torrent} torrent
|
|
966
|
-
* @returns {void}
|
|
967
|
-
*/
|
|
968
|
-
followTheReaders(torrent) {
|
|
969
|
-
if (!torrent || torrent.destroyed) {
|
|
970
|
-
return;
|
|
971
|
-
}
|
|
972
|
-
const usage = this.fileUsageByTorrent.get(torrent);
|
|
973
|
-
const isRead = Boolean(usage && usage.size > 0);
|
|
974
|
-
if (isRead) {
|
|
975
|
-
// Recorded on the torrent, as `hasActiveReader` and `hasUnmetDemand`
|
|
976
|
-
// beside it are: it is a fact about this torrent and it lives as long as
|
|
977
|
-
// the torrent does.
|
|
978
|
-
torrent.hasBeenRead = true;
|
|
979
|
-
}
|
|
980
|
-
const isWanted = isRead || demandFor(torrent).register.windows().length > 0;
|
|
981
|
-
// A TORRENT NOBODY HAS READ YET IS BEING SET UP, NOT ABANDONED. Field
|
|
982
|
-
// 2026-09-11, and it broke playback outright: a torrent was added at
|
|
983
|
-
// 20:57:17, its first peer connected at 20:57:18, and this pass took it out
|
|
984
|
-
// of the swarm at 20:57:21 — while the read of the file's edges was still
|
|
985
|
-
// in flight and the playback plan was being built. Nothing rejoined it,
|
|
986
|
-
// because rejoining waits for a reader and a reader cannot arrive: the
|
|
987
|
-
// header it needs is downloaded by the swarm that was just let go.
|
|
988
|
-
//
|
|
989
|
-
// The condition is a state and not a period: having been read at least once
|
|
990
|
-
// is what tells a film somebody left from a film nobody has opened yet. One
|
|
991
|
-
// that is never read at all goes by the pool's own idle removal, which is
|
|
992
|
-
// where that belongs.
|
|
993
|
-
if (!isWanted && torrent.hasBeenRead !== true) {
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
if (isWanted && torrent.paused === true) {
|
|
997
|
-
torrent.resume();
|
|
998
|
-
logger.info(
|
|
999
|
-
`torrent-pool: [${String(torrent.infoHash ?? "?").slice(0, 8)}] rejoined the swarm — somebody is reading it again`
|
|
1000
|
-
);
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
if (isWanted || torrent.paused === true) {
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
torrent.pause();
|
|
1007
|
-
const open = Array.isArray(torrent.wires) ? torrent.wires.length : 0;
|
|
1008
|
-
let closed = 0;
|
|
1009
|
-
for (const peer of [...(torrent._peers?.values?.() ?? [])]) {
|
|
1010
|
-
try {
|
|
1011
|
-
peer.destroy();
|
|
1012
|
-
closed += 1;
|
|
1013
|
-
} catch {
|
|
1014
|
-
// A peer already going: nothing to do, and nothing worth failing for.
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
logger.info(
|
|
1018
|
-
`torrent-pool: [${String(torrent.infoHash ?? "?").slice(0, 8)}] left the swarm — nobody is reading it, ` +
|
|
1019
|
-
`${open} connection(s) open, ${closed} let go; the data stays and the next reader rejoins`
|
|
1020
|
-
);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
1018
|
/**
|
|
1024
1019
|
* Re-evaluate and apply the client-wide upload limit from current swarm state
|
|
1025
1020
|
* (see {@link decideUploadLimit}). Runs on a timer; only calls into WebTorrent
|
|
@@ -1352,9 +1347,6 @@ export class TorrentPool {
|
|
|
1352
1347
|
this.#stateBackgroundFill(torrent);
|
|
1353
1348
|
}
|
|
1354
1349
|
this.#reportStalledDownloads();
|
|
1355
|
-
for (const torrent of this.torrents.values()) {
|
|
1356
|
-
this.followTheReaders(torrent);
|
|
1357
|
-
}
|
|
1358
1350
|
const { bytesPerSec, reason } = decideUploadLimit(active);
|
|
1359
1351
|
if (bytesPerSec === this.#uploadLimit) {
|
|
1360
1352
|
return;
|
|
@@ -1769,15 +1761,10 @@ export class TorrentPool {
|
|
|
1769
1761
|
// it recently accessed so LRU eviction keeps it.
|
|
1770
1762
|
this.#cancelIdleRemoval(torrent);
|
|
1771
1763
|
this.#lastAccess.set(torrent, Date.now());
|
|
1772
|
-
//
|
|
1773
|
-
//
|
|
1774
|
-
//
|
|
1775
|
-
|
|
1776
|
-
torrent.resume();
|
|
1777
|
-
logger.info(
|
|
1778
|
-
`torrent-pool: [${String(torrent.infoHash ?? "?").slice(0, 8)}] rejoined the swarm — a reader arrived`
|
|
1779
|
-
);
|
|
1780
|
-
}
|
|
1764
|
+
// AND REJOIN ITS SWARM, which is the other half of the pair: a departure
|
|
1765
|
+
// lets a swarm go, an arrival takes it back. Both are acted on at the
|
|
1766
|
+
// moment they happen, and neither is discovered by asking.
|
|
1767
|
+
rejoinSwarm(torrent);
|
|
1781
1768
|
usage.set(fileIndex, (usage.get(fileIndex) ?? 0) + 1);
|
|
1782
1769
|
|
|
1783
1770
|
let released = false;
|
|
@@ -1796,6 +1783,8 @@ export class TorrentPool {
|
|
|
1796
1783
|
this.fileUsageByTorrent.delete(torrent);
|
|
1797
1784
|
// No active readers — schedule removal (with store) after an idle TTL.
|
|
1798
1785
|
this.#scheduleIdleRemoval(torrent);
|
|
1786
|
+
// THE READER LEFT, AND THAT IS THE ONLY MOMENT A SWARM MAY BE LET GO.
|
|
1787
|
+
leaveSwarm(torrent);
|
|
1799
1788
|
}
|
|
1800
1789
|
};
|
|
1801
1790
|
}
|
|
@@ -3,21 +3,25 @@
|
|
|
3
3
|
*
|
|
4
4
|
* WebTorrent enforces `maxConns` only on peers it dials (`_drain`), while
|
|
5
5
|
* `_addIncomingPeer` checks that the torrent is neither destroyed nor paused
|
|
6
|
-
* and registers the peer. A proxy with its port mapped is reachable, so
|
|
7
|
-
* connections arrive and are never turned away — field
|
|
8
|
-
* connected at the start of one viewing, 596 at the end, 15 862
|
|
9
|
-
*
|
|
10
|
-
* served by reading a 4 MB piece off the disk for every 16 KB sent.
|
|
6
|
+
* and registers the peer. A proxy with its port mapped is reachable, so on a
|
|
7
|
+
* popular torrent connections arrive and are never turned away — field
|
|
8
|
+
* 2026-09-11: 249 connected at the start of one viewing, 596 at the end, 15 862
|
|
9
|
+
* more queued, on a file complete for three quarters of an hour.
|
|
11
10
|
*
|
|
12
|
-
* The rule is not a limit on connections. While anybody is reading, every
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* The rule is not a limit on connections. While anybody is reading, every one is
|
|
12
|
+
* worth keeping: the one that has delivered nothing yet may deliver next.
|
|
13
|
+
*
|
|
14
|
+
* AND IT IS ACTED ON AT THE DEPARTURE ITSELF. The first version asked every
|
|
15
|
+
* five seconds whether anybody was reading, and a torrent added three seconds
|
|
16
|
+
* earlier answered no — its edges still being read, its plan still being built.
|
|
17
|
+
* It left the swarm with 741 connections let go, and nothing rejoined it:
|
|
18
|
+
* rejoining waits for a reader, and the reader was waiting for the header the
|
|
19
|
+
* swarm had been fetching. Playback did not start at all.
|
|
15
20
|
*/
|
|
16
21
|
|
|
17
22
|
import test from "node:test";
|
|
18
23
|
import assert from "node:assert/strict";
|
|
19
|
-
import {
|
|
20
|
-
import { demandFor, forgetTorrent } from "../services/download/registry.js";
|
|
24
|
+
import { leaveSwarm, rejoinSwarm } from "../services/torrent-pool.js";
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
27
|
* A torrent that records what was done to it. Only the surface the rule
|
|
@@ -39,7 +43,7 @@ function torrentWith(peerCount) {
|
|
|
39
43
|
}
|
|
40
44
|
return {
|
|
41
45
|
infoHash: "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0",
|
|
42
|
-
files: [{ length: 1024 }],
|
|
46
|
+
files: [{ length: 1024 }, { length: 2048 }],
|
|
43
47
|
paused: false,
|
|
44
48
|
wires: [...peers.values()].map((peer) => peer.wire),
|
|
45
49
|
_peers: peers,
|
|
@@ -52,117 +56,37 @@ function torrentWith(peerCount) {
|
|
|
52
56
|
};
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
* @param {object} torrent
|
|
57
|
-
* @returns {TorrentPool}
|
|
58
|
-
*/
|
|
59
|
-
function poolWith(torrent) {
|
|
60
|
-
const pool = Object.create(TorrentPool.prototype);
|
|
61
|
-
pool.torrents = new Map([["source", torrent]]);
|
|
62
|
-
pool.fileUsageByTorrent = new WeakMap();
|
|
63
|
-
return pool;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
test("a torrent nobody is reading is let go of, and its data is not", () => {
|
|
59
|
+
test("letting a swarm go pauses the torrent and closes what the pause does not", () => {
|
|
67
60
|
const torrent = torrentWith(6);
|
|
68
|
-
const pool = poolWith(torrent);
|
|
69
|
-
try {
|
|
70
|
-
// Read once and left, which is the state this is about: a film nobody has
|
|
71
|
-
// opened yet is being set up, and the test below covers that.
|
|
72
|
-
pool.fileUsageByTorrent.set(torrent, new Map([[0, 1]]));
|
|
73
|
-
pool.followTheReaders(torrent);
|
|
74
|
-
pool.fileUsageByTorrent.set(torrent, new Map());
|
|
75
|
-
pool.followTheReaders(torrent);
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
assert.equal(torrent._peers.size, 0, "the connections the pause does not close were not let go");
|
|
79
|
-
} finally {
|
|
80
|
-
forgetTorrent(torrent);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
62
|
+
const closed = leaveSwarm(torrent);
|
|
83
63
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
pool.fileUsageByTorrent.set(torrent, new Map([[0, 1]]));
|
|
88
|
-
try {
|
|
89
|
-
pool.followTheReaders(torrent);
|
|
90
|
-
|
|
91
|
-
assert.equal(torrent.paused, false, "a torrent being read was taken out of its swarm");
|
|
92
|
-
assert.equal(torrent._peers.size, 6, "connections were closed while somebody was reading");
|
|
93
|
-
} finally {
|
|
94
|
-
forgetTorrent(torrent);
|
|
95
|
-
}
|
|
64
|
+
assert.equal(torrent.paused, true, "the library's own word for this was not used");
|
|
65
|
+
assert.equal(closed, 6);
|
|
66
|
+
assert.equal(torrent._peers.size, 0, "the connections the pause does not close were not let go");
|
|
96
67
|
});
|
|
97
68
|
|
|
98
|
-
test("a
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const pool = poolWith(torrent);
|
|
103
|
-
demandFor(torrent).register.state({
|
|
104
|
-
claimant: "read-1",
|
|
105
|
-
fileIndex: 0,
|
|
106
|
-
byteStart: 0,
|
|
107
|
-
byteEnd: 1023,
|
|
108
|
-
urgency: 0
|
|
109
|
-
});
|
|
110
|
-
try {
|
|
111
|
-
pool.followTheReaders(torrent);
|
|
112
|
-
assert.equal(torrent.paused, false, "a declared window did not count as somebody reading");
|
|
113
|
-
} finally {
|
|
114
|
-
forgetTorrent(torrent);
|
|
115
|
-
}
|
|
69
|
+
test("a swarm already let go is not let go twice", () => {
|
|
70
|
+
const torrent = torrentWith(4);
|
|
71
|
+
leaveSwarm(torrent);
|
|
72
|
+
assert.equal(leaveSwarm(torrent), 0);
|
|
116
73
|
});
|
|
117
74
|
|
|
118
|
-
test("the
|
|
75
|
+
test("the reader that comes back takes the swarm with it", () => {
|
|
119
76
|
const torrent = torrentWith(3);
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
pool.fileUsageByTorrent.set(torrent, new Map([[0, 1]]));
|
|
123
|
-
pool.followTheReaders(torrent);
|
|
124
|
-
pool.fileUsageByTorrent.set(torrent, new Map());
|
|
125
|
-
pool.followTheReaders(torrent);
|
|
126
|
-
assert.equal(torrent.paused, true);
|
|
77
|
+
leaveSwarm(torrent);
|
|
127
78
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
} finally {
|
|
132
|
-
forgetTorrent(torrent);
|
|
133
|
-
}
|
|
79
|
+
assert.equal(rejoinSwarm(torrent), true);
|
|
80
|
+
assert.equal(torrent.paused, false, "the next reader was left with a torrent that fetches nothing");
|
|
81
|
+
assert.equal(rejoinSwarm(torrent), false, "a torrent already in its swarm was resumed again");
|
|
134
82
|
});
|
|
135
83
|
|
|
136
|
-
test("a torrent nobody has read yet is
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
// waiting for the header the swarm was fetching.
|
|
84
|
+
test("a torrent nobody has read yet is never asked to leave", () => {
|
|
85
|
+
// The field failure of 2026-09-11 is closed by WHERE this is called from, not
|
|
86
|
+
// by anything inside it: leaving is a consequence of the last claim being
|
|
87
|
+
// released, and a torrent being opened has released nothing. There is no pass
|
|
88
|
+
// that can ask it.
|
|
142
89
|
const torrent = torrentWith(103);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
pool.followTheReaders(torrent);
|
|
146
|
-
assert.equal(torrent.paused, false, "a torrent being opened was taken out of its swarm");
|
|
147
|
-
assert.equal(torrent._peers.size, 103, "and its connections were let go");
|
|
148
|
-
} finally {
|
|
149
|
-
forgetTorrent(torrent);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("once it has been read, leaving is allowed again", () => {
|
|
154
|
-
const torrent = torrentWith(4);
|
|
155
|
-
const pool = poolWith(torrent);
|
|
156
|
-
try {
|
|
157
|
-
// A reader arrives and goes.
|
|
158
|
-
pool.fileUsageByTorrent.set(torrent, new Map([[0, 1]]));
|
|
159
|
-
pool.followTheReaders(torrent);
|
|
160
|
-
assert.equal(torrent.paused, false);
|
|
161
|
-
|
|
162
|
-
pool.fileUsageByTorrent.set(torrent, new Map());
|
|
163
|
-
pool.followTheReaders(torrent);
|
|
164
|
-
assert.equal(torrent.paused, true, "a film somebody left keeps its swarm for nobody");
|
|
165
|
-
} finally {
|
|
166
|
-
forgetTorrent(torrent);
|
|
167
|
-
}
|
|
90
|
+
assert.equal(torrent.paused, false);
|
|
91
|
+
assert.equal(torrent._peers.size, 103);
|
|
168
92
|
});
|