"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeRemoteSource = exports.makeCustomSource = exports.makeHttpSource = exports.makeXHRSource = exports.makeFetchSource = void 0; const httputils_js_1 = require("./httputils.js"); const basesource_js_1 = require("./basesource.js"); const blockedsource_js_1 = require("./blockedsource.js"); const fetch_js_1 = require("./client/fetch.js"); const xhr_js_1 = require("./client/xhr.js"); const http_js_1 = require("./client/http.js"); class RemoteSource extends basesource_js_1.BaseSource { /** * * @param {BaseClient} client * @param {object} headers * @param {numbers} maxRanges * @param {boolean} allowFullFile */ constructor(client, headers, maxRanges, allowFullFile) { super(); this.client = client; this.headers = headers; this.maxRanges = maxRanges; this.allowFullFile = allowFullFile; this._fileSize = null; } /** * * @param {Slice[]} slices */ async fetch(slices, signal) { // if we allow multi-ranges, split the incoming request into that many sub-requests // and join them afterwards if (this.maxRanges >= slices.length) { return this.fetchSlices(slices, signal); } else if (this.maxRanges > 0 && slices.length > 1) { // TODO: split into multiple multi-range requests // const subSlicesRequests = []; // for (let i = 0; i < slices.length; i += this.maxRanges) { // subSlicesRequests.push( // this.fetchSlices(slices.slice(i, i + this.maxRanges), signal), // ); // } // return (await Promise.all(subSlicesRequests)).flat(); } // otherwise make a single request for each slice return Promise.all(slices.map((slice) => this.fetchSlice(slice, signal))); } async fetchSlices(slices, signal) { const response = await this.client.request({ headers: { ...this.headers, Range: `bytes=${slices .map(({ offset, length }) => `${offset}-${offset + length}`) .join(',')}`, }, signal, }); if (!response.ok) { throw new Error('Error fetching data.'); } else if (response.status === 206) { const { type, params } = (0, httputils_js_1.parseContentType)(response.getHeader('content-type')); if (type === 'multipart/byteranges') { const byteRanges = (0, httputils_js_1.parseByteRanges)(await response.getData(), params.boundary); this._fileSize = byteRanges[0].fileSize || null; return byteRanges; } const data = await response.getData(); const { start, end, total } = (0, httputils_js_1.parseContentRange)(response.getHeader('content-range')); this._fileSize = total || null; const first = [{ data, offset: start, length: end - start, }]; if (slices.length > 1) { // we requested more than one slice, but got only the first // unfortunately, some HTTP Servers don't support multi-ranges // and return only the first // get the rest of the slices and fetch them iteratively const others = await Promise.all(slices.slice(1).map((slice) => this.fetchSlice(slice, signal))); return first.concat(others); } return first; } else { if (!this.allowFullFile) { throw new Error('Server responded with full file'); } const data = await response.getData(); this._fileSize = data.byteLength; return [{ data, offset: 0, length: data.byteLength, }]; } } async fetchSlice(slice, signal) { const { offset, length } = slice; const response = await this.client.request({ headers: { ...this.headers, Range: `bytes=${offset}-${offset + length}`, }, signal, }); // check the response was okay and if the server actually understands range requests if (!response.ok) { throw new Error('Error fetching data.'); } else if (response.status === 206) { const data = await response.getData(); const { total } = (0, httputils_js_1.parseContentRange)(response.getHeader('content-range')); this._fileSize = total || null; return { data, offset, length, }; } else { if (!this.allowFullFile) { throw new Error('Server responded with full file'); } const data = await response.getData(); this._fileSize = data.byteLength; return { data, offset: 0, length: data.byteLength, }; } } get fileSize() { return this._fileSize; } } function maybeWrapInBlockedSource(source, { blockSize, cacheSize }) { if (blockSize === null) { return source; } return new blockedsource_js_1.BlockedSource(source, { blockSize, cacheSize }); } function makeFetchSource(url, { headers = {}, credentials, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) { const client = new fetch_js_1.FetchClient(url, credentials); const source = new RemoteSource(client, headers, maxRanges, allowFullFile); return maybeWrapInBlockedSource(source, blockOptions); } exports.makeFetchSource = makeFetchSource; function makeXHRSource(url, { headers = {}, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) { const client = new xhr_js_1.XHRClient(url); const source = new RemoteSource(client, headers, maxRanges, allowFullFile); return maybeWrapInBlockedSource(source, blockOptions); } exports.makeXHRSource = makeXHRSource; function makeHttpSource(url, { headers = {}, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) { const client = new http_js_1.HttpClient(url); const source = new RemoteSource(client, headers, maxRanges, allowFullFile); return maybeWrapInBlockedSource(source, blockOptions); } exports.makeHttpSource = makeHttpSource; function makeCustomSource(client, { headers = {}, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) { const source = new RemoteSource(client, headers, maxRanges, allowFullFile); return maybeWrapInBlockedSource(source, blockOptions); } exports.makeCustomSource = makeCustomSource; /** * * @param {string} url * @param {object} options */ function makeRemoteSource(url, { forceXHR = false, ...clientOptions } = {}) { if (typeof fetch === 'function' && !forceXHR) { return makeFetchSource(url, clientOptions); } if (typeof XMLHttpRequest !== 'undefined') { return makeXHRSource(url, clientOptions); } return makeHttpSource(url, clientOptions); } exports.makeRemoteSource = makeRemoteSource; //# sourceMappingURL=remote.js.map