electron-updater 4.3.5 → 4.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/out/AppAdapter.js +19 -28
- package/out/AppAdapter.js.map +1 -1
- package/out/AppImageUpdater.js +103 -173
- package/out/AppImageUpdater.js.map +1 -1
- package/out/AppUpdater.d.ts +1 -3
- package/out/AppUpdater.js +517 -773
- package/out/AppUpdater.js.map +1 -1
- package/out/BaseUpdater.js +79 -103
- package/out/BaseUpdater.js.map +1 -1
- package/out/DownloadedUpdateHelper.js +150 -224
- package/out/DownloadedUpdateHelper.js.map +1 -1
- package/out/ElectronAppAdapter.js +35 -63
- package/out/ElectronAppAdapter.js.map +1 -1
- package/out/MacUpdater.d.ts +1 -1
- package/out/MacUpdater.js +132 -182
- package/out/MacUpdater.js.map +1 -1
- package/out/NoOpLogger.d.ts +6 -0
- package/out/NoOpLogger.js +19 -0
- package/out/NoOpLogger.js.map +1 -0
- package/out/NsisUpdater.js +199 -331
- package/out/NsisUpdater.js.map +1 -1
- package/out/differentialDownloader/DataSplitter.js +180 -252
- package/out/differentialDownloader/DataSplitter.js.map +1 -1
- package/out/differentialDownloader/DifferentialDownloader.d.ts +3 -0
- package/out/differentialDownloader/DifferentialDownloader.js +243 -308
- package/out/differentialDownloader/DifferentialDownloader.js.map +1 -1
- package/out/differentialDownloader/FileWithEmbeddedBlockMapDifferentialDownloader.js +29 -65
- package/out/differentialDownloader/FileWithEmbeddedBlockMapDifferentialDownloader.js.map +1 -1
- package/out/differentialDownloader/GenericDifferentialDownloader.js +7 -21
- package/out/differentialDownloader/GenericDifferentialDownloader.js.map +1 -1
- package/out/differentialDownloader/ProgressDifferentialDownloadCallbackTransform.d.ts +32 -0
- package/out/differentialDownloader/ProgressDifferentialDownloadCallbackTransform.js +90 -0
- package/out/differentialDownloader/ProgressDifferentialDownloadCallbackTransform.js.map +1 -0
- package/out/differentialDownloader/downloadPlanBuilder.js +99 -124
- package/out/differentialDownloader/downloadPlanBuilder.js.map +1 -1
- package/out/differentialDownloader/multipleRangeDownloader.js +99 -148
- package/out/differentialDownloader/multipleRangeDownloader.js.map +1 -1
- package/out/electronHttpExecutor.js +73 -106
- package/out/electronHttpExecutor.js.map +1 -1
- package/out/event.d.ts +35 -0
- package/out/event.js +39 -0
- package/out/event.js.map +1 -0
- package/out/log.d.ts +11 -0
- package/out/log.js +19 -0
- package/out/log.js.map +1 -0
- package/out/main.d.ts +1 -4
- package/out/main.js +60 -212
- package/out/main.js.map +1 -1
- package/out/providerFactory.js +49 -105
- package/out/providerFactory.js.map +1 -1
- package/out/providers/BintrayProvider.d.ts +2 -2
- package/out/providers/BintrayProvider.js +40 -95
- package/out/providers/BintrayProvider.js.map +1 -1
- package/out/providers/GenericProvider.d.ts +2 -2
- package/out/providers/GenericProvider.js +46 -80
- package/out/providers/GenericProvider.js.map +1 -1
- package/out/providers/GitHubProvider.d.ts +2 -2
- package/out/providers/GitHubProvider.js +122 -201
- package/out/providers/GitHubProvider.js.map +1 -1
- package/out/providers/PrivateGitHubProvider.js +87 -165
- package/out/providers/PrivateGitHubProvider.js.map +1 -1
- package/out/providers/Provider.js +122 -173
- package/out/providers/Provider.js.map +1 -1
- package/out/util.d.ts +5 -0
- package/out/util.js +42 -0
- package/out/util.js.map +1 -0
- package/out/windowsExecutableCodeSignatureVerifier.js +99 -139
- package/out/windowsExecutableCodeSignatureVerifier.js.map +1 -1
- package/package.json +33 -31
|
@@ -1,326 +1,261 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.DifferentialDownloader = void 0;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return data;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function _fsExtra() {
|
|
19
|
-
const data = require("fs-extra");
|
|
20
|
-
|
|
21
|
-
_fsExtra = function () {
|
|
22
|
-
return data;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
return data;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
var _fs = require("fs");
|
|
29
|
-
|
|
30
|
-
function _DataSplitter() {
|
|
31
|
-
const data = require("./DataSplitter");
|
|
32
|
-
|
|
33
|
-
_DataSplitter = function () {
|
|
34
|
-
return data;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
return data;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function _url() {
|
|
41
|
-
const data = require("url");
|
|
42
|
-
|
|
43
|
-
_url = function () {
|
|
44
|
-
return data;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
return data;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function _downloadPlanBuilder() {
|
|
51
|
-
const data = require("./downloadPlanBuilder");
|
|
52
|
-
|
|
53
|
-
_downloadPlanBuilder = function () {
|
|
54
|
-
return data;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return data;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function _multipleRangeDownloader() {
|
|
61
|
-
const data = require("./multipleRangeDownloader");
|
|
62
|
-
|
|
63
|
-
_multipleRangeDownloader = function () {
|
|
64
|
-
return data;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
return data;
|
|
68
|
-
}
|
|
69
|
-
|
|
4
|
+
const builder_util_runtime_1 = require("builder-util-runtime");
|
|
5
|
+
const fs_extra_1 = require("fs-extra");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const DataSplitter_1 = require("./DataSplitter");
|
|
8
|
+
const url_1 = require("url");
|
|
9
|
+
const downloadPlanBuilder_1 = require("./downloadPlanBuilder");
|
|
10
|
+
const multipleRangeDownloader_1 = require("./multipleRangeDownloader");
|
|
11
|
+
const ProgressDifferentialDownloadCallbackTransform_1 = require("./ProgressDifferentialDownloadCallbackTransform");
|
|
70
12
|
class DifferentialDownloader {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
createRequestOptions() {
|
|
81
|
-
const result = {
|
|
82
|
-
headers: { ...this.options.requestHeaders,
|
|
83
|
-
accept: "*/*"
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
(0, _builderUtilRuntime().configureRequestUrl)(this.options.newUrl, result); // user-agent, cache-control and other common options
|
|
87
|
-
|
|
88
|
-
(0, _builderUtilRuntime().configureRequestOptions)(result);
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
doDownload(oldBlockMap, newBlockMap) {
|
|
93
|
-
// we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it
|
|
94
|
-
if (oldBlockMap.version !== newBlockMap.version) {
|
|
95
|
-
throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const logger = this.logger;
|
|
99
|
-
const operations = (0, _downloadPlanBuilder().computeOperations)(oldBlockMap, newBlockMap, logger);
|
|
100
|
-
|
|
101
|
-
if (logger.debug != null) {
|
|
102
|
-
logger.debug(JSON.stringify(operations, null, 2));
|
|
13
|
+
// noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
|
|
14
|
+
constructor(blockAwareFileInfo, httpExecutor, options) {
|
|
15
|
+
this.blockAwareFileInfo = blockAwareFileInfo;
|
|
16
|
+
this.httpExecutor = httpExecutor;
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.fileMetadataBuffer = null;
|
|
19
|
+
this.logger = options.logger;
|
|
103
20
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
21
|
+
createRequestOptions() {
|
|
22
|
+
const result = {
|
|
23
|
+
headers: {
|
|
24
|
+
...this.options.requestHeaders,
|
|
25
|
+
accept: "*/*",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
builder_util_runtime_1.configureRequestUrl(this.options.newUrl, result);
|
|
29
|
+
// user-agent, cache-control and other common options
|
|
30
|
+
builder_util_runtime_1.configureRequestOptions(result);
|
|
31
|
+
return result;
|
|
116
32
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`);
|
|
125
|
-
return this.downloadFile(operations);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
downloadFile(tasks) {
|
|
129
|
-
const fdList = [];
|
|
130
|
-
|
|
131
|
-
const closeFiles = () => {
|
|
132
|
-
return Promise.all(fdList.map(openedFile => {
|
|
133
|
-
return (0, _fsExtra().close)(openedFile.descriptor).catch(e => {
|
|
134
|
-
this.logger.error(`cannot close file "${openedFile.path}": ${e}`);
|
|
135
|
-
});
|
|
136
|
-
}));
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
return this.doDownloadFile(tasks, fdList).then(closeFiles).catch(e => {
|
|
140
|
-
// then must be after catch here (since then always throws error)
|
|
141
|
-
return closeFiles().catch(closeFilesError => {
|
|
142
|
-
// closeFiles never throw error, but just to be sure
|
|
143
|
-
try {
|
|
144
|
-
this.logger.error(`cannot close files: ${closeFilesError}`);
|
|
145
|
-
} catch (errorOnLog) {
|
|
146
|
-
try {
|
|
147
|
-
console.error(errorOnLog);
|
|
148
|
-
} catch (ignored) {// ok, give up and ignore error
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
throw e;
|
|
153
|
-
}).then(() => {
|
|
154
|
-
throw e;
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async doDownloadFile(tasks, fdList) {
|
|
160
|
-
const oldFileFd = await (0, _fsExtra().open)(this.options.oldFile, "r");
|
|
161
|
-
fdList.push({
|
|
162
|
-
descriptor: oldFileFd,
|
|
163
|
-
path: this.options.oldFile
|
|
164
|
-
});
|
|
165
|
-
const newFileFd = await (0, _fsExtra().open)(this.options.newFile, "w");
|
|
166
|
-
fdList.push({
|
|
167
|
-
descriptor: newFileFd,
|
|
168
|
-
path: this.options.newFile
|
|
169
|
-
});
|
|
170
|
-
const fileOut = (0, _fs.createWriteStream)(this.options.newFile, {
|
|
171
|
-
fd: newFileFd
|
|
172
|
-
});
|
|
173
|
-
await new Promise((resolve, reject) => {
|
|
174
|
-
const streams = [];
|
|
175
|
-
const digestTransform = new (_builderUtilRuntime().DigestTransform)(this.blockAwareFileInfo.sha512); // to simply debug, do manual validation to allow file to be fully written
|
|
176
|
-
|
|
177
|
-
digestTransform.isValidateOnEnd = false;
|
|
178
|
-
streams.push(digestTransform); // noinspection JSArrowFunctionCanBeReplacedWithShorthand
|
|
179
|
-
|
|
180
|
-
fileOut.on("finish", () => {
|
|
181
|
-
fileOut.close(() => {
|
|
182
|
-
// remove from fd list because closed successfully
|
|
183
|
-
fdList.splice(1, 1);
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
digestTransform.validate();
|
|
187
|
-
} catch (e) {
|
|
188
|
-
reject(e);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
resolve();
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
streams.push(fileOut);
|
|
196
|
-
let lastStream = null;
|
|
197
|
-
|
|
198
|
-
for (const stream of streams) {
|
|
199
|
-
stream.on("error", reject);
|
|
200
|
-
|
|
201
|
-
if (lastStream == null) {
|
|
202
|
-
lastStream = stream;
|
|
203
|
-
} else {
|
|
204
|
-
lastStream = lastStream.pipe(stream);
|
|
33
|
+
doDownload(oldBlockMap, newBlockMap) {
|
|
34
|
+
// we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it
|
|
35
|
+
if (oldBlockMap.version !== newBlockMap.version) {
|
|
36
|
+
throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`);
|
|
205
37
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (this.options.isUseMultipleRangeRequest) {
|
|
212
|
-
w = (0, _multipleRangeDownloader().executeTasksUsingMultipleRangeRequests)(this, tasks, firstStream, oldFileFd, reject);
|
|
213
|
-
w(0);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
let downloadOperationCount = 0;
|
|
218
|
-
let actualUrl = null;
|
|
219
|
-
this.logger.info(`Differential download: ${this.options.newUrl}`);
|
|
220
|
-
const requestOptions = this.createRequestOptions();
|
|
221
|
-
requestOptions.redirect = "manual";
|
|
222
|
-
|
|
223
|
-
w = index => {
|
|
224
|
-
if (index >= tasks.length) {
|
|
225
|
-
if (this.fileMetadataBuffer != null) {
|
|
226
|
-
firstStream.write(this.fileMetadataBuffer);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
firstStream.end();
|
|
230
|
-
return;
|
|
38
|
+
const logger = this.logger;
|
|
39
|
+
const operations = downloadPlanBuilder_1.computeOperations(oldBlockMap, newBlockMap, logger);
|
|
40
|
+
if (logger.debug != null) {
|
|
41
|
+
logger.debug(JSON.stringify(operations, null, 2));
|
|
231
42
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
43
|
+
let downloadSize = 0;
|
|
44
|
+
let copySize = 0;
|
|
45
|
+
for (const operation of operations) {
|
|
46
|
+
const length = operation.end - operation.start;
|
|
47
|
+
if (operation.kind === downloadPlanBuilder_1.OperationKind.DOWNLOAD) {
|
|
48
|
+
downloadSize += length;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
copySize += length;
|
|
52
|
+
}
|
|
238
53
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const debug = this.logger.debug;
|
|
243
|
-
|
|
244
|
-
if (debug != null) {
|
|
245
|
-
debug(`download range: ${range}`);
|
|
54
|
+
const newSize = this.blockAwareFileInfo.size;
|
|
55
|
+
if (downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length) !== newSize) {
|
|
56
|
+
throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`);
|
|
246
57
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
58
|
+
logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`);
|
|
59
|
+
return this.downloadFile(operations);
|
|
60
|
+
}
|
|
61
|
+
downloadFile(tasks) {
|
|
62
|
+
const fdList = [];
|
|
63
|
+
const closeFiles = () => {
|
|
64
|
+
return Promise.all(fdList.map(openedFile => {
|
|
65
|
+
return fs_extra_1.close(openedFile.descriptor).catch(e => {
|
|
66
|
+
this.logger.error(`cannot close file "${openedFile.path}": ${e}`);
|
|
67
|
+
});
|
|
68
|
+
}));
|
|
69
|
+
};
|
|
70
|
+
return this.doDownloadFile(tasks, fdList)
|
|
71
|
+
.then(closeFiles)
|
|
72
|
+
.catch(e => {
|
|
73
|
+
// then must be after catch here (since then always throws error)
|
|
74
|
+
return closeFiles()
|
|
75
|
+
.catch(closeFilesError => {
|
|
76
|
+
// closeFiles never throw error, but just to be sure
|
|
77
|
+
try {
|
|
78
|
+
this.logger.error(`cannot close files: ${closeFilesError}`);
|
|
79
|
+
}
|
|
80
|
+
catch (errorOnLog) {
|
|
81
|
+
try {
|
|
82
|
+
console.error(errorOnLog);
|
|
83
|
+
}
|
|
84
|
+
catch (ignored) {
|
|
85
|
+
// ok, give up and ignore error
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw e;
|
|
89
|
+
})
|
|
90
|
+
.then(() => {
|
|
91
|
+
throw e;
|
|
92
|
+
});
|
|
265
93
|
});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
94
|
+
}
|
|
95
|
+
async doDownloadFile(tasks, fdList) {
|
|
96
|
+
const oldFileFd = await fs_extra_1.open(this.options.oldFile, "r");
|
|
97
|
+
fdList.push({ descriptor: oldFileFd, path: this.options.oldFile });
|
|
98
|
+
const newFileFd = await fs_extra_1.open(this.options.newFile, "w");
|
|
99
|
+
fdList.push({ descriptor: newFileFd, path: this.options.newFile });
|
|
100
|
+
const fileOut = fs_1.createWriteStream(this.options.newFile, { fd: newFileFd });
|
|
101
|
+
await new Promise((resolve, reject) => {
|
|
102
|
+
const streams = [];
|
|
103
|
+
// Create our download info transformer if we have one
|
|
104
|
+
let downloadInfoTransform = undefined;
|
|
105
|
+
if (!this.options.isUseMultipleRangeRequest && this.options.onProgress) {
|
|
106
|
+
// TODO: Does not support multiple ranges (someone feel free to PR this!)
|
|
107
|
+
const expectedByteCounts = [];
|
|
108
|
+
let grandTotalBytes = 0;
|
|
109
|
+
for (const task of tasks) {
|
|
110
|
+
if (task.kind === downloadPlanBuilder_1.OperationKind.DOWNLOAD) {
|
|
111
|
+
expectedByteCounts.push(task.end - task.start);
|
|
112
|
+
grandTotalBytes += task.end - task.start;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const progressDifferentialDownloadInfo = {
|
|
116
|
+
expectedByteCounts: expectedByteCounts,
|
|
117
|
+
grandTotal: grandTotalBytes,
|
|
118
|
+
};
|
|
119
|
+
downloadInfoTransform = new ProgressDifferentialDownloadCallbackTransform_1.ProgressDifferentialDownloadCallbackTransform(progressDifferentialDownloadInfo, this.options.cancellationToken, this.options.onProgress);
|
|
120
|
+
streams.push(downloadInfoTransform);
|
|
121
|
+
}
|
|
122
|
+
const digestTransform = new builder_util_runtime_1.DigestTransform(this.blockAwareFileInfo.sha512);
|
|
123
|
+
// to simply debug, do manual validation to allow file to be fully written
|
|
124
|
+
digestTransform.isValidateOnEnd = false;
|
|
125
|
+
streams.push(digestTransform);
|
|
126
|
+
// noinspection JSArrowFunctionCanBeReplacedWithShorthand
|
|
127
|
+
fileOut.on("finish", () => {
|
|
128
|
+
;
|
|
129
|
+
fileOut.close(() => {
|
|
130
|
+
// remove from fd list because closed successfully
|
|
131
|
+
fdList.splice(1, 1);
|
|
132
|
+
try {
|
|
133
|
+
digestTransform.validate();
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
reject(e);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
resolve(undefined);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
streams.push(fileOut);
|
|
143
|
+
let lastStream = null;
|
|
144
|
+
for (const stream of streams) {
|
|
145
|
+
stream.on("error", reject);
|
|
146
|
+
if (lastStream == null) {
|
|
147
|
+
lastStream = stream;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
lastStream = lastStream.pipe(stream);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const firstStream = streams[0];
|
|
154
|
+
let w;
|
|
155
|
+
if (this.options.isUseMultipleRangeRequest) {
|
|
156
|
+
w = multipleRangeDownloader_1.executeTasksUsingMultipleRangeRequests(this, tasks, firstStream, oldFileFd, reject);
|
|
157
|
+
w(0);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
let downloadOperationCount = 0;
|
|
161
|
+
let actualUrl = null;
|
|
162
|
+
this.logger.info(`Differential download: ${this.options.newUrl}`);
|
|
163
|
+
const requestOptions = this.createRequestOptions();
|
|
164
|
+
requestOptions.redirect = "manual";
|
|
165
|
+
w = (index) => {
|
|
166
|
+
var _a, _b;
|
|
167
|
+
if (index >= tasks.length) {
|
|
168
|
+
if (this.fileMetadataBuffer != null) {
|
|
169
|
+
firstStream.write(this.fileMetadataBuffer);
|
|
170
|
+
}
|
|
171
|
+
firstStream.end();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const operation = tasks[index++];
|
|
175
|
+
if (operation.kind === downloadPlanBuilder_1.OperationKind.COPY) {
|
|
176
|
+
// We are copying, let's not send status updates to the UI
|
|
177
|
+
if (downloadInfoTransform) {
|
|
178
|
+
downloadInfoTransform.beginFileCopy();
|
|
179
|
+
}
|
|
180
|
+
DataSplitter_1.copyData(operation, firstStream, oldFileFd, reject, () => w(index));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const range = `bytes=${operation.start}-${operation.end - 1}`;
|
|
184
|
+
requestOptions.headers.range = range;
|
|
185
|
+
(_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug) === null || _b === void 0 ? void 0 : _b.call(_a, `download range: ${range}`);
|
|
186
|
+
// We are starting to download
|
|
187
|
+
if (downloadInfoTransform) {
|
|
188
|
+
downloadInfoTransform.beginRangeDownload();
|
|
189
|
+
}
|
|
190
|
+
const request = this.httpExecutor.createRequest(requestOptions, response => {
|
|
191
|
+
// Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.
|
|
192
|
+
if (response.statusCode >= 400) {
|
|
193
|
+
reject(builder_util_runtime_1.createHttpError(response));
|
|
194
|
+
}
|
|
195
|
+
response.pipe(firstStream, {
|
|
196
|
+
end: false,
|
|
197
|
+
});
|
|
198
|
+
response.once("end", () => {
|
|
199
|
+
// Pass on that we are downloading a segment
|
|
200
|
+
if (downloadInfoTransform) {
|
|
201
|
+
downloadInfoTransform.endRangeDownload();
|
|
202
|
+
}
|
|
203
|
+
if (++downloadOperationCount === 100) {
|
|
204
|
+
downloadOperationCount = 0;
|
|
205
|
+
setTimeout(() => w(index), 1000);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
w(index);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
request.on("redirect", (statusCode, method, redirectUrl) => {
|
|
213
|
+
this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`);
|
|
214
|
+
actualUrl = redirectUrl;
|
|
215
|
+
builder_util_runtime_1.configureRequestUrl(new url_1.URL(actualUrl), requestOptions);
|
|
216
|
+
request.followRedirect();
|
|
217
|
+
});
|
|
218
|
+
this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
|
|
219
|
+
request.end();
|
|
220
|
+
};
|
|
221
|
+
w(0);
|
|
271
222
|
});
|
|
272
|
-
this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
|
|
273
|
-
request.end();
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
w(0);
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
async readRemoteBytes(start, endInclusive) {
|
|
281
|
-
const buffer = Buffer.allocUnsafe(endInclusive + 1 - start);
|
|
282
|
-
const requestOptions = this.createRequestOptions();
|
|
283
|
-
requestOptions.headers.range = `bytes=${start}-${endInclusive}`;
|
|
284
|
-
let position = 0;
|
|
285
|
-
await this.request(requestOptions, chunk => {
|
|
286
|
-
chunk.copy(buffer, position);
|
|
287
|
-
position += chunk.length;
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
if (position !== buffer.length) {
|
|
291
|
-
throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`);
|
|
292
223
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
224
|
+
async readRemoteBytes(start, endInclusive) {
|
|
225
|
+
const buffer = Buffer.allocUnsafe(endInclusive + 1 - start);
|
|
226
|
+
const requestOptions = this.createRequestOptions();
|
|
227
|
+
requestOptions.headers.range = `bytes=${start}-${endInclusive}`;
|
|
228
|
+
let position = 0;
|
|
229
|
+
await this.request(requestOptions, chunk => {
|
|
230
|
+
chunk.copy(buffer, position);
|
|
231
|
+
position += chunk.length;
|
|
232
|
+
});
|
|
233
|
+
if (position !== buffer.length) {
|
|
234
|
+
throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`);
|
|
302
235
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
236
|
+
return buffer;
|
|
237
|
+
}
|
|
238
|
+
request(requestOptions, dataHandler) {
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
const request = this.httpExecutor.createRequest(requestOptions, response => {
|
|
241
|
+
if (!multipleRangeDownloader_1.checkIsRangesSupported(response, reject)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
response.on("data", dataHandler);
|
|
245
|
+
response.on("end", () => resolve());
|
|
246
|
+
});
|
|
247
|
+
this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
|
|
248
|
+
request.end();
|
|
249
|
+
});
|
|
250
|
+
}
|
|
312
251
|
}
|
|
313
|
-
|
|
314
252
|
exports.DifferentialDownloader = DifferentialDownloader;
|
|
315
|
-
|
|
316
253
|
function formatBytes(value, symbol = " KB") {
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
254
|
+
return new Intl.NumberFormat("en").format((value / 1024).toFixed(2)) + symbol;
|
|
255
|
+
}
|
|
256
|
+
// safety
|
|
321
257
|
function removeQuery(url) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
// __ts-babel@6.0.4
|
|
258
|
+
const index = url.indexOf("?");
|
|
259
|
+
return index < 0 ? url : url.substring(0, index);
|
|
260
|
+
}
|
|
326
261
|
//# sourceMappingURL=DifferentialDownloader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/differentialDownloader/DifferentialDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import { BlockMapDataHolder, createHttpError, DigestTransform, HttpExecutor, configureRequestUrl, configureRequestOptions } from \"builder-util-runtime\"\nimport { BlockMap } from \"builder-util-runtime/out/blockMapApi\"\nimport { close, open } from \"fs-extra\"\nimport { createWriteStream } from \"fs\"\nimport { OutgoingHttpHeaders, RequestOptions } from \"http\"\nimport { Logger } from \"../main\"\nimport { copyData } from \"./DataSplitter\"\nimport { URL } from \"url\"\nimport { computeOperations, Operation, OperationKind } from \"./downloadPlanBuilder\"\nimport { checkIsRangesSupported, executeTasksUsingMultipleRangeRequests } from \"./multipleRangeDownloader\"\n\nexport interface DifferentialDownloaderOptions {\n readonly oldFile: string\n readonly newUrl: URL\n readonly logger: Logger\n readonly newFile: string\n\n readonly requestHeaders: OutgoingHttpHeaders | null\n\n readonly isUseMultipleRangeRequest?: boolean\n}\n\nexport abstract class DifferentialDownloader {\n fileMetadataBuffer: Buffer | null = null\n\n private readonly logger: Logger\n\n // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected\n constructor(protected readonly blockAwareFileInfo: BlockMapDataHolder, readonly httpExecutor: HttpExecutor<any>, readonly options: DifferentialDownloaderOptions) {\n this.logger = options.logger\n }\n\n createRequestOptions(): RequestOptions {\n const result = {\n headers: {\n ...this.options.requestHeaders,\n accept: \"*/*\",\n },\n }\n configureRequestUrl(this.options.newUrl, result)\n // user-agent, cache-control and other common options\n configureRequestOptions(result)\n return result\n }\n\n protected doDownload(oldBlockMap: BlockMap, newBlockMap: BlockMap): Promise<any> {\n // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it\n if (oldBlockMap.version !== newBlockMap.version) {\n throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`)\n }\n\n const logger = this.logger\n const operations = computeOperations(oldBlockMap, newBlockMap, logger)\n if (logger.debug != null) {\n logger.debug(JSON.stringify(operations, null, 2))\n }\n\n let downloadSize = 0\n let copySize = 0\n for (const operation of operations) {\n const length = operation.end - operation.start\n if (operation.kind === OperationKind.DOWNLOAD) {\n downloadSize += length\n }\n else {\n copySize += length\n }\n }\n\n const newSize = this.blockAwareFileInfo.size\n if ((downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length)) !== newSize) {\n throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`)\n }\n\n logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`)\n\n return this.downloadFile(operations)\n }\n\n private downloadFile(tasks: Array<Operation>): Promise<any> {\n const fdList: Array<OpenedFile> = []\n const closeFiles = (): Promise<Array<void>> => {\n return Promise.all(fdList.map(openedFile => {\n return close(openedFile.descriptor)\n .catch(e => {\n this.logger.error(`cannot close file \"${openedFile.path}\": ${e}`)\n })\n }))\n }\n return this.doDownloadFile(tasks, fdList)\n .then(closeFiles)\n .catch(e => {\n // then must be after catch here (since then always throws error)\n return closeFiles()\n .catch(closeFilesError => {\n // closeFiles never throw error, but just to be sure\n try {\n this.logger.error(`cannot close files: ${closeFilesError}`)\n }\n catch (errorOnLog) {\n try {\n console.error(errorOnLog)\n }\n catch (ignored) {\n // ok, give up and ignore error\n }\n }\n throw e\n })\n .then(() => {\n throw e\n })\n })\n }\n\n private async doDownloadFile(tasks: Array<Operation>, fdList: Array<OpenedFile>): Promise<any> {\n const oldFileFd = await open(this.options.oldFile, \"r\")\n fdList.push({descriptor: oldFileFd, path: this.options.oldFile})\n const newFileFd = await open(this.options.newFile, \"w\")\n fdList.push({descriptor: newFileFd, path: this.options.newFile})\n const fileOut = createWriteStream(this.options.newFile, {fd: newFileFd})\n await new Promise((resolve, reject) => {\n const streams: Array<any> = []\n const digestTransform = new DigestTransform(this.blockAwareFileInfo.sha512)\n // to simply debug, do manual validation to allow file to be fully written\n digestTransform.isValidateOnEnd = false\n streams.push(digestTransform)\n\n // noinspection JSArrowFunctionCanBeReplacedWithShorthand\n fileOut.on(\"finish\", () => {\n (fileOut.close as any)(() => {\n // remove from fd list because closed successfully\n fdList.splice(1, 1)\n try {\n digestTransform.validate()\n }\n catch (e) {\n reject(e)\n return\n }\n\n resolve()\n })\n })\n\n streams.push(fileOut)\n\n let lastStream = null\n for (const stream of streams) {\n stream.on(\"error\", reject)\n if (lastStream == null) {\n lastStream = stream\n }\n else {\n lastStream = lastStream.pipe(stream)\n }\n }\n\n const firstStream = streams[0]\n\n let w: any\n if (this.options.isUseMultipleRangeRequest) {\n w = executeTasksUsingMultipleRangeRequests(this, tasks, firstStream, oldFileFd, reject)\n w(0)\n return\n }\n\n let downloadOperationCount = 0\n let actualUrl: string | null = null\n this.logger.info(`Differential download: ${this.options.newUrl}`)\n\n const requestOptions = this.createRequestOptions();\n (requestOptions as any).redirect = \"manual\"\n\n w = (index: number): void => {\n if (index >= tasks.length) {\n if (this.fileMetadataBuffer != null) {\n firstStream.write(this.fileMetadataBuffer)\n }\n firstStream.end()\n return\n }\n\n const operation = tasks[index++]\n if (operation.kind === OperationKind.COPY) {\n copyData(operation, firstStream, oldFileFd, reject, () => w(index))\n return\n }\n\n const range = `bytes=${operation.start}-${operation.end - 1}`\n requestOptions.headers!!.range = range\n\n const debug = this.logger.debug\n if (debug != null) {\n debug(`download range: ${range}`)\n }\n\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n // Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.\n if (response.statusCode >= 400) {\n reject(createHttpError(response))\n }\n\n response.pipe(firstStream, {\n end: false\n })\n response.once(\"end\", () => {\n if (++downloadOperationCount === 100) {\n downloadOperationCount = 0\n setTimeout(() => w(index), 1000)\n }\n else {\n w(index)\n }\n })\n })\n request.on(\"redirect\", (statusCode: number, method: string, redirectUrl: string) => {\n this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`)\n actualUrl = redirectUrl\n configureRequestUrl(new URL(actualUrl), requestOptions)\n request.followRedirect()\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n }\n\n w(0)\n })\n }\n\n protected async readRemoteBytes(start: number, endInclusive: number): Promise<Buffer> {\n const buffer = Buffer.allocUnsafe((endInclusive + 1) - start)\n const requestOptions = this.createRequestOptions()\n requestOptions.headers!!.range = `bytes=${start}-${endInclusive}`\n let position = 0\n await this.request(requestOptions, chunk => {\n chunk.copy(buffer, position)\n position += chunk.length\n })\n\n if (position !== buffer.length) {\n throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`)\n }\n return buffer\n }\n\n private request(requestOptions: RequestOptions, dataHandler: (chunk: Buffer) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n if (!checkIsRangesSupported(response, reject)) {\n return\n }\n\n response.on(\"data\", dataHandler)\n response.on(\"end\", () => resolve())\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n })\n }\n}\n\nfunction formatBytes(value: number, symbol = \" KB\"): string {\n return new Intl.NumberFormat(\"en\").format((value / 1024).toFixed(2) as any) + symbol\n}\n\n// safety\nfunction removeQuery(url: string): string {\n const index = url.indexOf(\"?\")\n return index < 0 ? url : url.substring(0, index)\n}\n\ninterface OpenedFile {\n readonly descriptor: number\n readonly path: string\n}"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"DifferentialDownloader.js","sourceRoot":"","sources":["../../src/differentialDownloader/DifferentialDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import { BlockMapDataHolder, createHttpError, DigestTransform, HttpExecutor, configureRequestUrl, configureRequestOptions } from \"builder-util-runtime\"\nimport { BlockMap } from \"builder-util-runtime/out/blockMapApi\"\nimport { close, open } from \"fs-extra\"\nimport { createWriteStream } from \"fs\"\nimport { OutgoingHttpHeaders, RequestOptions } from \"http\"\nimport { ProgressInfo, CancellationToken } from \"builder-util-runtime\"\nimport { Logger } from \"../main\"\nimport { copyData } from \"./DataSplitter\"\nimport { URL } from \"url\"\nimport { computeOperations, Operation, OperationKind } from \"./downloadPlanBuilder\"\nimport { checkIsRangesSupported, executeTasksUsingMultipleRangeRequests } from \"./multipleRangeDownloader\"\nimport { ProgressDifferentialDownloadCallbackTransform, ProgressDifferentialDownloadInfo } from \"./ProgressDifferentialDownloadCallbackTransform\"\n\nexport interface DifferentialDownloaderOptions {\n readonly oldFile: string\n readonly newUrl: URL\n readonly logger: Logger\n readonly newFile: string\n\n readonly requestHeaders: OutgoingHttpHeaders | null\n\n readonly isUseMultipleRangeRequest?: boolean\n\n readonly cancellationToken: CancellationToken\n onProgress?: (progress: ProgressInfo) => void\n}\n\nexport abstract class DifferentialDownloader {\n fileMetadataBuffer: Buffer | null = null\n\n private readonly logger: Logger\n\n // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected\n constructor(protected readonly blockAwareFileInfo: BlockMapDataHolder, readonly httpExecutor: HttpExecutor<any>, readonly options: DifferentialDownloaderOptions) {\n this.logger = options.logger\n }\n\n createRequestOptions(): RequestOptions {\n const result = {\n headers: {\n ...this.options.requestHeaders,\n accept: \"*/*\",\n },\n }\n configureRequestUrl(this.options.newUrl, result)\n // user-agent, cache-control and other common options\n configureRequestOptions(result)\n return result\n }\n\n protected doDownload(oldBlockMap: BlockMap, newBlockMap: BlockMap): Promise<any> {\n // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it\n if (oldBlockMap.version !== newBlockMap.version) {\n throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`)\n }\n\n const logger = this.logger\n const operations = computeOperations(oldBlockMap, newBlockMap, logger)\n if (logger.debug != null) {\n logger.debug(JSON.stringify(operations, null, 2))\n }\n\n let downloadSize = 0\n let copySize = 0\n for (const operation of operations) {\n const length = operation.end - operation.start\n if (operation.kind === OperationKind.DOWNLOAD) {\n downloadSize += length\n } else {\n copySize += length\n }\n }\n\n const newSize = this.blockAwareFileInfo.size\n if (downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length) !== newSize) {\n throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`)\n }\n\n logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`)\n\n return this.downloadFile(operations)\n }\n\n private downloadFile(tasks: Array<Operation>): Promise<any> {\n const fdList: Array<OpenedFile> = []\n const closeFiles = (): Promise<Array<void>> => {\n return Promise.all(\n fdList.map(openedFile => {\n return close(openedFile.descriptor).catch(e => {\n this.logger.error(`cannot close file \"${openedFile.path}\": ${e}`)\n })\n })\n )\n }\n return this.doDownloadFile(tasks, fdList)\n .then(closeFiles)\n .catch(e => {\n // then must be after catch here (since then always throws error)\n return closeFiles()\n .catch(closeFilesError => {\n // closeFiles never throw error, but just to be sure\n try {\n this.logger.error(`cannot close files: ${closeFilesError}`)\n } catch (errorOnLog) {\n try {\n console.error(errorOnLog)\n } catch (ignored) {\n // ok, give up and ignore error\n }\n }\n throw e\n })\n .then(() => {\n throw e\n })\n })\n }\n\n private async doDownloadFile(tasks: Array<Operation>, fdList: Array<OpenedFile>): Promise<any> {\n const oldFileFd = await open(this.options.oldFile, \"r\")\n fdList.push({ descriptor: oldFileFd, path: this.options.oldFile })\n const newFileFd = await open(this.options.newFile, \"w\")\n fdList.push({ descriptor: newFileFd, path: this.options.newFile })\n const fileOut = createWriteStream(this.options.newFile, { fd: newFileFd })\n await new Promise((resolve, reject) => {\n const streams: Array<any> = []\n\n // Create our download info transformer if we have one\n let downloadInfoTransform: ProgressDifferentialDownloadCallbackTransform | undefined = undefined\n if (!this.options.isUseMultipleRangeRequest && this.options.onProgress) {\n // TODO: Does not support multiple ranges (someone feel free to PR this!)\n const expectedByteCounts: Array<number> = []\n let grandTotalBytes = 0\n\n for (const task of tasks) {\n if (task.kind === OperationKind.DOWNLOAD) {\n expectedByteCounts.push(task.end - task.start)\n grandTotalBytes += task.end - task.start\n }\n }\n\n const progressDifferentialDownloadInfo: ProgressDifferentialDownloadInfo = {\n expectedByteCounts: expectedByteCounts,\n grandTotal: grandTotalBytes,\n }\n\n downloadInfoTransform = new ProgressDifferentialDownloadCallbackTransform(progressDifferentialDownloadInfo, this.options.cancellationToken, this.options.onProgress)\n streams.push(downloadInfoTransform)\n }\n\n const digestTransform = new DigestTransform(this.blockAwareFileInfo.sha512)\n // to simply debug, do manual validation to allow file to be fully written\n digestTransform.isValidateOnEnd = false\n streams.push(digestTransform)\n\n // noinspection JSArrowFunctionCanBeReplacedWithShorthand\n fileOut.on(\"finish\", () => {\n ;(fileOut.close as any)(() => {\n // remove from fd list because closed successfully\n fdList.splice(1, 1)\n try {\n digestTransform.validate()\n } catch (e) {\n reject(e)\n return\n }\n\n resolve(undefined)\n })\n })\n\n streams.push(fileOut)\n\n let lastStream = null\n for (const stream of streams) {\n stream.on(\"error\", reject)\n if (lastStream == null) {\n lastStream = stream\n } else {\n lastStream = lastStream.pipe(stream)\n }\n }\n\n const firstStream = streams[0]\n\n let w: any\n if (this.options.isUseMultipleRangeRequest) {\n w = executeTasksUsingMultipleRangeRequests(this, tasks, firstStream, oldFileFd, reject)\n w(0)\n return\n }\n\n let downloadOperationCount = 0\n let actualUrl: string | null = null\n this.logger.info(`Differential download: ${this.options.newUrl}`)\n\n const requestOptions = this.createRequestOptions()\n ;(requestOptions as any).redirect = \"manual\"\n\n w = (index: number): void => {\n if (index >= tasks.length) {\n if (this.fileMetadataBuffer != null) {\n firstStream.write(this.fileMetadataBuffer)\n }\n firstStream.end()\n return\n }\n\n const operation = tasks[index++]\n if (operation.kind === OperationKind.COPY) {\n // We are copying, let's not send status updates to the UI\n if (downloadInfoTransform) {\n downloadInfoTransform.beginFileCopy()\n }\n\n copyData(operation, firstStream, oldFileFd, reject, () => w(index))\n return\n }\n\n const range = `bytes=${operation.start}-${operation.end - 1}`\n requestOptions.headers!.range = range\n\n this.logger?.debug?.(`download range: ${range}`)\n\n // We are starting to download\n if (downloadInfoTransform) {\n downloadInfoTransform.beginRangeDownload()\n }\n\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n // Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.\n if (response.statusCode >= 400) {\n reject(createHttpError(response))\n }\n\n response.pipe(firstStream, {\n end: false,\n })\n response.once(\"end\", () => {\n // Pass on that we are downloading a segment\n if (downloadInfoTransform) {\n downloadInfoTransform.endRangeDownload()\n }\n\n if (++downloadOperationCount === 100) {\n downloadOperationCount = 0\n setTimeout(() => w(index), 1000)\n } else {\n w(index)\n }\n })\n })\n request.on(\"redirect\", (statusCode: number, method: string, redirectUrl: string) => {\n this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`)\n actualUrl = redirectUrl\n configureRequestUrl(new URL(actualUrl), requestOptions)\n request.followRedirect()\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n }\n\n w(0)\n })\n }\n\n protected async readRemoteBytes(start: number, endInclusive: number): Promise<Buffer> {\n const buffer = Buffer.allocUnsafe(endInclusive + 1 - start)\n const requestOptions = this.createRequestOptions()\n requestOptions.headers!.range = `bytes=${start}-${endInclusive}`\n let position = 0\n await this.request(requestOptions, chunk => {\n chunk.copy(buffer, position)\n position += chunk.length\n })\n\n if (position !== buffer.length) {\n throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`)\n }\n return buffer\n }\n\n private request(requestOptions: RequestOptions, dataHandler: (chunk: Buffer) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n if (!checkIsRangesSupported(response, reject)) {\n return\n }\n\n response.on(\"data\", dataHandler)\n response.on(\"end\", () => resolve())\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n })\n }\n}\n\nfunction formatBytes(value: number, symbol = \" KB\"): string {\n return new Intl.NumberFormat(\"en\").format((value / 1024).toFixed(2) as any) + symbol\n}\n\n// safety\nfunction removeQuery(url: string): string {\n const index = url.indexOf(\"?\")\n return index < 0 ? url : url.substring(0, index)\n}\n\ninterface OpenedFile {\n readonly descriptor: number\n readonly path: string\n}\n"]}
|