From a15eedf37854344f7ffbcb0d373d848563817011 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Sun, 16 Mar 2025 21:45:45 +0200 Subject: [PATCH 1/3] fix(http): avoid closing stream multiple times (#2535) * fix(http): avoid closing stream multiple times closes #2533 * use different signal than empty vec --- .changes/http-controller-multiple-close.md | 7 ++++++ plugins/http/api-iife.js | 2 +- plugins/http/guest-js/index.ts | 25 ++++++++++------------ plugins/http/src/commands.rs | 9 +++++--- 4 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 .changes/http-controller-multiple-close.md diff --git a/.changes/http-controller-multiple-close.md b/.changes/http-controller-multiple-close.md new file mode 100644 index 00000000..bec342b1 --- /dev/null +++ b/.changes/http-controller-multiple-close.md @@ -0,0 +1,7 @@ +--- +"http": "patch" +"http-js": "patch" +--- + +Fix `fetch` occasionally throwing an error due to trying to close the underline stream twice. + diff --git a/plugins/http/api-iife.js b/plugins/http/api-iife.js index 07653dee..ec016360 100644 --- a/plugins/http/api-iife.js +++ b/plugins/http/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";function t(e,t,r,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}function r(e,t,r,n,s){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,r),r}var n,s,a;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),s.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:i})=>{if(i==t(this,s,"f"))for(t(this,n,"f").call(this,e),r(this,s,t(this,s,"f")+1);t(this,s,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,s,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,s,"f")],r(this,s,t(this,s,"f")+1)}else t(this,a,"f")[i]=e}))}set onmessage(e){r(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,s=new WeakMap,a=new WeakMap,i)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[i]()}}async function c(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}const d="Request cancelled";return e.fetch=async function(e,t){const r=t?.signal;if(r?.aborted)throw new Error(d);const n=t?.maxRedirections,s=t?.connectTimeout,a=t?.proxy,i=t?.danger;t&&(delete t.maxRedirections,delete t.connectTimeout,delete t.proxy,delete t.danger);const h=t?.headers?t.headers instanceof Headers?t.headers:new Headers(t.headers):new Headers,f=new Request(e,t),_=await f.arrayBuffer(),u=0!==_.byteLength?Array.from(new Uint8Array(_)):null;for(const[e,t]of f.headers)h.get(e)||h.set(e,t);const l=(h instanceof Headers?Array.from(h.entries()):Array.isArray(h)?h:Object.entries(h)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(r?.aborted)throw new Error(d);const w=await c("plugin:http|fetch",{clientConfig:{method:f.method,url:f.url,headers:l,data:u,maxRedirections:n,connectTimeout:s,proxy:a,danger:i}}),p=()=>c("plugin:http|fetch_cancel",{rid:w});if(r?.aborted)throw p(),new Error(d);r?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:m,url:T,headers:g,rid:A}=await c("plugin:http|fetch_send",{rid:w}),R=new ReadableStream({start:e=>{const t=new o;t.onmessage=t=>{r?.aborted?e.error(d):(t instanceof ArrayBuffer?0!=t.byteLength:0!=t.length)?e.enqueue(new Uint8Array(t)):e.close()},c("plugin:http|fetch_read_body",{rid:A,streamChannel:t}).catch((t=>{e.error(t)}))}}),b=new Response(R,{status:y,statusText:m});return Object.defineProperty(b,"url",{value:T}),Object.defineProperty(b,"headers",{value:new Headers(g)}),b},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";function t(e,t,r,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}function r(e,t,r,n,s){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,r),r}var n,s,a;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),s.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:i})=>{if(i==t(this,s,"f"))for(t(this,n,"f").call(this,e),r(this,s,t(this,s,"f")+1);t(this,s,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,s,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,s,"f")],r(this,s,t(this,s,"f")+1)}else t(this,a,"f")[i]=e}))}set onmessage(e){r(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,s=new WeakMap,a=new WeakMap,i)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[i]()}}async function c(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}const d="Request cancelled";return e.fetch=async function(e,t){const r=t?.signal;if(r?.aborted)throw new Error(d);const n=t?.maxRedirections,s=t?.connectTimeout,a=t?.proxy,i=t?.danger;t&&(delete t.maxRedirections,delete t.connectTimeout,delete t.proxy,delete t.danger);const h=t?.headers?t.headers instanceof Headers?t.headers:new Headers(t.headers):new Headers,f=new Request(e,t),_=await f.arrayBuffer(),u=0!==_.byteLength?Array.from(new Uint8Array(_)):null;for(const[e,t]of f.headers)h.get(e)||h.set(e,t);const l=(h instanceof Headers?Array.from(h.entries()):Array.isArray(h)?h:Object.entries(h)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(r?.aborted)throw new Error(d);const w=await c("plugin:http|fetch",{clientConfig:{method:f.method,url:f.url,headers:l,data:u,maxRedirections:n,connectTimeout:s,proxy:a,danger:i}}),p=()=>c("plugin:http|fetch_cancel",{rid:w});if(r?.aborted)throw p(),new Error(d);r?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:m,url:T,headers:g,rid:b}=await c("plugin:http|fetch_send",{rid:w}),A=new ReadableStream({start:e=>{const t=new o;t.onmessage=t=>{if(r?.aborted)return void e.error(d);const n=new Uint8Array(t),s=n[n.byteLength-1],a=n.slice(0,n.byteLength-1);1!=s?e.enqueue(a):e.close()},c("plugin:http|fetch_read_body",{rid:b,streamChannel:t}).catch((t=>{e.error(t)}))}}),R=new Response(A,{status:y,statusText:m});return Object.defineProperty(R,"url",{value:T}),Object.defineProperty(R,"headers",{value:new Headers(g)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})} diff --git a/plugins/http/guest-js/index.ts b/plugins/http/guest-js/index.ts index 4c33f072..62f4916c 100644 --- a/plugins/http/guest-js/index.ts +++ b/plugins/http/guest-js/index.ts @@ -239,19 +239,17 @@ export async function fetch( return } - // close when the signal to close (an empty chunk) - // is sent from the IPC. - if ( - res instanceof ArrayBuffer ? res.byteLength == 0 : res.length == 0 - ) { + const resUint8 = new Uint8Array(res) + const lastByte = resUint8[resUint8.byteLength - 1] + const actualRes = resUint8.slice(0, resUint8.byteLength - 1) + + // close when the signal to close (last byte is 1) is sent from the IPC. + if (lastByte == 1) { controller.close() return } - // the content conversion (like .text(), .json(), etc.) in Response - // must have Uint8Array as its content, else it will - // have untraceable error that's hard to debug. - controller.enqueue(new Uint8Array(res)) + controller.enqueue(actualRes) } // run a non-blocking body stream fetch @@ -269,12 +267,11 @@ export async function fetch( statusText }) - // url and headers are read only properties - // but seems like we can set them like this + // Set `Response` properties that are ignored by the + // constructor, like url and some headers // - // we define theme like this, because using `Response` - // constructor, it removes url and some headers - // like `set-cookie` headers + // Since url and headers are read only properties + // this is the only way to set them. Object.defineProperty(res, 'url', { value: url }) Object.defineProperty(res, 'headers', { value: new Headers(responseHeaders) diff --git a/plugins/http/src/commands.rs b/plugins/http/src/commands.rs index 18953121..bb47444e 100644 --- a/plugins/http/src/commands.rs +++ b/plugins/http/src/commands.rs @@ -426,11 +426,14 @@ pub async fn fetch_read_body( // send response through IPC channel while let Some(chunk) = res.chunk().await? { - stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk.to_vec()))?; + let mut chunk = chunk.to_vec(); + // append 0 to indicate we are not done yet + chunk.push(0); + stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk))?; } - // send empty vector when done - stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(Vec::new()))?; + // send 1 to indicate we are done + stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(vec![1]))?; Ok(()) } From 12c4537b8e4fed29b415ff817434b664c0596dac Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 16 Mar 2025 21:42:37 -0300 Subject: [PATCH 2/3] feat(updater): add support to riscv64 architecture (#2541) Tauri now supports it, so the updater should check the riscv64 arch and replace the {{arch}} and {{target}} variables properly. ref https://github.com/tauri-apps/tauri/pull/12602/ --- .changes/updater-riscv64.md | 6 ++++++ plugins/updater/src/updater.rs | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 .changes/updater-riscv64.md diff --git a/.changes/updater-riscv64.md b/.changes/updater-riscv64.md new file mode 100644 index 00000000..76d7d00d --- /dev/null +++ b/.changes/updater-riscv64.md @@ -0,0 +1,6 @@ +--- +"updater": patch +"updater-js": patch +--- + +Add support to the `riscv64` architecture. diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index 65c91749..1ae2cfe9 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -1223,6 +1223,8 @@ pub(crate) fn get_updater_arch() -> Option<&'static str> { Some("armv7") } else if cfg!(target_arch = "aarch64") { Some("aarch64") + } else if cfg!(target_arch = "riscv64") { + Some("riscv64") } else { None } From 4bbcdbd5561fc674cfc1d13f147276c8626c1a9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Mar 2025 21:51:48 -0300 Subject: [PATCH 3/3] publish new versions (#2542) --- .changes/http-controller-multiple-close.md | 7 ------- .changes/updater-riscv64.md | 6 ------ Cargo.lock | 6 +++--- examples/api/CHANGELOG.md | 7 +++++++ examples/api/package.json | 6 +++--- examples/api/src-tauri/CHANGELOG.md | 7 +++++++ examples/api/src-tauri/Cargo.toml | 6 +++--- plugins/http/CHANGELOG.md | 4 ++++ plugins/http/Cargo.toml | 2 +- plugins/http/package.json | 2 +- plugins/updater/CHANGELOG.md | 4 ++++ plugins/updater/Cargo.toml | 2 +- plugins/updater/package.json | 2 +- pnpm-lock.yaml | 4 ++-- 14 files changed, 37 insertions(+), 28 deletions(-) delete mode 100644 .changes/http-controller-multiple-close.md delete mode 100644 .changes/updater-riscv64.md diff --git a/.changes/http-controller-multiple-close.md b/.changes/http-controller-multiple-close.md deleted file mode 100644 index bec342b1..00000000 --- a/.changes/http-controller-multiple-close.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"http": "patch" -"http-js": "patch" ---- - -Fix `fetch` occasionally throwing an error due to trying to close the underline stream twice. - diff --git a/.changes/updater-riscv64.md b/.changes/updater-riscv64.md deleted file mode 100644 index 76d7d00d..00000000 --- a/.changes/updater-riscv64.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"updater": patch -"updater-js": patch ---- - -Add support to the `riscv64` architecture. diff --git a/Cargo.lock b/Cargo.lock index 77b0d2c7..605d15fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,7 +206,7 @@ checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "api" -version = "2.0.22" +version = "2.0.23" dependencies = [ "log", "serde", @@ -6705,7 +6705,7 @@ dependencies = [ [[package]] name = "tauri-plugin-http" -version = "2.4.1" +version = "2.4.2" dependencies = [ "data-url", "http", @@ -6951,7 +6951,7 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.6.0" +version = "2.6.1" dependencies = [ "base64 0.22.1", "dirs 6.0.0", diff --git a/examples/api/CHANGELOG.md b/examples/api/CHANGELOG.md index e09692f2..3797e923 100644 --- a/examples/api/CHANGELOG.md +++ b/examples/api/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.19] + +### Dependencies + +- Upgraded to `http-js@2.4.2` +- Upgraded to `updater-js@2.6.1` + ## \[2.0.18] ### Dependencies diff --git a/examples/api/package.json b/examples/api/package.json index 3e2c5237..b04e3a40 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -1,7 +1,7 @@ { "name": "api", "private": true, - "version": "2.0.18", + "version": "2.0.19", "type": "module", "scripts": { "dev": "vite --clearScreen false", @@ -20,7 +20,7 @@ "@tauri-apps/plugin-geolocation": "^2.2.0", "@tauri-apps/plugin-global-shortcut": "^2.2.0", "@tauri-apps/plugin-haptics": "^2.2.0", - "@tauri-apps/plugin-http": "^2.4.1", + "@tauri-apps/plugin-http": "^2.4.2", "@tauri-apps/plugin-nfc": "^2.2.0", "@tauri-apps/plugin-notification": "^2.2.2", "@tauri-apps/plugin-opener": "^2.2.6", @@ -28,7 +28,7 @@ "@tauri-apps/plugin-process": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-store": "^2.2.0", - "@tauri-apps/plugin-updater": "^2.6.0", + "@tauri-apps/plugin-updater": "^2.6.1", "@zerodevx/svelte-json-view": "1.0.11" }, "devDependencies": { diff --git a/examples/api/src-tauri/CHANGELOG.md b/examples/api/src-tauri/CHANGELOG.md index e735a65c..970732d4 100644 --- a/examples/api/src-tauri/CHANGELOG.md +++ b/examples/api/src-tauri/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.23] + +### Dependencies + +- Upgraded to `http@2.4.2` +- Upgraded to `updater@2.6.1` + ## \[2.0.22] ### Dependencies diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 3cc99d89..eeee7794 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "api" publish = false -version = "2.0.22" +version = "2.0.23" description = "An example Tauri Application showcasing the api" edition = "2021" rust-version = { workspace = true } @@ -27,7 +27,7 @@ tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.2.0" } tauri-plugin-http = { path = "../../../plugins/http", features = [ "multipart", -], version = "2.4.1" } +], version = "2.4.2" } tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.2.2", features = [ "windows7-compat", ] } @@ -53,7 +53,7 @@ features = [ [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.2.0" } tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.2.0" } -tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.6.0" } +tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.6.1" } tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" } [target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies] diff --git a/plugins/http/CHANGELOG.md b/plugins/http/CHANGELOG.md index 628f4715..22e7a07c 100644 --- a/plugins/http/CHANGELOG.md +++ b/plugins/http/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[2.4.2] + +- [`a15eedf3`](https://github.com/tauri-apps/plugins-workspace/commit/a15eedf37854344f7ffbcb0d373d848563817011) ([#2535](https://github.com/tauri-apps/plugins-workspace/pull/2535) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `fetch` occasionally throwing an error due to trying to close the underline stream twice. + ## \[2.4.1] - [`d3183aa9`](https://github.com/tauri-apps/plugins-workspace/commit/d3183aa99da7ca67e627394132ddeb3b85ccef06) ([#2522](https://github.com/tauri-apps/plugins-workspace/pull/2522) by [@adrieljss](https://github.com/tauri-apps/plugins-workspace/../../adrieljss)) Fix `fetch` blocking until the whole response is read even if it was a streaming response. diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml index 707c334d..b9676e34 100644 --- a/plugins/http/Cargo.toml +++ b/plugins/http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-http" -version = "2.4.1" +version = "2.4.2" description = "Access an HTTP client written in Rust." edition = { workspace = true } authors = { workspace = true } diff --git a/plugins/http/package.json b/plugins/http/package.json index 2c4ba838..3cd87295 100644 --- a/plugins/http/package.json +++ b/plugins/http/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-http", - "version": "2.4.1", + "version": "2.4.2", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" diff --git a/plugins/updater/CHANGELOG.md b/plugins/updater/CHANGELOG.md index 84b4eb84..c196b04a 100644 --- a/plugins/updater/CHANGELOG.md +++ b/plugins/updater/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[2.6.1] + +- [`12c4537b`](https://github.com/tauri-apps/plugins-workspace/commit/12c4537b8e4fed29b415ff817434b664c0596dac) ([#2541](https://github.com/tauri-apps/plugins-workspace/pull/2541) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add support to the `riscv64` architecture. + ## \[2.6.0] - [`faefcc9f`](https://github.com/tauri-apps/plugins-workspace/commit/faefcc9fd8c61f709d491649e255a7fcac82c09a) ([#2430](https://github.com/tauri-apps/plugins-workspace/pull/2430) by [@goenning](https://github.com/tauri-apps/plugins-workspace/../../goenning)) Add `UpdaterBuilder::configure_client` method on Rust side, to configure the `reqwest` client used to check and download the update. diff --git a/plugins/updater/Cargo.toml b/plugins/updater/Cargo.toml index c8799f59..322c4728 100644 --- a/plugins/updater/Cargo.toml +++ b/plugins/updater/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-updater" -version = "2.6.0" +version = "2.6.1" description = "In-app updates for Tauri applications." edition = { workspace = true } authors = { workspace = true } diff --git a/plugins/updater/package.json b/plugins/updater/package.json index 25e23039..a5147145 100644 --- a/plugins/updater/package.json +++ b/plugins/updater/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-updater", - "version": "2.6.0", + "version": "2.6.1", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55eedd4e..61aa2a84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,7 +84,7 @@ importers: specifier: ^2.2.0 version: link:../../plugins/haptics '@tauri-apps/plugin-http': - specifier: ^2.4.1 + specifier: ^2.4.2 version: link:../../plugins/http '@tauri-apps/plugin-nfc': specifier: ^2.2.0 @@ -108,7 +108,7 @@ importers: specifier: ^2.2.0 version: link:../../plugins/store '@tauri-apps/plugin-updater': - specifier: ^2.6.0 + specifier: ^2.6.1 version: link:../../plugins/updater '@zerodevx/svelte-json-view': specifier: 1.0.11