Skip to content

Commit 0858f99

Browse files
Support streaming drivers in XMLHTTPRequest-less platforms (nodejs, serviceworker) (#1364)
* xmlhttpreq partial support pt1 * XMLHTTPRequest shim cleanup * fix check for x-ndjson * change credit header at top of xhr shim
1 parent f9995a2 commit 0858f99

File tree

1 file changed

+59
-15
lines changed

1 file changed

+59
-15
lines changed

src/puter-js/src/lib/polyfills/xhrshim.js

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// https://www.npmjs.com/package/xhr-shim under MIT
1+
// Originally from https://www.npmjs.com/package/xhr-shim under MIT, heavily modified since
22

33
/* global module */
44
/* global EventTarget, AbortController, DOMException */
@@ -16,12 +16,51 @@ const sTimeout = Symbol("timeout");
1616
const sTimedOut = Symbol("timedOut");
1717
const sIsResponseText = Symbol("isResponseText");
1818

19+
// SO: https://stackoverflow.com/questions/49129643/how-do-i-merge-an-array-of-uint8arrays
20+
function mergeUint8Arrays(...arrays) {
21+
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
22+
const merged = new Uint8Array(totalSize);
23+
24+
arrays.forEach((array, i, arrays) => {
25+
const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
26+
merged.set(array, offset);
27+
});
28+
29+
return merged;
30+
}
31+
32+
/**
33+
* Exposes incoming data
34+
* @this {XMLHttpRequest}
35+
* @param {Uint8Array} bytes
36+
*/
37+
async function parseBody(bytes) {
38+
const responseType = this.responseType || "text";
39+
const textde = new TextDecoder();
40+
const finalMIME = this[sMIME] || this[sRespHeaders].get("content-type") || "text/plain";
41+
switch (responseType) {
42+
case "text":
43+
this.response = textde.decode(bytes)
44+
break;
45+
case "blob":
46+
this.response = new Blob([bytes], { type: finalMIME });
47+
break;
48+
case "arraybuffer":
49+
this.response = bytes.buffer;
50+
break;
51+
case "json":
52+
this.response = JSON.parse(textde.decode(bytes));
53+
break;
54+
}
55+
}
56+
1957
const XMLHttpRequestShim = class XMLHttpRequest extends EventTarget {
2058
onreadystatechange() {
2159

2260
}
2361

2462
set readyState(value) {
63+
if (this[sReadyState] === value) return; // dont do anything if "value" is already the internal value
2564
this[sReadyState] = value;
2665
this.dispatchEvent(new Event("readystatechange"));
2766
this.onreadystatechange(new Event("readystatechange"));
@@ -146,21 +185,26 @@ const XMLHttpRequestShim = class XMLHttpRequest extends EventTarget {
146185
this.status = resp.status;
147186
this.statusText = resp.statusText;
148187
this[sRespHeaders] = resp.headers;
149-
const finalMIME = this[sMIME] || this[sRespHeaders].get("content-type") || "text/plain";
150-
switch (responseType) {
151-
case "text":
152-
this.response = await resp.text();
153-
break;
154-
case "blob":
155-
this.response = new Blob([await resp.arrayBuffer()], { type: finalMIME });
156-
break;
157-
case "arraybuffer":
158-
this.response = await resp.arrayBuffer();
159-
break;
160-
case "json":
161-
this.response = await resp.json();
162-
break;
188+
this.readyState = this.constructor.HEADERS_RECEIVED;
189+
190+
if (resp.headers.get("content-type").includes("application/x-ndjson") || this.streamRequestBadForPerformance) {
191+
let bytes = new Uint8Array();
192+
for await (const chunk of resp.body) {
193+
this.readyState = this.constructor.LOADING;
194+
195+
bytes = mergeUint8Arrays(bytes, chunk);
196+
parseBody.call(this, bytes);
197+
this[sDispatch](new CustomEvent("progress"));
198+
}
199+
} else {
200+
const bytesChunks = [];
201+
for await (const chunk of resp.body) {
202+
bytesChunks.push(chunk)
203+
}
204+
parseBody.call(this, mergeUint8Arrays(...bytesChunks));
163205
}
206+
207+
164208
this.readyState = this.constructor.DONE;
165209
this[sDispatch](new CustomEvent("load"));
166210
}, err => {

0 commit comments

Comments
 (0)