Skip to content

Commit

Permalink
feat(updater): add download progress events (#3734)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Mar 19, 2022
1 parent 348a1ab commit f0db3f9
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 139 deletions.
5 changes: 5 additions & 0 deletions .changes/http-api-stream.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Added `bytes_stream` method to `tauri::api::http::Response`.
5 changes: 5 additions & 0 deletions .changes/updater-download-events.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Added download progress events to the updater.
6 changes: 3 additions & 3 deletions core/tauri/Cargo.toml
Expand Up @@ -73,7 +73,7 @@ percent-encoding = "2.1"
base64 = { version = "0.13", optional = true }
clap = { version = "3", optional = true }
notify-rust = { version = "4.5", optional = true }
reqwest = { version = "0.11", features = [ "json", "multipart" ], optional = true }
reqwest = { version = "0.11", features = [ "json", "multipart", "stream" ], optional = true }
bytes = { version = "1", features = [ "serde" ], optional = true }
attohttpc = { version = "0.18", features = [ "json", "form" ], optional = true }
open = { version = "2.0", optional = true }
Expand Down Expand Up @@ -137,10 +137,10 @@ updater = [
"fs-extract-api"
]
__updater-docs = [ "minisign-verify", "base64", "http-api", "dialog-ask" ]
http-api = [ "attohttpc" ]
http-api = [ "attohttpc", "bytes" ]
shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ]
fs-extract-api = [ "zip" ]
reqwest-client = [ "reqwest", "bytes" ]
reqwest-client = [ "reqwest" ]
process-command-api = [ "shared_child", "os_pipe", "memchr" ]
dialog = [ "rfd" ]
notification = [ "notify-rust" ]
Expand Down
71 changes: 70 additions & 1 deletion core/tauri/src/api/http.rs
Expand Up @@ -5,6 +5,7 @@
//! Types and functions related to HTTP request.

use http::{header::HeaderName, Method};
pub use http::{HeaderMap, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};
Expand Down Expand Up @@ -352,17 +353,85 @@ pub struct Response(ResponseType, reqwest::Response);
#[derive(Debug)]
pub struct Response(ResponseType, attohttpc::Response, Url);

#[cfg(not(feature = "reqwest-client"))]
struct AttohttpcByteReader(attohttpc::ResponseReader);

#[cfg(not(feature = "reqwest-client"))]
impl futures::Stream for AttohttpcByteReader {
type Item = crate::api::Result<bytes::Bytes>;

fn poll_next(
mut self: std::pin::Pin<&mut Self>,
_cx: &mut futures::task::Context<'_>,
) -> futures::task::Poll<Option<Self::Item>> {
use std::io::Read;
let mut buf = [0; 256];
match self.0.read(&mut buf) {
Ok(b) => {
if b == 0 {
futures::task::Poll::Ready(None)
} else {
futures::task::Poll::Ready(Some(Ok(buf[0..b].to_vec().into())))
}
}
Err(_) => futures::task::Poll::Ready(None),
}
}
}

impl Response {
/// Get the [`StatusCode`] of this Response.
pub fn status(&self) -> StatusCode {
self.1.status()
}

/// Get the headers of this Response.
pub fn headers(&self) -> &HeaderMap {
self.1.headers()
}

/// Reads the response as raw bytes.
pub async fn bytes(self) -> crate::api::Result<RawResponse> {
let status = self.1.status().as_u16();
let status = self.status().as_u16();
#[cfg(feature = "reqwest-client")]
let data = self.1.bytes().await?.to_vec();
#[cfg(not(feature = "reqwest-client"))]
let data = self.1.bytes()?;
Ok(RawResponse { status, data })
}

/// Convert the response into a Stream of [`bytes::Bytes`] from the body.
///
/// # Examples
///
/// ```no_run
/// use futures::StreamExt;
///
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// let client = tauri::api::http::ClientBuilder::new().build()?;
/// let mut stream = client.send(tauri::api::http::HttpRequestBuilder::new("GET", "http://httpbin.org/ip")?)
/// .await?
/// .bytes_stream();
///
/// while let Some(item) = stream.next().await {
/// println!("Chunk: {:?}", item?);
/// }
/// # Ok(())
/// # }
/// ```
pub fn bytes_stream(self) -> impl futures::Stream<Item = crate::api::Result<bytes::Bytes>> {
#[cfg(not(feature = "reqwest-client"))]
{
let (_, _, reader) = self.1.split();
AttohttpcByteReader(reader)
}
#[cfg(feature = "reqwest-client")]
{
use futures::StreamExt;
self.1.bytes_stream().map(|res| res.map_err(Into::into))
}
}

/// Reads the response.
///
/// Note that the body is serialized to a [`Value`].
Expand Down
16 changes: 4 additions & 12 deletions core/tauri/src/app.rs
Expand Up @@ -579,13 +579,9 @@ impl<R: Runtime> App<R> {
impl<R: Runtime> App<R> {
/// Runs the updater hook with built-in dialog.
fn run_updater_dialog(&self) {
let updater_config = self.manager.config().tauri.updater.clone();
let package_info = self.manager.package_info().clone();
let handle = self.handle();

crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(updater_config, package_info, handle).await
});
crate::async_runtime::spawn(async move { updater::check_update_with_dialog(handle).await });
}

fn run_updater(&self) {
Expand All @@ -597,22 +593,18 @@ impl<R: Runtime> App<R> {
if updater_config.dialog {
// if updater dialog is enabled spawn a new task
self.run_updater_dialog();
let config = self.manager.config().tauri.updater.clone();
let package_info = self.manager.package_info().clone();
// When dialog is enabled, if user want to recheck
// if an update is available after first start
// invoke the Event `tauri://update` from JS or rust side.
handle.listen_global(updater::EVENT_CHECK_UPDATE, move |_msg| {
let handle = handle_.clone();
let package_info = package_info.clone();
let config = config.clone();
// re-spawn task inside tokyo to launch the download
// we don't need to emit anything as everything is handled
// by the process (user is asked to restart at the end)
// and it's handled by the updater
crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(config, package_info, handle).await
});
crate::async_runtime::spawn(
async move { updater::check_update_with_dialog(handle).await },
);
});
} else {
// we only listen for `tauri://update`
Expand Down
10 changes: 9 additions & 1 deletion core/tauri/src/lib.rs
Expand Up @@ -242,8 +242,16 @@ pub enum UpdaterEvent {
/// The update version.
version: String,
},
/// The update is pending.
/// The update is pending and about to be downloaded.
Pending,
/// The update download received a progress event.
DownloadProgress {
/// The amount that was downloaded on this iteration.
/// Does not accumulate with previous chunks.
chunk_length: usize,
/// The total
content_length: Option<u64>,
},
/// The update has been applied and the app is now up to date.
Updated,
/// The app is already up to date.
Expand Down

0 comments on commit f0db3f9

Please sign in to comment.