aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolay Govorov <me@govorov.online>2026-01-10 23:17:53 +0000
committerNikolay Govorov <me@govorov.online>2026-01-11 18:23:31 +0000
commit402b75848d8e9cdcdad78433922dadd91361cc86 (patch)
tree265f12f5fde201de3d60aeeeabb07fba0ed08ca7
parent27bc365cd91f647bba9667e637e6f3960b3c500f (diff)
downloadtar
tar.gz
tar.bz2
tar.lz
tar.xz
tar.zst
zip
PoC for zig binaries proxy
Diffstat
-rw-r--r--Cargo.lock320+320 −0
-rw-r--r--Cargo.toml6+6 −0
-rw-r--r--src/controller_web.rs33+33 −0
-rw-r--r--src/controller_zig.rs93+93 −0
-rw-r--r--src/main.rs43+15 −28
-rw-r--r--src/service_config.rs23+23 −0
-rw-r--r--src/service_storage.rs47+47 −0
-rw-r--r--src/service_upstream.rs67+67 −0
8 files changed, 604 insertions, 28 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5ad4e6b..377a028 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,15 @@
version = 4
[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -86,12 +95,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
+name = "cc"
+version = "1.2.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -117,12 +152,39 @@ dependencies = [
]
[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
+
+[[package]]
name = "fnv"
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.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -171,6 +233,18 @@ dependencies = [
]
[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+]
+
+[[package]]
name = "h2"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -260,6 +334,23 @@ dependencies = [
"pin-utils",
"smallvec",
"tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
]
[[package]]
@@ -269,13 +360,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
dependencies = [
"bytes",
+ "futures-channel",
"futures-core",
+ "futures-util",
"http",
"http-body",
"hyper",
+ "libc",
"pin-project-lite",
+ "socket2",
"tokio",
"tower-service",
+ "tracing",
]
[[package]]
@@ -301,6 +397,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -371,12 +473,73 @@ dependencies = [
]
[[package]]
+name = "native-tls"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
+name = "openssl"
+version = "0.10.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -418,6 +581,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
name = "proc-macro2"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -436,6 +605,12 @@ dependencies = [
]
[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -445,18 +620,92 @@ dependencies = [
]
[[package]]
+name = "regex"
+version = "1.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+
+[[package]]
+name = "rustix"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "ryu"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
[[package]]
+name = "schannel"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
+name = "security-framework"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -522,6 +771,12 @@ dependencies = [
]
[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -577,6 +832,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
[[package]]
+name = "tempfile"
+version = "3.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
+dependencies = [
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "tokio"
version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -605,6 +873,16 @@ dependencies = [
]
[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
name = "tokio-util"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -666,24 +944,54 @@ dependencies = [
]
[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
+name = "wasip2"
+version = "1.0.1+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -773,6 +1081,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
+name = "wit-bindgen"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+
+[[package]]
name = "zmij"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -783,6 +1097,12 @@ name = "zorian"
version = "0.1.0"
dependencies = [
"axum",
+ "bytes",
+ "http-body-util",
+ "hyper",
+ "hyper-tls",
+ "hyper-util",
"minijinja",
+ "regex",
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
index c9b2b98..d46c974 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,5 +10,11 @@ repository = "https://github.com/mrdimidium/Zorian"
[dependencies]
axum = { version = "0.8.8", features = ["http2", "macros", "multipart"] }
+bytes = "1.11.0"
+http-body-util = "0.1.3"
+hyper = "1.8.1"
+hyper-tls = "0.6.0"
+hyper-util = { version = "0.1.19", features = ["client", "http1", "http2", "tokio"] }
minijinja = "2.14.0"
+regex = "1.12.2"
tokio = { version = "1.49.0", features = ["full"] }
diff --git a/src/controller_web.rs b/src/controller_web.rs
new file mode 100644
--- /dev/null
+++ b/src/controller_web.rs
@@ -0,0 +1,33 @@
+use axum::{Router, extract, http, response, routing};
+use std::sync::Arc;
+
+/// Handles html pages rendering and static files
+pub struct WebController {
+ jinja: minijinja::Environment<'static>,
+}
+
+impl WebController {
+ pub fn new() -> Self {
+ let mut jinja = minijinja::Environment::new();
+ jinja
+ .add_template("index", include_str!("./index.html"))
+ .unwrap();
+
+ Self { jinja }
+ }
+
+ pub fn router(self: Arc<Self>) -> Router {
+ Router::new()
+ .route("/", routing::get(Self::index))
+ .with_state(self)
+ }
+
+ async fn index(
+ extract::State(controller): extract::State<Arc<Self>>,
+ ) -> Result<response::Html<String>, http::StatusCode> {
+ let template = controller.jinja.get_template("index").unwrap();
+ let rendered = template.render(minijinja::context! {}).unwrap();
+
+ Ok(response::Html(rendered))
+ }
+}
diff --git a/src/controller_zig.rs b/src/controller_zig.rs
new file mode 100644
--- /dev/null
+++ b/src/controller_zig.rs
@@ -0,0 +1,93 @@
+use axum::{Router, body, extract, http, response, routing};
+use std::sync::{Arc, OnceLock};
+
+use crate::service_storage;
+use crate::service_upstream;
+
+pub struct ZigController {
+ storage: Arc<service_storage::StorageService>,
+ upstream: Arc<service_upstream::UpstreamService>,
+}
+
+impl ZigController {
+ pub fn new(
+ storage: Arc<service_storage::StorageService>,
+ upstream: Arc<service_upstream::UpstreamService>,
+ ) -> Self {
+ Self { storage, upstream }
+ }
+
+ pub fn router(self: Arc<Self>) -> Router {
+ Router::new()
+ .route("/zig/{filename}", routing::get(Self::download))
+ .with_state(self)
+ }
+
+ async fn download(
+ extract::State(controller): extract::State<Arc<Self>>,
+ extract::Path(filename): extract::Path<String>,
+ ) -> Result<response::Response, http::StatusCode> {
+ let version = Self::parse_version(&filename).ok_or(http::StatusCode::NOT_FOUND)?;
+ let url = Self::build_upstream_url(&filename, &version);
+
+ match controller.storage.get(&filename).await {
+ Ok(Some(entry)) => {
+ return Ok(Self::build_response(http::StatusCode::OK, entry));
+ }
+ Ok(None) => {}
+ Err(_) => {
+ return Err(http::StatusCode::INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ let entry = controller
+ .upstream
+ .fetch(service_upstream::DownloadRequest { url })
+ .await?;
+ let cache_entry = service_storage::File {
+ bytes: entry.bytes.clone(),
+ };
+
+ match controller.storage.put(&filename, &cache_entry).await {
+ Ok(()) => {}
+ Err(_) => {
+ return Err(http::StatusCode::OK);
+ }
+ }
+
+ Ok(Self::build_response(http::StatusCode::OK, cache_entry))
+ }
+
+ fn parse_version(filename: &str) -> Option<String> {
+ let re = Self::filename_regex();
+ re.captures(filename)
+ .and_then(|captures| captures.get(1))
+ .map(|match_| match_.as_str().to_string())
+ }
+
+ fn filename_regex() -> &'static regex::Regex {
+ static REGEX: OnceLock<regex::Regex> = OnceLock::new();
+ REGEX.get_or_init(|| {
+ regex::Regex::new(
+ r"^zig(?:|-bootstrap|-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+)-(\d+\.\d+\.\d+(?:-dev\.\d+\+[0-9a-f]+)?)\.(?:tar\.xz|zip)(?:\.minisig)?$",
+ )
+ .unwrap()
+ })
+ }
+
+ fn build_upstream_url(filename: &str, version: &str) -> String {
+ if version.contains("-dev.") {
+ format!("https://ziglang.org/builds/{filename}")
+ } else {
+ format!("https://ziglang.org/download/{version}/{filename}")
+ }
+ }
+
+ fn build_response(status: http::StatusCode, entry: service_storage::File) -> response::Response {
+ response::Response::builder()
+ .status(status)
+ .header(http::header::CONTENT_TYPE, "application/octet-stream")
+ .body(body::Body::from(entry.bytes))
+ .unwrap()
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index bcfc67b..c7870b5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,40 +1,27 @@
-use axum::{Router, extract::State, http, response, routing};
-use std::sync;
+use axum::Router;
+use std::sync::Arc;
-struct GlobalState {
- jinja: minijinja::Environment<'static>,
-}
+mod controller_web;
+mod controller_zig;
+mod service_config;
+mod service_storage;
+mod service_upstream;
#[tokio::main]
async fn main() {
- // init template engine and add templates
- let mut jinja = minijinja::Environment::new();
- jinja
- .add_template("index", include_str!("./index.html"))
- .unwrap();
+ let config = Arc::new(service_config::ConfigService::new());
+ let storage = Arc::new(service_storage::StorageService::new(config.clone()));
+ let upstream = Arc::new(service_upstream::UpstreamService::new());
- let app_state = sync::Arc::new(GlobalState { jinja });
+ let web_controller = Arc::new(controller_web::WebController::new());
+ let zig_controller = Arc::new(controller_zig::ZigController::new(storage, upstream));
let app = Router::new()
- .route("/", routing::get(handler_index))
- .with_state(app_state);
+ .merge(web_controller.router())
+ .merge(zig_controller.router());
- let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
- .await
- .unwrap();
+ let listener = tokio::net::TcpListener::bind(config.addr()).await.unwrap();
println!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}
-
-async fn handler_index(
- State(state): State<sync::Arc<GlobalState>>,
-) -> Result<response::Html<String>, http::StatusCode> {
- let template = state.jinja.get_template("index").unwrap();
-
- let rendered = template
- .render(minijinja::context! {})
- .unwrap();
-
- Ok(response::Html(rendered))
-}
diff --git a/src/service_config.rs b/src/service_config.rs
new file mode 100644
--- /dev/null
+++ b/src/service_config.rs
@@ -0,0 +1,23 @@
+use std::path::{Path, PathBuf};
+
+pub struct ConfigService {
+ addr: String,
+ dirname: PathBuf,
+}
+
+impl ConfigService {
+ pub fn new() -> Self {
+ Self {
+ addr: "0.0.0.0:3000".to_string(),
+ dirname: PathBuf::from("./zorian-storage"),
+ }
+ }
+
+ pub fn addr(&self) -> &str {
+ self.addr.as_str()
+ }
+
+ pub fn dirname(&self) -> &Path {
+ self.dirname.as_path()
+ }
+}
diff --git a/src/service_storage.rs b/src/service_storage.rs
new file mode 100644
--- /dev/null
+++ b/src/service_storage.rs
@@ -0,0 +1,47 @@
+use std::{io, path, sync};
+use tokio::fs;
+
+use super::service_config::ConfigService;
+
+#[derive(Clone)]
+pub struct File {
+ pub bytes: bytes::Bytes,
+}
+
+pub struct StorageService {
+ config: sync::Arc<ConfigService>,
+}
+
+impl StorageService {
+ pub fn new(config: sync::Arc<ConfigService>) -> Self {
+ Self { config }
+ }
+
+ pub async fn get(&self, filename: &str) -> Result<Option<File>, io::Error> {
+ let data_path = self.data_path(filename)?;
+ let bytes = match fs::read(&data_path).await {
+ Ok(bytes) => bytes,
+ Err(err) => {
+ return if err.kind() == io::ErrorKind::NotFound {
+ Ok(None)
+ } else {
+ Err(err)
+ };
+ }
+ };
+
+ Ok(Some(File {
+ bytes: bytes.into(),
+ }))
+ }
+
+ pub async fn put(&self, filename: &str, entry: &File) -> Result<(), io::Error> {
+ let data_path = self.data_path(filename)?;
+ fs::write(&data_path, &entry.bytes).await?;
+ Ok(())
+ }
+
+ fn data_path(&self, filename: &str) -> Result<path::PathBuf, io::Error> {
+ Ok(self.config.dirname().join(filename))
+ }
+}
diff --git a/src/service_upstream.rs b/src/service_upstream.rs
new file mode 100644
--- /dev/null
+++ b/src/service_upstream.rs
@@ -0,0 +1,67 @@
+use bytes::Bytes;
+use http_body_util::{BodyExt, Empty};
+use hyper::{Request, http};
+use hyper_tls::HttpsConnector;
+use hyper_util::client::legacy::{Client, connect::HttpConnector};
+use hyper_util::rt::TokioExecutor;
+
+#[derive(Clone)]
+pub struct DownloadRequest {
+ pub url: String,
+}
+
+#[derive(Clone)]
+pub struct File {
+ pub bytes: Bytes,
+ pub content_type: Option<String>,
+}
+
+pub struct UpstreamService {
+ client: Client<HttpsConnector<HttpConnector>, Empty<Bytes>>,
+}
+
+impl UpstreamService {
+ pub fn new() -> Self {
+ let https = HttpsConnector::new();
+ let client = Client::builder(TokioExecutor::new()).build(https);
+ Self { client }
+ }
+
+ pub async fn fetch(&self, request: DownloadRequest) -> Result<File, http::StatusCode> {
+ let request = Request::builder()
+ .method(http::Method::GET)
+ .uri(&request.url)
+ .header(http::header::USER_AGENT, "zorian/0.1")
+ .body(Empty::<Bytes>::new())
+ .unwrap();
+
+ let response = self
+ .client
+ .request(request)
+ .await
+ .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)?;
+
+ let (parts, body) = response.into_parts();
+ let status = parts.status;
+ if !status.is_success() {
+ return Err(status);
+ }
+
+ let content_type = parts
+ .headers
+ .get(http::header::CONTENT_TYPE)
+ .and_then(|value| value.to_str().ok())
+ .map(|value| value.to_string());
+
+ let bytes = body
+ .collect()
+ .await
+ .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)?
+ .to_bytes();
+
+ Ok(File {
+ bytes,
+ content_type,
+ })
+ }
+}