UNPKG

@appium/support

Version:

Support libs used across Appium packages

206 lines 8.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.uploadFile = uploadFile; exports.downloadFile = downloadFile; const lodash_1 = __importDefault(require("lodash")); const fs_1 = require("./fs"); const util_1 = require("./util"); const logger_1 = __importDefault(require("./logger")); const jsftp_1 = __importDefault(require("jsftp")); const timing_1 = require("./timing"); const axios_1 = __importDefault(require("axios")); const form_data_1 = __importDefault(require("form-data")); const DEFAULT_TIMEOUT_MS = 4 * 60 * 1000; /** * Uploads the given file to a remote location. HTTP(S) and FTP protocols are supported. */ async function uploadFile(localPath, remoteUri, uploadOptions = {}) { if (!(await fs_1.fs.exists(localPath))) { throw new Error(`'${localPath}' does not exist or is not accessible`); } const { isMetered = true } = uploadOptions; const url = new URL(remoteUri); const { size } = await fs_1.fs.stat(localPath); if (isMetered) { logger_1.default.info(`Uploading '${localPath}' of ${(0, util_1.toReadableSizeString)(size)} size to '${remoteUri}'`); } const timer = new timing_1.Timer().start(); if (isHttpUploadOptions(uploadOptions, url)) { if (!uploadOptions.fileFieldName) { uploadOptions.headers = { ...(lodash_1.default.isPlainObject(uploadOptions.headers) ? uploadOptions.headers : {}), 'Content-Length': size, }; } await uploadFileToHttp(fs_1.fs.createReadStream(localPath), url, uploadOptions); } else if (isFtpUploadOptions(uploadOptions, url)) { await uploadFileToFtp(fs_1.fs.createReadStream(localPath), url, uploadOptions); } else { throw new Error(`Cannot upload the file at '${localPath}' to '${remoteUri}'. ` + `Unsupported remote protocol '${url.protocol}'. ` + `Only http/https and ftp/ftps protocols are supported.`); } if (isMetered) { logger_1.default.info(`Uploaded '${localPath}' of ${(0, util_1.toReadableSizeString)(size)} size in ` + `${timer.getDuration().asSeconds.toFixed(3)}s`); } } /** * Downloads the given file via HTTP(S). * * @throws {Error} If download operation fails */ async function downloadFile(remoteUrl, dstPath, downloadOptions = {}) { const { isMetered = true, auth, timeout = DEFAULT_TIMEOUT_MS, headers } = downloadOptions; const requestOpts = { url: remoteUrl, responseType: 'stream', timeout, }; const axiosAuth = toAxiosAuth(auth); if (axiosAuth) { requestOpts.auth = axiosAuth; } if (lodash_1.default.isPlainObject(headers)) { requestOpts.headers = headers; } const timer = new timing_1.Timer().start(); let responseLength; try { const writer = fs_1.fs.createWriteStream(dstPath); const { data: responseStream, headers: responseHeaders } = await (0, axios_1.default)(requestOpts); responseLength = parseInt(String(responseHeaders['content-length'] ?? '0'), 10); responseStream.pipe(writer); await new Promise((resolve, reject) => { responseStream.once('error', reject); writer.once('finish', () => resolve()); writer.once('error', (e) => { responseStream.unpipe(writer); reject(e); }); }); } catch (err) { const message = err instanceof Error ? err.message : String(err); throw new Error(`Cannot download the file from ${remoteUrl}: ${message}`); } const { size } = await fs_1.fs.stat(dstPath); if (responseLength && size !== responseLength) { await fs_1.fs.rimraf(dstPath); throw new Error(`The size of the file downloaded from ${remoteUrl} (${size} bytes) ` + `differs from the one in Content-Length response header (${responseLength} bytes)`); } if (isMetered) { const secondsElapsed = timer.getDuration().asSeconds; logger_1.default.debug(`${remoteUrl} (${(0, util_1.toReadableSizeString)(size)}) ` + `has been downloaded to '${dstPath}' in ${secondsElapsed.toFixed(3)}s`); if (secondsElapsed >= 2) { const bytesPerSec = Math.floor(size / secondsElapsed); logger_1.default.debug(`Approximate download speed: ${(0, util_1.toReadableSizeString)(bytesPerSec)}/s`); } } } function toAxiosAuth(auth) { if (!auth || !lodash_1.default.isPlainObject(auth)) { return null; } const username = 'username' in auth ? auth.username : auth.user; const password = 'password' in auth ? auth.password : auth.pass; return username && password ? { username, password } : null; } async function uploadFileToHttp(localFileStream, parsedUri, uploadOptions = {}) { const { method = 'POST', timeout = DEFAULT_TIMEOUT_MS, headers, auth, fileFieldName = 'file', formFields, } = uploadOptions; const { href } = parsedUri; const requestOpts = { url: href, method, timeout, maxContentLength: Infinity, maxBodyLength: Infinity, }; const axiosAuth = toAxiosAuth(auth); if (axiosAuth) { requestOpts.auth = axiosAuth; } if (fileFieldName) { const form = new form_data_1.default(); if (formFields) { let pairs = []; if (lodash_1.default.isArray(formFields)) { pairs = formFields; } else if (lodash_1.default.isPlainObject(formFields)) { pairs = lodash_1.default.toPairs(formFields); } for (const [key, value] of pairs) { if (lodash_1.default.toLower(key) !== lodash_1.default.toLower(fileFieldName)) { form.append(key, value); } } } // AWS S3 POST upload requires this to be the last field; do not move before formFields. form.append(fileFieldName, localFileStream); requestOpts.headers = { ...(lodash_1.default.isPlainObject(headers) ? headers : {}), ...form.getHeaders(), }; requestOpts.data = form; } else { if (lodash_1.default.isPlainObject(headers)) { requestOpts.headers = headers; } requestOpts.data = localFileStream; } logger_1.default.debug(`Performing ${method} to ${href} with options (excluding data): ` + JSON.stringify(lodash_1.default.omit(requestOpts, ['data']))); const { status, statusText } = await (0, axios_1.default)(requestOpts); logger_1.default.info(`Server response: ${status} ${statusText}`); } async function uploadFileToFtp(localFileStream, parsedUri, uploadOptions = {}) { const { auth } = uploadOptions; const { protocol, hostname, port, pathname } = parsedUri; const ftpOpts = { host: hostname ?? '', port: port !== undefined && port !== '' ? lodash_1.default.parseInt(port, 10) : 21, }; if (auth?.user && auth?.pass) { ftpOpts.user = auth.user; ftpOpts.pass = auth.pass; } logger_1.default.debug(`${protocol.slice(0, -1)} upload options: ${JSON.stringify(ftpOpts)}`); return await new Promise((resolve, reject) => { new jsftp_1.default(ftpOpts).put(localFileStream, pathname, (err) => { if (err) { reject(err); } else { resolve(); } }); }); } function isHttpUploadOptions(opts, url) { try { return url.protocol === 'http:' || url.protocol === 'https:'; } catch { return false; } } /** Returns true if the URL is FTP, i.e. the options are for FTP upload. */ function isFtpUploadOptions(opts, url) { try { return url.protocol === 'ftp:'; } catch { return false; } } // #endregion //# sourceMappingURL=net.js.map