Skip to content

Commit a15eedf

Browse files
authored
fix(http): avoid closing stream multiple times (#2535)
* fix(http): avoid closing stream multiple times closes #2533 * use different signal than empty vec
1 parent 5347de8 commit a15eedf

File tree

4 files changed

+25
-18
lines changed

4 files changed

+25
-18
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"http": "patch"
3+
"http-js": "patch"
4+
---
5+
6+
Fix `fetch` occasionally throwing an error due to trying to close the underline stream twice.
7+

plugins/http/api-iife.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/http/guest-js/index.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -239,19 +239,17 @@ export async function fetch(
239239
return
240240
}
241241

242-
// close when the signal to close (an empty chunk)
243-
// is sent from the IPC.
244-
if (
245-
res instanceof ArrayBuffer ? res.byteLength == 0 : res.length == 0
246-
) {
242+
const resUint8 = new Uint8Array(res)
243+
const lastByte = resUint8[resUint8.byteLength - 1]
244+
const actualRes = resUint8.slice(0, resUint8.byteLength - 1)
245+
246+
// close when the signal to close (last byte is 1) is sent from the IPC.
247+
if (lastByte == 1) {
247248
controller.close()
248249
return
249250
}
250251

251-
// the content conversion (like .text(), .json(), etc.) in Response
252-
// must have Uint8Array as its content, else it will
253-
// have untraceable error that's hard to debug.
254-
controller.enqueue(new Uint8Array(res))
252+
controller.enqueue(actualRes)
255253
}
256254

257255
// run a non-blocking body stream fetch
@@ -269,12 +267,11 @@ export async function fetch(
269267
statusText
270268
})
271269

272-
// url and headers are read only properties
273-
// but seems like we can set them like this
270+
// Set `Response` properties that are ignored by the
271+
// constructor, like url and some headers
274272
//
275-
// we define theme like this, because using `Response`
276-
// constructor, it removes url and some headers
277-
// like `set-cookie` headers
273+
// Since url and headers are read only properties
274+
// this is the only way to set them.
278275
Object.defineProperty(res, 'url', { value: url })
279276
Object.defineProperty(res, 'headers', {
280277
value: new Headers(responseHeaders)

plugins/http/src/commands.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,14 @@ pub async fn fetch_read_body<R: Runtime>(
426426

427427
// send response through IPC channel
428428
while let Some(chunk) = res.chunk().await? {
429-
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk.to_vec()))?;
429+
let mut chunk = chunk.to_vec();
430+
// append 0 to indicate we are not done yet
431+
chunk.push(0);
432+
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk))?;
430433
}
431434

432-
// send empty vector when done
433-
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(Vec::new()))?;
435+
// send 1 to indicate we are done
436+
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(vec![1]))?;
434437

435438
Ok(())
436439
}

0 commit comments

Comments
 (0)