diff --git a/lib/bt-stream-factory.js b/lib/bt-stream-factory.js index 8e408f7..dae6284 100644 --- a/lib/bt-stream-factory.js +++ b/lib/bt-stream-factory.js @@ -4,7 +4,6 @@ const hat = require('hat'); const {Swarm} = require('./swarm'); const BTStream = require('./bt-stream'); - const createBTStream = ({dhtPort, hash: _hash}) => { const dht = new DHT(); const id = '-FRIDGE-' + hat(48); diff --git a/lib/bt-stream.js b/lib/bt-stream.js index cd64e28..ef15a6d 100644 --- a/lib/bt-stream.js +++ b/lib/bt-stream.js @@ -5,6 +5,7 @@ const log = require('./logger')('BTStream'); const createEmptyReadableStream = () => { const stream = new Readable(); + stream._read = () => null; setImmediate(() => stream.emit('error', new Error('File not found'))); return stream; }; @@ -38,50 +39,49 @@ module.exports = class BTStream { * @param {number} from Offset from file start position * @returns {*} */ - downloadFile({torrent, file, from}) { - const torrentHash = torrent.infoHash; + downloadFile({torrent, file, from, to}) { const pieces = torrent.pieces; const pieceSize = torrent.pieceLength; - const result = slicePieces({pieces, pieceSize, file, from}); + + const result = slicePieces({pieces, pieceSize, file, from, to}); + // TODO: lastLength какой-то некорректный. + // Нужно вообще его переосмыслить, потому что дальше должен передаваться реальный последний кусок, а не тот, который мы отрезаем. + // Возможно нужно взять из прошлой реализации const {firstOffset, lastLength, pieces: targetPieces} = result; + const {length, pieceLength, lastPieceLength} = torrent; + log('download file::', 'torrent', {length, pieceLength, lastPieceLength}); + log('download file::', `result`, {firstOffset, lastLength}); + + const bytesOffset = file.offset; + const pieceOffset = pieces.indexOf(targetPieces[0]) + Math.floor(bytesOffset / pieceSize); + + // TODO: создание pie происходит в двух местах. Сократить до одного. this._pie = new Pie({ - swarm: this._swarm, - torrentHash, - pieces: targetPieces, - pieceSize, - pieceIndexOffset: pieces.findIndex((it) => it === targetPieces[0]), - firstOffset, - lastLength, - }); + swarm: this._swarm, pieces: targetPieces, pieceSize, pieceOffset, firstOffset, lastLength + }, {from: firstOffset, to: lastLength}); return this._pie.getReadStream(); } - downloadFileByName({torrent, filename, offset}) { + downloadFileByName({torrent, filename, from, to}) { const file = torrent.files.find((file) => file.name === filename); if (file) { - return this.downloadFile({torrent, file, from: offset}); + return this.downloadFile({torrent, file, from, to}); } else { // TODO: Зачем эта функциональность? Тут же стоит бросить исключение. return createEmptyReadableStream(); } } - /** - * @param {{files: [],}} torrent - * @param {string} filePath - * @param {number} offset Offset from file start position - * @returns {*} - */ - downloadFileByPath({torrent, filePath, offset}) { - log('download file by path::0', {offset}); + downloadFileByPath({torrent, filePath, from, to}) { + console.log('download file by path::0', {from, to}); const file = torrent.files.find((file) => file.path === filePath); if (file) { - return this.downloadFile({torrent, file, from: offset}); + return this.downloadFile({torrent, file, from, to}); } else { // TODO: Зачем эта функциональность? Тут же стоит бросить исключение. return createEmptyReadableStream(); diff --git a/lib/logger.js b/lib/logger.js index 1efc25c..cf38769 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -9,10 +9,14 @@ const transports = { const send = transports.console; -const createLogger = (moduleName) => { +const createLogger = (moduleName, enable) => { let prefix = null; const log = (...args) => { + if (!enable) { + return; + } + if (prefix) { send(moduleName, prefix(), ...args); } else { @@ -43,4 +47,7 @@ const createLogger = (moduleName) => { return log; }; -module.exports = (moduleName) => createLogger(moduleName); +module.exports = (moduleName, enable) => { + return createLogger(moduleName, enable); +}; + diff --git a/lib/pie.js b/lib/pie.js index da58640..a523b68 100644 --- a/lib/pie.js +++ b/lib/pie.js @@ -1,49 +1,25 @@ -const crypto = require('crypto'); const { Readable } = require('stream'); const PieceLoader = require('./piece-loader'); -const log = require('./logger')('Pie'); +const {createBuffer, verifyPiece} = require('./pie.utils'); const CHUNK_SIZE = 16384; const READ_STREAM_ITERATION_COUNT = 8; -const createBuffer = (piece, size, hash) => { - const buffersPieces = Object - .keys(piece) - .sort((a, b) => parseInt(a, 10) - parseInt(b, 10)) - .map((key) => piece[key]); - - const buffer = Buffer.concat(buffersPieces, buffersPieces.length * size); - - const isGood = verifyPiece(buffer, hash); - log('verify is', isGood.toString().toUpperCase()); - - return buffer; -}; - -/** - * @return {string} - */ -const sha1 = (data) => { - return crypto.createHash('sha1') - .update(data) - .digest('hex'); -}; - -const verifyPiece = (buffer, referenceHash) => { - const pieceHash = sha1(buffer); - return pieceHash === referenceHash; -}; - - +let _buffer = null; +let _buffer1 = null; module.exports = class Pie { - constructor({pieces, pieceSize, firstOffset, lastLength, swarm, pieceIndexOffset}) { + constructor({pieces, pieceSize, pieceOffset, firstOffset, lastLength, swarm}, {from, to}) { + console.log('Pie::args', {pieces: pieces.length, pieceSize, pieceOffset, firstOffset, lastLength}, {from, to}) this._pieces = pieces; this._pieceSize = pieceSize; - this._pieceIndexOffset = pieceIndexOffset; - this._firstOffest = firstOffset; + this._pieceOffset = pieceOffset; + this._firstBytesOffset = firstOffset; this._lastLength = lastLength; this._swarm = swarm; + this._from = from; + this._to = to; + /** * @type {PassThrough} * @private @@ -64,13 +40,15 @@ module.exports = class Pie { */ this._pieceLoader = null; + // TODO: Remove onWire, because all use `swarm.getFreeWires()` this._swarm.getWires().forEach((it) => this._onWire(it)); this._swarm.on(this._swarm.EVENT_ADDED_WIRE, (wire) => this._onWire(wire)); - this.init(); + this._init(); } - async init() { + _init() { + console.log('pie::_init') this._readStream = Readable.from(this._generatorPieLoader(), { highWaterMark: READ_STREAM_ITERATION_COUNT, }) @@ -97,41 +75,140 @@ module.exports = class Pie { } async *_generatorPieLoader() { - const length = this._pieces.length; + const loadedPieces = []; + const length = this._pieces.length; + if (!_buffer) { + for (let i = length - 1; i < length && length > 1; i++) { + const index = i; + + + // TODO: Иногда загрузка висит на месте. + // TODO: скорее всего нужно убивать его спустя какое-то время. + const pieceSize = index === length - 1 ? this._lastLength : this._pieceSize; + const lastSize = index === length - 1 ? pieceSize % CHUNK_SIZE || CHUNK_SIZE : CHUNK_SIZE; + + // console.log('Pie::lastSize', lastSize) + this._pieceLoader = new PieceLoader({ + swarm: this._swarm, + chunkSize: CHUNK_SIZE, + pieceIndex: index + this._pieceOffset, + pieceSize, + lastSize: lastSize === 2047 ? 2048 : lastSize, + isDebug: length === 1, + }); + + const hash = this._pieces[index]; + const now = Date.now(); + + // console.log(`####### start load`); + console.log(`####### Load ${index + 1} of ${length}`) + const piece = await this._pieceLoader.load(); + // console.log(`####### finish load`); + + // const time = Date.now() - now; + // console.log({time, speed: CHUNK_SIZE / time / 1000}); + + this._pieceLoader.destroy(); + this._pieceLoader = null; + + let buffer = createBuffer(piece); + const isGood = verifyPiece(buffer, hash); + console.log(`####### Loaded ${index + 1} of ${length}`, `TARGET`, {i, pieceSize, lastSize}, 'verify is', isGood) + !isGood && console.log('verify is BAD!!!', isGood); + + loadedPieces.push(1); + + console.log(`*********** buffer size before`, buffer.length); + if (index === 0 && length > 1) { + buffer = Buffer.from(buffer.buffer, this._firstBytesOffset % pieceSize).slice(this._firstBytesOffset % pieceSize); + // buffer = Buffer.from(buffer.buffer, this._from); + } else if (index === length - 1) { + console.log(`!!!!!!!!!!!!`, `create buffer from last`); + buffer = Buffer.from(buffer.buffer, 0, this._lastLength); + // buffer = Buffer.from(buffer.buffer, 0, this._to); + } + + console.log(`to cache`) + console.log(`*********** buffer size after `, buffer.length); + + _buffer = buffer; + break; + } + } for (let i = 0; i < length; i++) { + if (length === 1 && _buffer) { + console.log(`from cache`) + // *********** buffer size before 7392962 offset 7343931 + // *********** buffer size after 7392962 + const pieceSize = this._lastLength; + console.log(`*********** buffer size before`, _buffer.length, `offset`, this._firstBytesOffset % pieceSize); + const buffer = Buffer.from(_buffer, this._firstBytesOffset % pieceSize).slice(this._firstBytesOffset % pieceSize); + console.log(`*********** buffer size after `, buffer.length); + + yield buffer; + continue; + } const index = i; - log(`Load piece ${index + 1} of ${length}`); + if (i === 0 && _buffer1 && length === 660) { + console.log(`####### Loaded ${index + 1} of ${length} from cache`); + yield _buffer1; + continue; + } + + // console.log(`####### Load ${index + 1} of ${length}`); // TODO: Иногда загрузка висит на месте. // TODO: скорее всего нужно убивать его спустя какое-то время. const pieceSize = index === length - 1 ? this._lastLength : this._pieceSize; - const lastSize = index === length - 1 ? pieceSize % CHUNK_SIZE : CHUNK_SIZE; + const lastSize = index === length - 1 ? pieceSize % CHUNK_SIZE || CHUNK_SIZE : CHUNK_SIZE; + // console.log('Pie::lastSize', lastSize) this._pieceLoader = new PieceLoader({ swarm: this._swarm, - wires: this._wires, chunkSize: CHUNK_SIZE, - pieceIndex: index + this._pieceIndexOffset, + pieceIndex: index + this._pieceOffset, pieceSize, - lastSize, + lastSize: lastSize === 2047 ? 2048 : lastSize, }); + const hash = this._pieces[index]; + const now = Date.now(); + + // console.log(`####### start load`); + console.log(`####### Load ${index + 1} of ${length}`) const piece = await this._pieceLoader.load(); + // console.log(`####### finish load`); + + // const time = Date.now() - now; + // console.log({time, speed: CHUNK_SIZE / time / 1000}); this._pieceLoader.destroy(); this._pieceLoader = null; - const hash = this._pieces[index]; - let buffer = createBuffer(piece, CHUNK_SIZE, hash); + let buffer = createBuffer(piece); + const isGood = verifyPiece(buffer, hash); + console.log(`####### Loaded ${index + 1} of ${length}`, `TARGET`, {i, pieceSize, lastSize}, 'verify is', isGood) + !isGood && console.log('verify is BAD!!!', isGood); + + loadedPieces.push(1); - if (index === 0) { - buffer = Buffer.from(buffer.buffer, this._firstOffest % pieceSize); + console.log(`*********** buffer size before`, buffer.length); + if (index === 0 && length > 1) { + buffer = Buffer.from(buffer.buffer, this._from % pieceSize).slice(this._from % pieceSize); + // buffer = Buffer.from(buffer.buffer, this._from); + } else if (index === 0) { + buffer = Buffer.from(buffer.buffer, this._firstBytesOffset % pieceSize).slice(this._firstBytesOffset % pieceSize); } else if (index === length - 1) { - log(`Create buffer from last piece`); + console.log(`!!!!!!!!!!!!`, `create buffer from last`); buffer = Buffer.from(buffer.buffer, 0, this._lastLength); + // buffer = Buffer.from(buffer.buffer, 0, this._to); } + console.log(`*********** buffer size after `, buffer.length); + if (i === 1 && length > 2) { + _buffer1 = buffer; + } yield buffer; } } diff --git a/lib/pie.utils.js b/lib/pie.utils.js new file mode 100644 index 0000000..a040436 --- /dev/null +++ b/lib/pie.utils.js @@ -0,0 +1,49 @@ +const crypto = require('crypto'); + +/** + * @param piece + * @returns {Buffer} + */ +const createBuffer = (piece) => { + const buffersPieces = Object + .keys(piece) + .sort((a, b) => parseInt(a, 10) - parseInt(b, 10)) + .map((key) => piece[key]); + + // const length = buffersPieces.reduce((acc, cur) => { + // // console.log('cur.length', cur.length) + // return acc + cur.length; + // }, 0); + + // console.log('size', size); + // console.log('length', length); + // console.log('length::after', length - buffersPieces[buffersPieces.length - 1].length); + + return Buffer.concat(buffersPieces); +}; + +/** + * @returns {string} + */ +const sha1 = (data) => { + return crypto.createHash('sha1') + .update(data) + .digest('hex'); +}; + + +/** + * @param buffer + * @param referenceHash + * @returns {boolean} + */ +const verifyPiece = (buffer, referenceHash) => { + const pieceHash = sha1(buffer); + return pieceHash === referenceHash; +}; + + +module.exports = { + createBuffer, + verifyPiece, +}; diff --git a/lib/piece-loader.js b/lib/piece-loader.js index 1ebc68a..01c4bbb 100644 --- a/lib/piece-loader.js +++ b/lib/piece-loader.js @@ -1,9 +1,7 @@ const Deferred = require('./deferred'); const log = require('./logger')('PieceLoader'); -const shuffle = (array) => { - return array.slice().sort(() => Math.random() - 0.5); -}; +const REQUEST_TIMEOUT = 1000 * 3; const createChunksMap = (count, pieces) => { const map = new Array(count).fill(`-`).map((it, i) => { @@ -20,14 +18,14 @@ const createChunksMap = (count, pieces) => { }; const loadChunk = async ({wire, offset, pieceIndex, chunkSize}) => { - //log(`load chunk`, {wire: typeof wire, offset, pieceIndex, chunkSize}); + log(`load chunk`, {wire: typeof wire, offset, pieceIndex, chunkSize}); // TODO: Use method Wire. Not use private property. if (!wire || wire._wire.peerChoking) { throw new Error(`Wire is not ready for load chunk`); } - return await wire.request({offset, pieceIndex, chunkSize, timeout: 3000}) + return await wire.request({offset, pieceIndex, chunkSize, timeout: REQUEST_TIMEOUT}) }; const isAllChunksDownloaded = (piece, chunksCount) => { @@ -35,6 +33,7 @@ const isAllChunksDownloaded = (piece, chunksCount) => { .filter((key) => piece[key] && piece[key] !== true) .length; + log('isAllChunksDownloaded::', {finishedItems, chunksCount}, finishedItems === chunksCount) return finishedItems === chunksCount; }; @@ -55,44 +54,58 @@ const getFreeChunksIndex = ({piece, chunksCount}) => { class PieceLoader { - constructor({swarm, wires, chunkSize, pieceIndex, pieceSize, lastSize}) { + constructor({swarm, chunkSize, pieceIndex, pieceSize, lastSize}) { this._swarm = swarm; - this._wires = wires || []; this._chunkSize = chunkSize; + // TODO: Возможно на последнем куске количество чанков считается неверно this._chunksCount = Math.ceil(pieceSize / chunkSize); this._pieceIndex = pieceIndex; this._lastSize = lastSize; + log(`PieceLoader::constructor`, {pieceIndex, chunkSize, _chunksCount: this._chunksCount, pieceSize, lastSize}) + log('PieceLoader', {chunkSize, pieceIndex, pieceSize, lastSize, count: this._chunksCount}); + this._piece = {}; this._onFinishDeferred = new Deferred(); } async load() { - // TODO: What will happen if _wires.length === 0? - if (this._wires.length) { - this._work(); - } - - const now = Date.now(); + this._work(); await this._onFinishDeferred.promise(); - log(`Piece is loaded`, {speed: this._chunkSize / (Date.now() - now) / 1000}); - return this._piece; } - addWires(wires) { - this._wires = this._wires.concat(wires); + addWires() { this._work(); } destroy() { - this._wires = null; this._piece = null; this._onFinishDeferred = null; } + async _work2() { + const freeChunksIndex = []; + const parallels = createParallels({taskCountInTime: 8}); + + for (const chunkIndex of freeChunksIndex) { + parallels.add(() => loadChunk(chunkIndex)); + } + + parallels.run(); + await parallels.complete(); + await parallels.next(); + + const piece = {}; + const result = parallels.getTasksResult(); + + for (let i = 0; i < result.length; i++) { + piece[i] = result[i]; + } + } + async _work() { const freeChunksIndex = getFreeChunksIndex({piece: this._piece, chunksCount: this._chunksCount}); const freeWire = this._swarm.getFreeWire(); @@ -103,12 +116,16 @@ class PieceLoader { } for (const chunkIndex of freeChunksIndex) { + log('_work::', {chunkIndex, freeChunksIndex}); this._piece[chunkIndex] = true; const offset = chunkIndex * this._chunkSize; let chunkSize = this._chunkSize; - if (chunkIndex === Object.keys(this._piece).length - 1) { + // Почему то все чанки получаются длиной последнего чанка - 2000 против 16000 + // Возможно дело в проверке ниже + + if (chunkIndex === this._chunksCount - 1) { chunkSize = this._lastSize; } @@ -121,20 +138,19 @@ class PieceLoader { return; } - return this._work(); + log(createChunksMap(this._chunksCount, this._piece)); + + this._work(); }) .catch((err) => { - log.error(`FAILED: id`, freeWire._id, `err`, err, `wires.length`, this._wires.length); - - if (String(err).indexOf(`wire is closed`) > -1) { - this._wires = this._wires.filter((it) => it !== freeWire); - } + log.error(`FAILED: id`, freeWire._id, `err`, err, `wires.length`, this._swarm._wires.length); + log('err', err, `work::catch::wires.length`, this._swarm._wires.length); delete this._piece[chunkIndex]; log('Chunk map after error', createChunksMap(this._chunksCount, this._piece)); - return this._work(); + this._work(); }); } } diff --git a/lib/swarm.js b/lib/swarm.js index be51820..5856047 100644 --- a/lib/swarm.js +++ b/lib/swarm.js @@ -54,8 +54,6 @@ const Swarm = class extends EventEmitter { return a._timeoutDiff - b._timeoutDiff; }); - targets = targets.slice(0, Math.round(targets.length / 2)) - return shuffle(targets)[0] || null; } @@ -99,9 +97,9 @@ const Swarm = class extends EventEmitter { if (String(err).indexOf(`wire is closed`) > -1) { // TODO: Отписать wire от всех событий. Иначе его может не забрать GC this._peerSwarm.remove(wire.peerAddress); - console.log(`pre removing`, this._wires.length) + log(`pre removing`, this._wires.length) this._wires = this._wires.filter((it) => it._wire !== wire); - console.log(`post removing`, this._wires.length) + log(`post removing`, this._wires.length) this.emit(this.EVENT_REMOVED_WIRE, wire); this.emit(this.EVENT_WIRES_CHANGE, this._wires); @@ -132,13 +130,13 @@ const Swarm = class extends EventEmitter { this.emit(this.EVENT_WIRES_CHANGE, this._wires); }); - wire.on(`close`, () => { - this._peerSwarm.remove(wire.peerAddress); - this._wires = this._wires.filter((it) => it !== wire); - - this.emit(this.EVENT_REMOVED_WIRE, wire); - this.emit(this.EVENT_WIRES_CHANGE, this._wires); - }) + // wire.on(`close`, () => { + // this._peerSwarm.remove(wire.peerAddress); + // this._wires = this._wires.filter((it) => it !== wire); + // + // this.emit(this.EVENT_REMOVED_WIRE, wire); + // this.emit(this.EVENT_WIRES_CHANGE, this._wires); + // }) } _removeAllWireListeners() { diff --git a/lib/utils.js b/lib/utils.js index b7d5857..48155c2 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -5,14 +5,15 @@ * @param {number} from Offset for file * @returns {{pieces: string[], firstOffset: number, lastLength: number}} */ -const slicePieces = ({pieces, pieceSize, file, from}) => { +const slicePieces = ({pieces, pieceSize, file, from, to}) => { const offset = file.offset + from; - const length = file.length - from; + const length = (to || file.length) - from; const offsetPieces = Math.floor(offset / pieceSize); const _pieces = pieces.slice(offsetPieces, Math.ceil((length + offset) / pieceSize)); const firstOffset = offset % pieceSize; - const lastLength = (length + firstOffset) % pieceSize || pieceSize; + // TODO: Разобраться, нужно ли это (+1) или нет + const lastLength = ((length + firstOffset) % pieceSize) + 1 || pieceSize; return { pieces: _pieces, diff --git a/lib/utils.spec.js b/lib/utils.spec.js index 9f1fd54..844b23d 100644 --- a/lib/utils.spec.js +++ b/lib/utils.spec.js @@ -13,6 +13,7 @@ describe('utils', () => { length: 10, }, from: 0, + to: 10, }); expect(typeof result.firstOffset).toEqual(`number`); @@ -30,6 +31,7 @@ describe('utils', () => { length: 10, }, from: 0, + to: 10, }); const output = { @@ -51,6 +53,7 @@ describe('utils', () => { length: 9, // 10 - 1 : piecesLength - offset }, from: 0, + to: 9, }); const output = { @@ -72,6 +75,7 @@ describe('utils', () => { length: 8, // 10 - 1 : piecesLength - offset }, from: 0, + to: 8, }); const output = { @@ -93,6 +97,7 @@ describe('utils', () => { length: 7, // 10 - 1 : piecesLength - offset }, from: 1, + to: 7, }); const output = { @@ -111,9 +116,10 @@ describe('utils', () => { pieceSize: 1, file: { offset: 2, - length: 6, + length: 6, // 10 - 1 : piecesLength - offset }, from: 1, + to: 6, }); const output = { @@ -135,6 +141,7 @@ describe('utils', () => { length: 12, // 10 - 1 : piecesLength - offset }, from: 1, + to: 12, }); const output = { @@ -157,6 +164,7 @@ describe('utils', () => { length: 11, // 10 - 1 : piecesLength - offset }, from: 1, + to: 11, }); const output = { @@ -178,6 +186,7 @@ describe('utils', () => { length: 11, // 10 - 1 : piecesLength - offset }, from: 1, + to: 11, }); const output = { @@ -189,5 +198,50 @@ describe('utils', () => { expect(result).toMatchObject(output); }); + describe('Check arg [to]', () => { + it('Should return correct result::Example 3::with file offset and not full', () => { + const pieces = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + const result = slicePieces({ + pieces, + pieceSize: 1, + file: { + offset: 1, + length: 8, // 10 - 1 : piecesLength - offset + }, + from: 0, + to: 7, + }); + + const output = { + firstOffset: 0, + lastLength: 1, + pieces: ['1', '2', '3', '4', '5', '6', '7'], + }; + + expect(result).toMatchObject(output); + }); + + it('Should return correct result::Example 8::with file offset and not zero from', () => { + const pieces = ['000', '111', '222', '333', '444', '555', '666', '777', '888', '999']; + const result = slicePieces({ + pieces, + pieceSize: 3, + file: { + offset: 2, + length: 11, // 10 - 1 : piecesLength - offset + }, + from: 1, + to: 7, + }); + + const output = { + firstOffset: 0, + lastLength: 3, + pieces: ['111', '222'], + }; + + expect(result).toMatchObject(output); + }); + }); }); }); diff --git a/lib/vendor/peer-wire-swarm.js b/lib/vendor/peer-wire-swarm.js index 9fd0ff0..a2ea352 100644 --- a/lib/vendor/peer-wire-swarm.js +++ b/lib/vendor/peer-wire-swarm.js @@ -13,7 +13,7 @@ var RECONNECT_WAIT = [1000, 5000, 15000]; var DEFAULT_SIZE = 100; const withLogs = (name, callback) => (...args) => { - console.log('peer-wire-swarm::Call::', name); + // console.log('peer-wire-swarm::Call::', name); return callback(...args); }; diff --git a/lib/wire.js b/lib/wire.js index f3de46a..e5b61db 100644 --- a/lib/wire.js +++ b/lib/wire.js @@ -41,17 +41,19 @@ const Wire = class extends EventEmitter { return; } + if (this._closed) { + reject(`closed`); + return; + } + const errorTimeoutId = setTimeout(() => { this._status = Status.FREE; this._requestsCount = this._requestsCount - 1; this._timeoutDiff = this._timeoutDiff + 100; - reject(this.EVENT_ERROR_TIMEOUT); + reject({err: this.EVENT_ERROR_TIMEOUT, t: this._timeoutDiff, r: this._requestsCount}); }, timeout + this._timeoutDiff); - if (this._closed) { - return; - } this._wire.request(pieceIndex, offset, chunkSize, (err, chunk) => { clearTimeout(errorTimeoutId);