mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-21 11:26:15 +02:00
refactor(http): migrate to tauri's new resource table (#834)
* refactor(http): migrate to tauri's new resource table * fmt * change file
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"http": "patch"
|
||||
---
|
||||
|
||||
**Breaking change** Removed `Error::InvalidRequestId` variant and added `Error::Tauri` variant.
|
||||
@@ -113,6 +113,8 @@ export async function fetch(
|
||||
delete init.proxy;
|
||||
}
|
||||
|
||||
const signal = init?.signal;
|
||||
|
||||
const req = new Request(input, init);
|
||||
const buffer = await req.arrayBuffer();
|
||||
const reqData = buffer.byteLength ? Array.from(new Uint8Array(buffer)) : null;
|
||||
@@ -129,7 +131,7 @@ export async function fetch(
|
||||
},
|
||||
});
|
||||
|
||||
req.signal.addEventListener("abort", () => {
|
||||
signal?.addEventListener("abort", () => {
|
||||
invoke("plugin:http|fetch_cancel", {
|
||||
rid,
|
||||
});
|
||||
@@ -140,17 +142,21 @@ export async function fetch(
|
||||
statusText: string;
|
||||
headers: [[string, string]];
|
||||
url: string;
|
||||
rid: number;
|
||||
}
|
||||
|
||||
const { status, statusText, url, headers } = await invoke<FetchSendResponse>(
|
||||
"plugin:http|fetch_send",
|
||||
{
|
||||
rid,
|
||||
},
|
||||
);
|
||||
const {
|
||||
status,
|
||||
statusText,
|
||||
url,
|
||||
headers,
|
||||
rid: responseRid,
|
||||
} = await invoke<FetchSendResponse>("plugin:http|fetch_send", {
|
||||
rid,
|
||||
});
|
||||
|
||||
const body = await invoke<number[]>("plugin:http|fetch_read_body", {
|
||||
rid,
|
||||
rid: responseRid,
|
||||
});
|
||||
|
||||
const res = new Response(new Uint8Array(body), {
|
||||
|
||||
@@ -1 +1 @@
|
||||
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)}return"function"==typeof SuppressedError&&SuppressedError,e.fetch=async function(e,r){const n=r?.maxRedirections,i=r?.connectTimeout,a=r?.proxy;r&&(delete r.maxRedirections,delete r.connectTimeout,delete r.proxy);const o=new Request(e,r),s=await o.arrayBuffer(),u=s.byteLength?Array.from(new Uint8Array(s)):null,c=await t("plugin:http|fetch",{clientConfig:{method:o.method,url:o.url,headers:Array.from(o.headers.entries()),data:u,maxRedirections:n,connectTimeout:i,proxy:a}});o.signal.addEventListener("abort",(()=>{t("plugin:http|fetch_cancel",{rid:c})}));const{status:_,statusText:d,url:p,headers:f}=await t("plugin:http|fetch_send",{rid:c}),l=await t("plugin:http|fetch_read_body",{rid:c}),h=new Response(new Uint8Array(l),{headers:f,status:_,statusText:d});return Object.defineProperty(h,"url",{value:p}),h},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)}return"function"==typeof SuppressedError&&SuppressedError,e.fetch=async function(e,r){const n=r?.maxRedirections,i=r?.connectTimeout,a=r?.proxy;r&&(delete r.maxRedirections,delete r.connectTimeout,delete r.proxy);const o=r?.signal,s=new Request(e,r),u=await s.arrayBuffer(),c=u.byteLength?Array.from(new Uint8Array(u)):null,d=await t("plugin:http|fetch",{clientConfig:{method:s.method,url:s.url,headers:Array.from(s.headers.entries()),data:c,maxRedirections:n,connectTimeout:i,proxy:a}});o?.addEventListener("abort",(()=>{t("plugin:http|fetch_cancel",{rid:d})}));const{status:_,statusText:p,url:f,headers:l,rid:h}=await t("plugin:http|fetch_send",{rid:d}),y=await t("plugin:http|fetch_read_body",{rid:h}),T=new Response(new Uint8Array(y),{headers:l,status:_,statusText:p});return Object.defineProperty(T,"url",{value:f}),T},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
|
||||
|
||||
@@ -2,14 +2,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc, time::Duration};
|
||||
|
||||
use http::{header, HeaderName, HeaderValue, Method, StatusCode};
|
||||
use reqwest::{redirect::Policy, NoProxy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{command, AppHandle, Runtime};
|
||||
use tauri::{async_runtime::Mutex, command, AppHandle, Manager, ResourceId, Runtime};
|
||||
|
||||
use crate::{Error, FetchRequest, HttpExt, RequestId};
|
||||
use crate::{Error, HttpExt, Result};
|
||||
|
||||
struct ReqwestResponse(reqwest::Response);
|
||||
|
||||
type CancelableResponseResult = Result<Result<reqwest::Response>>;
|
||||
type CancelableResponseFuture =
|
||||
Pin<Box<dyn Future<Output = CancelableResponseResult> + Send + Sync>>;
|
||||
|
||||
struct FetchRequest(Mutex<CancelableResponseFuture>);
|
||||
impl FetchRequest {
|
||||
fn new(f: CancelableResponseFuture) -> Self {
|
||||
Self(Mutex::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl tauri::Resource for FetchRequest {}
|
||||
impl tauri::Resource for ReqwestResponse {}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -18,6 +34,7 @@ pub struct FetchResponse {
|
||||
status_text: String,
|
||||
headers: Vec<(String, String)>,
|
||||
url: String,
|
||||
rid: ResourceId,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -114,7 +131,7 @@ fn attach_proxy(
|
||||
pub async fn fetch<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
client_config: ClientConfig,
|
||||
) -> crate::Result<RequestId> {
|
||||
) -> crate::Result<ResourceId> {
|
||||
let ClientConfig {
|
||||
method,
|
||||
url,
|
||||
@@ -183,11 +200,9 @@ pub async fn fetch<R: Runtime>(
|
||||
request = request.body(data);
|
||||
}
|
||||
|
||||
let http_state = app.http();
|
||||
let rid = http_state.next_id();
|
||||
let fut = async move { Ok(request.send().await.map_err(Into::into)) };
|
||||
let mut request_table = http_state.requests.lock().await;
|
||||
request_table.insert(rid, FetchRequest::new(Box::pin(fut)));
|
||||
let mut resources_table = app.resources_table();
|
||||
let rid = resources_table.add(FetchRequest::new(Box::pin(fut)));
|
||||
|
||||
Ok(rid)
|
||||
} else {
|
||||
@@ -206,11 +221,9 @@ pub async fn fetch<R: Runtime>(
|
||||
.header(header::CONTENT_TYPE, data_url.mime_type().to_string())
|
||||
.body(reqwest::Body::from(body))?;
|
||||
|
||||
let http_state = app.http();
|
||||
let rid = http_state.next_id();
|
||||
let fut = async move { Ok(Ok(reqwest::Response::from(response))) };
|
||||
let mut request_table = http_state.requests.lock().await;
|
||||
request_table.insert(rid, FetchRequest::new(Box::pin(fut)));
|
||||
let mut resources_table = app.resources_table();
|
||||
let rid = resources_table.add(FetchRequest::new(Box::pin(fut)));
|
||||
Ok(rid)
|
||||
}
|
||||
_ => Err(Error::SchemeNotSupport(scheme.to_string())),
|
||||
@@ -218,24 +231,25 @@ pub async fn fetch<R: Runtime>(
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn fetch_cancel<R: Runtime>(app: AppHandle<R>, rid: RequestId) -> crate::Result<()> {
|
||||
let mut request_table = app.http().requests.lock().await;
|
||||
let req = request_table
|
||||
.get_mut(&rid)
|
||||
.ok_or(Error::InvalidRequestId(rid))?;
|
||||
*req = FetchRequest::new(Box::pin(async { Err(Error::RequestCanceled) }));
|
||||
pub async fn fetch_cancel<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<()> {
|
||||
let req = {
|
||||
let resources_table = app.resources_table();
|
||||
resources_table.get::<FetchRequest>(rid)?
|
||||
};
|
||||
let mut req = req.0.lock().await;
|
||||
*req = Box::pin(async { Err(Error::RequestCanceled) });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn fetch_send<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: RequestId,
|
||||
rid: ResourceId,
|
||||
) -> crate::Result<FetchResponse> {
|
||||
let mut request_table = app.http().requests.lock().await;
|
||||
let req = request_table
|
||||
.remove(&rid)
|
||||
.ok_or(Error::InvalidRequestId(rid))?;
|
||||
let req = {
|
||||
let mut resources_table = app.resources_table();
|
||||
resources_table.take::<FetchRequest>(rid)?
|
||||
};
|
||||
|
||||
let res = match req.0.lock().await.as_mut().await {
|
||||
Ok(Ok(res)) => res,
|
||||
@@ -252,25 +266,27 @@ pub async fn fetch_send<R: Runtime>(
|
||||
));
|
||||
}
|
||||
|
||||
app.http().responses.lock().await.insert(rid, res);
|
||||
let mut resources_table = app.resources_table();
|
||||
let rid = resources_table.add(ReqwestResponse(res));
|
||||
|
||||
Ok(FetchResponse {
|
||||
status: status.as_u16(),
|
||||
status_text: status.canonical_reason().unwrap_or_default().to_string(),
|
||||
headers,
|
||||
url,
|
||||
rid,
|
||||
})
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn fetch_read_body<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: RequestId,
|
||||
rid: ResourceId,
|
||||
) -> crate::Result<tauri::ipc::Response> {
|
||||
let mut response_table = app.http().responses.lock().await;
|
||||
let res = response_table
|
||||
.remove(&rid)
|
||||
.ok_or(Error::InvalidRequestId(rid))?;
|
||||
|
||||
let res = {
|
||||
let mut resources_table = app.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()))
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
use serde::{Serialize, Serializer};
|
||||
use url::Url;
|
||||
|
||||
use crate::RequestId;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
@@ -39,8 +37,8 @@ pub enum Error {
|
||||
DataUrlError,
|
||||
#[error("failed to decode data url into bytes")]
|
||||
DataUrlDecodeError,
|
||||
#[error("invalid request id: {0}")]
|
||||
InvalidRequestId(RequestId),
|
||||
#[error(transparent)]
|
||||
Tauri(#[from] tauri::Error),
|
||||
#[error(transparent)]
|
||||
Utf8(#[from] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
//!
|
||||
//! Access the HTTP client written in Rust.
|
||||
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::{collections::HashMap, future::Future, pin::Pin};
|
||||
|
||||
pub use reqwest;
|
||||
use reqwest::Response;
|
||||
use tauri::async_runtime::Mutex;
|
||||
use tauri::{
|
||||
plugin::{Builder, TauriPlugin},
|
||||
AppHandle, Manager, Runtime,
|
||||
@@ -25,34 +20,10 @@ mod config;
|
||||
mod error;
|
||||
mod scope;
|
||||
|
||||
type RequestId = u32;
|
||||
type CancelableResponseResult = Result<Result<reqwest::Response>>;
|
||||
type CancelableResponseFuture =
|
||||
Pin<Box<dyn Future<Output = CancelableResponseResult> + Send + Sync>>;
|
||||
type RequestTable = HashMap<RequestId, FetchRequest>;
|
||||
type ResponseTable = HashMap<RequestId, Response>;
|
||||
|
||||
struct FetchRequest(Mutex<CancelableResponseFuture>);
|
||||
impl FetchRequest {
|
||||
fn new(f: CancelableResponseFuture) -> Self {
|
||||
Self(Mutex::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
struct Http<R: Runtime> {
|
||||
#[allow(dead_code)]
|
||||
app: AppHandle<R>,
|
||||
scope: scope::Scope,
|
||||
current_id: AtomicU32,
|
||||
requests: Mutex<RequestTable>,
|
||||
responses: Mutex<ResponseTable>,
|
||||
}
|
||||
|
||||
impl<R: Runtime> Http<R> {
|
||||
fn next_id(&self) -> RequestId {
|
||||
self.current_id
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
trait HttpExt<R: Runtime> {
|
||||
@@ -78,9 +49,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
|
||||
let default_scope = HttpAllowlistScope::default();
|
||||
app.manage(Http {
|
||||
app: app.clone(),
|
||||
current_id: 0.into(),
|
||||
requests: Default::default(),
|
||||
responses: Default::default(),
|
||||
scope: scope::Scope::new(
|
||||
api.config()
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user