remote.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.makeRemoteSource = exports.makeHttpSource = exports.makeXHRSource = exports.makeFetchSource = void 0;
  4. const httputils_js_1 = require("./httputils.js");
  5. const basesource_js_1 = require("./basesource.js");
  6. const blockedsource_js_1 = require("./blockedsource.js");
  7. const fetch_js_1 = require("./client/fetch.js");
  8. const xhr_js_1 = require("./client/xhr.js");
  9. const http_js_1 = require("./client/http.js");
  10. class RemoteSource extends basesource_js_1.BaseSource {
  11. /**
  12. *
  13. * @param {BaseClient} client
  14. * @param {object} headers
  15. * @param {numbers} maxRanges
  16. * @param {boolean} allowFullFile
  17. */
  18. constructor(client, headers, maxRanges, allowFullFile) {
  19. super();
  20. this.client = client;
  21. this.headers = headers;
  22. this.maxRanges = maxRanges;
  23. this.allowFullFile = allowFullFile;
  24. this._fileSize = null;
  25. }
  26. /**
  27. *
  28. * @param {Slice[]} slices
  29. */
  30. async fetch(slices, signal) {
  31. // if we allow multi-ranges, split the incoming request into that many sub-requests
  32. // and join them afterwards
  33. if (this.maxRanges >= slices.length) {
  34. return this.fetchSlices(slices, signal);
  35. }
  36. else if (this.maxRanges > 0 && slices.length > 1) {
  37. // TODO: split into multiple multi-range requests
  38. // const subSlicesRequests = [];
  39. // for (let i = 0; i < slices.length; i += this.maxRanges) {
  40. // subSlicesRequests.push(
  41. // this.fetchSlices(slices.slice(i, i + this.maxRanges), signal),
  42. // );
  43. // }
  44. // return (await Promise.all(subSlicesRequests)).flat();
  45. }
  46. // otherwise make a single request for each slice
  47. return Promise.all(slices.map((slice) => this.fetchSlice(slice, signal)));
  48. }
  49. async fetchSlices(slices, signal) {
  50. const response = await this.client.request({
  51. headers: {
  52. ...this.headers,
  53. Range: `bytes=${slices
  54. .map(({ offset, length }) => `${offset}-${offset + length}`)
  55. .join(',')}`,
  56. },
  57. signal,
  58. });
  59. if (!response.ok) {
  60. throw new Error('Error fetching data.');
  61. }
  62. else if (response.status === 206) {
  63. const { type, params } = (0, httputils_js_1.parseContentType)(response.getHeader('content-type'));
  64. if (type === 'multipart/byteranges') {
  65. const byteRanges = (0, httputils_js_1.parseByteRanges)(await response.getData(), params.boundary);
  66. this._fileSize = byteRanges[0].fileSize || null;
  67. return byteRanges;
  68. }
  69. const data = await response.getData();
  70. const { start, end, total } = (0, httputils_js_1.parseContentRange)(response.getHeader('content-range'));
  71. this._fileSize = total || null;
  72. const first = [{
  73. data,
  74. offset: start,
  75. length: end - start,
  76. }];
  77. if (slices.length > 1) {
  78. // we requested more than one slice, but got only the first
  79. // unfortunately, some HTTP Servers don't support multi-ranges
  80. // and return only the first
  81. // get the rest of the slices and fetch them iteratively
  82. const others = await Promise.all(slices.slice(1).map((slice) => this.fetchSlice(slice, signal)));
  83. return first.concat(others);
  84. }
  85. return first;
  86. }
  87. else {
  88. if (!this.allowFullFile) {
  89. throw new Error('Server responded with full file');
  90. }
  91. const data = await response.getData();
  92. this._fileSize = data.byteLength;
  93. return [{
  94. data,
  95. offset: 0,
  96. length: data.byteLength,
  97. }];
  98. }
  99. }
  100. async fetchSlice(slice, signal) {
  101. const { offset, length } = slice;
  102. const response = await this.client.request({
  103. headers: {
  104. ...this.headers,
  105. Range: `bytes=${offset}-${offset + length}`,
  106. },
  107. signal,
  108. });
  109. // check the response was okay and if the server actually understands range requests
  110. if (!response.ok) {
  111. throw new Error('Error fetching data.');
  112. }
  113. else if (response.status === 206) {
  114. const data = await response.getData();
  115. const { total } = (0, httputils_js_1.parseContentRange)(response.getHeader('content-range'));
  116. this._fileSize = total || null;
  117. return {
  118. data,
  119. offset,
  120. length,
  121. };
  122. }
  123. else {
  124. if (!this.allowFullFile) {
  125. throw new Error('Server responded with full file');
  126. }
  127. const data = await response.getData();
  128. this._fileSize = data.byteLength;
  129. return {
  130. data,
  131. offset: 0,
  132. length: data.byteLength,
  133. };
  134. }
  135. }
  136. get fileSize() {
  137. return this._fileSize;
  138. }
  139. }
  140. function maybeWrapInBlockedSource(source, { blockSize, cacheSize }) {
  141. if (blockSize === null) {
  142. return source;
  143. }
  144. return new blockedsource_js_1.BlockedSource(source, { blockSize, cacheSize });
  145. }
  146. function makeFetchSource(url, { headers = {}, credentials, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) {
  147. const client = new fetch_js_1.FetchClient(url, credentials);
  148. const source = new RemoteSource(client, headers, maxRanges, allowFullFile);
  149. return maybeWrapInBlockedSource(source, blockOptions);
  150. }
  151. exports.makeFetchSource = makeFetchSource;
  152. function makeXHRSource(url, { headers = {}, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) {
  153. const client = new xhr_js_1.XHRClient(url);
  154. const source = new RemoteSource(client, headers, maxRanges, allowFullFile);
  155. return maybeWrapInBlockedSource(source, blockOptions);
  156. }
  157. exports.makeXHRSource = makeXHRSource;
  158. function makeHttpSource(url, { headers = {}, maxRanges = 0, allowFullFile = false, ...blockOptions } = {}) {
  159. const client = new http_js_1.HttpClient(url);
  160. const source = new RemoteSource(client, headers, maxRanges, allowFullFile);
  161. return maybeWrapInBlockedSource(source, blockOptions);
  162. }
  163. exports.makeHttpSource = makeHttpSource;
  164. /**
  165. *
  166. * @param {string} url
  167. * @param {object} options
  168. */
  169. function makeRemoteSource(url, { forceXHR = false, ...clientOptions } = {}) {
  170. if (typeof fetch === 'function' && !forceXHR) {
  171. return makeFetchSource(url, clientOptions);
  172. }
  173. if (typeof XMLHttpRequest !== 'undefined') {
  174. return makeXHRSource(url, clientOptions);
  175. }
  176. return makeHttpSource(url, clientOptions);
  177. }
  178. exports.makeRemoteSource = makeRemoteSource;
  179. //# sourceMappingURL=remote.js.map