1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | Object.defineProperty(exports, "__esModule", {
|
25 | value: true
|
26 | });
|
27 | exports.PDFNetworkStream = void 0;
|
28 |
|
29 | var _util = require("../shared/util.js");
|
30 |
|
31 | var _network_utils = require("./network_utils.js");
|
32 |
|
33 | ;
|
34 | const OK_RESPONSE = 200;
|
35 | const PARTIAL_CONTENT_RESPONSE = 206;
|
36 |
|
37 | function getArrayBuffer(xhr) {
|
38 | const data = xhr.response;
|
39 |
|
40 | if (typeof data !== "string") {
|
41 | return data;
|
42 | }
|
43 |
|
44 | const array = (0, _util.stringToBytes)(data);
|
45 | return array.buffer;
|
46 | }
|
47 |
|
48 | class NetworkManager {
|
49 | constructor(url, args = {}) {
|
50 | this.url = url;
|
51 | this.isHttp = /^https?:/i.test(url);
|
52 | this.httpHeaders = this.isHttp && args.httpHeaders || Object.create(null);
|
53 | this.withCredentials = args.withCredentials || false;
|
54 |
|
55 | this.getXhr = args.getXhr || function NetworkManager_getXhr() {
|
56 | return new XMLHttpRequest();
|
57 | };
|
58 |
|
59 | this.currXhrId = 0;
|
60 | this.pendingRequests = Object.create(null);
|
61 | }
|
62 |
|
63 | requestRange(begin, end, listeners) {
|
64 | const args = {
|
65 | begin,
|
66 | end
|
67 | };
|
68 |
|
69 | for (const prop in listeners) {
|
70 | args[prop] = listeners[prop];
|
71 | }
|
72 |
|
73 | return this.request(args);
|
74 | }
|
75 |
|
76 | requestFull(listeners) {
|
77 | return this.request(listeners);
|
78 | }
|
79 |
|
80 | request(args) {
|
81 | const xhr = this.getXhr();
|
82 | const xhrId = this.currXhrId++;
|
83 | const pendingRequest = this.pendingRequests[xhrId] = {
|
84 | xhr
|
85 | };
|
86 | xhr.open("GET", this.url);
|
87 | xhr.withCredentials = this.withCredentials;
|
88 |
|
89 | for (const property in this.httpHeaders) {
|
90 | const value = this.httpHeaders[property];
|
91 |
|
92 | if (typeof value === "undefined") {
|
93 | continue;
|
94 | }
|
95 |
|
96 | xhr.setRequestHeader(property, value);
|
97 | }
|
98 |
|
99 | if (this.isHttp && "begin" in args && "end" in args) {
|
100 | xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
|
101 | pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE;
|
102 | } else {
|
103 | pendingRequest.expectedStatus = OK_RESPONSE;
|
104 | }
|
105 |
|
106 | xhr.responseType = "arraybuffer";
|
107 |
|
108 | if (args.onError) {
|
109 | xhr.onerror = function (evt) {
|
110 | args.onError(xhr.status);
|
111 | };
|
112 | }
|
113 |
|
114 | xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
|
115 | xhr.onprogress = this.onProgress.bind(this, xhrId);
|
116 | pendingRequest.onHeadersReceived = args.onHeadersReceived;
|
117 | pendingRequest.onDone = args.onDone;
|
118 | pendingRequest.onError = args.onError;
|
119 | pendingRequest.onProgress = args.onProgress;
|
120 | xhr.send(null);
|
121 | return xhrId;
|
122 | }
|
123 |
|
124 | onProgress(xhrId, evt) {
|
125 | const pendingRequest = this.pendingRequests[xhrId];
|
126 |
|
127 | if (!pendingRequest) {
|
128 | return;
|
129 | }
|
130 |
|
131 | pendingRequest.onProgress?.(evt);
|
132 | }
|
133 |
|
134 | onStateChange(xhrId, evt) {
|
135 | const pendingRequest = this.pendingRequests[xhrId];
|
136 |
|
137 | if (!pendingRequest) {
|
138 | return;
|
139 | }
|
140 |
|
141 | const xhr = pendingRequest.xhr;
|
142 |
|
143 | if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
|
144 | pendingRequest.onHeadersReceived();
|
145 | delete pendingRequest.onHeadersReceived;
|
146 | }
|
147 |
|
148 | if (xhr.readyState !== 4) {
|
149 | return;
|
150 | }
|
151 |
|
152 | if (!(xhrId in this.pendingRequests)) {
|
153 | return;
|
154 | }
|
155 |
|
156 | delete this.pendingRequests[xhrId];
|
157 |
|
158 | if (xhr.status === 0 && this.isHttp) {
|
159 | pendingRequest.onError?.(xhr.status);
|
160 | return;
|
161 | }
|
162 |
|
163 | const xhrStatus = xhr.status || OK_RESPONSE;
|
164 | const ok_response_on_range_request = xhrStatus === OK_RESPONSE && pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
|
165 |
|
166 | if (!ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus) {
|
167 | pendingRequest.onError?.(xhr.status);
|
168 | return;
|
169 | }
|
170 |
|
171 | const chunk = getArrayBuffer(xhr);
|
172 |
|
173 | if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
|
174 | const rangeHeader = xhr.getResponseHeader("Content-Range");
|
175 | const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
|
176 | pendingRequest.onDone({
|
177 | begin: parseInt(matches[1], 10),
|
178 | chunk
|
179 | });
|
180 | } else if (chunk) {
|
181 | pendingRequest.onDone({
|
182 | begin: 0,
|
183 | chunk
|
184 | });
|
185 | } else {
|
186 | pendingRequest.onError?.(xhr.status);
|
187 | }
|
188 | }
|
189 |
|
190 | getRequestXhr(xhrId) {
|
191 | return this.pendingRequests[xhrId].xhr;
|
192 | }
|
193 |
|
194 | isPendingRequest(xhrId) {
|
195 | return xhrId in this.pendingRequests;
|
196 | }
|
197 |
|
198 | abortRequest(xhrId) {
|
199 | const xhr = this.pendingRequests[xhrId].xhr;
|
200 | delete this.pendingRequests[xhrId];
|
201 | xhr.abort();
|
202 | }
|
203 |
|
204 | }
|
205 |
|
206 | class PDFNetworkStream {
|
207 | constructor(source) {
|
208 | this._source = source;
|
209 | this._manager = new NetworkManager(source.url, {
|
210 | httpHeaders: source.httpHeaders,
|
211 | withCredentials: source.withCredentials
|
212 | });
|
213 | this._rangeChunkSize = source.rangeChunkSize;
|
214 | this._fullRequestReader = null;
|
215 | this._rangeRequestReaders = [];
|
216 | }
|
217 |
|
218 | _onRangeRequestReaderClosed(reader) {
|
219 | const i = this._rangeRequestReaders.indexOf(reader);
|
220 |
|
221 | if (i >= 0) {
|
222 | this._rangeRequestReaders.splice(i, 1);
|
223 | }
|
224 | }
|
225 |
|
226 | getFullReader() {
|
227 | (0, _util.assert)(!this._fullRequestReader, "PDFNetworkStream.getFullReader can only be called once.");
|
228 | this._fullRequestReader = new PDFNetworkStreamFullRequestReader(this._manager, this._source);
|
229 | return this._fullRequestReader;
|
230 | }
|
231 |
|
232 | getRangeReader(begin, end) {
|
233 | const reader = new PDFNetworkStreamRangeRequestReader(this._manager, begin, end);
|
234 | reader.onClosed = this._onRangeRequestReaderClosed.bind(this);
|
235 |
|
236 | this._rangeRequestReaders.push(reader);
|
237 |
|
238 | return reader;
|
239 | }
|
240 |
|
241 | cancelAllRequests(reason) {
|
242 | this._fullRequestReader?.cancel(reason);
|
243 |
|
244 | for (const reader of this._rangeRequestReaders.slice(0)) {
|
245 | reader.cancel(reason);
|
246 | }
|
247 | }
|
248 |
|
249 | }
|
250 |
|
251 | exports.PDFNetworkStream = PDFNetworkStream;
|
252 |
|
253 | class PDFNetworkStreamFullRequestReader {
|
254 | constructor(manager, source) {
|
255 | this._manager = manager;
|
256 | const args = {
|
257 | onHeadersReceived: this._onHeadersReceived.bind(this),
|
258 | onDone: this._onDone.bind(this),
|
259 | onError: this._onError.bind(this),
|
260 | onProgress: this._onProgress.bind(this)
|
261 | };
|
262 | this._url = source.url;
|
263 | this._fullRequestId = manager.requestFull(args);
|
264 | this._headersReceivedCapability = (0, _util.createPromiseCapability)();
|
265 | this._disableRange = source.disableRange || false;
|
266 | this._contentLength = source.length;
|
267 | this._rangeChunkSize = source.rangeChunkSize;
|
268 |
|
269 | if (!this._rangeChunkSize && !this._disableRange) {
|
270 | this._disableRange = true;
|
271 | }
|
272 |
|
273 | this._isStreamingSupported = false;
|
274 | this._isRangeSupported = false;
|
275 | this._cachedChunks = [];
|
276 | this._requests = [];
|
277 | this._done = false;
|
278 | this._storedError = undefined;
|
279 | this._filename = null;
|
280 | this.onProgress = null;
|
281 | }
|
282 |
|
283 | _onHeadersReceived() {
|
284 | const fullRequestXhrId = this._fullRequestId;
|
285 |
|
286 | const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
|
287 |
|
288 | const getResponseHeader = name => {
|
289 | return fullRequestXhr.getResponseHeader(name);
|
290 | };
|
291 |
|
292 | const {
|
293 | allowRangeRequests,
|
294 | suggestedLength
|
295 | } = (0, _network_utils.validateRangeRequestCapabilities)({
|
296 | getResponseHeader,
|
297 | isHttp: this._manager.isHttp,
|
298 | rangeChunkSize: this._rangeChunkSize,
|
299 | disableRange: this._disableRange
|
300 | });
|
301 |
|
302 | if (allowRangeRequests) {
|
303 | this._isRangeSupported = true;
|
304 | }
|
305 |
|
306 | this._contentLength = suggestedLength || this._contentLength;
|
307 | this._filename = (0, _network_utils.extractFilenameFromHeader)(getResponseHeader);
|
308 |
|
309 | if (this._isRangeSupported) {
|
310 | this._manager.abortRequest(fullRequestXhrId);
|
311 | }
|
312 |
|
313 | this._headersReceivedCapability.resolve();
|
314 | }
|
315 |
|
316 | _onDone(data) {
|
317 | if (data) {
|
318 | if (this._requests.length > 0) {
|
319 | const requestCapability = this._requests.shift();
|
320 |
|
321 | requestCapability.resolve({
|
322 | value: data.chunk,
|
323 | done: false
|
324 | });
|
325 | } else {
|
326 | this._cachedChunks.push(data.chunk);
|
327 | }
|
328 | }
|
329 |
|
330 | this._done = true;
|
331 |
|
332 | if (this._cachedChunks.length > 0) {
|
333 | return;
|
334 | }
|
335 |
|
336 | for (const requestCapability of this._requests) {
|
337 | requestCapability.resolve({
|
338 | value: undefined,
|
339 | done: true
|
340 | });
|
341 | }
|
342 |
|
343 | this._requests.length = 0;
|
344 | }
|
345 |
|
346 | _onError(status) {
|
347 | this._storedError = (0, _network_utils.createResponseStatusError)(status, this._url);
|
348 |
|
349 | this._headersReceivedCapability.reject(this._storedError);
|
350 |
|
351 | for (const requestCapability of this._requests) {
|
352 | requestCapability.reject(this._storedError);
|
353 | }
|
354 |
|
355 | this._requests.length = 0;
|
356 | this._cachedChunks.length = 0;
|
357 | }
|
358 |
|
359 | _onProgress(evt) {
|
360 | this.onProgress?.({
|
361 | loaded: evt.loaded,
|
362 | total: evt.lengthComputable ? evt.total : this._contentLength
|
363 | });
|
364 | }
|
365 |
|
366 | get filename() {
|
367 | return this._filename;
|
368 | }
|
369 |
|
370 | get isRangeSupported() {
|
371 | return this._isRangeSupported;
|
372 | }
|
373 |
|
374 | get isStreamingSupported() {
|
375 | return this._isStreamingSupported;
|
376 | }
|
377 |
|
378 | get contentLength() {
|
379 | return this._contentLength;
|
380 | }
|
381 |
|
382 | get headersReady() {
|
383 | return this._headersReceivedCapability.promise;
|
384 | }
|
385 |
|
386 | async read() {
|
387 | if (this._storedError) {
|
388 | throw this._storedError;
|
389 | }
|
390 |
|
391 | if (this._cachedChunks.length > 0) {
|
392 | const chunk = this._cachedChunks.shift();
|
393 |
|
394 | return {
|
395 | value: chunk,
|
396 | done: false
|
397 | };
|
398 | }
|
399 |
|
400 | if (this._done) {
|
401 | return {
|
402 | value: undefined,
|
403 | done: true
|
404 | };
|
405 | }
|
406 |
|
407 | const requestCapability = (0, _util.createPromiseCapability)();
|
408 |
|
409 | this._requests.push(requestCapability);
|
410 |
|
411 | return requestCapability.promise;
|
412 | }
|
413 |
|
414 | cancel(reason) {
|
415 | this._done = true;
|
416 |
|
417 | this._headersReceivedCapability.reject(reason);
|
418 |
|
419 | for (const requestCapability of this._requests) {
|
420 | requestCapability.resolve({
|
421 | value: undefined,
|
422 | done: true
|
423 | });
|
424 | }
|
425 |
|
426 | this._requests.length = 0;
|
427 |
|
428 | if (this._manager.isPendingRequest(this._fullRequestId)) {
|
429 | this._manager.abortRequest(this._fullRequestId);
|
430 | }
|
431 |
|
432 | this._fullRequestReader = null;
|
433 | }
|
434 |
|
435 | }
|
436 |
|
437 | class PDFNetworkStreamRangeRequestReader {
|
438 | constructor(manager, begin, end) {
|
439 | this._manager = manager;
|
440 | const args = {
|
441 | onDone: this._onDone.bind(this),
|
442 | onError: this._onError.bind(this),
|
443 | onProgress: this._onProgress.bind(this)
|
444 | };
|
445 | this._url = manager.url;
|
446 | this._requestId = manager.requestRange(begin, end, args);
|
447 | this._requests = [];
|
448 | this._queuedChunk = null;
|
449 | this._done = false;
|
450 | this._storedError = undefined;
|
451 | this.onProgress = null;
|
452 | this.onClosed = null;
|
453 | }
|
454 |
|
455 | _close() {
|
456 | this.onClosed?.(this);
|
457 | }
|
458 |
|
459 | _onDone(data) {
|
460 | const chunk = data.chunk;
|
461 |
|
462 | if (this._requests.length > 0) {
|
463 | const requestCapability = this._requests.shift();
|
464 |
|
465 | requestCapability.resolve({
|
466 | value: chunk,
|
467 | done: false
|
468 | });
|
469 | } else {
|
470 | this._queuedChunk = chunk;
|
471 | }
|
472 |
|
473 | this._done = true;
|
474 |
|
475 | for (const requestCapability of this._requests) {
|
476 | requestCapability.resolve({
|
477 | value: undefined,
|
478 | done: true
|
479 | });
|
480 | }
|
481 |
|
482 | this._requests.length = 0;
|
483 |
|
484 | this._close();
|
485 | }
|
486 |
|
487 | _onError(status) {
|
488 | this._storedError = (0, _network_utils.createResponseStatusError)(status, this._url);
|
489 |
|
490 | for (const requestCapability of this._requests) {
|
491 | requestCapability.reject(this._storedError);
|
492 | }
|
493 |
|
494 | this._requests.length = 0;
|
495 | this._queuedChunk = null;
|
496 | }
|
497 |
|
498 | _onProgress(evt) {
|
499 | if (!this.isStreamingSupported) {
|
500 | this.onProgress?.({
|
501 | loaded: evt.loaded
|
502 | });
|
503 | }
|
504 | }
|
505 |
|
506 | get isStreamingSupported() {
|
507 | return false;
|
508 | }
|
509 |
|
510 | async read() {
|
511 | if (this._storedError) {
|
512 | throw this._storedError;
|
513 | }
|
514 |
|
515 | if (this._queuedChunk !== null) {
|
516 | const chunk = this._queuedChunk;
|
517 | this._queuedChunk = null;
|
518 | return {
|
519 | value: chunk,
|
520 | done: false
|
521 | };
|
522 | }
|
523 |
|
524 | if (this._done) {
|
525 | return {
|
526 | value: undefined,
|
527 | done: true
|
528 | };
|
529 | }
|
530 |
|
531 | const requestCapability = (0, _util.createPromiseCapability)();
|
532 |
|
533 | this._requests.push(requestCapability);
|
534 |
|
535 | return requestCapability.promise;
|
536 | }
|
537 |
|
538 | cancel(reason) {
|
539 | this._done = true;
|
540 |
|
541 | for (const requestCapability of this._requests) {
|
542 | requestCapability.resolve({
|
543 | value: undefined,
|
544 | done: true
|
545 | });
|
546 | }
|
547 |
|
548 | this._requests.length = 0;
|
549 |
|
550 | if (this._manager.isPendingRequest(this._requestId)) {
|
551 | this._manager.abortRequest(this._requestId);
|
552 | }
|
553 |
|
554 | this._close();
|
555 | }
|
556 |
|
557 | } |
\ | No newline at end of file |