Skip to content

Commit 4d9301f

Browse files
committed
Simplify the NetworkManager class
Store pending requests in a `WeakMap`, which allows working directly with the `XMLHttpRequest`-data and removes the need for a couple of methods. Simplify the `PDFNetworkStreamRangeRequestReader.prototype._onDone` method, since after removing `moz-chunked-arraybuffer` support (see PR 10678) it's never called without an argument. Stop sending the `begin`-property to the `onDone`-callbacks since it's unused. Originally this code was shared with the Firefox PDF Viewer, many years ago, and then that property mattered. Re-factor the `status` validation, to avoid checking for a "bad" range-request response unconditionally.
1 parent 95f62f3 commit 4d9301f

1 file changed

Lines changed: 58 additions & 95 deletions

File tree

src/display/network.js

Lines changed: 58 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,32 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
3131
const OK_RESPONSE = 200;
3232
const PARTIAL_CONTENT_RESPONSE = 206;
3333

34-
function getArrayBuffer(xhr) {
35-
const data = xhr.response;
36-
if (typeof data !== "string") {
37-
return data;
38-
}
39-
return stringToBytes(data).buffer;
34+
function getArrayBuffer(val) {
35+
return typeof val !== "string" ? val : stringToBytes(val).buffer;
4036
}
4137

4238
class NetworkManager {
39+
#pendingRequests = new WeakMap();
40+
4341
_responseOrigin = null;
4442

4543
constructor({ url, httpHeaders, withCredentials }) {
4644
this.url = url;
4745
this.isHttp = /^https?:/i.test(url);
4846
this.headers = createHeaders(this.isHttp, httpHeaders);
4947
this.withCredentials = withCredentials || false;
50-
51-
this.currXhrId = 0;
52-
this.pendingRequests = Object.create(null);
5348
}
5449

5550
request(args) {
5651
const xhr = new XMLHttpRequest();
57-
const xhrId = this.currXhrId++;
58-
const pendingRequest = (this.pendingRequests[xhrId] = { xhr });
52+
const pendingRequest = {
53+
validateStatus: null,
54+
onHeadersReceived: args.onHeadersReceived,
55+
onDone: args.onDone,
56+
onError: args.onError,
57+
onProgress: args.onProgress,
58+
};
59+
this.#pendingRequests.set(xhr, pendingRequest);
5960

6061
xhr.open("GET", this.url);
6162
xhr.withCredentials = this.withCredentials;
@@ -64,44 +65,38 @@ class NetworkManager {
6465
}
6566
if (this.isHttp && "begin" in args && "end" in args) {
6667
xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
67-
pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE;
68+
69+
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
70+
// "A server MAY ignore the Range header". This means it's possible to
71+
// get a 200 rather than a 206 response from a range request.
72+
pendingRequest.validateStatus = status =>
73+
status === PARTIAL_CONTENT_RESPONSE || status === OK_RESPONSE;
6874
} else {
69-
pendingRequest.expectedStatus = OK_RESPONSE;
75+
pendingRequest.validateStatus = status => status === OK_RESPONSE;
7076
}
7177
xhr.responseType = "arraybuffer";
7278

7379
assert(args.onError, "Expected `onError` callback to be provided.");
74-
xhr.onerror = () => {
75-
args.onError(xhr.status);
76-
};
77-
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
78-
xhr.onprogress = this.onProgress.bind(this, xhrId);
79-
80-
pendingRequest.onHeadersReceived = args.onHeadersReceived;
81-
pendingRequest.onDone = args.onDone;
82-
pendingRequest.onError = args.onError;
83-
pendingRequest.onProgress = args.onProgress;
80+
xhr.onerror = () => args.onError(xhr.status);
81+
xhr.onreadystatechange = this.#onStateChange.bind(this, xhr);
82+
xhr.onprogress = this.#onProgress.bind(this, xhr);
8483

8584
xhr.send(null);
8685

87-
return xhrId;
86+
return xhr;
8887
}
8988

90-
onProgress(xhrId, evt) {
91-
const pendingRequest = this.pendingRequests[xhrId];
92-
if (!pendingRequest) {
93-
return; // Maybe abortRequest was called...
94-
}
95-
pendingRequest.onProgress?.(evt);
89+
#onProgress(xhr, evt) {
90+
const pendingRequest = this.#pendingRequests.get(xhr);
91+
pendingRequest?.onProgress?.(evt);
9692
}
9793

98-
onStateChange(xhrId, evt) {
99-
const pendingRequest = this.pendingRequests[xhrId];
94+
#onStateChange(xhr, evt) {
95+
const pendingRequest = this.#pendingRequests.get(xhr);
10096
if (!pendingRequest) {
10197
return; // Maybe abortRequest was called...
10298
}
10399

104-
const xhr = pendingRequest.xhr;
105100
if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
106101
pendingRequest.onHeadersReceived();
107102
delete pendingRequest.onHeadersReceived;
@@ -111,13 +106,12 @@ class NetworkManager {
111106
return;
112107
}
113108

114-
if (!(xhrId in this.pendingRequests)) {
109+
if (!this.#pendingRequests.has(xhr)) {
115110
// The XHR request might have been aborted in onHeadersReceived()
116111
// callback, in which case we should abort request.
117112
return;
118113
}
119-
120-
delete this.pendingRequests[xhrId];
114+
this.#pendingRequests.delete(xhr);
121115

122116
// Success status == 0 can be on ftp, file and other protocols.
123117
if (xhr.status === 0 && this.isHttp) {
@@ -126,56 +120,33 @@ class NetworkManager {
126120
}
127121
const xhrStatus = xhr.status || OK_RESPONSE;
128122

129-
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
130-
// "A server MAY ignore the Range header". This means it's possible to
131-
// get a 200 rather than a 206 response from a range request.
132-
const ok_response_on_range_request =
133-
xhrStatus === OK_RESPONSE &&
134-
pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
135-
136-
if (
137-
!ok_response_on_range_request &&
138-
xhrStatus !== pendingRequest.expectedStatus
139-
) {
123+
if (!pendingRequest.validateStatus(xhrStatus)) {
140124
pendingRequest.onError(xhr.status);
141125
return;
142126
}
143127

144-
const chunk = getArrayBuffer(xhr);
128+
const chunk = getArrayBuffer(xhr.response);
145129
if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
146130
const rangeHeader = xhr.getResponseHeader("Content-Range");
147-
const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
148-
if (matches) {
149-
pendingRequest.onDone({
150-
begin: parseInt(matches[1], 10),
151-
chunk,
152-
});
131+
if (/bytes (\d+)-(\d+)\/(\d+)/.test(rangeHeader)) {
132+
pendingRequest.onDone(chunk);
153133
} else {
154134
warn(`Missing or invalid "Content-Range" header.`);
155135
pendingRequest.onError(0);
156136
}
157137
} else if (chunk) {
158-
pendingRequest.onDone({
159-
begin: 0,
160-
chunk,
161-
});
138+
pendingRequest.onDone(chunk);
162139
} else {
163140
pendingRequest.onError(xhr.status);
164141
}
165142
}
166143

167-
getRequestXhr(xhrId) {
168-
return this.pendingRequests[xhrId].xhr;
169-
}
170-
171-
isPendingRequest(xhrId) {
172-
return xhrId in this.pendingRequests;
173-
}
174-
175-
abortRequest(xhrId) {
176-
const xhr = this.pendingRequests[xhrId].xhr;
177-
delete this.pendingRequests[xhrId];
178-
xhr.abort();
144+
// Abort the request, if it's pending.
145+
abortRequest(xhr) {
146+
if (this.#pendingRequests.has(xhr)) {
147+
this.#pendingRequests.delete(xhr);
148+
xhr.abort();
149+
}
179150
}
180151
}
181152

@@ -234,7 +205,7 @@ class PDFNetworkStreamFullRequestReader {
234205
this._manager = manager;
235206

236207
this._url = source.url;
237-
this._fullRequestId = manager.request({
208+
this._fullRequestXhr = manager.request({
238209
onHeadersReceived: this._onHeadersReceived.bind(this),
239210
onDone: this._onDone.bind(this),
240211
onError: this._onError.bind(this),
@@ -261,8 +232,7 @@ class PDFNetworkStreamFullRequestReader {
261232
}
262233

263234
_onHeadersReceived() {
264-
const fullRequestXhrId = this._fullRequestId;
265-
const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
235+
const fullRequestXhr = this._fullRequestXhr;
266236

267237
this._manager._responseOrigin = getResponseOrigin(
268238
fullRequestXhr.responseURL
@@ -303,20 +273,18 @@ class PDFNetworkStreamFullRequestReader {
303273
// requests, there will be an issue for sites where you can only
304274
// request the pdf once. However, if this is the case, then the
305275
// server should not be returning that it can support range requests.
306-
this._manager.abortRequest(fullRequestXhrId);
276+
this._manager.abortRequest(fullRequestXhr);
307277
}
308278

309279
this._headersCapability.resolve();
310280
}
311281

312-
_onDone(data) {
313-
if (data) {
314-
if (this._requests.length > 0) {
315-
const requestCapability = this._requests.shift();
316-
requestCapability.resolve({ value: data.chunk, done: false });
317-
} else {
318-
this._cachedChunks.push(data.chunk);
319-
}
282+
_onDone(chunk) {
283+
if (this._requests.length > 0) {
284+
const requestCapability = this._requests.shift();
285+
requestCapability.resolve({ value: chunk, done: false });
286+
} else {
287+
this._cachedChunks.push(chunk);
320288
}
321289
this._done = true;
322290
if (this._cachedChunks.length > 0) {
@@ -390,10 +358,9 @@ class PDFNetworkStreamFullRequestReader {
390358
requestCapability.resolve({ value: undefined, done: true });
391359
}
392360
this._requests.length = 0;
393-
if (this._manager.isPendingRequest(this._fullRequestId)) {
394-
this._manager.abortRequest(this._fullRequestId);
395-
}
396-
this._fullRequestReader = null;
361+
362+
this._manager.abortRequest(this._fullRequestXhr);
363+
this._fullRequestXhr = null;
397364
}
398365
}
399366

@@ -403,7 +370,7 @@ class PDFNetworkStreamRangeRequestReader {
403370
this._manager = manager;
404371

405372
this._url = manager.url;
406-
this._requestId = manager.request({
373+
this._requestXhr = manager.request({
407374
begin,
408375
end,
409376
onHeadersReceived: this._onHeadersReceived.bind(this),
@@ -421,9 +388,7 @@ class PDFNetworkStreamRangeRequestReader {
421388
}
422389

423390
_onHeadersReceived() {
424-
const responseOrigin = getResponseOrigin(
425-
this._manager.getRequestXhr(this._requestId)?.responseURL
426-
);
391+
const responseOrigin = getResponseOrigin(this._requestXhr?.responseURL);
427392

428393
if (responseOrigin !== this._manager._responseOrigin) {
429394
this._storedError = new Error(
@@ -437,8 +402,7 @@ class PDFNetworkStreamRangeRequestReader {
437402
this.onClosed?.(this);
438403
}
439404

440-
_onDone(data) {
441-
const chunk = data.chunk;
405+
_onDone(chunk) {
442406
if (this._requests.length > 0) {
443407
const requestCapability = this._requests.shift();
444408
requestCapability.resolve({ value: chunk, done: false });
@@ -495,9 +459,8 @@ class PDFNetworkStreamRangeRequestReader {
495459
requestCapability.resolve({ value: undefined, done: true });
496460
}
497461
this._requests.length = 0;
498-
if (this._manager.isPendingRequest(this._requestId)) {
499-
this._manager.abortRequest(this._requestId);
500-
}
462+
463+
this._manager.abortRequest(this._requestXhr);
501464
this._close();
502465
}
503466
}

0 commit comments

Comments
 (0)