diff --git a/src/display/fetch_stream.js b/src/display/fetch_stream.js new file mode 100644 index 00000000000000..9c8637af7c31f1 --- /dev/null +++ b/src/display/fetch_stream.js @@ -0,0 +1,197 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { assert, createPromiseCapability } from '../shared/util'; +import { validateRangeRequestCapabilities } from './network_utils'; + +class PDFFetchStream { + constructor(options) { + this.options = options; + this.source = options.source; + this.isHttp = /^https?:/i.test(this.source.url); + this.httpHeaders = (this.isHttp && this.source.httpHeaders) || {}; + + this._fullRequestReader = null; + this._rangeRequestReaders = []; + } + + getFullReader() { + assert(!this._fullRequestReader); + this._fullRequestReader = new PDFFetchStreamReader(this); + return this._fullRequestReader; + } + + getRangeReader(begin, end) { + let reader = new PDFFetchStreamRangeReader(this, begin, end); + this._rangeRequestReaders.push(reader); + return reader; + } + + cancelAllRequests(reason) { + if (this._fullRequestReader) { + this._fullRequestReader.cancel(reason); + } + let readers = this._rangeRequestReaders.slice(0); + readers.forEach(function(reader) { + reader.cancel(reason); + }); + } +} + +class PDFFetchStreamReader { + constructor(stream) { + this._stream = stream; + this._readableStream = null; + this._reader = null; + this._loaded = 0; + this._contentLength = this._stream.source.length; + this._headersCapability = createPromiseCapability(); + this._disableRange = this._stream.options.disableRange; + this._rangeChunkSize = this._stream.source.rangeChunkSize; + if (!this._rangeChunkSize && !this._disableRange) { + this._disableRange = true; + } + + this._isRangeSupported = !this._stream.options.disableRange; + this._isStreamingSupported = !this._stream.source.disableStream; + + this._headers = new Headers(); + for (let property in this._stream.httpHeaders) { + let value = this._stream.httpHeaders[property]; + if (typeof value === 'undefined') { + continue; + } + this._headers.append(property, value); + } + + fetch(this._stream.source.url, { + method: 'GET', + headers: this._headers, + }).then((response) => { + this._headersCapability.resolve(); + this._readableStream = response.body; + this._reader = this._readableStream.getReader(); + + let { allowRangeRequests, suggestedLength, } = + validateRangeRequestCapabilities({ + getResponseHeader: (name) => { + return response.headers.get(name); + }, + isHttp: this._stream.isHttp, + rangeChunkSize: this._rangeChunkSize, + disableRange: this._disableRange, + }); + + this._contentLength = suggestedLength; + this._isRangeSupported = allowRangeRequests; + }).catch(this._headersCapability.reject); + + this.onProgress = null; + } + + get headersReady() { + return this._headersCapability.promise; + } + + get contentLength() { + return this._contentLength; + } + + get isRangeSupported() { + return this._isRangeSupported; + } + + get isStreamingSupported() { + return this._isStreamingSupported; + } + + read() { + return this._reader.read().then(({ value, done, }) => { + if (done) { + return Promise.resolve({ value, done, }); + } + this._loaded += value.byteLength; + if (this.onProgress) { + this.onProgress({ + loaded: this._loaded, + total: this._contentLength, + }); + } + return Promise.resolve({ value, done, }); + }); + } + + cancel(reason) { + this._reader.cancel(reason); + } +} + +class PDFFetchStreamRangeReader { + constructor(stream, begin, end) { + this._stream = stream; + this._readableStream = null; + this._reader = null; + this._loaded = 0; + + this._isStreamingSupported = !stream.source.disableStream; + + this._headers = new Headers(); + for (let property in this._stream.httpHeaders) { + let value = this._stream.httpHeaders[property]; + if (typeof value === 'undefined') { + continue; + } + this._headers.append(property, value); + } + + let rangeStr = begin + '-' + (end - 1); + this._headers.append('Range', rangeStr); + + fetch(this._stream.source.url, { + method: 'GET', + headers: this._headers, + }).then((response) => { + this._readableStream = response.body; + this._reader = this._readableStream.getReader(); + }); + + this.onProgress = null; + } + + get isStreamingSupported() { + return this._isStreamingSupported; + } + + read() { + return this._reader.read().then(({ value, done, }) => { + if (done) { + return Promise.resolve({ value, done, }); + } + this._loaded += value.byteLength; + if (this.onProgress) { + this.onProgress({ loaded: this._loaded, }); + } + return Promise.resolve({ value, done, }); + }); + } + + cancel(reason) { + this._reader.cancel(reason); + } +} + +export { + PDFFetchStream, +};