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.PDFNodeStream = void 0;
|
28 |
|
29 | var _util = require("../shared/util.js");
|
30 |
|
31 | var _network_utils = require("./network_utils.js");
|
32 |
|
33 | ;
|
34 |
|
35 | const fs = require("fs");
|
36 |
|
37 | const http = require("http");
|
38 |
|
39 | const https = require("https");
|
40 |
|
41 | const url = require("url");
|
42 |
|
43 | const fileUriRegex = /^file:\/\/\/[a-zA-Z]:\//;
|
44 |
|
45 | function parseUrl(sourceUrl) {
|
46 | const parsedUrl = url.parse(sourceUrl);
|
47 |
|
48 | if (parsedUrl.protocol === "file:" || parsedUrl.host) {
|
49 | return parsedUrl;
|
50 | }
|
51 |
|
52 | if (/^[a-z]:[/\\]/i.test(sourceUrl)) {
|
53 | return url.parse(`file:///${sourceUrl}`);
|
54 | }
|
55 |
|
56 | if (!parsedUrl.host) {
|
57 | parsedUrl.protocol = "file:";
|
58 | }
|
59 |
|
60 | return parsedUrl;
|
61 | }
|
62 |
|
63 | class PDFNodeStream {
|
64 | constructor(source) {
|
65 | this.source = source;
|
66 | this.url = parseUrl(source.url);
|
67 | this.isHttp = this.url.protocol === "http:" || this.url.protocol === "https:";
|
68 | this.isFsUrl = this.url.protocol === "file:";
|
69 | this.httpHeaders = this.isHttp && source.httpHeaders || {};
|
70 | this._fullRequestReader = null;
|
71 | this._rangeRequestReaders = [];
|
72 | }
|
73 |
|
74 | get _progressiveDataLength() {
|
75 | return this._fullRequestReader?._loaded ?? 0;
|
76 | }
|
77 |
|
78 | getFullReader() {
|
79 | (0, _util.assert)(!this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once.");
|
80 | this._fullRequestReader = this.isFsUrl ? new PDFNodeStreamFsFullReader(this) : new PDFNodeStreamFullReader(this);
|
81 | return this._fullRequestReader;
|
82 | }
|
83 |
|
84 | getRangeReader(start, end) {
|
85 | if (end <= this._progressiveDataLength) {
|
86 | return null;
|
87 | }
|
88 |
|
89 | const rangeReader = this.isFsUrl ? new PDFNodeStreamFsRangeReader(this, start, end) : new PDFNodeStreamRangeReader(this, start, end);
|
90 |
|
91 | this._rangeRequestReaders.push(rangeReader);
|
92 |
|
93 | return rangeReader;
|
94 | }
|
95 |
|
96 | cancelAllRequests(reason) {
|
97 | if (this._fullRequestReader) {
|
98 | this._fullRequestReader.cancel(reason);
|
99 | }
|
100 |
|
101 | for (const reader of this._rangeRequestReaders.slice(0)) {
|
102 | reader.cancel(reason);
|
103 | }
|
104 | }
|
105 |
|
106 | }
|
107 |
|
108 | exports.PDFNodeStream = PDFNodeStream;
|
109 |
|
110 | class BaseFullReader {
|
111 | constructor(stream) {
|
112 | this._url = stream.url;
|
113 | this._done = false;
|
114 | this._storedError = null;
|
115 | this.onProgress = null;
|
116 | const source = stream.source;
|
117 | this._contentLength = source.length;
|
118 | this._loaded = 0;
|
119 | this._filename = null;
|
120 | this._disableRange = source.disableRange || false;
|
121 | this._rangeChunkSize = source.rangeChunkSize;
|
122 |
|
123 | if (!this._rangeChunkSize && !this._disableRange) {
|
124 | this._disableRange = true;
|
125 | }
|
126 |
|
127 | this._isStreamingSupported = !source.disableStream;
|
128 | this._isRangeSupported = !source.disableRange;
|
129 | this._readableStream = null;
|
130 | this._readCapability = (0, _util.createPromiseCapability)();
|
131 | this._headersCapability = (0, _util.createPromiseCapability)();
|
132 | }
|
133 |
|
134 | get headersReady() {
|
135 | return this._headersCapability.promise;
|
136 | }
|
137 |
|
138 | get filename() {
|
139 | return this._filename;
|
140 | }
|
141 |
|
142 | get contentLength() {
|
143 | return this._contentLength;
|
144 | }
|
145 |
|
146 | get isRangeSupported() {
|
147 | return this._isRangeSupported;
|
148 | }
|
149 |
|
150 | get isStreamingSupported() {
|
151 | return this._isStreamingSupported;
|
152 | }
|
153 |
|
154 | async read() {
|
155 | await this._readCapability.promise;
|
156 |
|
157 | if (this._done) {
|
158 | return {
|
159 | value: undefined,
|
160 | done: true
|
161 | };
|
162 | }
|
163 |
|
164 | if (this._storedError) {
|
165 | throw this._storedError;
|
166 | }
|
167 |
|
168 | const chunk = this._readableStream.read();
|
169 |
|
170 | if (chunk === null) {
|
171 | this._readCapability = (0, _util.createPromiseCapability)();
|
172 | return this.read();
|
173 | }
|
174 |
|
175 | this._loaded += chunk.length;
|
176 |
|
177 | if (this.onProgress) {
|
178 | this.onProgress({
|
179 | loaded: this._loaded,
|
180 | total: this._contentLength
|
181 | });
|
182 | }
|
183 |
|
184 | const buffer = new Uint8Array(chunk).buffer;
|
185 | return {
|
186 | value: buffer,
|
187 | done: false
|
188 | };
|
189 | }
|
190 |
|
191 | cancel(reason) {
|
192 | if (!this._readableStream) {
|
193 | this._error(reason);
|
194 |
|
195 | return;
|
196 | }
|
197 |
|
198 | this._readableStream.destroy(reason);
|
199 | }
|
200 |
|
201 | _error(reason) {
|
202 | this._storedError = reason;
|
203 |
|
204 | this._readCapability.resolve();
|
205 | }
|
206 |
|
207 | _setReadableStream(readableStream) {
|
208 | this._readableStream = readableStream;
|
209 | readableStream.on("readable", () => {
|
210 | this._readCapability.resolve();
|
211 | });
|
212 | readableStream.on("end", () => {
|
213 | readableStream.destroy();
|
214 | this._done = true;
|
215 |
|
216 | this._readCapability.resolve();
|
217 | });
|
218 | readableStream.on("error", reason => {
|
219 | this._error(reason);
|
220 | });
|
221 |
|
222 | if (!this._isStreamingSupported && this._isRangeSupported) {
|
223 | this._error(new _util.AbortException("streaming is disabled"));
|
224 | }
|
225 |
|
226 | if (this._storedError) {
|
227 | this._readableStream.destroy(this._storedError);
|
228 | }
|
229 | }
|
230 |
|
231 | }
|
232 |
|
233 | class BaseRangeReader {
|
234 | constructor(stream) {
|
235 | this._url = stream.url;
|
236 | this._done = false;
|
237 | this._storedError = null;
|
238 | this.onProgress = null;
|
239 | this._loaded = 0;
|
240 | this._readableStream = null;
|
241 | this._readCapability = (0, _util.createPromiseCapability)();
|
242 | const source = stream.source;
|
243 | this._isStreamingSupported = !source.disableStream;
|
244 | }
|
245 |
|
246 | get isStreamingSupported() {
|
247 | return this._isStreamingSupported;
|
248 | }
|
249 |
|
250 | async read() {
|
251 | await this._readCapability.promise;
|
252 |
|
253 | if (this._done) {
|
254 | return {
|
255 | value: undefined,
|
256 | done: true
|
257 | };
|
258 | }
|
259 |
|
260 | if (this._storedError) {
|
261 | throw this._storedError;
|
262 | }
|
263 |
|
264 | const chunk = this._readableStream.read();
|
265 |
|
266 | if (chunk === null) {
|
267 | this._readCapability = (0, _util.createPromiseCapability)();
|
268 | return this.read();
|
269 | }
|
270 |
|
271 | this._loaded += chunk.length;
|
272 |
|
273 | if (this.onProgress) {
|
274 | this.onProgress({
|
275 | loaded: this._loaded
|
276 | });
|
277 | }
|
278 |
|
279 | const buffer = new Uint8Array(chunk).buffer;
|
280 | return {
|
281 | value: buffer,
|
282 | done: false
|
283 | };
|
284 | }
|
285 |
|
286 | cancel(reason) {
|
287 | if (!this._readableStream) {
|
288 | this._error(reason);
|
289 |
|
290 | return;
|
291 | }
|
292 |
|
293 | this._readableStream.destroy(reason);
|
294 | }
|
295 |
|
296 | _error(reason) {
|
297 | this._storedError = reason;
|
298 |
|
299 | this._readCapability.resolve();
|
300 | }
|
301 |
|
302 | _setReadableStream(readableStream) {
|
303 | this._readableStream = readableStream;
|
304 | readableStream.on("readable", () => {
|
305 | this._readCapability.resolve();
|
306 | });
|
307 | readableStream.on("end", () => {
|
308 | readableStream.destroy();
|
309 | this._done = true;
|
310 |
|
311 | this._readCapability.resolve();
|
312 | });
|
313 | readableStream.on("error", reason => {
|
314 | this._error(reason);
|
315 | });
|
316 |
|
317 | if (this._storedError) {
|
318 | this._readableStream.destroy(this._storedError);
|
319 | }
|
320 | }
|
321 |
|
322 | }
|
323 |
|
324 | function createRequestOptions(parsedUrl, headers) {
|
325 | return {
|
326 | protocol: parsedUrl.protocol,
|
327 | auth: parsedUrl.auth,
|
328 | host: parsedUrl.hostname,
|
329 | port: parsedUrl.port,
|
330 | path: parsedUrl.path,
|
331 | method: "GET",
|
332 | headers
|
333 | };
|
334 | }
|
335 |
|
336 | class PDFNodeStreamFullReader extends BaseFullReader {
|
337 | constructor(stream) {
|
338 | super(stream);
|
339 |
|
340 | const handleResponse = response => {
|
341 | if (response.statusCode === 404) {
|
342 | const error = new _util.MissingPDFException(`Missing PDF "${this._url}".`);
|
343 | this._storedError = error;
|
344 |
|
345 | this._headersCapability.reject(error);
|
346 |
|
347 | return;
|
348 | }
|
349 |
|
350 | this._headersCapability.resolve();
|
351 |
|
352 | this._setReadableStream(response);
|
353 |
|
354 | const getResponseHeader = name => {
|
355 | return this._readableStream.headers[name.toLowerCase()];
|
356 | };
|
357 |
|
358 | const {
|
359 | allowRangeRequests,
|
360 | suggestedLength
|
361 | } = (0, _network_utils.validateRangeRequestCapabilities)({
|
362 | getResponseHeader,
|
363 | isHttp: stream.isHttp,
|
364 | rangeChunkSize: this._rangeChunkSize,
|
365 | disableRange: this._disableRange
|
366 | });
|
367 | this._isRangeSupported = allowRangeRequests;
|
368 | this._contentLength = suggestedLength || this._contentLength;
|
369 | this._filename = (0, _network_utils.extractFilenameFromHeader)(getResponseHeader);
|
370 | };
|
371 |
|
372 | this._request = null;
|
373 |
|
374 | if (this._url.protocol === "http:") {
|
375 | this._request = http.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);
|
376 | } else {
|
377 | this._request = https.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);
|
378 | }
|
379 |
|
380 | this._request.on("error", reason => {
|
381 | this._storedError = reason;
|
382 |
|
383 | this._headersCapability.reject(reason);
|
384 | });
|
385 |
|
386 | this._request.end();
|
387 | }
|
388 |
|
389 | }
|
390 |
|
391 | class PDFNodeStreamRangeReader extends BaseRangeReader {
|
392 | constructor(stream, start, end) {
|
393 | super(stream);
|
394 | this._httpHeaders = {};
|
395 |
|
396 | for (const property in stream.httpHeaders) {
|
397 | const value = stream.httpHeaders[property];
|
398 |
|
399 | if (typeof value === "undefined") {
|
400 | continue;
|
401 | }
|
402 |
|
403 | this._httpHeaders[property] = value;
|
404 | }
|
405 |
|
406 | this._httpHeaders.Range = `bytes=${start}-${end - 1}`;
|
407 |
|
408 | const handleResponse = response => {
|
409 | if (response.statusCode === 404) {
|
410 | const error = new _util.MissingPDFException(`Missing PDF "${this._url}".`);
|
411 | this._storedError = error;
|
412 | return;
|
413 | }
|
414 |
|
415 | this._setReadableStream(response);
|
416 | };
|
417 |
|
418 | this._request = null;
|
419 |
|
420 | if (this._url.protocol === "http:") {
|
421 | this._request = http.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);
|
422 | } else {
|
423 | this._request = https.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);
|
424 | }
|
425 |
|
426 | this._request.on("error", reason => {
|
427 | this._storedError = reason;
|
428 | });
|
429 |
|
430 | this._request.end();
|
431 | }
|
432 |
|
433 | }
|
434 |
|
435 | class PDFNodeStreamFsFullReader extends BaseFullReader {
|
436 | constructor(stream) {
|
437 | super(stream);
|
438 | let path = decodeURIComponent(this._url.path);
|
439 |
|
440 | if (fileUriRegex.test(this._url.href)) {
|
441 | path = path.replace(/^\//, "");
|
442 | }
|
443 |
|
444 | fs.lstat(path, (error, stat) => {
|
445 | if (error) {
|
446 | if (error.code === "ENOENT") {
|
447 | error = new _util.MissingPDFException(`Missing PDF "${path}".`);
|
448 | }
|
449 |
|
450 | this._storedError = error;
|
451 |
|
452 | this._headersCapability.reject(error);
|
453 |
|
454 | return;
|
455 | }
|
456 |
|
457 | this._contentLength = stat.size;
|
458 |
|
459 | this._setReadableStream(fs.createReadStream(path));
|
460 |
|
461 | this._headersCapability.resolve();
|
462 | });
|
463 | }
|
464 |
|
465 | }
|
466 |
|
467 | class PDFNodeStreamFsRangeReader extends BaseRangeReader {
|
468 | constructor(stream, start, end) {
|
469 | super(stream);
|
470 | let path = decodeURIComponent(this._url.path);
|
471 |
|
472 | if (fileUriRegex.test(this._url.href)) {
|
473 | path = path.replace(/^\//, "");
|
474 | }
|
475 |
|
476 | this._setReadableStream(fs.createReadStream(path, {
|
477 | start,
|
478 | end: end - 1
|
479 | }));
|
480 | }
|
481 |
|
482 | } |
\ | No newline at end of file |