mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-06-06 13:53:54 +02:00
fix(http): use tokio oneshot channel for detecting abort (#1395)
* fix(http): properly handle aborting closes #1376 * abort early in JS * avoid using unnecessary mutexes * fix lint * update bundle * simplify
This commit is contained in:
@@ -11,8 +11,9 @@ use tauri::{
|
||||
async_runtime::Mutex,
|
||||
command,
|
||||
ipc::{CommandScope, GlobalScope},
|
||||
Manager, ResourceId, Runtime, State, Webview,
|
||||
Manager, ResourceId, ResourceTable, Runtime, State, Webview,
|
||||
};
|
||||
use tokio::sync::oneshot::{channel, Receiver, Sender};
|
||||
|
||||
use crate::{
|
||||
scope::{Entry, Scope},
|
||||
@@ -22,20 +23,47 @@ use crate::{
|
||||
const HTTP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
|
||||
|
||||
struct ReqwestResponse(reqwest::Response);
|
||||
impl tauri::Resource for ReqwestResponse {}
|
||||
|
||||
type CancelableResponseResult = Result<Result<reqwest::Response>>;
|
||||
type CancelableResponseResult = 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))
|
||||
struct FetchRequest {
|
||||
fut: Mutex<CancelableResponseFuture>,
|
||||
abort_tx_rid: ResourceId,
|
||||
abort_rx_rid: ResourceId,
|
||||
}
|
||||
impl tauri::Resource for FetchRequest {}
|
||||
|
||||
struct AbortSender(Sender<()>);
|
||||
impl tauri::Resource for AbortRecveiver {}
|
||||
|
||||
impl AbortSender {
|
||||
fn abort(self) {
|
||||
let _ = self.0.send(());
|
||||
}
|
||||
}
|
||||
|
||||
impl tauri::Resource for FetchRequest {}
|
||||
impl tauri::Resource for ReqwestResponse {}
|
||||
struct AbortRecveiver(Receiver<()>);
|
||||
impl tauri::Resource for AbortSender {}
|
||||
|
||||
trait AddRequest {
|
||||
fn add_request(&mut self, fut: CancelableResponseFuture) -> ResourceId;
|
||||
}
|
||||
|
||||
impl AddRequest for ResourceTable {
|
||||
fn add_request(&mut self, fut: CancelableResponseFuture) -> ResourceId {
|
||||
let (tx, rx) = channel::<()>();
|
||||
let (tx, rx) = (AbortSender(tx), AbortRecveiver(rx));
|
||||
let req = FetchRequest {
|
||||
fut: Mutex::new(fut),
|
||||
abort_tx_rid: self.add(tx),
|
||||
abort_rx_rid: self.add(rx),
|
||||
};
|
||||
self.add(req)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -239,9 +267,9 @@ pub async fn fetch<R: Runtime>(
|
||||
request = request.body(data);
|
||||
}
|
||||
|
||||
let fut = async move { Ok(request.send().await.map_err(Into::into)) };
|
||||
let fut = async move { request.send().await.map_err(Into::into) };
|
||||
let mut resources_table = webview.resources_table();
|
||||
let rid = resources_table.add(FetchRequest::new(Box::pin(fut)));
|
||||
let rid = resources_table.add_request(Box::pin(fut));
|
||||
|
||||
Ok(rid)
|
||||
} else {
|
||||
@@ -260,9 +288,9 @@ pub async fn fetch<R: Runtime>(
|
||||
.header(header::CONTENT_TYPE, data_url.mime_type().to_string())
|
||||
.body(reqwest::Body::from(body))?;
|
||||
|
||||
let fut = async move { Ok(Ok(reqwest::Response::from(response))) };
|
||||
let fut = async move { Ok(reqwest::Response::from(response)) };
|
||||
let mut resources_table = webview.resources_table();
|
||||
let rid = resources_table.add(FetchRequest::new(Box::pin(fut)));
|
||||
let rid = resources_table.add_request(Box::pin(fut));
|
||||
Ok(rid)
|
||||
}
|
||||
_ => Err(Error::SchemeNotSupport(scheme.to_string())),
|
||||
@@ -270,14 +298,13 @@ pub async fn fetch<R: Runtime>(
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn fetch_cancel<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> crate::Result<()> {
|
||||
let req = {
|
||||
let resources_table = webview.resources_table();
|
||||
resources_table.get::<FetchRequest>(rid)?
|
||||
};
|
||||
let mut req = req.0.lock().await;
|
||||
*req = Box::pin(async { Err(Error::RequestCanceled) });
|
||||
|
||||
pub fn fetch_cancel<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> crate::Result<()> {
|
||||
let mut resources_table = webview.resources_table();
|
||||
let req = resources_table.get::<FetchRequest>(rid)?;
|
||||
let abort_tx = resources_table.take::<AbortSender>(req.abort_tx_rid)?;
|
||||
if let Some(abort_tx) = Arc::into_inner(abort_tx) {
|
||||
abort_tx.abort();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -286,14 +313,26 @@ pub async fn fetch_send<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
) -> crate::Result<FetchResponse> {
|
||||
let req = {
|
||||
let (req, abort_rx) = {
|
||||
let mut resources_table = webview.resources_table();
|
||||
resources_table.take::<FetchRequest>(rid)?
|
||||
let req = resources_table.get::<FetchRequest>(rid)?;
|
||||
let abort_rx = resources_table.take::<AbortRecveiver>(req.abort_rx_rid)?;
|
||||
(req, abort_rx)
|
||||
};
|
||||
|
||||
let res = match req.0.lock().await.as_mut().await {
|
||||
Ok(Ok(res)) => res,
|
||||
Ok(Err(e)) | Err(e) => return Err(e),
|
||||
let Some(abort_rx) = Arc::into_inner(abort_rx) else {
|
||||
return Err(Error::RequestCanceled);
|
||||
};
|
||||
|
||||
let mut fut = req.fut.lock().await;
|
||||
|
||||
let res = tokio::select! {
|
||||
res = fut.as_mut() => res?,
|
||||
_ = abort_rx.0 => {
|
||||
let mut resources_table = webview.resources_table();
|
||||
resources_table.close(rid)?;
|
||||
return Err(Error::RequestCanceled);
|
||||
}
|
||||
};
|
||||
|
||||
let status = res.status();
|
||||
|
||||
Reference in New Issue
Block a user