mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-08-19 19:17:15 +02:00
feat: add stream support
This commit is contained in:
Generated
+1
-1
@@ -6659,7 +6659,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-http"
|
name = "tauri-plugin-http"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-url",
|
"data-url",
|
||||||
"http",
|
"http",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tauri-plugin-http"
|
name = "tauri-plugin-http"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
description = "Access an HTTP client written in Rust."
|
description = "Access an HTTP client written in Rust."
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -106,20 +106,6 @@ export interface DangerousSettings {
|
|||||||
acceptInvalidHostnames?: boolean
|
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'
|
const ERROR_REQUEST_CANCELLED = 'Request canceled'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,19 +186,6 @@ export async function fetch(
|
|||||||
throw new Error(ERROR_REQUEST_CANCELLED)
|
throw new Error(ERROR_REQUEST_CANCELLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
const streamChannel = new Channel<StreamMessage>()
|
|
||||||
|
|
||||||
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<number>('plugin:http|fetch', {
|
const rid = await invoke<number>('plugin:http|fetch', {
|
||||||
clientConfig: {
|
clientConfig: {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
@@ -223,8 +196,7 @@ export async function fetch(
|
|||||||
connectTimeout,
|
connectTimeout,
|
||||||
proxy,
|
proxy,
|
||||||
danger
|
danger
|
||||||
},
|
}
|
||||||
streamChannel
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const abort = () => invoke('plugin:http|fetch_cancel', { rid })
|
const abort = () => invoke('plugin:http|fetch_cancel', { rid })
|
||||||
@@ -251,15 +223,30 @@ export async function fetch(
|
|||||||
status,
|
status,
|
||||||
statusText,
|
statusText,
|
||||||
url,
|
url,
|
||||||
headers: responseHeaders
|
headers: responseHeaders,
|
||||||
|
rid: responseRid
|
||||||
} = await invoke<FetchSendResponse>('plugin:http|fetch_send', {
|
} = await invoke<FetchSendResponse>('plugin:http|fetch_send', {
|
||||||
rid
|
rid
|
||||||
})
|
})
|
||||||
|
|
||||||
const res = new Response(readableStreamBody, {
|
const body = await invoke<ArrayBuffer | number[]>(
|
||||||
status,
|
'plugin:http|fetch_read_body',
|
||||||
statusText
|
{
|
||||||
})
|
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
|
// url and headers are read only properties
|
||||||
// but seems like we can set them like this
|
// but seems like we can set them like this
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tauri-apps/plugin-http",
|
"name": "@tauri-apps/plugin-http",
|
||||||
"version": "2.4.0",
|
"version": "2.3.0",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Tauri Programme within The Commons Conservancy"
|
"Tauri Programme within The Commons Conservancy"
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.0.0",
|
"@tauri-apps/api": "^2.0.0"
|
||||||
"@tauri-apps/plugin-http": "link:"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
use std::{future::Future, pin::Pin, str::FromStr, sync::Arc, time::Duration};
|
use std::{future::Future, pin::Pin, str::FromStr, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use http::{header, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
use http::{header, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
|
||||||
@@ -11,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};
|
||||||
@@ -23,8 +22,6 @@ use crate::{
|
|||||||
|
|
||||||
const HTTP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
|
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);
|
struct ReqwestResponse(reqwest::Response);
|
||||||
impl tauri::Resource for ReqwestResponse {}
|
impl tauri::Resource for ReqwestResponse {}
|
||||||
|
|
||||||
@@ -129,12 +126,6 @@ pub struct BasicAuth {
|
|||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
|
||||||
pub struct StreamMessage {
|
|
||||||
value: Option<Vec<u8>>,
|
|
||||||
done: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn proxy_creator(
|
fn proxy_creator(
|
||||||
url_or_config: UrlOrConfig,
|
url_or_config: UrlOrConfig,
|
||||||
@@ -190,7 +181,6 @@ pub async fn fetch<R: Runtime>(
|
|||||||
client_config: ClientConfig,
|
client_config: ClientConfig,
|
||||||
command_scope: CommandScope<Entry>,
|
command_scope: CommandScope<Entry>,
|
||||||
global_scope: GlobalScope<Entry>,
|
global_scope: GlobalScope<Entry>,
|
||||||
stream_channel: Channel<StreamMessage>
|
|
||||||
) -> crate::Result<ResourceId> {
|
) -> crate::Result<ResourceId> {
|
||||||
let ClientConfig {
|
let ClientConfig {
|
||||||
method,
|
method,
|
||||||
@@ -324,24 +314,7 @@ pub async fn fetch<R: Runtime>(
|
|||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
tracing::trace!("{:?}", request);
|
tracing::trace!("{:?}", request);
|
||||||
|
|
||||||
let fut = async move {
|
let fut = async move { request.send().await.map_err(Into::into) };
|
||||||
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 mut resources_table = webview.resources_table();
|
let mut resources_table = webview.resources_table();
|
||||||
let rid = resources_table.add_request(Box::pin(fut));
|
let rid = resources_table.add_request(Box::pin(fut));
|
||||||
|
|
||||||
@@ -437,6 +410,19 @@ pub async fn fetch_send<R: Runtime>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub(crate) async fn fetch_read_body<R: Runtime>(
|
||||||
|
webview: Webview<R>,
|
||||||
|
rid: ResourceId,
|
||||||
|
) -> crate::Result<tauri::ipc::Response> {
|
||||||
|
let res = {
|
||||||
|
let mut resources_table = webview.resources_table();
|
||||||
|
resources_table.take::<ReqwestResponse>(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
|
// forbidden headers per fetch spec https://fetch.spec.whatwg.org/#terminology-headers
|
||||||
#[cfg(not(feature = "unsafe-headers"))]
|
#[cfg(not(feature = "unsafe-headers"))]
|
||||||
fn is_unsafe_header(header: &HeaderName) -> bool {
|
fn is_unsafe_header(header: &HeaderName) -> bool {
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
commands::fetch,
|
commands::fetch,
|
||||||
commands::fetch_cancel,
|
commands::fetch_cancel,
|
||||||
commands::fetch_send
|
commands::fetch_send,
|
||||||
|
commands::fetch_read_body,
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+11
-16
@@ -229,9 +229,6 @@ importers:
|
|||||||
'@tauri-apps/api':
|
'@tauri-apps/api':
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.3.0
|
version: 2.3.0
|
||||||
'@tauri-apps/plugin-http':
|
|
||||||
specifier: 'link:'
|
|
||||||
version: 'link:'
|
|
||||||
|
|
||||||
plugins/log:
|
plugins/log:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2286,9 +2283,9 @@ snapshots:
|
|||||||
- encoding
|
- encoding
|
||||||
- mocha
|
- mocha
|
||||||
|
|
||||||
'@covector/assemble@0.12.0(mocha@10.8.2)':
|
'@covector/assemble@0.12.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@covector/command': 0.8.0(mocha@10.8.2)
|
'@covector/command': 0.8.0
|
||||||
'@covector/files': 0.8.0
|
'@covector/files': 0.8.0
|
||||||
effection: 2.0.8(mocha@10.8.2)
|
effection: 2.0.8(mocha@10.8.2)
|
||||||
js-yaml: 4.1.0
|
js-yaml: 4.1.0
|
||||||
@@ -2299,10 +2296,9 @@ snapshots:
|
|||||||
unified: 9.2.2
|
unified: 9.2.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- mocha
|
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@covector/changelog@0.12.0(mocha@10.8.2)':
|
'@covector/changelog@0.12.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@covector/files': 0.8.0
|
'@covector/files': 0.8.0
|
||||||
effection: 2.0.8(mocha@10.8.2)
|
effection: 2.0.8(mocha@10.8.2)
|
||||||
@@ -2312,16 +2308,14 @@ snapshots:
|
|||||||
unified: 9.2.2
|
unified: 9.2.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- mocha
|
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@covector/command@0.8.0(mocha@10.8.2)':
|
'@covector/command@0.8.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@effection/process': 2.1.4(mocha@10.8.2)
|
'@effection/process': 2.1.4
|
||||||
effection: 2.0.8(mocha@10.8.2)
|
effection: 2.0.8(mocha@10.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- mocha
|
|
||||||
|
|
||||||
'@covector/files@0.8.0':
|
'@covector/files@0.8.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2368,8 +2362,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
effection: 2.0.8(mocha@10.8.2)
|
effection: 2.0.8(mocha@10.8.2)
|
||||||
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:
|
dependencies:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
ctrlc-windows: 2.2.0
|
ctrlc-windows: 2.2.0
|
||||||
@@ -2377,7 +2373,6 @@ snapshots:
|
|||||||
shellwords: 0.1.1
|
shellwords: 0.1.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- mocha
|
|
||||||
|
|
||||||
'@effection/stream@2.0.6':
|
'@effection/stream@2.0.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3167,9 +3162,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@clack/prompts': 0.7.0
|
'@clack/prompts': 0.7.0
|
||||||
'@covector/apply': 0.10.0(mocha@10.8.2)
|
'@covector/apply': 0.10.0(mocha@10.8.2)
|
||||||
'@covector/assemble': 0.12.0(mocha@10.8.2)
|
'@covector/assemble': 0.12.0
|
||||||
'@covector/changelog': 0.12.0(mocha@10.8.2)
|
'@covector/changelog': 0.12.0
|
||||||
'@covector/command': 0.8.0(mocha@10.8.2)
|
'@covector/command': 0.8.0
|
||||||
'@covector/files': 0.8.0
|
'@covector/files': 0.8.0
|
||||||
effection: 2.0.8(mocha@10.8.2)
|
effection: 2.0.8(mocha@10.8.2)
|
||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user