fix(http): fix aborting a streaming response

closes #2557
pull/2562/head
amrbashir 3 months ago
parent 35c1cd9aa5
commit fec2b7f28c
No known key found for this signature in database

@ -0,0 +1,7 @@
---
"http": "patch"
"http-js": "patch"
---
Fix aborting a request in the middle of a streaming response.

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

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

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

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

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

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

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

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

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

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

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

@ -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__})}

@ -6,7 +6,13 @@
#[allow(dead_code)] #[allow(dead_code)]
mod scope; 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. /// HTTP scope entry.
#[derive(schemars::JsonSchema)] #[derive(schemars::JsonSchema)]

@ -26,7 +26,7 @@
* @module * @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. * Configuration of a proxy that a Client should pass requests to.
@ -229,37 +229,53 @@ export async function fetch(
rid rid
}) })
const dropBody = () => {
return invoke('plugin:http|fetch_cancel_body', { rid: responseRid })
}
const readChunk = async (
controller: ReadableStreamDefaultController<any>
) => {
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({ const readableStreamBody = new ReadableStream({
start: (controller) => { start: (controller) => {
const streamChannel = new Channel<ArrayBuffer | number[]>() // abort early here if needed and drop the body
streamChannel.onmessage = (res: ArrayBuffer | number[]) => { if (signal?.aborted) {
// close early if aborted controller.error(ERROR_REQUEST_CANCELLED)
if (signal?.aborted) { void dropBody()
controller.error(ERROR_REQUEST_CANCELLED) return
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)
} }
// run a non-blocking body stream fetch signal?.addEventListener('abort', () => {
invoke('plugin:http|fetch_read_body', { controller.error(ERROR_REQUEST_CANCELLED)
rid: responseRid, void dropBody()
streamChannel
}).catch((e) => {
controller.error(e)
}) })
} },
pull: async (controller) => readChunk(controller)
}) })
const res = new Response(readableStreamBody, { const res = new Response(readableStreamBody, {

@ -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"]

@ -15,8 +15,9 @@ All fetch operations are enabled.
- `allow-fetch` - `allow-fetch`
- `allow-fetch-cancel` - `allow-fetch-cancel`
- `allow-fetch-read-body`
- `allow-fetch-send` - `allow-fetch-send`
- `allow-fetch-read-body`
- `allow-fetch-cancel-body`
## Permission Table ## Permission Table
@ -82,6 +83,32 @@ Denies the fetch_cancel command without any pre-configured scope.
<tr> <tr>
<td> <td>
`http:allow-fetch-cancel-body`
</td>
<td>
Enables the fetch_cancel_body command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`http:deny-fetch-cancel-body`
</td>
<td>
Denies the fetch_cancel_body command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`http:allow-fetch-read-body` `http:allow-fetch-read-body`
</td> </td>

@ -17,6 +17,7 @@ All fetch operations are enabled.
permissions = [ permissions = [
"allow-fetch", "allow-fetch",
"allow-fetch-cancel", "allow-fetch-cancel",
"allow-fetch-read-body",
"allow-fetch-send", "allow-fetch-send",
"allow-fetch-read-body",
"allow-fetch-cancel-body",
] ]

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -314,6 +314,16 @@
"type": "string", "type": "string",
"const": "deny-fetch-cancel" "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.", "description": "Enables the fetch_read_body command without any pre-configured scope.",
"type": "string", "type": "string",

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use tauri::{ use tauri::{
async_runtime::Mutex, async_runtime::Mutex,
command, command,
ipc::{Channel, CommandScope, GlobalScope}, ipc::{CommandScope, GlobalScope},
Manager, ResourceId, ResourceTable, Runtime, State, Webview, Manager, ResourceId, ResourceTable, Runtime, State, Webview,
}; };
use tokio::sync::oneshot::{channel, Receiver, Sender}; use tokio::sync::oneshot::{channel, Receiver, Sender};
@ -415,26 +415,42 @@ pub async fn fetch_send<R: Runtime>(
pub async fn fetch_read_body<R: Runtime>( pub async fn fetch_read_body<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
rid: ResourceId, rid: ResourceId,
stream_channel: Channel<tauri::ipc::InvokeResponseBody>, ) -> crate::Result<tauri::ipc::Response> {
) -> crate::Result<()> {
let res = { let res = {
let mut resources_table = webview.resources_table(); let resources_table = webview.resources_table();
resources_table.take::<ReqwestResponse>(rid)? resources_table.get::<ReqwestResponse>(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 let Some(chunk) = res.chunk().await? else {
while let Some(chunk) = res.chunk().await? { let mut resources_table = webview.resources_table();
let mut chunk = chunk.to_vec(); resources_table.close(rid)?;
// append 0 to indicate we are not done yet
chunk.push(0);
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk))?;
}
// send 1 to indicate we are done // return a response with a single byte to indicate that the body is empty
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(vec![1]))?; 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<R: Runtime>(
webview: Webview<R>,
rid: ResourceId,
) -> crate::Result<()> {
let mut resources_table = webview.resources_table();
resources_table.close(rid)?;
Ok(()) Ok(())
} }

@ -84,7 +84,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
commands::fetch, commands::fetch,
commands::fetch_cancel, commands::fetch_cancel,
commands::fetch_send, commands::fetch_send,
commands::fetch_read_body commands::fetch_read_body,
commands::fetch_cancel_body,
]) ])
.build() .build()
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save