mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-28 21:51:47 +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:
1 parent
ea8eadce85
commit
ae0cb92438
6 files changed
+68
-75
No files matched your search
@@ -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;
|
delete init.proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const signal = init?.signal;
|
||||||
|
|
||||||
const req = new Request(input, init);
|
const req = new Request(input, init);
|
||||||
const buffer = await req.arrayBuffer();
|
const buffer = await req.arrayBuffer();
|
||||||
const reqData = buffer.byteLength ? Array.from(new Uint8Array(buffer)) : null;
|
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", {
|
invoke("plugin:http|fetch_cancel", {
|
||||||
rid,
|
rid,
|
||||||
});
|
});
|
||||||
@@ -140,17 +142,21 @@ export async function fetch(
|
|||||||
statusText: string;
|
statusText: string;
|
||||||
headers: [[string, string]];
|
headers: [[string, string]];
|
||||||
url: string;
|
url: string;
|
||||||
|
rid: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status, statusText, url, headers } = await invoke<FetchSendResponse>(
|
const {
|
||||||
"plugin:http|fetch_send",
|
status,
|
||||||
{
|
statusText,
|
||||||
rid,
|
url,
|
||||||
},
|
headers,
|
||||||
);
|
rid: responseRid,
|
||||||
|
} = await invoke<FetchSendResponse>("plugin:http|fetch_send", {
|
||||||
|
rid,
|
||||||
|
});
|
||||||
|
|
||||||
const body = await invoke<number[]>("plugin:http|fetch_read_body", {
|
const body = await invoke<number[]>("plugin:http|fetch_read_body", {
|
||||||
rid,
|
rid: responseRid,
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = new Response(new Uint8Array(body), {
|
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: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// 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 http::{header, HeaderName, HeaderValue, Method, StatusCode};
|
||||||
use reqwest::{redirect::Policy, NoProxy};
|
use reqwest::{redirect::Policy, NoProxy};
|
||||||
use serde::{Deserialize, Serialize};
|
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)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -18,6 +34,7 @@ pub struct FetchResponse {
|
|||||||
status_text: String,
|
status_text: String,
|
||||||
headers: Vec<(String, String)>,
|
headers: Vec<(String, String)>,
|
||||||
url: String,
|
url: String,
|
||||||
|
rid: ResourceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -114,7 +131,7 @@ fn attach_proxy(
|
|||||||
pub async fn fetch<R: Runtime>(
|
pub async fn fetch<R: Runtime>(
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
client_config: ClientConfig,
|
client_config: ClientConfig,
|
||||||
) -> crate::Result<RequestId> {
|
) -> crate::Result<ResourceId> {
|
||||||
let ClientConfig {
|
let ClientConfig {
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
@@ -183,11 +200,9 @@ pub async fn fetch<R: Runtime>(
|
|||||||
request = request.body(data);
|
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 fut = async move { Ok(request.send().await.map_err(Into::into)) };
|
||||||
let mut request_table = http_state.requests.lock().await;
|
let mut resources_table = app.resources_table();
|
||||||
request_table.insert(rid, FetchRequest::new(Box::pin(fut)));
|
let rid = resources_table.add(FetchRequest::new(Box::pin(fut)));
|
||||||
|
|
||||||
Ok(rid)
|
Ok(rid)
|
||||||
} else {
|
} else {
|
||||||
@@ -206,11 +221,9 @@ pub async fn fetch<R: Runtime>(
|
|||||||
.header(header::CONTENT_TYPE, data_url.mime_type().to_string())
|
.header(header::CONTENT_TYPE, data_url.mime_type().to_string())
|
||||||
.body(reqwest::Body::from(body))?;
|
.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 fut = async move { Ok(Ok(reqwest::Response::from(response))) };
|
||||||
let mut request_table = http_state.requests.lock().await;
|
let mut resources_table = app.resources_table();
|
||||||
request_table.insert(rid, FetchRequest::new(Box::pin(fut)));
|
let rid = resources_table.add(FetchRequest::new(Box::pin(fut)));
|
||||||
Ok(rid)
|
Ok(rid)
|
||||||
}
|
}
|
||||||
_ => Err(Error::SchemeNotSupport(scheme.to_string())),
|
_ => Err(Error::SchemeNotSupport(scheme.to_string())),
|
||||||
@@ -218,24 +231,25 @@ pub async fn fetch<R: Runtime>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn fetch_cancel<R: Runtime>(app: AppHandle<R>, rid: RequestId) -> crate::Result<()> {
|
pub async fn fetch_cancel<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<()> {
|
||||||
let mut request_table = app.http().requests.lock().await;
|
let req = {
|
||||||
let req = request_table
|
let resources_table = app.resources_table();
|
||||||
.get_mut(&rid)
|
resources_table.get::<FetchRequest>(rid)?
|
||||||
.ok_or(Error::InvalidRequestId(rid))?;
|
};
|
||||||
*req = FetchRequest::new(Box::pin(async { Err(Error::RequestCanceled) }));
|
let mut req = req.0.lock().await;
|
||||||
|
*req = Box::pin(async { Err(Error::RequestCanceled) });
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn fetch_send<R: Runtime>(
|
pub async fn fetch_send<R: Runtime>(
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
rid: RequestId,
|
rid: ResourceId,
|
||||||
) -> crate::Result<FetchResponse> {
|
) -> crate::Result<FetchResponse> {
|
||||||
let mut request_table = app.http().requests.lock().await;
|
let req = {
|
||||||
let req = request_table
|
let mut resources_table = app.resources_table();
|
||||||
.remove(&rid)
|
resources_table.take::<FetchRequest>(rid)?
|
||||||
.ok_or(Error::InvalidRequestId(rid))?;
|
};
|
||||||
|
|
||||||
let res = match req.0.lock().await.as_mut().await {
|
let res = match req.0.lock().await.as_mut().await {
|
||||||
Ok(Ok(res)) => res,
|
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 {
|
Ok(FetchResponse {
|
||||||
status: status.as_u16(),
|
status: status.as_u16(),
|
||||||
status_text: status.canonical_reason().unwrap_or_default().to_string(),
|
status_text: status.canonical_reason().unwrap_or_default().to_string(),
|
||||||
headers,
|
headers,
|
||||||
url,
|
url,
|
||||||
|
rid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub(crate) async fn fetch_read_body<R: Runtime>(
|
pub(crate) async fn fetch_read_body<R: Runtime>(
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
rid: RequestId,
|
rid: ResourceId,
|
||||||
) -> crate::Result<tauri::ipc::Response> {
|
) -> crate::Result<tauri::ipc::Response> {
|
||||||
let mut response_table = app.http().responses.lock().await;
|
let res = {
|
||||||
let res = response_table
|
let mut resources_table = app.resources_table();
|
||||||
.remove(&rid)
|
resources_table.take::<ReqwestResponse>(rid)?
|
||||||
.ok_or(Error::InvalidRequestId(rid))?;
|
};
|
||||||
|
let res = Arc::into_inner(res).unwrap().0;
|
||||||
Ok(tauri::ipc::Response::new(res.bytes().await?.to_vec()))
|
Ok(tauri::ipc::Response::new(res.bytes().await?.to_vec()))
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,6 @@
|
|||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::RequestId;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@@ -39,8 +37,8 @@ pub enum Error {
|
|||||||
DataUrlError,
|
DataUrlError,
|
||||||
#[error("failed to decode data url into bytes")]
|
#[error("failed to decode data url into bytes")]
|
||||||
DataUrlDecodeError,
|
DataUrlDecodeError,
|
||||||
#[error("invalid request id: {0}")]
|
#[error(transparent)]
|
||||||
InvalidRequestId(RequestId),
|
Tauri(#[from] tauri::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Utf8(#[from] std::string::FromUtf8Error),
|
Utf8(#[from] std::string::FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Access the HTTP client written in Rust.
|
//! Access the HTTP client written in Rust.
|
||||||
|
|
||||||
use std::sync::atomic::AtomicU32;
|
|
||||||
use std::{collections::HashMap, future::Future, pin::Pin};
|
|
||||||
|
|
||||||
pub use reqwest;
|
pub use reqwest;
|
||||||
use reqwest::Response;
|
|
||||||
use tauri::async_runtime::Mutex;
|
|
||||||
use tauri::{
|
use tauri::{
|
||||||
plugin::{Builder, TauriPlugin},
|
plugin::{Builder, TauriPlugin},
|
||||||
AppHandle, Manager, Runtime,
|
AppHandle, Manager, Runtime,
|
||||||
@@ -25,34 +20,10 @@ mod config;
|
|||||||
mod error;
|
mod error;
|
||||||
mod scope;
|
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> {
|
struct Http<R: Runtime> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
scope: scope::Scope,
|
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> {
|
trait HttpExt<R: Runtime> {
|
||||||
@@ -78,9 +49,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
|
|||||||
let default_scope = HttpAllowlistScope::default();
|
let default_scope = HttpAllowlistScope::default();
|
||||||
app.manage(Http {
|
app.manage(Http {
|
||||||
app: app.clone(),
|
app: app.clone(),
|
||||||
current_id: 0.into(),
|
|
||||||
requests: Default::default(),
|
|
||||||
responses: Default::default(),
|
|
||||||
scope: scope::Scope::new(
|
scope: scope::Scope::new(
|
||||||
api.config()
|
api.config()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|||||||
Reference in new issue
Block a user