From 9004c4ed29a48b6877a1153ab81037055d8f534c Mon Sep 17 00:00:00 2001 From: Edward Shen Date: Tue, 2 Aug 2022 02:10:58 -0700 Subject: [PATCH] Web workers --- Cargo.lock | 196 +++++++- common/src/lib.rs | 22 +- web/Cargo.toml | 2 +- web/src/bg_encrypt.ts | 33 ++ web/src/idb_object.rs | 6 +- web/src/index.js | 5 +- web/src/lib.rs | 89 ++-- web/src/main.scss | 7 +- web/src/render.tsx | 53 +- web/vendor/MPLUS_FONTS | 2 +- yarn.lock | 1039 +++++++++++++++++++--------------------- 11 files changed, 811 insertions(+), 643 deletions(-) create mode 100644 web/src/bg_encrypt.ts diff --git a/Cargo.lock b/Cargo.lock index 07d7d86..79b708d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,6 +361,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.2" @@ -419,6 +435,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "filetime" version = "0.2.17" @@ -453,6 +478,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -766,6 +806,19 @@ dependencies = [ "tokio-rustls", ] +[[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 = "idna" version = "0.2.3" @@ -787,6 +840,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.5.0" @@ -941,6 +1003,24 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.1" @@ -1053,7 +1133,7 @@ dependencies = [ "js-sys", "mime_guess", "omegaupload-common", - "reqwasm", + "reqwest", "serde", "tar", "tree_magic_mini", @@ -1075,6 +1155,51 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.2.0" @@ -1265,6 +1390,15 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "reqwasm" version = "0.5.0" @@ -1290,11 +1424,13 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "rustls", @@ -1303,6 +1439,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "tower-service", "url", @@ -1381,6 +1518,16 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + [[package]] name = "sct" version = "0.7.0" @@ -1400,6 +1547,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.140" @@ -1571,6 +1741,20 @@ dependencies = [ "xattr", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -1671,6 +1855,16 @@ dependencies = [ "syn", ] +[[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-rustls" version = "0.23.4" diff --git a/common/src/lib.rs b/common/src/lib.rs index c4383f7..1af1797 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -294,18 +294,18 @@ impl From for HeaderValue { pub struct ParseHeaderValueError; -#[cfg(feature = "wasm")] -impl TryFrom for Expiration { - type Error = ParseHeaderValueError; +// #[cfg(feature = "wasm")] +// impl TryFrom> for Expiration { +// type Error = ParseHeaderValueError; - fn try_from(headers: reqwasm::http::Headers) -> Result { - headers - .get(http::header::EXPIRES.as_str()) - .as_deref() - .and_then(|v| Self::try_from(v).ok()) - .ok_or(ParseHeaderValueError) - } -} +// fn try_from(headers: reqwest::header::HeaderMap) -> Result { +// headers +// .get(http::header::EXPIRES.as_str()) +// .as_deref() +// .and_then(|v| Self::try_from(v).ok()) +// .ok_or(ParseHeaderValueError) +// } +// } impl TryFrom for Expiration { type Error = ParseHeaderValueError; diff --git a/web/Cargo.toml b/web/Cargo.toml index 785c528..7e38919 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -19,7 +19,6 @@ gloo-console = "0.2.1" http = "0.2.8" js-sys = "0.3.59" mime_guess = "2.0.4" -reqwasm = "0.5.0" tree_magic_mini = { version = "3.0.3", features = ["with-gpl-data"] } serde = { version = "1.0.140", features = ["derive"] } wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] } @@ -27,6 +26,7 @@ wasm-bindgen-futures = "0.4.32" zip = { version = "0.6.2", default-features = false, features = ["deflate"] } flate2 = "1.0.24" tar = "0.4.38" +reqwest = "0.11" [dependencies.web-sys] version = "0.3.59" diff --git a/web/src/bg_encrypt.ts b/web/src/bg_encrypt.ts new file mode 100644 index 0000000..97f89c7 --- /dev/null +++ b/web/src/bg_encrypt.ts @@ -0,0 +1,33 @@ +// OmegaUpload Web Frontend +// Copyright (C) 2021 Edward Shen +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import { encrypt_array_buffer } from '../pkg'; + +interface BgData { + location: string, + data: any +} + +addEventListener('message', (event: MessageEvent) => { + let { location, data } = event.data; + console.log('[js-worker] Sending data to rust in a worker thread...'); + encrypt_array_buffer(location, data).then(url => { + console.log("done buffer"); + postMessage(url); + }).catch(e => console.error(e)); +}) + +postMessage("init"); \ No newline at end of file diff --git a/web/src/idb_object.rs b/web/src/idb_object.rs index d13ea5c..bd99aba 100644 --- a/web/src/idb_object.rs +++ b/web/src/idb_object.rs @@ -16,6 +16,7 @@ use std::{hint::unreachable_unchecked, marker::PhantomData}; +use gloo_console::log; use js_sys::{Array, JsString, Object}; use wasm_bindgen::JsValue; @@ -37,7 +38,10 @@ impl From> for Object { Ok(o) => o, // SAFETY: IdbObject maintains the invariant that it can eventually // be constructed into a JS object. - _ => unsafe { unreachable_unchecked() }, + _ => { + log!("IdbObject invariant violated?!"); + unsafe { unreachable_unchecked() } + } } } } diff --git a/web/src/index.js b/web/src/index.js index 3fef26f..9854987 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -1,3 +1,6 @@ import { start } from '../pkg'; +import './main.scss'; -start(); \ No newline at end of file +start(); + +window.addEventListener("hashchange", () => location.reload()); diff --git a/web/src/lib.rs b/web/src/lib.rs index ff09e37..fec6129 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -24,17 +24,16 @@ use decrypt::{DecryptedData, MimeType}; use gloo_console::{error, log}; use http::uri::PathAndQuery; use http::{StatusCode, Uri}; -use js_sys::{Array, JsString, Object, Uint8Array}; +use js_sys::{Array, JsString, Object}; use omegaupload_common::base64; use omegaupload_common::crypto::seal_in_place; use omegaupload_common::crypto::{Error as CryptoError, Key}; use omegaupload_common::fragment::Builder; use omegaupload_common::secrecy::{ExposeSecret, Secret, SecretString, SecretVec}; use omegaupload_common::{Expiration, PartialParsedUrl, Url}; -use reqwasm::http::Request; use wasm_bindgen::prelude::{wasm_bindgen, Closure}; use wasm_bindgen::{JsCast, JsValue}; -use wasm_bindgen_futures::{spawn_local, JsFuture}; +use wasm_bindgen_futures::spawn_local; use web_sys::{Event, IdbObjectStore, IdbOpenDbRequest, IdbTransactionMode, Location, Window}; use crate::decrypt::decrypt; @@ -173,51 +172,36 @@ pub fn start() { #[wasm_bindgen] #[allow(clippy::future_not_send)] -pub fn encrypt_string(data: String) { - spawn_local(async move { - if let Err(e) = do_encrypt(data.into_bytes()).await { - log!(format!("[rs] Error encrypting string: {}", e)); - } - }); -} - -#[wasm_bindgen] -#[allow(clippy::future_not_send)] -pub fn encrypt_array_buffer(data: Vec) { - spawn_local(async move { - if let Err(e) = do_encrypt(data).await { - log!(format!("[rs] Error encrypting array buffer: {}", e)); - } - }); +pub async fn encrypt_array_buffer(location: String, data: Vec) -> Result { + do_encrypt(location, data).await.map_err(|e| { + log!(format!("[rs] Error encrypting array buffer: {}", e)); + JsString::from(e.to_string()) + }) } #[allow(clippy::future_not_send)] -async fn do_encrypt(mut data: Vec) -> Result<()> { +async fn do_encrypt(location: String, mut data: Vec) -> Result { let (data, key) = { let enc_key = seal_in_place(&mut data, None)?; let key = SecretString::new(base64::encode(&enc_key.expose_secret().as_ref())); (data, key) }; - let s: String = location().to_string().into(); - let mut url = Url::from_str(&s)?; + let mut url = Url::from_str(&location)?; let fragment = Builder::new(key); - let js_data = Uint8Array::new_with_length(u32::try_from(data.len()).expect("Data too large")); - js_data.copy_from(&data); - - let short_code = Request::post(url.as_ref()) - .body(js_data) + let short_code = reqwest::Client::new() + .post(url.as_ref()) + .body(data) .send() .await? .text() .await?; + url.set_path(&short_code); url.set_fragment(Some(fragment.build().expose_secret())); - location() - .set_href(url.as_ref()) - .expect("Unable to navigate to encrypted upload"); - Ok(()) + + Ok(JsString::from(url.as_ref())) } #[allow(clippy::future_not_send)] @@ -228,31 +212,26 @@ async fn fetch_resources( name: Option, language: Option, ) -> Result<()> { - match Request::get(&request_uri.to_string()).send().await { + match reqwest::Client::new() + .get(&request_uri.to_string()) + .send() + .await + { Ok(resp) if resp.status() == StatusCode::OK => { - let expires = Expiration::try_from(resp.headers()).map_or_else( - |_| "This item does not expire.".to_string(), - |expires| expires.to_string(), - ); + let expires = resp + .headers() + .get(http::header::EXPIRES) + .and_then(|header| Expiration::try_from(header).ok()) + .map_or_else( + || "This item does not expire.".to_string(), + |expires| expires.to_string(), + ); - let data = { - let data_fut = resp - .as_raw() - .array_buffer() - .expect("to get raw bytes from a response"); - let data = match JsFuture::from(data_fut).await { - Ok(data) => data, - Err(e) => { - render_message( - "Network failure: Failed to completely read encryption paste.".into(), - ); - bail!(format!( - "JsFuture returned an error while fetching resp buffer: {e:?}", - )); - } - }; - Uint8Array::new(&data).to_vec() - }; + let data = resp + .bytes() + .await + .expect("to get raw bytes from a response") + .to_vec(); if data.len() as u128 > DOWNLOAD_SIZE_LIMIT { render_message("The paste is too large to decrypt from the web browser. You must use the CLI tool to download this paste.".into()); @@ -300,7 +279,7 @@ async fn fetch_resources( render_message("Invalid paste URL.".into()); } Ok(err) => { - render_message(err.status_text().into()); + render_message(err.status().as_str().into()); } Err(err) => { render_message(format!("{err}").into()); diff --git a/web/src/main.scss b/web/src/main.scss index cdd6d05..37a3bb6 100644 --- a/web/src/main.scss +++ b/web/src/main.scss @@ -40,6 +40,7 @@ hr { main { display: inline-flex; min-width: 100%; + max-width: 100%; justify-content: center; } @@ -121,11 +122,9 @@ textarea { .button { @extend .hljs; - border: 1px solid white; - border-radius: $padding; - padding: $padding; - margin: $padding; font-size: 16px; + text-decoration: underline; + border: none; &:hover { cursor: pointer; diff --git a/web/src/render.tsx b/web/src/render.tsx index dcbf663..2e731bd 100644 --- a/web/src/render.tsx +++ b/web/src/render.tsx @@ -14,28 +14,29 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import './main.scss'; import ReactDom from 'react-dom'; import React, { useState } from 'react'; -import { encrypt_string, encrypt_array_buffer } from '../pkg'; -import hljs from 'highlight.js' -(window as any).hljs = hljs; -require('highlightjs-line-numbers.js'); +let hljs; +if (typeof WorkerGlobalScope === 'undefined' || !(self instanceof WorkerGlobalScope)) { + hljs = require('highlight.js'); + (window as any).hljs = hljs; + require('highlightjs-line-numbers.js'); +} + const FileForm = () => { const handleChange = (event: React.ChangeEvent) => { let file = event.target.files![0]; const fr = new FileReader(); fr.onload = (_e) => { - let data = new Uint8Array(fr.result as ArrayBuffer); - encrypt_array_buffer(data); + encryptMessage(new Uint8Array(fr.result as ArrayBuffer)); } fr.readAsArrayBuffer(file); } return <> -