From 59888b38ddd04b6ead04af50e072a014b48cd4b1 Mon Sep 17 00:00:00 2001 From: Hydroxycarbamide Date: Sun, 4 Sep 2022 14:53:52 +0200 Subject: [PATCH] Implement partial download --- src-tauri/Cargo.lock | 309 ++++++++++++++++++++++++++++++ src-tauri/Cargo.toml | 3 + src-tauri/src/main.rs | 92 +++++++++ src-tauri/src/partial_download.rs | 75 ++++++++ src/App.svelte | 26 ++- src/main.ts | 6 +- tailwind.config.cjs | 1 + 7 files changed, 507 insertions(+), 5 deletions(-) create mode 100644 src-tauri/src/partial_download.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index cc914c9..41eb8ce 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -57,10 +66,13 @@ checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a" name = "app" version = "0.1.0" dependencies = [ + "error-chain", + "reqwest", "serde", "serde_json", "tauri", "tauri-build", + "tempdir", ] [[package]] @@ -111,6 +123,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" @@ -607,6 +634,25 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "backtrace", + "version_check", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -679,6 +725,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futf" version = "0.1.5" @@ -919,6 +971,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "gio" version = "0.15.12" @@ -1079,6 +1137,25 @@ dependencies = [ "syn", ] +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1134,12 +1211,72 @@ dependencies = [ "itoa 1.0.3", ] +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "http-range" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.3", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "ico" version = "0.1.0" @@ -1235,6 +1372,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itoa" version = "0.4.8" @@ -1472,6 +1615,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "miniz_oxide" version = "0.5.3" @@ -1481,6 +1630,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + [[package]] name = "native-tls" version = "0.2.10" @@ -1670,6 +1831,15 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.14.0" @@ -2059,6 +2229,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -2104,6 +2287,21 @@ dependencies = [ "rand_core 0.6.3", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -2149,6 +2347,15 @@ dependencies = [ "cty", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2204,6 +2411,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rfd" version = "0.9.1" @@ -2229,6 +2473,12 @@ dependencies = [ "windows 0.37.0", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.3.3" @@ -2901,6 +3151,16 @@ dependencies = [ "windows 0.37.0", ] +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -2995,11 +3255,38 @@ checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42" dependencies = [ "autocfg", "bytes", + "libc", "memchr", + "mio", "num_cpus", "once_cell", "pin-project-lite", "socket2", + "winapi", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] @@ -3011,6 +3298,12 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.36" @@ -3082,6 +3375,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "typenum" version = "1.15.0" @@ -3222,6 +3521,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2f1dbb2..9c6044b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,6 +16,9 @@ tauri-build = { version = "1.0.4", features = [] } [dependencies] serde_json = "1.0" +tempdir = "0.3.7" +reqwest = { version = "0.11", features = ["blocking"] } +error-chain = "0.12" serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.0.5", features = ["api-all"] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e994ea4..af5257d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,8 +3,100 @@ windows_subsystem = "windows" )] +use std::io::Cursor; +use std::path::PathBuf; +use std::io::copy; +use std::path::Path; +use std::process::Command; +use std::fs::File; +use std::io::{self, Write}; +use tempdir::TempDir; +use reqwest; + +mod partial_download; + fn main() { tauri::Builder::default() + .invoke_handler(tauri::generate_handler![java_version, download_revanced_cli]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } + +#[tauri::command] +fn java_version() -> Result { + let stdout = match Command::new("java") + .arg("--version") + .output() { + Ok(result) => result.stdout.clone(), + Err(_e) => return Err("Java version not found".into()), + }; + + let s = std::str::from_utf8(&stdout) + .expect("Could not read output"); + + println!("{}", s); + Ok(s.into()) +} + +#[tauri::command] +fn download_revanced_cli() -> Result { + let response = match download_revanced_cli_in_tmp() { + Ok(response) => response, + Err(_) => return Err("Could not create temp dir".into()), + }; + Ok(response.into()) +} + +fn download_revanced_cli_somewhere(tmp_dir_path: &Path) -> Result { + let target = "https://github.com/revanced/revanced-cli/releases/download/v2.9.5/revanced-cli-2.9.5-all.jar"; + + let fname = tmp_dir_path.join("revanced_cli.jar"); + let path = fname.clone(); + let string_path = path + .to_str() + .expect("Could not read path"); + let dest = { + println!("will be located under: '{:?}'", fname); + match File::create(fname) { + Ok(file) => file, + Err(_) => return Err("Could not create file".to_owned()), + } + }; + partial_download::download(target, &dest).; + + let stdout = match Command::new("java") + .arg("-jar") + .arg(string_path) + .arg("--version") + .output() { + Ok(result) => result.stdout.clone(), + Err(_e) => return Err("revanced-cli version not found".into()), + }; + + let s = std::str::from_utf8(&stdout) + .expect("Could not read output"); + + println!("{}", s); + Ok(s.into()) +} + +fn download_revanced_cli_in_tmp() -> Result { + let tmp_dir = TempDir::new("example")?; + let file_path = tmp_dir.path().join("my-temporary-note.txt"); + let mut tmp_file = File::create(file_path.clone())?; + writeln!(tmp_file, "Brian was here. Briefly."); + + match file_path.to_str() { + None => println!("No file path for the file"), + Some(path) => println!("{}", path) + } + + let response = match download_revanced_cli_somewhere(tmp_dir.path()) { + Ok(response) => response, + Err(errmsg) => panic!("{}", errmsg), + }; + + drop(tmp_file); + tmp_dir.close()?; + Ok(response) +} \ No newline at end of file diff --git a/src-tauri/src/partial_download.rs b/src-tauri/src/partial_download.rs new file mode 100644 index 0000000..336c975 --- /dev/null +++ b/src-tauri/src/partial_download.rs @@ -0,0 +1,75 @@ +use error_chain::error_chain; +use reqwest::header::{HeaderValue, CONTENT_LENGTH, RANGE}; +use reqwest::StatusCode; +use std::fs::File; +use std::str::FromStr; + +error_chain! { + foreign_links { + Io(std::io::Error); + Reqwest(reqwest::Error); + Header(reqwest::header::ToStrError); + } +} + +struct PartialRangeIter { + start: u64, + end: u64, + buffer_size: u32, +} + +impl PartialRangeIter { + pub fn new(start: u64, end: u64, buffer_size: u32) -> Result { + if buffer_size == 0 { + Err("invalid buffer_size, give a value greater than zero.")?; + } + Ok(PartialRangeIter { + start, + end, + buffer_size, + }) + } +} + +impl Iterator for PartialRangeIter { + type Item = HeaderValue; + fn next(&mut self) -> Option { + if self.start > self.end { + None + } else { + let prev_start = self.start; + self.start += std::cmp::min(self.buffer_size as u64, self.end - self.start + 1); + Some(HeaderValue::from_str(&format!("bytes={}-{}", prev_start, self.start - 1)).expect("string provided by format!")) + } + } +} + +pub fn download(url: &str, mut output_file: &File) -> Result<()> { + const CHUNK_SIZE: u32 = 2048000; + + let client = reqwest::blocking::Client::new(); + let response = client.head(url).send()?; + let length = response + .headers() + .get(CONTENT_LENGTH) + .ok_or("response doesn't include the content length")?; + let length = u64::from_str(length.to_str()?).map_err(|_| "invalid Content-Length header")?; + + println!("starting download..."); + for range in PartialRangeIter::new(0, length - 1, CHUNK_SIZE)? { + println!("range {:?}", range); + let mut response = client.get(url).header(RANGE, range).send()?; + + let status = response.status(); + if !(status == StatusCode::OK || status == StatusCode::PARTIAL_CONTENT) { + error_chain::bail!("Unexpected server response: {}", status) + } + std::io::copy(&mut response, &mut output_file)?; + } + + let content = response.text()?; + std::io::copy(&mut content.as_bytes(), &mut output_file)?; + + println!("Finished with success!"); + Ok(()) +} \ No newline at end of file diff --git a/src/App.svelte b/src/App.svelte index 0c6cde6..3398adf 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,6 +1,25 @@
@@ -16,6 +35,9 @@
+ + +

{version}

diff --git a/src/main.ts b/src/main.ts index c030412..68c10c3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,8 @@ -import "./app.postcss"; -import App from "./App.svelte"; +import './app.postcss'; +import App from './App.svelte'; const app = new App({ - target: document.getElementById("app"), + target: document.getElementById('app'), }); export default app; diff --git a/tailwind.config.cjs b/tailwind.config.cjs index f2d1abc..1d78079 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,5 +1,6 @@ const config = { content: [ + "index.html", "./src/**/*.{html,js,svelte,ts}", "./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}" ],