@azure/storage-blob
Version:
Microsoft Azure Storage SDK for JavaScript - Blob
131 lines • 5.03 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.RetriableReadableStream = void 0;
const abort_controller_1 = require("@azure/abort-controller");
const node_stream_1 = require("node:stream");
/**
* ONLY AVAILABLE IN NODE.JS RUNTIME.
*
* A Node.js ReadableStream will internally retry when internal ReadableStream unexpected ends.
*/
class RetriableReadableStream extends node_stream_1.Readable {
start;
offset;
end;
getter;
source;
retries = 0;
maxRetryRequests;
onProgress;
options;
/**
* Creates an instance of RetriableReadableStream.
*
* @param source - The current ReadableStream returned from getter
* @param getter - A method calling downloading request returning
* a new ReadableStream from specified offset
* @param offset - Offset position in original data source to read
* @param count - How much data in original data source to read
* @param options -
*/
constructor(source, getter, offset, count, options = {}) {
super({ highWaterMark: options.highWaterMark });
this.getter = getter;
this.source = source;
this.start = offset;
this.offset = offset;
this.end = offset + count - 1;
this.maxRetryRequests =
options.maxRetryRequests && options.maxRetryRequests >= 0 ? options.maxRetryRequests : 0;
this.onProgress = options.onProgress;
this.options = options;
this.setSourceEventHandlers();
}
_read() {
this.source.resume();
}
setSourceEventHandlers() {
this.source.on("data", this.sourceDataHandler);
this.source.on("end", this.sourceErrorOrEndHandler);
this.source.on("error", this.sourceErrorOrEndHandler);
// needed for Node14
this.source.on("aborted", this.sourceAbortedHandler);
}
removeSourceEventHandlers() {
this.source.removeListener("data", this.sourceDataHandler);
this.source.removeListener("end", this.sourceErrorOrEndHandler);
this.source.removeListener("error", this.sourceErrorOrEndHandler);
this.source.removeListener("aborted", this.sourceAbortedHandler);
}
sourceDataHandler = (data) => {
if (this.options.doInjectErrorOnce) {
this.options.doInjectErrorOnce = undefined;
this.source.pause();
this.sourceErrorOrEndHandler();
this.source.destroy();
return;
}
// console.log(
// `Offset: ${this.offset}, Received ${data.length} from internal stream`
// );
this.offset += data.length;
if (this.onProgress) {
this.onProgress({ loadedBytes: this.offset - this.start });
}
if (!this.push(data)) {
this.source.pause();
}
};
sourceAbortedHandler = () => {
const abortError = new abort_controller_1.AbortError("The operation was aborted.");
this.destroy(abortError);
};
sourceErrorOrEndHandler = (err) => {
if (err && err.name === "AbortError") {
this.destroy(err);
return;
}
// console.log(
// `Source stream emits end or error, offset: ${
// this.offset
// }, dest end : ${this.end}`
// );
this.removeSourceEventHandlers();
if (this.offset - 1 === this.end) {
this.push(null);
}
else if (this.offset <= this.end) {
// console.log(
// `retries: ${this.retries}, max retries: ${this.maxRetries}`
// );
if (this.retries < this.maxRetryRequests) {
this.retries += 1;
this.getter(this.offset)
.then((newSource) => {
this.source = newSource;
this.setSourceEventHandlers();
return;
})
.catch((error) => {
this.destroy(error);
});
}
else {
this.destroy(new Error(`Data corruption failure: received less data than required and reached maxRetires limitation. Received data offset: ${this.offset - 1}, data needed offset: ${this.end}, retries: ${this.retries}, max retries: ${this.maxRetryRequests}`));
}
}
else {
this.destroy(new Error(`Data corruption failure: Received more data than original request, data needed offset is ${this.end}, received offset: ${this.offset - 1}`));
}
};
_destroy(error, callback) {
// remove listener from source and release source
this.removeSourceEventHandlers();
this.source.destroy();
callback(error === null ? undefined : error);
}
}
exports.RetriableReadableStream = RetriableReadableStream;
//# sourceMappingURL=RetriableReadableStream.js.map