core-3nweb-client-lib 0.43.9 → 0.43.12
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/build/api-defs/files.d.ts +32 -8
- package/build/api-defs/web3n.d.ts +2 -0
- package/build/core/asmail/inbox/index.js +1 -1
- package/build/core/asmail/inbox/msg-downloader.d.ts +2 -12
- package/build/core/asmail/inbox/msg-downloader.js +13 -46
- package/build/core/storage/synced/downloader.d.ts +6 -15
- package/build/core/storage/synced/downloader.js +25 -48
- package/build/core/storage/synced/obj-files.d.ts +4 -2
- package/build/core/storage/synced/obj-files.js +7 -7
- package/build/core/storage/synced/storage.d.ts +4 -2
- package/build/core/storage/synced/storage.js +2 -2
- package/build/core/storage/synced/upsyncer.d.ts +2 -4
- package/build/core/storage/synced/upsyncer.js +19 -13
- package/build/core-ipc/file.d.ts +10 -0
- package/build/core-ipc/file.js +23 -13
- package/build/core-ipc/fs.js +15 -8
- package/build/lib-client/3nstorage/storage-owner.js +36 -29
- package/build/lib-client/3nweb-signup.js +7 -7
- package/build/lib-client/asmail/recipient.js +14 -11
- package/build/lib-client/asmail/sender.js +24 -25
- package/build/lib-client/asmail/service-config.js +3 -6
- package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
- package/build/lib-client/cryptor/cryptor.wasm +0 -0
- package/build/lib-client/fs-utils/files.js +4 -4
- package/build/lib-client/mailer-id/login.js +7 -7
- package/build/lib-client/mailer-id/provisioner.js +9 -7
- package/build/lib-client/objs-on-disk/file-writing-proc.d.ts +1 -1
- package/build/lib-client/objs-on-disk/obj-on-disk.d.ts +51 -3
- package/build/lib-client/objs-on-disk/obj-on-disk.js +414 -34
- package/build/lib-client/request-utils.d.ts +0 -7
- package/build/lib-client/request-utils.js +16 -11
- package/build/lib-client/service-locator.js +11 -8
- package/build/lib-client/user-with-mid-session.js +2 -6
- package/build/lib-client/user-with-pkl-session.js +25 -24
- package/build/lib-client/xsp-fs/common.d.ts +8 -3
- package/build/lib-client/xsp-fs/common.js +1 -1
- package/build/lib-client/xsp-fs/file.d.ts +3 -1
- package/build/lib-client/xsp-fs/file.js +4 -4
- package/build/lib-client/xsp-fs/folder-node.d.ts +1 -1
- package/build/lib-client/xsp-fs/folder-node.js +4 -4
- package/build/lib-client/xsp-fs/fs.d.ts +3 -1
- package/build/lib-client/xsp-fs/fs.js +4 -4
- package/build/lib-client/xsp-fs/node-in-fs.d.ts +6 -4
- package/build/lib-client/xsp-fs/node-in-fs.js +14 -9
- package/build/lib-common/buffer-utils.js +4 -1
- package/build/lib-common/exceptions/http.d.ts +11 -1
- package/build/lib-common/exceptions/http.js +10 -2
- package/build/lib-common/objs-on-disk/utils.d.ts +1 -1
- package/build/lib-common/processes/labelled-exec-pools.d.ts +1 -1
- package/build/lib-common/processes/labelled-exec-pools.js +8 -2
- package/build/protos/asmail.proto.js +545 -116
- package/build/protos/file.proto.js +485 -56
- package/build/protos/fs.proto.js +545 -116
- package/package.json +1 -1
- package/protos/file.proto +8 -2
- package/protos/fs.proto +3 -3
- /package/build/lib-common/objs-on-disk/{obj-file.d.ts → obj-version-file.d.ts} +0 -0
- /package/build/lib-common/objs-on-disk/{obj-file.js → obj-version-file.js} +0 -0
|
@@ -16,41 +16,54 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.ObjOnDisk = void 0;
|
|
19
|
+
exports.DownloadsRunner = exports.Download = exports.ObjOnDisk = void 0;
|
|
20
|
+
exports.splitSegsDownloads = splitSegsDownloads;
|
|
21
|
+
exports.generateSectionsFrom = generateSectionsFrom;
|
|
20
22
|
const buffer_utils_1 = require("../../lib-common/buffer-utils");
|
|
21
23
|
const wrapping_1 = require("../../lib-common/byte-streaming/wrapping");
|
|
22
24
|
const assert_1 = require("../../lib-common/assert");
|
|
23
25
|
const file_writing_proc_1 = require("./file-writing-proc");
|
|
24
26
|
const operators_1 = require("rxjs/operators");
|
|
25
|
-
const
|
|
27
|
+
const obj_version_file_1 = require("../../lib-common/objs-on-disk/obj-version-file");
|
|
26
28
|
const utils_for_observables_1 = require("../../lib-common/utils-for-observables");
|
|
29
|
+
const types_1 = require("util/types");
|
|
30
|
+
const deferred_1 = require("../../lib-common/processes/deferred");
|
|
27
31
|
class ObjOnDisk {
|
|
28
|
-
constructor(objId, version, objFile, downloader, readable, getBaseSegsOnDisk) {
|
|
32
|
+
constructor(objId, version, objFile, downloader, readable, getBaseSegsOnDisk, downloadsInProgress = undefined) {
|
|
29
33
|
this.objId = objId;
|
|
30
34
|
this.version = version;
|
|
31
35
|
this.objFile = objFile;
|
|
32
36
|
this.downloader = downloader;
|
|
33
37
|
this.readable = readable;
|
|
34
38
|
this.getBaseSegsOnDisk = getBaseSegsOnDisk;
|
|
39
|
+
this.downloadsInProgress = downloadsInProgress;
|
|
35
40
|
this.proxiedHeader = undefined;
|
|
41
|
+
if (this.downloadsInProgress) {
|
|
42
|
+
this.downloadsInProgress.saveSegs = this.objFile.saveSegs.bind(this.objFile);
|
|
43
|
+
this.downloadsInProgress.segsThatNeedDownload = this.segsThatNeedDownload.bind(this);
|
|
44
|
+
}
|
|
36
45
|
Object.seal(this);
|
|
37
46
|
}
|
|
38
47
|
static async forExistingFile(objId, version, path, downloader, getBase) {
|
|
39
|
-
const objFile = await
|
|
48
|
+
const objFile = await obj_version_file_1.ObjVersionFile.forExisting(path);
|
|
40
49
|
return new ObjOnDisk(objId, version, objFile, downloader, true, getBase);
|
|
41
50
|
}
|
|
42
51
|
static async createFileForExistingVersion(objId, version, path, downloader, getBase, initDownload) {
|
|
52
|
+
let download;
|
|
43
53
|
if (!initDownload) {
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
download = new Download(objId, version, downloader);
|
|
55
|
+
initDownload = await download.getInitParts();
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
download = undefined;
|
|
46
59
|
}
|
|
47
|
-
const {
|
|
60
|
+
const { header, layout, segs } = initDownload;
|
|
48
61
|
if (layout.base !== undefined) {
|
|
49
62
|
// XXX with diff-ed downloads can't assume segs' offset to be 0
|
|
50
63
|
throw new Error(`Current implementation cannot consume diff-ed downloads`);
|
|
51
64
|
}
|
|
52
|
-
const objFile = await
|
|
53
|
-
const obj = new ObjOnDisk(objId, version, objFile, downloader, true, getBase);
|
|
65
|
+
const objFile = await obj_version_file_1.ObjVersionFile.createNew(path);
|
|
66
|
+
const obj = new ObjOnDisk(objId, version, objFile, downloader, true, getBase, download);
|
|
54
67
|
await objFile.setSegsLayout(layout, false);
|
|
55
68
|
await objFile.saveHeader(header, !segs);
|
|
56
69
|
if (segs) {
|
|
@@ -59,7 +72,7 @@ class ObjOnDisk {
|
|
|
59
72
|
return obj;
|
|
60
73
|
}
|
|
61
74
|
static async createFileForWriteOfNewVersion(objId, version, path, encSub, downloader, getBase) {
|
|
62
|
-
const objFile = await
|
|
75
|
+
const objFile = await obj_version_file_1.ObjVersionFile.createNew(path);
|
|
63
76
|
const obj = new ObjOnDisk(objId, version, objFile, downloader, false, getBase);
|
|
64
77
|
const write$ = file_writing_proc_1.FileWritingProc.makeFor(objFile, encSub)
|
|
65
78
|
.pipe((0, operators_1.tap)({
|
|
@@ -104,12 +117,21 @@ class ObjOnDisk {
|
|
|
104
117
|
bytes.push(chunk);
|
|
105
118
|
}
|
|
106
119
|
else {
|
|
107
|
-
const chunkBytes = await this.downloadAndSaveSegsChunk(chunk);
|
|
120
|
+
const chunkBytes = await this.downloads().downloadAndSaveSegsChunk(chunk);
|
|
108
121
|
bytes.push(chunkBytes);
|
|
109
122
|
}
|
|
110
123
|
}
|
|
111
124
|
return (0, buffer_utils_1.joinByteArrs)(bytes);
|
|
112
125
|
}
|
|
126
|
+
downloads() {
|
|
127
|
+
if (!this.downloadsInProgress) {
|
|
128
|
+
if (!this.downloader) {
|
|
129
|
+
throw new Error(`Object ${this.objId}, version ${this.version}, is not on a disk.`);
|
|
130
|
+
}
|
|
131
|
+
this.downloadsInProgress = new Download(this.objId, this.version, this.downloader, this.objFile.saveSegs.bind(this.objFile), this.segsThatNeedDownload.bind(this));
|
|
132
|
+
}
|
|
133
|
+
return this.downloadsInProgress;
|
|
134
|
+
}
|
|
113
135
|
async readSegsOnlyFromDisk(offset, len) {
|
|
114
136
|
const segsLocations = this.objFile.segsLocations(offset, len);
|
|
115
137
|
const bytesAndChunks = [];
|
|
@@ -158,26 +180,13 @@ class ObjOnDisk {
|
|
|
158
180
|
}
|
|
159
181
|
return bytesAndChunks;
|
|
160
182
|
}
|
|
161
|
-
async downloadAndSaveSegsChunk(chunk) {
|
|
162
|
-
if (!this.downloader) {
|
|
163
|
-
throw new Error(`Object ${this.objId}, version ${this.version}, segments section ofs=${chunk.thisVerOfs}, len=${chunk.len} is not on a disk.`);
|
|
164
|
-
}
|
|
165
|
-
const chunkBytes = await this.downloader.getSegs(this.objId, this.version, chunk.thisVerOfs, chunk.thisVerOfs + chunk.len);
|
|
166
|
-
if (chunkBytes.length !== chunk.len) {
|
|
167
|
-
throw new Error(`Download yielded a different length of a segment section`);
|
|
168
|
-
}
|
|
169
|
-
const baseVerOfs = ((chunk.type === 'base') ?
|
|
170
|
-
chunk.baseVerOfs : undefined);
|
|
171
|
-
await this.objFile.saveSegs(chunkBytes, chunk.thisVerOfs, baseVerOfs, true);
|
|
172
|
-
return chunkBytes;
|
|
173
|
-
}
|
|
174
183
|
getSrc() {
|
|
175
184
|
if (!this.readable) {
|
|
176
185
|
throw new Error(`Version ${this.version} of obj ${this.objId} is not readable, yet`);
|
|
177
186
|
}
|
|
178
|
-
const segSrc = (0, wrapping_1.wrapAndSyncSource)(new ByteSourceFromObjOnDisk(
|
|
187
|
+
const segSrc = (0, wrapping_1.wrapAndSyncSource)(new ByteSourceFromObjOnDisk(this.readSegs.bind(this), this.objFile.getTotalSegsLen.bind(this.objFile)));
|
|
179
188
|
const objSrc = {
|
|
180
|
-
readHeader:
|
|
189
|
+
readHeader: this.readHeader.bind(this),
|
|
181
190
|
segSrc,
|
|
182
191
|
version: this.version
|
|
183
192
|
};
|
|
@@ -195,10 +204,10 @@ class ObjOnDisk {
|
|
|
195
204
|
segsThatNeedDownload() {
|
|
196
205
|
const totalLen = this.objFile.getTotalSegsLen();
|
|
197
206
|
const allSegs = this.objFile.segsLocations(0, totalLen);
|
|
198
|
-
return allSegs.filter(({ type }) => (type === 'new'));
|
|
207
|
+
return allSegs.filter(({ type }) => ((type === 'new') || (type === 'base')));
|
|
199
208
|
}
|
|
200
209
|
doesFileNeedDownload() {
|
|
201
|
-
return (this.segsThatNeedDownload().length
|
|
210
|
+
return (this.segsThatNeedDownload().length !== 0);
|
|
202
211
|
}
|
|
203
212
|
numOfBytesNeedingDownload() {
|
|
204
213
|
let totalLen = 0;
|
|
@@ -207,16 +216,387 @@ class ObjOnDisk {
|
|
|
207
216
|
}
|
|
208
217
|
return totalLen;
|
|
209
218
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
for (const chunk of needDownload) {
|
|
213
|
-
await this.downloadAndSaveSegsChunk(chunk);
|
|
214
|
-
}
|
|
219
|
+
startDownloadInBackground(eventSink) {
|
|
220
|
+
return this.downloads().downloadAllInBackground(eventSink);
|
|
215
221
|
}
|
|
216
222
|
}
|
|
217
223
|
exports.ObjOnDisk = ObjOnDisk;
|
|
218
224
|
Object.freeze(ObjOnDisk.prototype);
|
|
219
225
|
Object.freeze(ObjOnDisk);
|
|
226
|
+
class Download {
|
|
227
|
+
constructor(objId, version, downloader, saveSegs, segsThatNeedDownload) {
|
|
228
|
+
this.objId = objId;
|
|
229
|
+
this.version = version;
|
|
230
|
+
this.downloader = downloader;
|
|
231
|
+
this.saveSegs = saveSegs;
|
|
232
|
+
this.segsThatNeedDownload = segsThatNeedDownload;
|
|
233
|
+
this.segsAsapRequest = [];
|
|
234
|
+
this.initDataRequest = undefined;
|
|
235
|
+
this.inBkgrnd = undefined;
|
|
236
|
+
Object.seal(this);
|
|
237
|
+
}
|
|
238
|
+
getInitParts() {
|
|
239
|
+
if (!this.initDataRequest) {
|
|
240
|
+
this.initDataRequest = (0, deferred_1.defer)();
|
|
241
|
+
}
|
|
242
|
+
this.downloader.schedule(this);
|
|
243
|
+
return this.initDataRequest.promise;
|
|
244
|
+
}
|
|
245
|
+
downloadAndSaveSegsChunk(chunk) {
|
|
246
|
+
if (!this.saveSegs) {
|
|
247
|
+
throw new Error(`saveSegs is not initialized`);
|
|
248
|
+
}
|
|
249
|
+
// XXX
|
|
250
|
+
// - check existing requests for overlaps and if there
|
|
251
|
+
// - overlaps are cut out into promise only section
|
|
252
|
+
// - resulting sections' deferred/promised bytes are all awaited, and combined in returned promise
|
|
253
|
+
const segments = [];
|
|
254
|
+
const deferred = (0, deferred_1.defer)();
|
|
255
|
+
this.segsAsapRequest.push({
|
|
256
|
+
chunk,
|
|
257
|
+
deferred,
|
|
258
|
+
chunkBytes: [],
|
|
259
|
+
segments: segments
|
|
260
|
+
});
|
|
261
|
+
const { thisVerOfs: ofs, len } = chunk;
|
|
262
|
+
const generator = generateSectionsFrom(this.downloader.splitSegsDownloads(ofs, ofs + len));
|
|
263
|
+
const next = generator.next();
|
|
264
|
+
segments.push({
|
|
265
|
+
deferred: (0, deferred_1.defer)(),
|
|
266
|
+
sectionBytes: [],
|
|
267
|
+
generator,
|
|
268
|
+
nextSection: next.value
|
|
269
|
+
});
|
|
270
|
+
this.downloader.schedule(this);
|
|
271
|
+
return deferred.promise;
|
|
272
|
+
}
|
|
273
|
+
downloadAllInBackground(eventSink) {
|
|
274
|
+
if (!this.segsThatNeedDownload) {
|
|
275
|
+
throw new Error(`segsThatNeedDownload is not initialized`);
|
|
276
|
+
}
|
|
277
|
+
if (this.inBkgrnd) {
|
|
278
|
+
return this.inBkgrnd.downloadTaskId;
|
|
279
|
+
}
|
|
280
|
+
const chunks = this.segsThatNeedDownload();
|
|
281
|
+
if (chunks.length === 0) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.inBkgrnd = {
|
|
285
|
+
downloadTaskId: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER),
|
|
286
|
+
eventSink,
|
|
287
|
+
totalBytesToDownload: chunks.reduce((total, chunk) => (total + chunk.len), 0)
|
|
288
|
+
};
|
|
289
|
+
eventSink({
|
|
290
|
+
type: 'download-started',
|
|
291
|
+
downloadTaskId: this.inBkgrnd.downloadTaskId,
|
|
292
|
+
path: '',
|
|
293
|
+
version: this.version,
|
|
294
|
+
totalBytesToDownload: this.inBkgrnd.totalBytesToDownload
|
|
295
|
+
});
|
|
296
|
+
if (this.segsAsapRequest.length === 0) {
|
|
297
|
+
this.inBkgrnd.chunks = chunks;
|
|
298
|
+
}
|
|
299
|
+
this.downloader.schedule(this);
|
|
300
|
+
return this.inBkgrnd.downloadTaskId;
|
|
301
|
+
}
|
|
302
|
+
nextActionType() {
|
|
303
|
+
if (this.initDataRequest) {
|
|
304
|
+
return { runASAP: true };
|
|
305
|
+
}
|
|
306
|
+
else if (this.segsAsapRequest.length > 0) {
|
|
307
|
+
return { runASAP: true };
|
|
308
|
+
}
|
|
309
|
+
else if (this.inBkgrnd) {
|
|
310
|
+
return { runASAP: false };
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
startNextAction() {
|
|
317
|
+
if (this.initDataRequest) {
|
|
318
|
+
return this.processInitDataRequest();
|
|
319
|
+
}
|
|
320
|
+
else if (this.segsAsapRequest.length > 0) {
|
|
321
|
+
return this.processSomeAsapSegs();
|
|
322
|
+
}
|
|
323
|
+
else if (this.inBkgrnd) {
|
|
324
|
+
return this.processBkgrnd();
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async processInitDataRequest() {
|
|
331
|
+
var _a, _b;
|
|
332
|
+
try {
|
|
333
|
+
const parts = await this.downloader.getLayoutWithHeaderAndFirstSegs(this.objId, this.version);
|
|
334
|
+
(_a = this.initDataRequest) === null || _a === void 0 ? void 0 : _a.resolve(parts);
|
|
335
|
+
}
|
|
336
|
+
catch (exc) {
|
|
337
|
+
(_b = this.initDataRequest) === null || _b === void 0 ? void 0 : _b.reject(exc);
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
this.initDataRequest = undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async processSomeAsapSegs() {
|
|
344
|
+
var _a;
|
|
345
|
+
if ((_a = this.inBkgrnd) === null || _a === void 0 ? void 0 : _a.runData) {
|
|
346
|
+
this.inBkgrnd.runData = undefined;
|
|
347
|
+
this.inBkgrnd.chunks = undefined;
|
|
348
|
+
}
|
|
349
|
+
const { chunk, chunkBytes, deferred, segments } = this.segsAsapRequest[0];
|
|
350
|
+
const segment = segments[0];
|
|
351
|
+
try {
|
|
352
|
+
if ((0, types_1.isPromise)(segment)) {
|
|
353
|
+
chunkBytes.push(await segment);
|
|
354
|
+
segments.shift();
|
|
355
|
+
}
|
|
356
|
+
else if (segment.nextSection) {
|
|
357
|
+
const baseVerOfs = ((chunk.type === 'base') ? chunk.baseVerOfs : undefined);
|
|
358
|
+
const { ofs, len } = segment.nextSection;
|
|
359
|
+
const bytes = await this.downloader.getSegs(this.objId, this.version, ofs, ofs + len);
|
|
360
|
+
await this.saveSegs(bytes, ofs, baseVerOfs, true);
|
|
361
|
+
segment.sectionBytes.push(bytes);
|
|
362
|
+
const next = segment.generator.next();
|
|
363
|
+
if (next.done) {
|
|
364
|
+
chunkBytes.push(...segment.sectionBytes);
|
|
365
|
+
segments.shift();
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
segment.nextSection = next.value;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
chunkBytes.push(...segment.sectionBytes);
|
|
373
|
+
segments.shift();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
deferred.reject(err);
|
|
378
|
+
this.segsAsapRequest.shift();
|
|
379
|
+
}
|
|
380
|
+
if (segments.length === 0) {
|
|
381
|
+
deferred.resolve((0, buffer_utils_1.joinByteArrs)(chunkBytes));
|
|
382
|
+
this.segsAsapRequest.shift();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async processBkgrnd() {
|
|
386
|
+
if (!this.inBkgrnd) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (!this.inBkgrnd.chunks) {
|
|
390
|
+
this.inBkgrnd.chunks = this.segsThatNeedDownload();
|
|
391
|
+
if (this.inBkgrnd.chunks.length === 0) {
|
|
392
|
+
this.completeBkgrnd();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
else if (this.inBkgrnd.chunks.length === 0) {
|
|
397
|
+
this.completeBkgrnd();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const chunk = this.inBkgrnd.chunks[0];
|
|
401
|
+
if (!this.inBkgrnd.runData) {
|
|
402
|
+
const { thisVerOfs: ofs, len } = chunk;
|
|
403
|
+
this.inBkgrnd.runData = {
|
|
404
|
+
generator: generateSectionsFrom(this.downloader.splitSegsDownloads(ofs, ofs + len))
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const next = this.inBkgrnd.runData.generator.next();
|
|
408
|
+
if (next.done) {
|
|
409
|
+
this.inBkgrnd.chunks.shift();
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const baseVerOfs = ((chunk.type === 'base') ? chunk.baseVerOfs : undefined);
|
|
413
|
+
const { len, ofs } = next.value;
|
|
414
|
+
this.inBkgrnd.runData.currentDownload = { ofs, len, deferred: (0, deferred_1.defer)() };
|
|
415
|
+
this.inBkgrnd.runData.currentDownload.deferred.promise.catch(noop);
|
|
416
|
+
try {
|
|
417
|
+
const bytes = await this.downloader.getSegs(this.objId, this.version, ofs, ofs + len);
|
|
418
|
+
await this.saveSegs(bytes, ofs, baseVerOfs, true);
|
|
419
|
+
this.emitDownloadEvent('download-progress', {
|
|
420
|
+
totalBytesToDownload: this.inBkgrnd.totalBytesToDownload,
|
|
421
|
+
bytesLeftToDownload: this.inBkgrnd.chunks.slice(1).reduce((total, chunk) => (total + chunk.len), 0) + (chunk.len - (ofs + len - chunk.thisVerOfs))
|
|
422
|
+
});
|
|
423
|
+
this.inBkgrnd.runData.currentDownload.deferred.resolve(bytes);
|
|
424
|
+
}
|
|
425
|
+
catch (exc) {
|
|
426
|
+
this.inBkgrnd.runData.currentDownload.deferred.reject(exc);
|
|
427
|
+
}
|
|
428
|
+
finally {
|
|
429
|
+
this.inBkgrnd.runData.currentDownload = undefined;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
completeBkgrnd() {
|
|
433
|
+
this.emitDownloadEvent('download-done', {});
|
|
434
|
+
this.inBkgrnd = undefined;
|
|
435
|
+
}
|
|
436
|
+
emitDownloadEvent(type, field) {
|
|
437
|
+
var _a;
|
|
438
|
+
(_a = this.inBkgrnd) === null || _a === void 0 ? void 0 : _a.eventSink({
|
|
439
|
+
type,
|
|
440
|
+
path: '',
|
|
441
|
+
version: this.version,
|
|
442
|
+
downloadTaskId: this.inBkgrnd.downloadTaskId,
|
|
443
|
+
...field
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
exports.Download = Download;
|
|
448
|
+
Object.freeze(Download.prototype);
|
|
449
|
+
Object.freeze(Download);
|
|
450
|
+
class DownloadsRunner {
|
|
451
|
+
constructor(asapPoolMax = 5, longPoolMax = 1) {
|
|
452
|
+
this.downloads = new Map();
|
|
453
|
+
this.asapPool = makeExecPool(asapPoolMax);
|
|
454
|
+
this.longRunsPool = makeExecPool(longPoolMax);
|
|
455
|
+
Object.seal(this);
|
|
456
|
+
}
|
|
457
|
+
schedule(download) {
|
|
458
|
+
const found = this.downloads.get(download);
|
|
459
|
+
if (found) {
|
|
460
|
+
if (found.isRunning) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
const next = download.nextActionType();
|
|
465
|
+
if (!next) {
|
|
466
|
+
this.removeDownload(download);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const pool = (next.runASAP ? this.asapPool : this.longRunsPool);
|
|
470
|
+
if (found.pool !== pool) {
|
|
471
|
+
removeFromArray(found.pool.queue, download);
|
|
472
|
+
found.pool = pool;
|
|
473
|
+
found.pool.queue.push(download);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
const next = download.nextActionType();
|
|
479
|
+
if (!next) {
|
|
480
|
+
this.removeDownload(download);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const pool = (next.runASAP ? this.asapPool : this.longRunsPool);
|
|
484
|
+
this.downloads.set(download, { isRunning: false, pool });
|
|
485
|
+
pool.queue.push(download);
|
|
486
|
+
}
|
|
487
|
+
this.triggerRuns();
|
|
488
|
+
}
|
|
489
|
+
removeDownload(download) {
|
|
490
|
+
const found = this.downloads.get(download);
|
|
491
|
+
if (found) {
|
|
492
|
+
this.downloads.delete(download);
|
|
493
|
+
removeFromArray(found.pool.queue, download);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
triggerRuns() {
|
|
497
|
+
this.triggerRunsInPool(this.asapPool);
|
|
498
|
+
this.triggerRunsInPool(this.longRunsPool);
|
|
499
|
+
}
|
|
500
|
+
triggerRunsInPool(pool) {
|
|
501
|
+
while ((pool.queue.length > 0) && (pool.numOfRunning < pool.max)) {
|
|
502
|
+
const download = pool.queue.shift();
|
|
503
|
+
const info = this.downloads.get(download);
|
|
504
|
+
if (!info) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
let promise = download.startNextAction();
|
|
508
|
+
if (!promise) {
|
|
509
|
+
this.removeDownload(download);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
info.isRunning = true;
|
|
513
|
+
pool.numOfRunning += 1;
|
|
514
|
+
if (pool === this.asapPool) {
|
|
515
|
+
promise = promise.then(() => {
|
|
516
|
+
const next = download.nextActionType();
|
|
517
|
+
if (!next) {
|
|
518
|
+
this.removeDownload(download);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (next.runASAP) {
|
|
522
|
+
this.asapPool.queue.unshift(download);
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
this.longRunsPool.queue.push(download);
|
|
526
|
+
info.pool = this.longRunsPool;
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
promise = promise.then(() => {
|
|
532
|
+
const next = download.nextActionType();
|
|
533
|
+
if (!next) {
|
|
534
|
+
this.removeDownload(download);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (next.runASAP) {
|
|
538
|
+
this.asapPool.queue.push(download);
|
|
539
|
+
info.pool = this.asapPool;
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
this.longRunsPool.queue.unshift(download);
|
|
543
|
+
}
|
|
544
|
+
}, (_exc) => {
|
|
545
|
+
this.longRunsPool.queue.unshift(download);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
promise.finally(() => {
|
|
549
|
+
info.isRunning = false;
|
|
550
|
+
pool.numOfRunning -= 1;
|
|
551
|
+
this.triggerRuns();
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
exports.DownloadsRunner = DownloadsRunner;
|
|
557
|
+
Object.freeze(DownloadsRunner.prototype);
|
|
558
|
+
Object.freeze(DownloadsRunner);
|
|
559
|
+
function makeExecPool(max) {
|
|
560
|
+
return { queue: [], max, numOfRunning: 0 };
|
|
561
|
+
}
|
|
562
|
+
function removeFromArray(arr, elem) {
|
|
563
|
+
const ind = arr.indexOf(elem);
|
|
564
|
+
if (ind >= 0) {
|
|
565
|
+
arr.splice(ind, 1);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function splitSegsDownloads(start, end, max) {
|
|
569
|
+
const parts = [];
|
|
570
|
+
let ofs = start;
|
|
571
|
+
const repeatCount = Math.floor((end - start) / max);
|
|
572
|
+
if (repeatCount > 1) {
|
|
573
|
+
const repeatLen = max * repeatCount;
|
|
574
|
+
parts.push({ ofs, len: max, repeatCount, repeatLen });
|
|
575
|
+
ofs += repeatLen;
|
|
576
|
+
}
|
|
577
|
+
else if (repeatCount === 1) {
|
|
578
|
+
parts.push({ ofs, len: max });
|
|
579
|
+
ofs += max;
|
|
580
|
+
}
|
|
581
|
+
const tailLen = end - ofs;
|
|
582
|
+
if (tailLen > 0) {
|
|
583
|
+
parts.push({ ofs, len: tailLen });
|
|
584
|
+
}
|
|
585
|
+
return parts;
|
|
586
|
+
}
|
|
587
|
+
function* generateSectionsFrom(sections) {
|
|
588
|
+
for (const { ofs, len, repeatCount, repeatLen } of sections) {
|
|
589
|
+
if (repeatCount) {
|
|
590
|
+
for (let i = 0; i < repeatCount; i += 1) {
|
|
591
|
+
yield { ofs: ofs + (i * len), len };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
yield { ofs, len };
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function noop() { }
|
|
220
600
|
class ByteSourceFromObjOnDisk {
|
|
221
601
|
constructor(readSegs, totalSegsLen) {
|
|
222
602
|
this.readSegs = readSegs;
|
|
@@ -225,7 +605,7 @@ class ByteSourceFromObjOnDisk {
|
|
|
225
605
|
Object.seal(this);
|
|
226
606
|
}
|
|
227
607
|
async readNext(len) {
|
|
228
|
-
(0, assert_1.assert)((Number.isInteger(len) && (len >= 0))
|
|
608
|
+
(0, assert_1.assert)((len === undefined) || (Number.isInteger(len) && (len >= 0)), 'Illegal length parameter given: ' + len);
|
|
229
609
|
const start = this.segsPointer;
|
|
230
610
|
if (len === undefined) {
|
|
231
611
|
const segsLen = this.totalSegsLen();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { HTTPException } from '../lib-common/exceptions/http';
|
|
2
1
|
import * as https from 'https';
|
|
3
2
|
import { ClientRequest } from 'http';
|
|
4
3
|
export declare const SESSION_ID_HEADER = "X-Session-Id";
|
|
@@ -30,12 +29,6 @@ export type ContentType = 'application/json' | 'application/octet-stream' | 'tex
|
|
|
30
29
|
export type RequestFn<T> = (opts: RequestOpts, contentType?: ContentType, reqBody?: Uint8Array) => Promise<Reply<T>>;
|
|
31
30
|
export declare function processRequest<T>(requester: (opts: https.RequestOptions) => ClientRequest, httpsOpts: https.RequestOptions, opts: RequestOpts, reqBody: Uint8Array | undefined, attempt?: number): Promise<Reply<T>>;
|
|
32
31
|
export declare function formHttpsReqOpts(opts: RequestOpts, contentType?: ContentType, reqBody?: Uint8Array): https.RequestOptions;
|
|
33
|
-
/**
|
|
34
|
-
* @param rep
|
|
35
|
-
* @param errMsg
|
|
36
|
-
* @return http exception based on given reply, with an optional message
|
|
37
|
-
*/
|
|
38
|
-
export declare function makeException(rep: Reply<any>, errMsg?: string): HTTPException;
|
|
39
32
|
export declare function extractIntHeader(rep: Reply<any>, headerName: string): number;
|
|
40
33
|
export interface NetClient {
|
|
41
34
|
/**
|
|
@@ -18,7 +18,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.CONTENT_TYPE_HEADER = exports.SESSION_ID_HEADER = void 0;
|
|
19
19
|
exports.processRequest = processRequest;
|
|
20
20
|
exports.formHttpsReqOpts = formHttpsReqOpts;
|
|
21
|
-
exports.makeException = makeException;
|
|
22
21
|
exports.extractIntHeader = extractIntHeader;
|
|
23
22
|
exports.makeNetClient = makeNetClient;
|
|
24
23
|
const http_1 = require("../lib-common/exceptions/http");
|
|
@@ -134,7 +133,11 @@ function formReply(res, resBody, reqOpts) {
|
|
|
134
133
|
}
|
|
135
134
|
catch (err) {
|
|
136
135
|
if (resContentType === 'application/json') {
|
|
137
|
-
throw (0, http_1.makeHTTPException)(reqOpts.url, reqOpts.method, status,
|
|
136
|
+
throw (0, http_1.makeHTTPException)(reqOpts.url, reqOpts.method, status, {
|
|
137
|
+
message: `Cannot parse received bytes as json`,
|
|
138
|
+
cause: err,
|
|
139
|
+
malformedResponse: true
|
|
140
|
+
});
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
}
|
|
@@ -168,18 +171,20 @@ function makeHeaders(headers) {
|
|
|
168
171
|
}
|
|
169
172
|
};
|
|
170
173
|
}
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
function makeException(rep
|
|
177
|
-
|
|
178
|
-
}
|
|
174
|
+
// /**
|
|
175
|
+
// * @param rep
|
|
176
|
+
// * @param errMsg
|
|
177
|
+
// * @return http exception based on given reply, with an optional message
|
|
178
|
+
// */
|
|
179
|
+
// export function makeException(rep: Reply<any>, message?: string): HTTPException {
|
|
180
|
+
// return makeHTTPException(rep.url, rep.method, rep.status, { message });
|
|
181
|
+
// }
|
|
179
182
|
function extractIntHeader(rep, headerName) {
|
|
180
183
|
const intHeader = parseInt(rep.headers.get(headerName));
|
|
181
184
|
if (isNaN(intHeader)) {
|
|
182
|
-
throw
|
|
185
|
+
throw (0, http_1.makeMalformedReplyHTTPException)(rep, {
|
|
186
|
+
message: `Malformed response: header ${headerName} is missing or malformed`
|
|
187
|
+
});
|
|
183
188
|
}
|
|
184
189
|
return intHeader;
|
|
185
190
|
}
|
|
@@ -23,8 +23,8 @@ exports.makeServiceLocator = makeServiceLocator;
|
|
|
23
23
|
exports.getMailerIdInfoFor = getMailerIdInfoFor;
|
|
24
24
|
const jwkeys_1 = require("../lib-common/jwkeys");
|
|
25
25
|
const url_1 = require("url");
|
|
26
|
-
const request_utils_1 = require("./request-utils");
|
|
27
26
|
const runtime_1 = require("../lib-common/exceptions/runtime");
|
|
27
|
+
const http_1 = require("../lib-common/exceptions/http");
|
|
28
28
|
async function readJSONLocatedAt(client, url) {
|
|
29
29
|
if ((0, url_1.parse)(url).protocol !== 'https:') {
|
|
30
30
|
throw new Error("Url protocol must be https.");
|
|
@@ -36,12 +36,12 @@ async function readJSONLocatedAt(client, url) {
|
|
|
36
36
|
});
|
|
37
37
|
if (rep.status === 200) {
|
|
38
38
|
if (!rep.data) {
|
|
39
|
-
throw (0,
|
|
39
|
+
throw (0, http_1.makeMalformedReplyHTTPException)(rep);
|
|
40
40
|
}
|
|
41
41
|
return rep;
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
|
-
throw (0,
|
|
44
|
+
throw (0, http_1.makeUnexpectedStatusHTTPException)(rep);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
function transformPathToCompleteUri(url, path, rep) {
|
|
@@ -49,7 +49,7 @@ function transformPathToCompleteUri(url, path, rep) {
|
|
|
49
49
|
const protoAndHost = `${uInit.protocol}//${uInit.host}`;
|
|
50
50
|
const uPath = (0, url_1.parse)(path);
|
|
51
51
|
if (!uPath.path || !uPath.href || !uPath.href.startsWith(uPath.path)) {
|
|
52
|
-
throw (0,
|
|
52
|
+
throw (0, http_1.makeMalformedReplyHTTPException)(rep, { message: `Malformed path parameter ${path}` });
|
|
53
53
|
}
|
|
54
54
|
if (uPath.href.startsWith('/')) {
|
|
55
55
|
return `${protoAndHost}${uPath.href}`;
|
|
@@ -92,7 +92,7 @@ async function mailerIdInfoAt(client, url) {
|
|
|
92
92
|
transform.provisioning = transformPathToCompleteUri(url, json.provisioning, rep);
|
|
93
93
|
}
|
|
94
94
|
else {
|
|
95
|
-
throw (0,
|
|
95
|
+
throw (0, http_1.makeMalformedReplyHTTPException)(rep);
|
|
96
96
|
}
|
|
97
97
|
if ((0, jwkeys_1.isLikeSignedKeyCert)(json["current-cert"])) {
|
|
98
98
|
transform.currentCert = json["current-cert"];
|
|
@@ -100,7 +100,7 @@ async function mailerIdInfoAt(client, url) {
|
|
|
100
100
|
json["previous-certs"].filter(jwkeys_1.isLikeSignedKeyCert) : []);
|
|
101
101
|
}
|
|
102
102
|
else {
|
|
103
|
-
throw (0,
|
|
103
|
+
throw (0, http_1.makeMalformedReplyHTTPException)(rep);
|
|
104
104
|
}
|
|
105
105
|
Object.freeze(transform);
|
|
106
106
|
return transform;
|
|
@@ -224,7 +224,8 @@ const DNS_ERR_CODE = {
|
|
|
224
224
|
NODATA: 'ENODATA',
|
|
225
225
|
NOTFOUND: 'ENOTFOUND',
|
|
226
226
|
ESERVFAIL: 'ESERVFAIL',
|
|
227
|
-
ECONNREFUSED: 'ECONNREFUSED'
|
|
227
|
+
ECONNREFUSED: 'ECONNREFUSED',
|
|
228
|
+
ETIMEOUT: 'ETIMEOUT'
|
|
228
229
|
};
|
|
229
230
|
Object.freeze(DNS_ERR_CODE);
|
|
230
231
|
function makeServiceLocator(resolver) {
|
|
@@ -244,7 +245,9 @@ function makeServiceLocator(resolver) {
|
|
|
244
245
|
if (code === DNS_ERR_CODE.NODATA) {
|
|
245
246
|
throw noServiceRecordExc(address);
|
|
246
247
|
}
|
|
247
|
-
else if ((code === DNS_ERR_CODE.ESERVFAIL)
|
|
248
|
+
else if ((code === DNS_ERR_CODE.ESERVFAIL)
|
|
249
|
+
|| (code === DNS_ERR_CODE.ECONNREFUSED)
|
|
250
|
+
|| (code === DNS_ERR_CODE.ETIMEOUT)) {
|
|
248
251
|
throw noConnectionExc({ code, hostname, message });
|
|
249
252
|
}
|
|
250
253
|
else if (hostname) {
|