Skip to content

Commit

Permalink
http utils
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianLars committed Nov 17, 2024
1 parent 76a11b1 commit 5e71792
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 213 deletions.
10 changes: 7 additions & 3 deletions crates/tauri-bundler/src/bundle/windows/msi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ use crate::{
windows::{
sign::try_sign,
util::{
download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer,
extract_zip, HashAlgorithm, WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
download_webview2_bootstrapper, download_webview2_offline_installer,
WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
},
},
},
utils::{fs_utils::copy_file, CommandExt},
utils::{
fs_utils::copy_file,
http_utils::{download_and_verify, extract_zip, HashAlgorithm},
CommandExt,
},
};
use anyhow::{bail, Context};
use handlebars::{html_escape, to_json, Handlebars};
Expand Down
22 changes: 14 additions & 8 deletions crates/tauri-bundler/src/bundle/windows/nsis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use crate::bundle::settings::Arch;
use crate::bundle::windows::sign::{sign_command, try_sign};

use crate::{
bundle::windows::util::{
download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer,
verify_file_hash, HashAlgorithm, NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME,
bundle::{
settings::Arch,
windows::{
sign::{sign_command, try_sign},
util::{
download_webview2_bootstrapper, download_webview2_offline_installer,
NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME,
},
},
},
utils::{
http_utils::{download_and_verify, extract_zip, verify_file_hash, HashAlgorithm},
CommandExt,
},
utils::CommandExt,
Settings,
};
use tauri_utils::display_path;
Expand Down Expand Up @@ -106,7 +112,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
{
let data = download_and_verify(NSIS_URL, NSIS_SHA1, HashAlgorithm::Sha1)?;
log::info!("extracting NSIS");
crate::bundle::windows::util::extract_zip(&data, _tauri_tools_path)?;
extract_zip(&data, _tauri_tools_path)?;
fs::rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?;
}

Expand Down
204 changes: 2 additions & 202 deletions crates/tauri-bundler/src/bundle/windows/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@
// SPDX-License-Identifier: MIT

use std::{
fs::{create_dir_all, File},
io::{Cursor, Read, Write},
fs::create_dir_all,
path::{Path, PathBuf},
};

use regex::Regex;
use sha2::Digest;
use url::Url;
use zip::ZipArchive;
use crate::utils::http_utils::download;

pub const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703";
pub const WEBVIEW2_OFFLINE_INSTALLER_X86_URL: &str =
Expand Down Expand Up @@ -69,148 +65,6 @@ pub fn download_webview2_offline_installer(base_path: &Path, arch: &str) -> crat
Ok(file_path)
}

fn generate_github_mirror_url_from_template(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE")
.ok()
.and_then(|template| {
let re =
Regex::new(r"https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.*)").unwrap();
re.captures(github_url).map(|caps| {
template
.replace("<owner>", &caps[1])
.replace("<repo>", &caps[2])
.replace("<version>", &caps[3])
.replace("<asset>", &caps[4])
})
})
}

fn generate_github_mirror_url_from_base(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR")
.ok()
.and_then(|cdn| Url::parse(&cdn).ok())
.map(|mut cdn| {
cdn.set_path(github_url);
cdn.to_string()
})
}

fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> {
if !url.starts_with("https://github.com/") {
return None;
}

generate_github_mirror_url_from_template(url)
.or_else(|| generate_github_mirror_url_from_base(url))
.map(|alt_url| (ureq::AgentBuilder::new().build(), alt_url))
}

fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
generate_github_alternative_url(url).unwrap_or((
ureq::AgentBuilder::new().try_proxy_from_env(true).build(),
url.to_owned(),
))
}

pub fn download(url: &str) -> crate::Result<Vec<u8>> {
let (agent, final_url) = create_agent_and_url(url);

log::info!(action = "Downloading"; "{}", final_url);

let response = agent.get(&final_url).call().map_err(Box::new)?;
let mut bytes = Vec::new();
response.into_reader().read_to_end(&mut bytes)?;
Ok(bytes)
}

#[derive(Clone, Copy)]
pub enum HashAlgorithm {
#[cfg(target_os = "windows")]
Sha256,
Sha1,
}

/// Function used to download a file and checks SHA256 to verify the download.
pub fn download_and_verify(
url: &str,
hash: &str,
hash_algorithm: HashAlgorithm,
) -> crate::Result<Vec<u8>> {
let data = download(url)?;
log::info!("validating hash");
verify_hash(&data, hash, hash_algorithm)?;
Ok(data)
}

pub fn verify_hash(data: &[u8], hash: &str, hash_algorithm: HashAlgorithm) -> crate::Result<()> {
match hash_algorithm {
#[cfg(target_os = "windows")]
HashAlgorithm::Sha256 => {
let hasher = sha2::Sha256::new();
verify_data_with_hasher(data, hash, hasher)
}
HashAlgorithm::Sha1 => {
let hasher = sha1::Sha1::new();
verify_data_with_hasher(data, hash, hasher)
}
}
}

fn verify_data_with_hasher(data: &[u8], hash: &str, mut hasher: impl Digest) -> crate::Result<()> {
hasher.update(data);

let url_hash = hasher.finalize().to_vec();
let expected_hash = hex::decode(hash)?;
if expected_hash == url_hash {
Ok(())
} else {
Err(crate::Error::HashError)
}
}

pub fn verify_file_hash<P: AsRef<Path>>(
path: P,
hash: &str,
hash_algorithm: HashAlgorithm,
) -> crate::Result<()> {
let data = std::fs::read(path)?;
verify_hash(&data, hash, hash_algorithm)
}

/// Extracts the zips from memory into a usable path.
#[allow(dead_code)]
pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
let cursor = Cursor::new(data);

let mut zipa = ZipArchive::new(cursor)?;

for i in 0..zipa.len() {
let mut file = zipa.by_index(i)?;

if let Some(name) = file.enclosed_name() {
let dest_path = path.join(name);
if file.is_dir() {
create_dir_all(&dest_path)?;
continue;
}

let parent = dest_path.parent().expect("Failed to get parent");

if !parent.exists() {
create_dir_all(parent)?;
}

let mut buff: Vec<u8> = Vec::new();
file.read_to_end(&mut buff)?;
let mut fileout = File::create(dest_path).expect("Failed to open file");

fileout.write_all(&buff)?;
}
}

Ok(())
}

#[cfg(target_os = "windows")]
pub fn os_bitness<'a>() -> Option<&'a str> {
use windows_sys::Win32::System::SystemInformation::{
Expand All @@ -225,57 +79,3 @@ pub fn os_bitness<'a>() -> Option<&'a str> {
_ => None,
}
}

#[cfg(test)]
mod tests {
use super::generate_github_mirror_url_from_template;
use std::env;

const GITHUB_ASSET_URL: &str =
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
const NON_GITHUB_ASSET_URL: &str = "https://someotherwebsite.com/somefile.zip";

#[test]
fn test_generate_mirror_url_no_env_var() {
env::remove_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE");

assert!(generate_github_mirror_url_from_template(GITHUB_ASSET_URL).is_none());
}

#[test]
fn test_generate_mirror_url_non_github_url() {
env::set_var(
"TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE",
"https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
);

assert!(generate_github_mirror_url_from_template(NON_GITHUB_ASSET_URL).is_none());
}

struct TestCase {
template: &'static str,
expected_url: &'static str,
}

#[test]
fn test_generate_mirror_url_correctly() {
let test_cases = vec![
TestCase {
template: "https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
expected_url: "https://mirror.example.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip",
},
TestCase {
template: "https://mirror.example.com/<asset>",
expected_url: "https://mirror.example.com/wix311-binaries.zip",
},
];

for case in test_cases {
env::set_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", case.template);
assert_eq!(
generate_github_mirror_url_from_template(GITHUB_ASSET_URL),
Some(case.expected_url.to_string())
);
}
}
}
Loading

0 comments on commit 5e71792

Please sign in to comment.