From 5edea816802fa8637982db96505832abbc7c450f Mon Sep 17 00:00:00 2001 From: adrieljss Date: Sun, 2 Mar 2025 23:07:44 +0800 Subject: [PATCH] feat: add stream support --- Cargo.lock | 2 +- plugins/http/Cargo.toml | 2 +- plugins/http/guest-js/index.ts | 57 +++++++++++++--------------------- plugins/http/package.json | 5 ++- plugins/http/src/commands.rs | 44 +++++++++----------------- plugins/http/src/lib.rs | 3 +- pnpm-lock.yaml | 27 +++++++--------- 7 files changed, 54 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 488d41f3..1154189d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6659,7 +6659,7 @@ dependencies = [ [[package]] name = "tauri-plugin-http" -version = "2.4.0" +version = "2.3.0" dependencies = [ "data-url", "http", diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml index 76d94d5a..9aa49e0e 100644 --- a/plugins/http/Cargo.toml +++ b/plugins/http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-http" -version = "2.4.0" +version = "2.3.0" description = "Access an HTTP client written in Rust." edition = { workspace = true } authors = { workspace = true } diff --git a/plugins/http/guest-js/index.ts b/plugins/http/guest-js/index.ts index 6c163509..bea18e44 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. @@ -106,20 +106,6 @@ export interface DangerousSettings { acceptInvalidHostnames?: boolean } -/** - * Stream Packet for IPC - */ -export interface StreamMessage { - /** - * The chunk - an array of bytes sent from Rust. - */ - value?: ArrayBuffer | number[] - /** - * Is the stream done. - */ - done: boolean -} - const ERROR_REQUEST_CANCELLED = 'Request canceled' /** @@ -200,19 +186,6 @@ export async function fetch( throw new Error(ERROR_REQUEST_CANCELLED) } - const streamChannel = new Channel() - - const readableStreamBody = new ReadableStream({ - start: (controller) => { - streamChannel.onmessage = (res: StreamMessage) => { - // close early if aborted - if (signal?.aborted) controller.error(ERROR_REQUEST_CANCELLED) - if (res.done) controller.close() - controller.enqueue(res.value) - } - } - }) - const rid = await invoke('plugin:http|fetch', { clientConfig: { method: req.method, @@ -223,8 +196,7 @@ export async function fetch( connectTimeout, proxy, danger - }, - streamChannel + } }) const abort = () => invoke('plugin:http|fetch_cancel', { rid }) @@ -251,15 +223,30 @@ export async function fetch( status, statusText, url, - headers: responseHeaders + headers: responseHeaders, + rid: responseRid } = await invoke('plugin:http|fetch_send', { rid }) - const res = new Response(readableStreamBody, { - status, - statusText - }) + const body = await invoke( + 'plugin:http|fetch_read_body', + { + rid: responseRid + } + ) + + const res = new Response( + body instanceof ArrayBuffer && body.byteLength !== 0 + ? body + : body instanceof Array && body.length > 0 + ? new Uint8Array(body) + : null, + { + status, + statusText + } + ) // url and headers are read only properties // but seems like we can set them like this diff --git a/plugins/http/package.json b/plugins/http/package.json index 561bd228..02ea80bf 100644 --- a/plugins/http/package.json +++ b/plugins/http/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-http", - "version": "2.4.0", + "version": "2.3.0", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -24,7 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.0.0", - "@tauri-apps/plugin-http": "link:" + "@tauri-apps/api": "^2.0.0" } } diff --git a/plugins/http/src/commands.rs b/plugins/http/src/commands.rs index b0c6aab7..3dc0297e 100644 --- a/plugins/http/src/commands.rs +++ b/plugins/http/src/commands.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT - use std::{future::Future, pin::Pin, str::FromStr, sync::Arc, time::Duration}; use http::{header, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; @@ -11,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}; @@ -23,8 +22,6 @@ use crate::{ const HTTP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); -// reqwest::Response is never read, but might be needed for future use. -#[allow(dead_code)] struct ReqwestResponse(reqwest::Response); impl tauri::Resource for ReqwestResponse {} @@ -129,12 +126,6 @@ pub struct BasicAuth { password: String, } -#[derive(Clone, Serialize)] -pub struct StreamMessage { - value: Option>, - done: bool, -} - #[inline] fn proxy_creator( url_or_config: UrlOrConfig, @@ -190,7 +181,6 @@ pub async fn fetch( client_config: ClientConfig, command_scope: CommandScope, global_scope: GlobalScope, - stream_channel: Channel ) -> crate::Result { let ClientConfig { method, @@ -324,24 +314,7 @@ pub async fn fetch( #[cfg(feature = "tracing")] tracing::trace!("{:?}", request); - let fut = async move { - let mut res = request.send().await?; - - // send response through IPC channel - while let Some(chunk) = res.chunk().await? { - stream_channel.send(StreamMessage{ - value: Some(chunk.to_vec()), - done: false, - })?; - } - - stream_channel.send(StreamMessage { value: None, done: true })?; - - // return that response - Ok(res) - }; - - + let fut = async move { request.send().await.map_err(Into::into) }; let mut resources_table = webview.resources_table(); let rid = resources_table.add_request(Box::pin(fut)); @@ -437,6 +410,19 @@ pub async fn fetch_send( }) } +#[tauri::command] +pub(crate) async fn fetch_read_body( + webview: Webview, + rid: ResourceId, +) -> crate::Result { + let res = { + let mut resources_table = webview.resources_table(); + resources_table.take::(rid)? + }; + let res = Arc::into_inner(res).unwrap().0; + Ok(tauri::ipc::Response::new(res.bytes().await?.to_vec())) +} + // forbidden headers per fetch spec https://fetch.spec.whatwg.org/#terminology-headers #[cfg(not(feature = "unsafe-headers"))] fn is_unsafe_header(header: &HeaderName) -> bool { diff --git a/plugins/http/src/lib.rs b/plugins/http/src/lib.rs index 4e11e561..d775760c 100644 --- a/plugins/http/src/lib.rs +++ b/plugins/http/src/lib.rs @@ -36,7 +36,8 @@ pub fn init() -> TauriPlugin { .invoke_handler(tauri::generate_handler![ commands::fetch, commands::fetch_cancel, - commands::fetch_send + commands::fetch_send, + commands::fetch_read_body, ]) .build() } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 256958a6..c7e5a71c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -229,9 +229,6 @@ importers: '@tauri-apps/api': specifier: ^2.0.0 version: 2.3.0 - '@tauri-apps/plugin-http': - specifier: 'link:' - version: 'link:' plugins/log: dependencies: @@ -2286,9 +2283,9 @@ snapshots: - encoding - mocha - '@covector/assemble@0.12.0(mocha@10.8.2)': + '@covector/assemble@0.12.0': dependencies: - '@covector/command': 0.8.0(mocha@10.8.2) + '@covector/command': 0.8.0 '@covector/files': 0.8.0 effection: 2.0.8(mocha@10.8.2) js-yaml: 4.1.0 @@ -2299,10 +2296,9 @@ snapshots: unified: 9.2.2 transitivePeerDependencies: - encoding - - mocha - supports-color - '@covector/changelog@0.12.0(mocha@10.8.2)': + '@covector/changelog@0.12.0': dependencies: '@covector/files': 0.8.0 effection: 2.0.8(mocha@10.8.2) @@ -2312,16 +2308,14 @@ snapshots: unified: 9.2.2 transitivePeerDependencies: - encoding - - mocha - supports-color - '@covector/command@0.8.0(mocha@10.8.2)': + '@covector/command@0.8.0': dependencies: - '@effection/process': 2.1.4(mocha@10.8.2) + '@effection/process': 2.1.4 effection: 2.0.8(mocha@10.8.2) transitivePeerDependencies: - encoding - - mocha '@covector/files@0.8.0': dependencies: @@ -2368,8 +2362,10 @@ snapshots: dependencies: effection: 2.0.8(mocha@10.8.2) mocha: 10.8.2 + transitivePeerDependencies: + - encoding - '@effection/process@2.1.4(mocha@10.8.2)': + '@effection/process@2.1.4': dependencies: cross-spawn: 7.0.6 ctrlc-windows: 2.2.0 @@ -2377,7 +2373,6 @@ snapshots: shellwords: 0.1.1 transitivePeerDependencies: - encoding - - mocha '@effection/stream@2.0.6': dependencies: @@ -3167,9 +3162,9 @@ snapshots: dependencies: '@clack/prompts': 0.7.0 '@covector/apply': 0.10.0(mocha@10.8.2) - '@covector/assemble': 0.12.0(mocha@10.8.2) - '@covector/changelog': 0.12.0(mocha@10.8.2) - '@covector/command': 0.8.0(mocha@10.8.2) + '@covector/assemble': 0.12.0 + '@covector/changelog': 0.12.0 + '@covector/command': 0.8.0 '@covector/files': 0.8.0 effection: 2.0.8(mocha@10.8.2) globby: 11.1.0