Use Headers consistently in the different IPDFStream implementations
The `Headers` functionality is now available in all browsers/environments that we support, which allows us to consolidate and simplify how the `httpHeaders` API-option is handled; see https://developer.mozilla.org/en-US/docs/Web/API/Headers#browser_compatibility Also, simplifies the old `NetworkManager`-constructor a little bit.
This commit is contained in:
parent
0676ea19cf
commit
d3a94f17cb
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import { AbortException, assert, warn } from "../shared/util.js";
|
import { AbortException, assert, warn } from "../shared/util.js";
|
||||||
import {
|
import {
|
||||||
|
createHeaders,
|
||||||
createResponseStatusError,
|
createResponseStatusError,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
@ -38,18 +39,6 @@ function createFetchOptions(headers, withCredentials, abortController) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHeaders(httpHeaders) {
|
|
||||||
const headers = new Headers();
|
|
||||||
for (const property in httpHeaders) {
|
|
||||||
const value = httpHeaders[property];
|
|
||||||
if (value === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
headers.append(property, value);
|
|
||||||
}
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getArrayBuffer(val) {
|
function getArrayBuffer(val) {
|
||||||
if (val instanceof Uint8Array) {
|
if (val instanceof Uint8Array) {
|
||||||
return val.buffer;
|
return val.buffer;
|
||||||
@ -66,7 +55,7 @@ class PDFFetchStream {
|
|||||||
constructor(source) {
|
constructor(source) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.isHttp = /^https?:/i.test(source.url);
|
this.isHttp = /^https?:/i.test(source.url);
|
||||||
this.httpHeaders = (this.isHttp && source.httpHeaders) || {};
|
this.headers = createHeaders(this.isHttp, source.httpHeaders);
|
||||||
|
|
||||||
this._fullRequestReader = null;
|
this._fullRequestReader = null;
|
||||||
this._rangeRequestReaders = [];
|
this._rangeRequestReaders = [];
|
||||||
@ -123,17 +112,13 @@ class PDFFetchStreamReader {
|
|||||||
this._abortController = new AbortController();
|
this._abortController = new AbortController();
|
||||||
this._isStreamingSupported = !source.disableStream;
|
this._isStreamingSupported = !source.disableStream;
|
||||||
this._isRangeSupported = !source.disableRange;
|
this._isRangeSupported = !source.disableRange;
|
||||||
|
// Always create a copy of the headers.
|
||||||
this._headers = createHeaders(this._stream.httpHeaders);
|
const headers = new Headers(stream.headers);
|
||||||
|
|
||||||
const url = source.url;
|
const url = source.url;
|
||||||
fetch(
|
fetch(
|
||||||
url,
|
url,
|
||||||
createFetchOptions(
|
createFetchOptions(headers, this._withCredentials, this._abortController)
|
||||||
this._headers,
|
|
||||||
this._withCredentials,
|
|
||||||
this._abortController
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!validateResponseStatus(response.status)) {
|
if (!validateResponseStatus(response.status)) {
|
||||||
@ -147,7 +132,7 @@ class PDFFetchStreamReader {
|
|||||||
const { allowRangeRequests, suggestedLength } =
|
const { allowRangeRequests, suggestedLength } =
|
||||||
validateRangeRequestCapabilities({
|
validateRangeRequestCapabilities({
|
||||||
getResponseHeader,
|
getResponseHeader,
|
||||||
isHttp: this._stream.isHttp,
|
isHttp: stream.isHttp,
|
||||||
rangeChunkSize: this._rangeChunkSize,
|
rangeChunkSize: this._rangeChunkSize,
|
||||||
disableRange: this._disableRange,
|
disableRange: this._disableRange,
|
||||||
});
|
});
|
||||||
@ -222,17 +207,14 @@ class PDFFetchStreamRangeReader {
|
|||||||
this._isStreamingSupported = !source.disableStream;
|
this._isStreamingSupported = !source.disableStream;
|
||||||
|
|
||||||
this._abortController = new AbortController();
|
this._abortController = new AbortController();
|
||||||
this._headers = createHeaders(this._stream.httpHeaders);
|
// Always create a copy of the headers.
|
||||||
this._headers.append("Range", `bytes=${begin}-${end - 1}`);
|
const headers = new Headers(stream.headers);
|
||||||
|
headers.append("Range", `bytes=${begin}-${end - 1}`);
|
||||||
|
|
||||||
const url = source.url;
|
const url = source.url;
|
||||||
fetch(
|
fetch(
|
||||||
url,
|
url,
|
||||||
createFetchOptions(
|
createFetchOptions(headers, this._withCredentials, this._abortController)
|
||||||
this._headers,
|
|
||||||
this._withCredentials,
|
|
||||||
this._abortController
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!validateResponseStatus(response.status)) {
|
if (!validateResponseStatus(response.status)) {
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import { assert, stringToBytes } from "../shared/util.js";
|
import { assert, stringToBytes } from "../shared/util.js";
|
||||||
import {
|
import {
|
||||||
|
createHeaders,
|
||||||
createResponseStatusError,
|
createResponseStatusError,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
@ -38,11 +39,11 @@ function getArrayBuffer(xhr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NetworkManager {
|
class NetworkManager {
|
||||||
constructor(url, args = {}) {
|
constructor({ url, httpHeaders, withCredentials }) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.isHttp = /^https?:/i.test(url);
|
this.isHttp = /^https?:/i.test(url);
|
||||||
this.httpHeaders = (this.isHttp && args.httpHeaders) || Object.create(null);
|
this.headers = createHeaders(this.isHttp, httpHeaders);
|
||||||
this.withCredentials = args.withCredentials || false;
|
this.withCredentials = withCredentials || false;
|
||||||
|
|
||||||
this.currXhrId = 0;
|
this.currXhrId = 0;
|
||||||
this.pendingRequests = Object.create(null);
|
this.pendingRequests = Object.create(null);
|
||||||
@ -70,12 +71,8 @@ class NetworkManager {
|
|||||||
|
|
||||||
xhr.open("GET", this.url);
|
xhr.open("GET", this.url);
|
||||||
xhr.withCredentials = this.withCredentials;
|
xhr.withCredentials = this.withCredentials;
|
||||||
for (const property in this.httpHeaders) {
|
for (const [key, val] of this.headers) {
|
||||||
const value = this.httpHeaders[property];
|
xhr.setRequestHeader(key, val);
|
||||||
if (value === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader(property, value);
|
|
||||||
}
|
}
|
||||||
if (this.isHttp && "begin" in args && "end" in args) {
|
if (this.isHttp && "begin" in args && "end" in args) {
|
||||||
xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
|
xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
|
||||||
@ -194,10 +191,7 @@ class NetworkManager {
|
|||||||
class PDFNetworkStream {
|
class PDFNetworkStream {
|
||||||
constructor(source) {
|
constructor(source) {
|
||||||
this._source = source;
|
this._source = source;
|
||||||
this._manager = new NetworkManager(source.url, {
|
this._manager = new NetworkManager(source);
|
||||||
httpHeaders: source.httpHeaders,
|
|
||||||
withCredentials: source.withCredentials,
|
|
||||||
});
|
|
||||||
this._rangeChunkSize = source.rangeChunkSize;
|
this._rangeChunkSize = source.rangeChunkSize;
|
||||||
this._fullRequestReader = null;
|
this._fullRequestReader = null;
|
||||||
this._rangeRequestReaders = [];
|
this._rangeRequestReaders = [];
|
||||||
|
|||||||
@ -21,6 +21,21 @@ import {
|
|||||||
import { getFilenameFromContentDispositionHeader } from "./content_disposition.js";
|
import { getFilenameFromContentDispositionHeader } from "./content_disposition.js";
|
||||||
import { isPdfFile } from "./display_utils.js";
|
import { isPdfFile } from "./display_utils.js";
|
||||||
|
|
||||||
|
function createHeaders(isHttp, httpHeaders) {
|
||||||
|
const headers = new Headers();
|
||||||
|
|
||||||
|
if (!isHttp || !httpHeaders || typeof httpHeaders !== "object") {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
for (const key in httpHeaders) {
|
||||||
|
const val = httpHeaders[key];
|
||||||
|
if (val !== undefined) {
|
||||||
|
headers.append(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
function validateRangeRequestCapabilities({
|
function validateRangeRequestCapabilities({
|
||||||
getResponseHeader,
|
getResponseHeader,
|
||||||
isHttp,
|
isHttp,
|
||||||
@ -98,6 +113,7 @@ function validateResponseStatus(status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
createHeaders,
|
||||||
createResponseStatusError,
|
createResponseStatusError,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import { AbortException, assert, MissingPDFException } from "../shared/util.js";
|
import { AbortException, assert, MissingPDFException } from "../shared/util.js";
|
||||||
import {
|
import {
|
||||||
|
createHeaders,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
} from "./network_utils.js";
|
} from "./network_utils.js";
|
||||||
@ -53,7 +54,7 @@ class PDFNodeStream {
|
|||||||
this.url.protocol === "http:" || this.url.protocol === "https:";
|
this.url.protocol === "http:" || this.url.protocol === "https:";
|
||||||
// Check if url refers to filesystem.
|
// Check if url refers to filesystem.
|
||||||
this.isFsUrl = this.url.protocol === "file:";
|
this.isFsUrl = this.url.protocol === "file:";
|
||||||
this.httpHeaders = (this.isHttp && source.httpHeaders) || {};
|
this.headers = createHeaders(this.isHttp, source.httpHeaders);
|
||||||
|
|
||||||
this._fullRequestReader = null;
|
this._fullRequestReader = null;
|
||||||
this._rangeRequestReaders = [];
|
this._rangeRequestReaders = [];
|
||||||
@ -291,6 +292,9 @@ class PDFNodeStreamFullReader extends BaseFullReader {
|
|||||||
constructor(stream) {
|
constructor(stream) {
|
||||||
super(stream);
|
super(stream);
|
||||||
|
|
||||||
|
// Node.js requires the `headers` to be a regular Object.
|
||||||
|
const headers = Object.fromEntries(stream.headers);
|
||||||
|
|
||||||
const handleResponse = response => {
|
const handleResponse = response => {
|
||||||
if (response.statusCode === 404) {
|
if (response.statusCode === 404) {
|
||||||
const error = new MissingPDFException(`Missing PDF "${this._url}".`);
|
const error = new MissingPDFException(`Missing PDF "${this._url}".`);
|
||||||
@ -321,11 +325,7 @@ class PDFNodeStreamFullReader extends BaseFullReader {
|
|||||||
this._filename = extractFilenameFromHeader(getResponseHeader);
|
this._filename = extractFilenameFromHeader(getResponseHeader);
|
||||||
};
|
};
|
||||||
|
|
||||||
this._request = createRequest(
|
this._request = createRequest(this._url, headers, handleResponse);
|
||||||
this._url,
|
|
||||||
stream.httpHeaders,
|
|
||||||
handleResponse
|
|
||||||
);
|
|
||||||
|
|
||||||
this._request.on("error", reason => {
|
this._request.on("error", reason => {
|
||||||
this._storedError = reason;
|
this._storedError = reason;
|
||||||
@ -342,15 +342,9 @@ class PDFNodeStreamRangeReader extends BaseRangeReader {
|
|||||||
constructor(stream, start, end) {
|
constructor(stream, start, end) {
|
||||||
super(stream);
|
super(stream);
|
||||||
|
|
||||||
this._httpHeaders = {};
|
// Node.js requires the `headers` to be a regular Object.
|
||||||
for (const property in stream.httpHeaders) {
|
const headers = Object.fromEntries(stream.headers);
|
||||||
const value = stream.httpHeaders[property];
|
headers.Range = `bytes=${start}-${end - 1}`;
|
||||||
if (value === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this._httpHeaders[property] = value;
|
|
||||||
}
|
|
||||||
this._httpHeaders.Range = `bytes=${start}-${end - 1}`;
|
|
||||||
|
|
||||||
const handleResponse = response => {
|
const handleResponse = response => {
|
||||||
if (response.statusCode === 404) {
|
if (response.statusCode === 404) {
|
||||||
@ -361,7 +355,7 @@ class PDFNodeStreamRangeReader extends BaseRangeReader {
|
|||||||
this._setReadableStream(response);
|
this._setReadableStream(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
this._request = createRequest(this._url, this._httpHeaders, handleResponse);
|
this._request = createRequest(this._url, headers, handleResponse);
|
||||||
|
|
||||||
this._request.on("error", reason => {
|
this._request.on("error", reason => {
|
||||||
this._storedError = reason;
|
this._storedError = reason;
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
createHeaders,
|
||||||
createResponseStatusError,
|
createResponseStatusError,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
@ -25,6 +26,44 @@ import {
|
|||||||
} from "../../src/shared/util.js";
|
} from "../../src/shared/util.js";
|
||||||
|
|
||||||
describe("network_utils", function () {
|
describe("network_utils", function () {
|
||||||
|
describe("createHeaders", function () {
|
||||||
|
it("returns empty `Headers` for invalid input", function () {
|
||||||
|
const headersArr = [
|
||||||
|
createHeaders(
|
||||||
|
/* isHttp = */ false,
|
||||||
|
/* httpHeaders = */ { "Content-Length": 100 }
|
||||||
|
),
|
||||||
|
createHeaders(/* isHttp = */ true, /* httpHeaders = */ undefined),
|
||||||
|
createHeaders(/* isHttp = */ true, /* httpHeaders = */ null),
|
||||||
|
createHeaders(/* isHttp = */ true, /* httpHeaders = */ "abc"),
|
||||||
|
createHeaders(/* isHttp = */ true, /* httpHeaders = */ 123),
|
||||||
|
];
|
||||||
|
const emptyObj = Object.create(null);
|
||||||
|
|
||||||
|
for (const headers of headersArr) {
|
||||||
|
expect(Object.fromEntries(headers)).toEqual(emptyObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns populated `Headers` for valid input", function () {
|
||||||
|
const headers = createHeaders(
|
||||||
|
/* isHttp = */ true,
|
||||||
|
/* httpHeaders = */ {
|
||||||
|
"Content-Length": 100,
|
||||||
|
"Accept-Ranges": "bytes",
|
||||||
|
"Dummy-null": null,
|
||||||
|
"Dummy-undefined": undefined,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(Object.fromEntries(headers)).toEqual({
|
||||||
|
"content-length": "100",
|
||||||
|
"accept-ranges": "bytes",
|
||||||
|
"dummy-null": "null",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("validateRangeRequestCapabilities", function () {
|
describe("validateRangeRequestCapabilities", function () {
|
||||||
it("rejects invalid rangeChunkSize", function () {
|
it("rejects invalid rangeChunkSize", function () {
|
||||||
expect(function () {
|
expect(function () {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user