Summary: Should help to investigate further issues like https://fb.workplace.com/groups/flippersupport/permalink/1577911229356196/ Reviewed By: passy Differential Revision: D43692927 fbshipit-source-id: 723b051f748946c89ece85fb6c11c40705196fe4
146 lines
4.2 KiB
TypeScript
146 lines
4.2 KiB
TypeScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
*/
|
|
|
|
import {FlipperServerCommands, FlipperServerEvents, uuid} from 'flipper-common';
|
|
import {pathExists} from 'fs-extra';
|
|
import {promises, createWriteStream, ReadStream} from 'fs';
|
|
import axios from 'axios';
|
|
import http from 'http';
|
|
import https from 'https';
|
|
|
|
const {unlink} = promises;
|
|
|
|
// Adapter which forces node.js implementation for axios instead of browser implementation
|
|
// used by default in Electron. Node.js implementation is better, because it
|
|
// supports streams which can be used for direct downloading to disk.
|
|
const axiosHttpAdapter = require('axios/lib/adapters/http'); // eslint-disable-line import/no-commonjs
|
|
|
|
export const commandDownloadFileStartFactory =
|
|
(
|
|
emit: (
|
|
event: 'download-file-update',
|
|
payload: FlipperServerEvents['download-file-update'],
|
|
) => void,
|
|
): FlipperServerCommands['download-file-start'] =>
|
|
async (
|
|
url,
|
|
dest,
|
|
{method = 'GET', timeout, maxRedirects, headers, overwrite} = {},
|
|
) => {
|
|
const destExists = await pathExists(dest);
|
|
|
|
if (destExists) {
|
|
if (!overwrite) {
|
|
throw new Error(
|
|
'FlipperServerImpl -> executing "download-file" -> path already exists and overwrite set to false',
|
|
);
|
|
}
|
|
|
|
await unlink(dest);
|
|
}
|
|
|
|
console.debug('commandDownloadFileStartFactory -> start', {
|
|
http: {
|
|
usedSockets: Object.keys(http.globalAgent.sockets).length,
|
|
freeSocket: Object.keys(http.globalAgent.freeSockets).length,
|
|
maxSockets: http.globalAgent.maxSockets,
|
|
maxTotalSockets: http.globalAgent.maxTotalSockets,
|
|
},
|
|
https: {
|
|
usedSockets: Object.keys(https.globalAgent.sockets).length,
|
|
freeSocket: Object.keys(https.globalAgent.freeSockets).length,
|
|
maxSockets: https.globalAgent.maxSockets,
|
|
maxTotalSockets: https.globalAgent.maxTotalSockets,
|
|
},
|
|
});
|
|
|
|
const downloadId = uuid();
|
|
|
|
const response = await axios.request<ReadStream>({
|
|
method,
|
|
url,
|
|
responseType: 'stream',
|
|
adapter: axiosHttpAdapter,
|
|
timeout,
|
|
maxRedirects,
|
|
headers,
|
|
});
|
|
let totalSize = parseInt(response.headers['content-length'], 10);
|
|
if (Number.isNaN(totalSize)) {
|
|
totalSize = 0;
|
|
}
|
|
|
|
const writeStream = response.data.pipe(
|
|
createWriteStream(dest, {autoClose: true}),
|
|
);
|
|
let downloaded = 0;
|
|
response.data.on('data', (data: any) => {
|
|
downloaded += Buffer.byteLength(data);
|
|
emit('download-file-update', {
|
|
id: downloadId,
|
|
downloaded,
|
|
totalSize,
|
|
status: 'downloading',
|
|
});
|
|
});
|
|
|
|
response.data.on('error', (e: Error) => {
|
|
writeStream.destroy(e);
|
|
});
|
|
|
|
writeStream.on('finish', () => {
|
|
emit('download-file-update', {
|
|
id: downloadId,
|
|
downloaded,
|
|
totalSize,
|
|
status: 'success',
|
|
});
|
|
console.debug('commandDownloadFileStartFactory -> finish', {
|
|
http: {
|
|
usedSockets: Object.keys(http.globalAgent.sockets).length,
|
|
freeSocket: Object.keys(http.globalAgent.freeSockets).length,
|
|
},
|
|
https: {
|
|
usedSockets: Object.keys(https.globalAgent.sockets).length,
|
|
freeSocket: Object.keys(https.globalAgent.freeSockets).length,
|
|
},
|
|
});
|
|
});
|
|
|
|
writeStream.on('error', (e: Error) => {
|
|
response.data.destroy();
|
|
emit('download-file-update', {
|
|
id: downloadId,
|
|
downloaded,
|
|
totalSize,
|
|
status: 'error',
|
|
message: e.message,
|
|
stack: e.stack,
|
|
});
|
|
console.debug('commandDownloadFileStartFactory -> error', {
|
|
http: {
|
|
usedSockets: Object.keys(http.globalAgent.sockets).length,
|
|
freeSocket: Object.keys(http.globalAgent.freeSockets).length,
|
|
},
|
|
https: {
|
|
usedSockets: Object.keys(https.globalAgent.sockets).length,
|
|
freeSocket: Object.keys(https.globalAgent.freeSockets).length,
|
|
},
|
|
});
|
|
});
|
|
|
|
return {
|
|
id: downloadId,
|
|
headers: response.headers,
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
totalSize,
|
|
};
|
|
};
|