1"use strict"; 2var __importDefault = (this && this.__importDefault) || function (mod) { 3 return (mod && mod.__esModule) ? mod : { "default": mod }; 4}; 5Object.defineProperty(exports, "__esModule", { value: true }); 6exports.DefaultFetcher = exports.BaseFetcher = void 0; 7const debug_1 = __importDefault(require("debug")); 8const fs_1 = __importDefault(require("fs")); 9const make_fetch_happen_1 = __importDefault(require("make-fetch-happen")); 10const util_1 = __importDefault(require("util")); 11const error_1 = require("./error"); 12const tmpfile_1 = require("./utils/tmpfile"); 13const log = (0, debug_1.default)('tuf:fetch'); 14class BaseFetcher { 15 // Download file from given URL. The file is downloaded to a temporary 16 // location and then passed to the given handler. The handler is responsible 17 // for moving the file to its final location. The temporary file is deleted 18 // after the handler returns. 19 async downloadFile(url, maxLength, handler) { 20 return (0, tmpfile_1.withTempFile)(async (tmpFile) => { 21 const reader = await this.fetch(url); 22 let numberOfBytesReceived = 0; 23 const fileStream = fs_1.default.createWriteStream(tmpFile); 24 // Read the stream a chunk at a time so that we can check 25 // the length of the file as we go 26 try { 27 for await (const chunk of reader) { 28 const bufferChunk = Buffer.from(chunk); 29 numberOfBytesReceived += bufferChunk.length; 30 if (numberOfBytesReceived > maxLength) { 31 throw new error_1.DownloadLengthMismatchError('Max length reached'); 32 } 33 await writeBufferToStream(fileStream, bufferChunk); 34 } 35 } 36 finally { 37 // Make sure we always close the stream 38 await util_1.default.promisify(fileStream.close).bind(fileStream)(); 39 } 40 return handler(tmpFile); 41 }); 42 } 43 // Download bytes from given URL. 44 async downloadBytes(url, maxLength) { 45 return this.downloadFile(url, maxLength, async (file) => { 46 const stream = fs_1.default.createReadStream(file); 47 const chunks = []; 48 for await (const chunk of stream) { 49 chunks.push(chunk); 50 } 51 return Buffer.concat(chunks); 52 }); 53 } 54} 55exports.BaseFetcher = BaseFetcher; 56class DefaultFetcher extends BaseFetcher { 57 constructor(options = {}) { 58 super(); 59 this.timeout = options.timeout; 60 this.retry = options.retry; 61 } 62 async fetch(url) { 63 log('GET %s', url); 64 const response = await (0, make_fetch_happen_1.default)(url, { 65 timeout: this.timeout, 66 retry: this.retry, 67 }); 68 if (!response.ok || !response?.body) { 69 throw new error_1.DownloadHTTPError('Failed to download', response.status); 70 } 71 return response.body; 72 } 73} 74exports.DefaultFetcher = DefaultFetcher; 75const writeBufferToStream = async (stream, buffer) => { 76 return new Promise((resolve, reject) => { 77 stream.write(buffer, (err) => { 78 if (err) { 79 reject(err); 80 } 81 resolve(true); 82 }); 83 }); 84}; 85