From fec2b7f28c90a59bc80a13cdc1046da76c6ef8b1 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Sat, 22 Mar 2025 10:06:06 +0200 Subject: [PATCH] fix(http): fix aborting a streaming response closes #2557 --- .changes/http-stream-cancel.md | 7 ++ .../autostart/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../biometric/permissions/schemas/schema.json | 4 +- plugins/cli/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../deep-link/permissions/schemas/schema.json | 4 +- .../dialog/permissions/schemas/schema.json | 4 +- plugins/fs/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../haptics/permissions/schemas/schema.json | 4 +- plugins/http/api-iife.js | 2 +- plugins/http/build.rs | 8 ++- plugins/http/guest-js/index.ts | 70 ++++++++++++------- .../commands/fetch_cancel_body.toml | 13 ++++ .../permissions/autogenerated/reference.md | 29 +++++++- plugins/http/permissions/default.toml | 3 +- plugins/http/permissions/schemas/schema.json | 14 +++- plugins/http/src/commands.rs | 46 ++++++++---- plugins/http/src/lib.rs | 3 +- plugins/log/permissions/schemas/schema.json | 4 +- plugins/nfc/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../opener/permissions/schemas/schema.json | 4 +- plugins/os/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../process/permissions/schemas/schema.json | 4 +- plugins/shell/permissions/schemas/schema.json | 4 +- plugins/sql/permissions/schemas/schema.json | 4 +- plugins/store/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- .../updater/permissions/schemas/schema.json | 4 +- .../upload/permissions/schemas/schema.json | 4 +- .../websocket/permissions/schemas/schema.json | 4 +- .../permissions/schemas/schema.json | 4 +- 36 files changed, 198 insertions(+), 101 deletions(-) create mode 100644 .changes/http-stream-cancel.md create mode 100644 plugins/http/permissions/autogenerated/commands/fetch_cancel_body.toml diff --git a/.changes/http-stream-cancel.md b/.changes/http-stream-cancel.md new file mode 100644 index 00000000..66c29f71 --- /dev/null +++ b/.changes/http-stream-cancel.md @@ -0,0 +1,7 @@ +--- +"http": "patch" +"http-js": "patch" +--- + +Fix aborting a request in the middle of a streaming response. + diff --git a/plugins/autostart/permissions/schemas/schema.json b/plugins/autostart/permissions/schemas/schema.json index 59c81f52..defb923c 100644 --- a/plugins/autostart/permissions/schemas/schema.json +++ b/plugins/autostart/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/barcode-scanner/permissions/schemas/schema.json b/plugins/barcode-scanner/permissions/schemas/schema.json index f41214b4..dc2ab019 100644 --- a/plugins/barcode-scanner/permissions/schemas/schema.json +++ b/plugins/barcode-scanner/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/biometric/permissions/schemas/schema.json b/plugins/biometric/permissions/schemas/schema.json index cc4d04d5..891877b6 100644 --- a/plugins/biometric/permissions/schemas/schema.json +++ b/plugins/biometric/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/cli/permissions/schemas/schema.json b/plugins/cli/permissions/schemas/schema.json index b376890e..fb1ec637 100644 --- a/plugins/cli/permissions/schemas/schema.json +++ b/plugins/cli/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/clipboard-manager/permissions/schemas/schema.json b/plugins/clipboard-manager/permissions/schemas/schema.json index c2763492..7e23e9a3 100644 --- a/plugins/clipboard-manager/permissions/schemas/schema.json +++ b/plugins/clipboard-manager/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/deep-link/permissions/schemas/schema.json b/plugins/deep-link/permissions/schemas/schema.json index 7d887dc2..1c96c7d5 100644 --- a/plugins/deep-link/permissions/schemas/schema.json +++ b/plugins/deep-link/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/dialog/permissions/schemas/schema.json b/plugins/dialog/permissions/schemas/schema.json index ed8c0733..4fa8bdf3 100644 --- a/plugins/dialog/permissions/schemas/schema.json +++ b/plugins/dialog/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/fs/permissions/schemas/schema.json b/plugins/fs/permissions/schemas/schema.json index 2c13d5c6..e8706b04 100644 --- a/plugins/fs/permissions/schemas/schema.json +++ b/plugins/fs/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/geolocation/permissions/schemas/schema.json b/plugins/geolocation/permissions/schemas/schema.json index 4474ec6b..fe0129bb 100644 --- a/plugins/geolocation/permissions/schemas/schema.json +++ b/plugins/geolocation/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/global-shortcut/permissions/schemas/schema.json b/plugins/global-shortcut/permissions/schemas/schema.json index 66b92b07..6270e7f3 100644 --- a/plugins/global-shortcut/permissions/schemas/schema.json +++ b/plugins/global-shortcut/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/haptics/permissions/schemas/schema.json b/plugins/haptics/permissions/schemas/schema.json index 763e0a72..889da78f 100644 --- a/plugins/haptics/permissions/schemas/schema.json +++ b/plugins/haptics/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/http/api-iife.js b/plugins/http/api-iife.js index ec016360..5856df7e 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: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__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;const r="Request cancelled";return e.fetch=async function(e,n){const a=n?.signal;if(a?.aborted)throw new Error(r);const o=n?.maxRedirections,s=n?.connectTimeout,i=n?.proxy,d=n?.danger;n&&(delete n.maxRedirections,delete n.connectTimeout,delete n.proxy,delete n.danger);const c=n?.headers?n.headers instanceof Headers?n.headers:new Headers(n.headers):new Headers,u=new Request(e,n),_=await u.arrayBuffer(),l=0!==_.byteLength?Array.from(new Uint8Array(_)):null;for(const[e,t]of u.headers)c.get(e)||c.set(e,t);const f=(c instanceof Headers?Array.from(c.entries()):Array.isArray(c)?c:Object.entries(c)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(a?.aborted)throw new Error(r);const h=await t("plugin:http|fetch",{clientConfig:{method:u.method,url:u.url,headers:f,data:l,maxRedirections:o,connectTimeout:s,proxy:i,danger:d}}),p=()=>t("plugin:http|fetch_cancel",{rid:h});if(a?.aborted)throw p(),new Error(r);a?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:w,url:b,headers:g,rid:T}=await t("plugin:http|fetch_send",{rid:h}),R=()=>t("plugin:http|fetch_cancel_body",{rid:T}),m=new ReadableStream({start:e=>{if(a?.aborted)return e.error(r),void R();a?.addEventListener("abort",(()=>{e.error(r),R()}))},pull:async e=>(async e=>{let r;try{r=await t("plugin:http|fetch_read_body",{rid:T})}catch(t){return e.error(t),void R()}const n=new Uint8Array(r),a=n[n.byteLength-1],o=n.slice(0,n.byteLength-1);1===a&&e.close(),e.enqueue(o)})(e)}),A=new Response(m,{status:y,statusText:w});return Object.defineProperty(A,"url",{value:b}),Object.defineProperty(A,"headers",{value:new Headers(g)}),A},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})} diff --git a/plugins/http/build.rs b/plugins/http/build.rs index a4b802ad..31fa0237 100644 --- a/plugins/http/build.rs +++ b/plugins/http/build.rs @@ -6,7 +6,13 @@ #[allow(dead_code)] mod scope; -const COMMANDS: &[&str] = &["fetch", "fetch_cancel", "fetch_send", "fetch_read_body"]; +const COMMANDS: &[&str] = &[ + "fetch", + "fetch_cancel", + "fetch_send", + "fetch_read_body", + "fetch_cancel_body", +]; /// HTTP scope entry. #[derive(schemars::JsonSchema)] diff --git a/plugins/http/guest-js/index.ts b/plugins/http/guest-js/index.ts index 62f4916c..037d3b12 100644 --- a/plugins/http/guest-js/index.ts +++ b/plugins/http/guest-js/index.ts @@ -26,7 +26,7 @@ * @module */ -import { Channel, invoke } from '@tauri-apps/api/core' +import { invoke } from '@tauri-apps/api/core' /** * Configuration of a proxy that a Client should pass requests to. @@ -229,37 +229,53 @@ export async function fetch( rid }) + const dropBody = () => { + return invoke('plugin:http|fetch_cancel_body', { rid: responseRid }) + } + + const readChunk = async ( + controller: ReadableStreamDefaultController + ) => { + let data: ArrayBuffer + try { + data = await invoke('plugin:http|fetch_read_body', { + rid: responseRid + }) + } catch (e) { + // close the stream if an error occurs + // and drop the body on Rust side + controller.error(e) + void dropBody() + return + } + + const dataUint8 = new Uint8Array(data) + const lastByte = dataUint8[dataUint8.byteLength - 1] + const actualData = dataUint8.slice(0, dataUint8.byteLength - 1) + + // close when the signal to close (last byte is 1) is sent from the IPC. + if (lastByte === 1) { + controller.close() + } + + controller.enqueue(actualData) + } + const readableStreamBody = new ReadableStream({ start: (controller) => { - const streamChannel = new Channel() - streamChannel.onmessage = (res: ArrayBuffer | number[]) => { - // close early if aborted - if (signal?.aborted) { - controller.error(ERROR_REQUEST_CANCELLED) - return - } - - 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 - } - - controller.enqueue(actualRes) + // abort early here if needed and drop the body + if (signal?.aborted) { + controller.error(ERROR_REQUEST_CANCELLED) + void dropBody() + return } - // run a non-blocking body stream fetch - invoke('plugin:http|fetch_read_body', { - rid: responseRid, - streamChannel - }).catch((e) => { - controller.error(e) + signal?.addEventListener('abort', () => { + controller.error(ERROR_REQUEST_CANCELLED) + void dropBody() }) - } + }, + pull: async (controller) => readChunk(controller) }) const res = new Response(readableStreamBody, { diff --git a/plugins/http/permissions/autogenerated/commands/fetch_cancel_body.toml b/plugins/http/permissions/autogenerated/commands/fetch_cancel_body.toml new file mode 100644 index 00000000..19e66520 --- /dev/null +++ b/plugins/http/permissions/autogenerated/commands/fetch_cancel_body.toml @@ -0,0 +1,13 @@ +# Automatically generated - DO NOT EDIT! + +"$schema" = "../../schemas/schema.json" + +[[permission]] +identifier = "allow-fetch-cancel-body" +description = "Enables the fetch_cancel_body command without any pre-configured scope." +commands.allow = ["fetch_cancel_body"] + +[[permission]] +identifier = "deny-fetch-cancel-body" +description = "Denies the fetch_cancel_body command without any pre-configured scope." +commands.deny = ["fetch_cancel_body"] diff --git a/plugins/http/permissions/autogenerated/reference.md b/plugins/http/permissions/autogenerated/reference.md index 4126f0b9..f056be2e 100644 --- a/plugins/http/permissions/autogenerated/reference.md +++ b/plugins/http/permissions/autogenerated/reference.md @@ -15,8 +15,9 @@ All fetch operations are enabled. - `allow-fetch` - `allow-fetch-cancel` -- `allow-fetch-read-body` - `allow-fetch-send` +- `allow-fetch-read-body` +- `allow-fetch-cancel-body` ## Permission Table @@ -82,6 +83,32 @@ Denies the fetch_cancel command without any pre-configured scope. +`http:allow-fetch-cancel-body` + + + + +Enables the fetch_cancel_body command without any pre-configured scope. + + + + + + + +`http:deny-fetch-cancel-body` + + + + +Denies the fetch_cancel_body command without any pre-configured scope. + + + + + + + `http:allow-fetch-read-body` diff --git a/plugins/http/permissions/default.toml b/plugins/http/permissions/default.toml index b469536d..541bf49f 100644 --- a/plugins/http/permissions/default.toml +++ b/plugins/http/permissions/default.toml @@ -17,6 +17,7 @@ All fetch operations are enabled. permissions = [ "allow-fetch", "allow-fetch-cancel", - "allow-fetch-read-body", "allow-fetch-send", + "allow-fetch-read-body", + "allow-fetch-cancel-body", ] diff --git a/plugins/http/permissions/schemas/schema.json b/plugins/http/permissions/schemas/schema.json index 794ee204..626fea45 100644 --- a/plugins/http/permissions/schemas/schema.json +++ b/plugins/http/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -314,6 +314,16 @@ "type": "string", "const": "deny-fetch-cancel" }, + { + "description": "Enables the fetch_cancel_body command without any pre-configured scope.", + "type": "string", + "const": "allow-fetch-cancel-body" + }, + { + "description": "Denies the fetch_cancel_body command without any pre-configured scope.", + "type": "string", + "const": "deny-fetch-cancel-body" + }, { "description": "Enables the fetch_read_body command without any pre-configured scope.", "type": "string", diff --git a/plugins/http/src/commands.rs b/plugins/http/src/commands.rs index bb47444e..0ed6a12a 100644 --- a/plugins/http/src/commands.rs +++ b/plugins/http/src/commands.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use tauri::{ async_runtime::Mutex, command, - ipc::{Channel, CommandScope, GlobalScope}, + ipc::{CommandScope, GlobalScope}, Manager, ResourceId, ResourceTable, Runtime, State, Webview, }; use tokio::sync::oneshot::{channel, Receiver, Sender}; @@ -415,26 +415,42 @@ pub async fn fetch_send( pub async fn fetch_read_body( webview: Webview, rid: ResourceId, - stream_channel: Channel, -) -> crate::Result<()> { +) -> crate::Result { let res = { - let mut resources_table = webview.resources_table(); - resources_table.take::(rid)? + let resources_table = webview.resources_table(); + resources_table.get::(rid)? }; - let mut res = Arc::into_inner(res).unwrap().0; + // SAFETY: we can access the inner value mutably + // because we are the only ones with a reference to it + // and we don't want to use `Arc::into_inner` because we want to keep the value in the table + // for potential future calls to `fetch_cancel_body` + let res_ptr = Arc::as_ptr(&res) as *mut ReqwestResponse; + let res = unsafe { &mut *res_ptr }; + let res = &mut res.0; - // send response through IPC channel - while let Some(chunk) = res.chunk().await? { - 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))?; - } + let Some(chunk) = res.chunk().await? else { + let mut resources_table = webview.resources_table(); + resources_table.close(rid)?; - // send 1 to indicate we are done - stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(vec![1]))?; + // return a response with a single byte to indicate that the body is empty + return Ok(tauri::ipc::Response::new(vec![1])); + }; + + let mut chunk = chunk.to_vec(); + // append a 0 byte to indicate that the body is not empty + chunk.push(0); + Ok(tauri::ipc::Response::new(chunk)) +} + +#[command] +pub async fn fetch_cancel_body( + webview: Webview, + rid: ResourceId, +) -> crate::Result<()> { + let mut resources_table = webview.resources_table(); + resources_table.close(rid)?; Ok(()) } diff --git a/plugins/http/src/lib.rs b/plugins/http/src/lib.rs index 5acc2b47..364d3bff 100644 --- a/plugins/http/src/lib.rs +++ b/plugins/http/src/lib.rs @@ -84,7 +84,8 @@ pub fn init() -> TauriPlugin { commands::fetch, commands::fetch_cancel, commands::fetch_send, - commands::fetch_read_body + commands::fetch_read_body, + commands::fetch_cancel_body, ]) .build() } diff --git a/plugins/log/permissions/schemas/schema.json b/plugins/log/permissions/schemas/schema.json index 78d88826..547d1fd8 100644 --- a/plugins/log/permissions/schemas/schema.json +++ b/plugins/log/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/nfc/permissions/schemas/schema.json b/plugins/nfc/permissions/schemas/schema.json index 5fe3743c..69b4eb23 100644 --- a/plugins/nfc/permissions/schemas/schema.json +++ b/plugins/nfc/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/notification/permissions/schemas/schema.json b/plugins/notification/permissions/schemas/schema.json index 433f367f..4c50cde6 100644 --- a/plugins/notification/permissions/schemas/schema.json +++ b/plugins/notification/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/opener/permissions/schemas/schema.json b/plugins/opener/permissions/schemas/schema.json index b958ac63..4f289a93 100644 --- a/plugins/opener/permissions/schemas/schema.json +++ b/plugins/opener/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/os/permissions/schemas/schema.json b/plugins/os/permissions/schemas/schema.json index ad053532..ad3317c7 100644 --- a/plugins/os/permissions/schemas/schema.json +++ b/plugins/os/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/positioner/permissions/schemas/schema.json b/plugins/positioner/permissions/schemas/schema.json index ccf55156..4b1d9221 100644 --- a/plugins/positioner/permissions/schemas/schema.json +++ b/plugins/positioner/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/process/permissions/schemas/schema.json b/plugins/process/permissions/schemas/schema.json index 95f67149..1818871e 100644 --- a/plugins/process/permissions/schemas/schema.json +++ b/plugins/process/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/shell/permissions/schemas/schema.json b/plugins/shell/permissions/schemas/schema.json index e70c3926..ff343467 100644 --- a/plugins/shell/permissions/schemas/schema.json +++ b/plugins/shell/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/sql/permissions/schemas/schema.json b/plugins/sql/permissions/schemas/schema.json index e3add537..241cf33c 100644 --- a/plugins/sql/permissions/schemas/schema.json +++ b/plugins/sql/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/store/permissions/schemas/schema.json b/plugins/store/permissions/schemas/schema.json index 4237bc62..d3cfc815 100644 --- a/plugins/store/permissions/schemas/schema.json +++ b/plugins/store/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/stronghold/permissions/schemas/schema.json b/plugins/stronghold/permissions/schemas/schema.json index 5657e9bb..c7781f1f 100644 --- a/plugins/stronghold/permissions/schemas/schema.json +++ b/plugins/stronghold/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/updater/permissions/schemas/schema.json b/plugins/updater/permissions/schemas/schema.json index 2df800da..8264f3e1 100644 --- a/plugins/updater/permissions/schemas/schema.json +++ b/plugins/updater/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/upload/permissions/schemas/schema.json b/plugins/upload/permissions/schemas/schema.json index abe3a09f..1d8d9c4a 100644 --- a/plugins/upload/permissions/schemas/schema.json +++ b/plugins/upload/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/websocket/permissions/schemas/schema.json b/plugins/websocket/permissions/schemas/schema.json index 9f574650..989d0159 100644 --- a/plugins/websocket/permissions/schemas/schema.json +++ b/plugins/websocket/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" diff --git a/plugins/window-state/permissions/schemas/schema.json b/plugins/window-state/permissions/schemas/schema.json index 67888bc6..b94c2573 100644 --- a/plugins/window-state/permissions/schemas/schema.json +++ b/plugins/window-state/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null"