Skip to content

Commit 035c704

Browse files
authored
[fetch] Do not collect all data when streaming requests. (#26048)
Previously, when `EMSCRIPTEN_FETCH_STREAM_DATA` was used, we would collect all the streamed data chunks and create the full response when finished in the XHR polyfill. The full response is not used by the C fetch API, so we can avoid storing it when streaming is enabled.
1 parent d04526e commit 035c704

File tree

1 file changed

+28
-11
lines changed

1 file changed

+28
-11
lines changed

src/Fetch.js

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ var FetchXHR = class {
4141
_aborted = false;
4242
_responseHeaders = null;
4343

44+
// When enabled the the data will be streamed using progress events. If the full result is needed
45+
// the data must be collected during the progress events.
46+
_streamData = false;
47+
4448
// --- Private state management ---
4549
_changeReadyState(state) {
4650
this.readyState = state;
@@ -174,15 +178,19 @@ var FetchXHR = class {
174178
const contentLength = +response.headers.get('Content-Length');
175179

176180
let receivedLength = 0;
177-
const chunks = [];
181+
// When streaming data don't collect all of the chunks into one large chunk. It's up to the
182+
// user to collect the data as it comes in.
183+
const chunks = this._streamData ? null : [];
178184

179185
while (true) {
180186
const { done, value } = await reader.read();
181187
if (done) {
182188
break;
183189
}
184190

185-
chunks.push(value);
191+
if (!this._streamData) {
192+
chunks.push(value);
193+
}
186194
receivedLength += value.length;
187195

188196
if (this.onprogress) {
@@ -197,16 +205,19 @@ var FetchXHR = class {
197205
}
198206
}
199207

200-
// Combine chunks into a single Uint8Array.
201-
const allChunks = new Uint8Array(receivedLength);
202-
let position = 0;
203-
for (const chunk of chunks) {
204-
allChunks.set(chunk, position);
205-
position += chunk.length;
208+
if (this._streamData) {
209+
this.response = null;
210+
} else {
211+
// Combine chunks into a single Uint8Array.
212+
const allChunks = new Uint8Array(receivedLength);
213+
let position = 0;
214+
for (const chunk of chunks) {
215+
allChunks.set(chunk, position);
216+
position += chunk.length;
217+
}
218+
// Convert to ArrayBuffer as requested by responseType
219+
this.response = allChunks.buffer;
206220
}
207-
208-
// Convert to ArrayBuffer as requested by responseType
209-
this.response = allChunks.buffer;
210221
} catch (error) {
211222
this.statusText = error.message;
212223

@@ -517,6 +528,7 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) {
517528
var xhr = new XMLHttpRequest();
518529
#endif
519530
xhr.withCredentials = !!{{{ makeGetValue('fetch_attr', C_STRUCTS.emscripten_fetch_attr_t.withCredentials, 'u8') }}};;
531+
xhr._streamData = fetchAttrStreamData;
520532
#if FETCH_DEBUG
521533
dbg(`fetch: xhr.timeout: ${xhr.timeout}, xhr.withCredentials: ${xhr.withCredentials}`);
522534
dbg(`fetch: xhr.open(requestMethod="${requestMethod}", url: "${url}", userName: ${userNameStr}, password: ${passwordStr}`);
@@ -609,6 +621,11 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) {
609621
if (xhr.status >= 200 && xhr.status < 300) {
610622
#if FETCH_DEBUG
611623
dbg(`fetch: xhr of URL "${xhr.url_}" / responseURL "${xhr.responseURL}" succeeded with status ${xhr.status}`);
624+
#endif
625+
#if ASSERTIONS
626+
if (fetchAttrStreamData) {
627+
assert(xhr.response === null);
628+
}
612629
#endif
613630
onsuccess(fetch, xhr, e);
614631
} else {

0 commit comments

Comments
 (0)