You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
6.9 KiB
179 lines
6.9 KiB
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.makeRemoteSource = 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 onyl the first
|
|
// get the rest of the slices and fetch them iteratetively
|
|
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;
|
|
/**
|
|
*
|
|
* @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
|