'use strict'; const {Transform} = require('stream'); module.exports = { download(response, emitter, downloadBodySize) { let downloaded = 0; return new Transform({ transform(chunk, encoding, callback) { downloaded += chunk.length; const percent = downloadBodySize ? downloaded / downloadBodySize : 0; // Let `flush()` be responsible for emitting the last event if (percent < 1) { emitter.emit('downloadProgress', { percent, transferred: downloaded, total: downloadBodySize }); } callback(null, chunk); }, flush(callback) { emitter.emit('downloadProgress', { percent: 1, transferred: downloaded, total: downloadBodySize }); callback(); } }); }, upload(request, emitter, uploadBodySize) { const uploadEventFrequency = 150; let uploaded = 0; let progressInterval; emitter.emit('uploadProgress', { percent: 0, transferred: 0, total: uploadBodySize }); request.once('error', () => { clearInterval(progressInterval); }); request.once('response', () => { clearInterval(progressInterval); emitter.emit('uploadProgress', { percent: 1, transferred: uploaded, total: uploadBodySize }); }); request.once('socket', socket => { const onSocketConnect = () => { progressInterval = setInterval(() => { const lastUploaded = uploaded; /* istanbul ignore next: see #490 (occurs randomly!) */ const headersSize = request._header ? Buffer.byteLength(request._header) : 0; uploaded = socket.bytesWritten - headersSize; // Don't emit events with unchanged progress and // prevent last event from being emitted, because // it's emitted when `response` is emitted if (uploaded === lastUploaded || uploaded === uploadBodySize) { return; } emitter.emit('uploadProgress', { percent: uploadBodySize ? uploaded / uploadBodySize : 0, transferred: uploaded, total: uploadBodySize }); }, uploadEventFrequency); }; /* istanbul ignore next: hard to test */ if (socket.connecting) { socket.once('connect', onSocketConnect); } else if (socket.writable) { // The socket is being reused from pool, // so the connect event will not be emitted onSocketConnect(); } }); } };