2
0

fetch_stream.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * JavaScript code in this page
  4. *
  5. * Copyright 2022 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * JavaScript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.PDFFetchStream = void 0;
  27. var _util = require("../shared/util.js");
  28. var _network_utils = require("./network_utils.js");
  29. ;
  30. function createFetchOptions(headers, withCredentials, abortController) {
  31. return {
  32. method: "GET",
  33. headers,
  34. signal: abortController.signal,
  35. mode: "cors",
  36. credentials: withCredentials ? "include" : "same-origin",
  37. redirect: "follow"
  38. };
  39. }
  40. function createHeaders(httpHeaders) {
  41. const headers = new Headers();
  42. for (const property in httpHeaders) {
  43. const value = httpHeaders[property];
  44. if (value === undefined) {
  45. continue;
  46. }
  47. headers.append(property, value);
  48. }
  49. return headers;
  50. }
  51. class PDFFetchStream {
  52. constructor(source) {
  53. this.source = source;
  54. this.isHttp = /^https?:/i.test(source.url);
  55. this.httpHeaders = this.isHttp && source.httpHeaders || {};
  56. this._fullRequestReader = null;
  57. this._rangeRequestReaders = [];
  58. }
  59. get _progressiveDataLength() {
  60. return this._fullRequestReader?._loaded ?? 0;
  61. }
  62. getFullReader() {
  63. (0, _util.assert)(!this._fullRequestReader, "PDFFetchStream.getFullReader can only be called once.");
  64. this._fullRequestReader = new PDFFetchStreamReader(this);
  65. return this._fullRequestReader;
  66. }
  67. getRangeReader(begin, end) {
  68. if (end <= this._progressiveDataLength) {
  69. return null;
  70. }
  71. const reader = new PDFFetchStreamRangeReader(this, begin, end);
  72. this._rangeRequestReaders.push(reader);
  73. return reader;
  74. }
  75. cancelAllRequests(reason) {
  76. this._fullRequestReader?.cancel(reason);
  77. for (const reader of this._rangeRequestReaders.slice(0)) {
  78. reader.cancel(reason);
  79. }
  80. }
  81. }
  82. exports.PDFFetchStream = PDFFetchStream;
  83. class PDFFetchStreamReader {
  84. constructor(stream) {
  85. this._stream = stream;
  86. this._reader = null;
  87. this._loaded = 0;
  88. this._filename = null;
  89. const source = stream.source;
  90. this._withCredentials = source.withCredentials || false;
  91. this._contentLength = source.length;
  92. this._headersCapability = (0, _util.createPromiseCapability)();
  93. this._disableRange = source.disableRange || false;
  94. this._rangeChunkSize = source.rangeChunkSize;
  95. if (!this._rangeChunkSize && !this._disableRange) {
  96. this._disableRange = true;
  97. }
  98. this._abortController = new AbortController();
  99. this._isStreamingSupported = !source.disableStream;
  100. this._isRangeSupported = !source.disableRange;
  101. this._headers = createHeaders(this._stream.httpHeaders);
  102. const url = source.url;
  103. fetch(url, createFetchOptions(this._headers, this._withCredentials, this._abortController)).then(response => {
  104. if (!(0, _network_utils.validateResponseStatus)(response.status)) {
  105. throw (0, _network_utils.createResponseStatusError)(response.status, url);
  106. }
  107. this._reader = response.body.getReader();
  108. this._headersCapability.resolve();
  109. const getResponseHeader = name => {
  110. return response.headers.get(name);
  111. };
  112. const {
  113. allowRangeRequests,
  114. suggestedLength
  115. } = (0, _network_utils.validateRangeRequestCapabilities)({
  116. getResponseHeader,
  117. isHttp: this._stream.isHttp,
  118. rangeChunkSize: this._rangeChunkSize,
  119. disableRange: this._disableRange
  120. });
  121. this._isRangeSupported = allowRangeRequests;
  122. this._contentLength = suggestedLength || this._contentLength;
  123. this._filename = (0, _network_utils.extractFilenameFromHeader)(getResponseHeader);
  124. if (!this._isStreamingSupported && this._isRangeSupported) {
  125. this.cancel(new _util.AbortException("Streaming is disabled."));
  126. }
  127. }).catch(this._headersCapability.reject);
  128. this.onProgress = null;
  129. }
  130. get headersReady() {
  131. return this._headersCapability.promise;
  132. }
  133. get filename() {
  134. return this._filename;
  135. }
  136. get contentLength() {
  137. return this._contentLength;
  138. }
  139. get isRangeSupported() {
  140. return this._isRangeSupported;
  141. }
  142. get isStreamingSupported() {
  143. return this._isStreamingSupported;
  144. }
  145. async read() {
  146. await this._headersCapability.promise;
  147. const {
  148. value,
  149. done
  150. } = await this._reader.read();
  151. if (done) {
  152. return {
  153. value,
  154. done
  155. };
  156. }
  157. this._loaded += value.byteLength;
  158. this.onProgress?.({
  159. loaded: this._loaded,
  160. total: this._contentLength
  161. });
  162. const buffer = new Uint8Array(value).buffer;
  163. return {
  164. value: buffer,
  165. done: false
  166. };
  167. }
  168. cancel(reason) {
  169. this._reader?.cancel(reason);
  170. this._abortController.abort();
  171. }
  172. }
  173. class PDFFetchStreamRangeReader {
  174. constructor(stream, begin, end) {
  175. this._stream = stream;
  176. this._reader = null;
  177. this._loaded = 0;
  178. const source = stream.source;
  179. this._withCredentials = source.withCredentials || false;
  180. this._readCapability = (0, _util.createPromiseCapability)();
  181. this._isStreamingSupported = !source.disableStream;
  182. this._abortController = new AbortController();
  183. this._headers = createHeaders(this._stream.httpHeaders);
  184. this._headers.append("Range", `bytes=${begin}-${end - 1}`);
  185. const url = source.url;
  186. fetch(url, createFetchOptions(this._headers, this._withCredentials, this._abortController)).then(response => {
  187. if (!(0, _network_utils.validateResponseStatus)(response.status)) {
  188. throw (0, _network_utils.createResponseStatusError)(response.status, url);
  189. }
  190. this._readCapability.resolve();
  191. this._reader = response.body.getReader();
  192. }).catch(this._readCapability.reject);
  193. this.onProgress = null;
  194. }
  195. get isStreamingSupported() {
  196. return this._isStreamingSupported;
  197. }
  198. async read() {
  199. await this._readCapability.promise;
  200. const {
  201. value,
  202. done
  203. } = await this._reader.read();
  204. if (done) {
  205. return {
  206. value,
  207. done
  208. };
  209. }
  210. this._loaded += value.byteLength;
  211. this.onProgress?.({
  212. loaded: this._loaded
  213. });
  214. const buffer = new Uint8Array(value).buffer;
  215. return {
  216. value: buffer,
  217. done: false
  218. };
  219. }
  220. cancel(reason) {
  221. this._reader?.cancel(reason);
  222. this._abortController.abort();
  223. }
  224. }