teyyare 0.2.6 → 0.2.7
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/bin/teyyare.js +2 -2
- package/package.json +1 -1
- package/src/index.js +25 -1
- package/src/receiver/pipeline.js +18 -1
- package/src/sender/pipeline.js +33 -4
package/bin/teyyare.js
CHANGED
|
@@ -56,7 +56,7 @@ async function main() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (command === '--version' || command === '-v' || command === 'version') {
|
|
59
|
-
console.log('0.2.
|
|
59
|
+
console.log('0.2.7');
|
|
60
60
|
process.exit(0);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -170,7 +170,7 @@ Peak RSS: ${(snap.peakRSS / (1024 * 1024)).toFixed(2)} MB
|
|
|
170
170
|
`);
|
|
171
171
|
process.exit(0);
|
|
172
172
|
} else if (command === 'status') {
|
|
173
|
-
console.log('\x1b[1m\x1b[36mTeyyare\x1b[0m v0.2.
|
|
173
|
+
console.log('\x1b[1m\x1b[36mTeyyare\x1b[0m v0.2.7 status: ready');
|
|
174
174
|
} else {
|
|
175
175
|
console.error(`Unknown command: ${command}`);
|
|
176
176
|
printUsage();
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -141,8 +141,14 @@ TCPTransport.prototype._attachSocketReader = function(socket) {
|
|
|
141
141
|
if (idx !== -1) {
|
|
142
142
|
pool.splice(idx, 1);
|
|
143
143
|
}
|
|
144
|
+
if (pool.length === 0 && this.onPeerDisconnect) {
|
|
145
|
+
this.onPeerDisconnect(peer);
|
|
146
|
+
}
|
|
144
147
|
}
|
|
145
148
|
}
|
|
149
|
+
if (this._inboundSockets.size === 0 && this.onPeerDisconnect) {
|
|
150
|
+
this.onPeerDisconnect(1);
|
|
151
|
+
}
|
|
146
152
|
});
|
|
147
153
|
|
|
148
154
|
socket.on('error', () => {
|
|
@@ -458,6 +464,15 @@ export async function serve({
|
|
|
458
464
|
|
|
459
465
|
let activeSender = null;
|
|
460
466
|
|
|
467
|
+
transport.onPeerDisconnect = async (peer) => {
|
|
468
|
+
if (activeSender) {
|
|
469
|
+
const s = activeSender;
|
|
470
|
+
activeSender = null;
|
|
471
|
+
await s.abort();
|
|
472
|
+
}
|
|
473
|
+
replication.reset();
|
|
474
|
+
};
|
|
475
|
+
|
|
461
476
|
replication.onBatch(async (msg) => {
|
|
462
477
|
if (!msg.buffers || msg.buffers.length < 2) return;
|
|
463
478
|
const [metaBuf, payloadBuf] = msg.buffers;
|
|
@@ -481,8 +496,9 @@ export async function serve({
|
|
|
481
496
|
|
|
482
497
|
// Abort any existing running sender before starting new transfer
|
|
483
498
|
if (activeSender) {
|
|
484
|
-
activeSender
|
|
499
|
+
const s = activeSender;
|
|
485
500
|
activeSender = null;
|
|
501
|
+
await s.abort();
|
|
486
502
|
}
|
|
487
503
|
|
|
488
504
|
const manifest = await TransferManifest.build(resolved);
|
|
@@ -534,6 +550,12 @@ export async function serve({
|
|
|
534
550
|
receiver,
|
|
535
551
|
metrics,
|
|
536
552
|
async close() {
|
|
553
|
+
if (activeSender) {
|
|
554
|
+
const s = activeSender;
|
|
555
|
+
activeSender = null;
|
|
556
|
+
await s.abort();
|
|
557
|
+
}
|
|
558
|
+
await receiver.close();
|
|
537
559
|
metrics.stop();
|
|
538
560
|
await transport.close();
|
|
539
561
|
}
|
|
@@ -600,6 +622,7 @@ export async function send({
|
|
|
600
622
|
try {
|
|
601
623
|
await sender.transfer();
|
|
602
624
|
} finally {
|
|
625
|
+
await sender.abort();
|
|
603
626
|
metrics.stop();
|
|
604
627
|
await transport.close();
|
|
605
628
|
}
|
|
@@ -687,6 +710,7 @@ export async function pull({
|
|
|
687
710
|
await receiver.waitForCompletion();
|
|
688
711
|
} finally {
|
|
689
712
|
if (progress) progress.stop();
|
|
713
|
+
await receiver.close();
|
|
690
714
|
metrics.stop();
|
|
691
715
|
await transport.close();
|
|
692
716
|
}
|
package/src/receiver/pipeline.js
CHANGED
|
@@ -370,6 +370,7 @@ export class ReceiverPipeline {
|
|
|
370
370
|
// Sync disk buffers
|
|
371
371
|
await file.fileHandle.sync();
|
|
372
372
|
await file.fileHandle.close();
|
|
373
|
+
file.fileHandle = null;
|
|
373
374
|
|
|
374
375
|
// Verify whole-file checksum if available
|
|
375
376
|
const expectedSha = file.sha256;
|
|
@@ -389,13 +390,29 @@ export class ReceiverPipeline {
|
|
|
389
390
|
this.completedFiles.add(file.destPath);
|
|
390
391
|
}
|
|
391
392
|
|
|
393
|
+
async close() {
|
|
394
|
+
for (const [fileId, file] of this.activeFiles.entries()) {
|
|
395
|
+
if (file.fileHandle) {
|
|
396
|
+
try {
|
|
397
|
+
await file.fileHandle.close();
|
|
398
|
+
} catch {}
|
|
399
|
+
file.fileHandle = null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
this.activeFiles.clear();
|
|
403
|
+
this._pendingChunks.clear();
|
|
404
|
+
}
|
|
405
|
+
|
|
392
406
|
async _computeFileSha256(filePath) {
|
|
393
407
|
return new Promise((resolve, reject) => {
|
|
394
408
|
const hash = crypto.createHash('sha256');
|
|
395
409
|
const stream = createReadStream(filePath);
|
|
396
410
|
stream.on('data', (d) => hash.update(d));
|
|
397
411
|
stream.on('end', () => resolve(hash.digest('hex')));
|
|
398
|
-
stream.on('error',
|
|
412
|
+
stream.on('error', (err) => {
|
|
413
|
+
stream.destroy(err);
|
|
414
|
+
reject(err);
|
|
415
|
+
});
|
|
399
416
|
});
|
|
400
417
|
}
|
|
401
418
|
}
|
package/src/sender/pipeline.js
CHANGED
|
@@ -141,14 +141,27 @@ export class SenderPipeline {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
});
|
|
144
|
+
this._activeHandle = null;
|
|
145
|
+
this._abortWaiters = new Set();
|
|
144
146
|
this.aborted = false;
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
abort() {
|
|
149
|
+
async abort() {
|
|
148
150
|
this.aborted = true;
|
|
151
|
+
for (const waiter of this._abortWaiters) {
|
|
152
|
+
waiter();
|
|
153
|
+
}
|
|
154
|
+
this._abortWaiters.clear();
|
|
155
|
+
if (this._activeHandle) {
|
|
156
|
+
const h = this._activeHandle;
|
|
157
|
+
this._activeHandle = null;
|
|
158
|
+
try {
|
|
159
|
+
await h.close();
|
|
160
|
+
} catch {}
|
|
161
|
+
}
|
|
149
162
|
if (this.engine) {
|
|
150
163
|
try {
|
|
151
|
-
this.engine.
|
|
164
|
+
this.engine.reset();
|
|
152
165
|
} catch {}
|
|
153
166
|
}
|
|
154
167
|
}
|
|
@@ -157,7 +170,19 @@ export class SenderPipeline {
|
|
|
157
170
|
if (this.aborted) return;
|
|
158
171
|
while (this.engine.isBackpressured()) {
|
|
159
172
|
if (this.aborted) return;
|
|
160
|
-
await
|
|
173
|
+
await new Promise((resolve) => {
|
|
174
|
+
let done = false;
|
|
175
|
+
const waiter = () => {
|
|
176
|
+
if (!done) {
|
|
177
|
+
done = true;
|
|
178
|
+
this._abortWaiters.delete(waiter);
|
|
179
|
+
resolve();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
this._abortWaiters.add(waiter);
|
|
183
|
+
this.engine.ready().then(waiter, waiter);
|
|
184
|
+
});
|
|
185
|
+
if (this.aborted) return;
|
|
161
186
|
await new Promise((r) => setImmediate(r));
|
|
162
187
|
}
|
|
163
188
|
if (this.aborted) return;
|
|
@@ -216,6 +241,7 @@ export class SenderPipeline {
|
|
|
216
241
|
|
|
217
242
|
if (file.size > 0) {
|
|
218
243
|
const handle = await fs.open(filePath, 'r');
|
|
244
|
+
this._activeHandle = handle;
|
|
219
245
|
try {
|
|
220
246
|
for (let i = 0; i < totalFileChunks; i++) {
|
|
221
247
|
if (this.aborted) break;
|
|
@@ -282,7 +308,10 @@ export class SenderPipeline {
|
|
|
282
308
|
}
|
|
283
309
|
}
|
|
284
310
|
} finally {
|
|
285
|
-
|
|
311
|
+
this._activeHandle = null;
|
|
312
|
+
try {
|
|
313
|
+
await handle.close();
|
|
314
|
+
} catch {}
|
|
286
315
|
}
|
|
287
316
|
}
|
|
288
317
|
|