Compare commits
9 commits
e33c0c73e1
...
3a855a7e4a
Author | SHA1 | Date | |
---|---|---|---|
3a855a7e4a | |||
5300afa205 | |||
c5383639f5 | |||
88561f7c2c | |||
ce03ce0baf | |||
e78315025d | |||
a8e5d09ff0 | |||
b4f27c5f8c | |||
5b67431778 |
18 changed files with 617 additions and 191 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,4 +3,5 @@
|
||||||
/cache
|
/cache
|
||||||
flamegraph*.svg
|
flamegraph*.svg
|
||||||
perf.data*
|
perf.data*
|
||||||
dhat.out.*
|
dhat.out.*
|
||||||
|
settings.yaml
|
90
Cargo.lock
generated
90
Cargo.lock
generated
|
@ -1,5 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-codec"
|
name = "actix-codec"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -563,13 +565,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.14"
|
version = "0.99.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cc7b9cef1e351660e5443924e4f43ab25fbbed3e9a5f052df3677deb4d6b320"
|
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"rustc_version 0.3.3",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -600,6 +603,12 @@ version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ed25519"
|
name = "ed25519"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
@ -1105,6 +1114,12 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "local-channel"
|
name = "local-channel"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1139,6 +1154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1176,6 +1192,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
|
"serde_yaml",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"sodiumoxide",
|
"sodiumoxide",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
@ -1361,6 +1378,15 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||||
|
dependencies = [
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -1628,6 +1654,15 @@ dependencies = [
|
||||||
"semver 0.9.0",
|
"semver 0.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||||
|
dependencies = [
|
||||||
|
"semver 0.11.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -1693,7 +1728,16 @@ version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver-parser",
|
"semver-parser 0.7.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser 0.10.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1708,6 +1752,15 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.126"
|
version = "1.0.126"
|
||||||
|
@ -1762,6 +1815,18 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.8.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
|
||||||
|
dependencies = [
|
||||||
|
"dtoa",
|
||||||
|
"linked-hash-map",
|
||||||
|
"serde",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -2055,9 +2120,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.19.0"
|
version = "0.19.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41c72bb3368a39bb153f226d8dd426ee33d54bc31f44037c078391b4a0b8dc04"
|
checksum = "a68b8aff80646f09ec11e59b56091a5278ee527d5baeca938f1a5dbd9a15a859"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
|
@ -2292,6 +2357,12 @@ version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
|
@ -2569,6 +2640,15 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd"
|
name = "zstd"
|
||||||
version = "0.7.0+zstd.1.4.9"
|
version = "0.7.0+zstd.1.4.9"
|
||||||
|
|
|
@ -26,7 +26,7 @@ ctrlc = "3"
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
log = "0.4"
|
log = { version = "0.4", features = [ "serde" ] }
|
||||||
lfu_cache = "1"
|
lfu_cache = "1"
|
||||||
lru = "0.6"
|
lru = "0.6"
|
||||||
parking_lot = "0.11"
|
parking_lot = "0.11"
|
||||||
|
@ -36,6 +36,7 @@ rustls = "0.19"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde_repr = "0.1"
|
serde_repr = "0.1"
|
||||||
|
serde_yaml = "0.8"
|
||||||
simple_logger = "1"
|
simple_logger = "1"
|
||||||
sodiumoxide = "0.2"
|
sodiumoxide = "0.2"
|
||||||
sqlx = { version = "0.5", features = [ "runtime-actix-rustls", "sqlite", "time", "chrono", "macros" ] }
|
sqlx = { version = "0.5", features = [ "runtime-actix-rustls", "sqlite", "time", "chrono", "macros" ] }
|
||||||
|
|
15
build.rs
15
build.rs
|
@ -1,10 +1,25 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
use vergen::{vergen, Config, ShaKind};
|
use vergen::{vergen, Config, ShaKind};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
// Initialize vergen stuff
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
*config.git_mut().sha_kind_mut() = ShaKind::Short;
|
*config.git_mut().sha_kind_mut() = ShaKind::Short;
|
||||||
vergen(config)?;
|
vergen(config)?;
|
||||||
|
|
||||||
|
// Initialize SQL stuff
|
||||||
|
let project_root = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
Command::new("mkdir")
|
||||||
|
.args(["cache", "--parents"])
|
||||||
|
.current_dir(&project_root)
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
Command::new("sqlite3")
|
||||||
|
.args(["cache/metadata.sqlite", include_str!("db_queries/init.sql")])
|
||||||
|
.current_dir(&project_root)
|
||||||
|
.output()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
14
docs/ciphers.md
Normal file
14
docs/ciphers.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Ciphers
|
||||||
|
|
||||||
|
This client relies on rustls, which only supports a subset of TLS ciphers.
|
||||||
|
Specifically, only TLS 1.2 ECDSA GCM ciphers as well as all TLS 1.3 ciphers are
|
||||||
|
supported. This means that clients that only support older, more insecure
|
||||||
|
ciphers may not be able to connect to this client.
|
||||||
|
|
||||||
|
In practice, this means this client's failure rate may be higher than expected.
|
||||||
|
This is okay, and within specifications.
|
||||||
|
|
||||||
|
## Why even bother?
|
||||||
|
|
||||||
|
Well, Australia has officially banned hentai... so I gotta make sure my mates
|
||||||
|
over there won't get in trouble if I'm connecting to them.
|
14
docs/unstable_options.md
Normal file
14
docs/unstable_options.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Unstable Options
|
||||||
|
|
||||||
|
Unstable options are options that are either experimental, dangerous, for
|
||||||
|
development only, or a mix of the three. The following table describes each
|
||||||
|
option. Generally speaking, you should never need to enable these unless you
|
||||||
|
know what you're doing.
|
||||||
|
|
||||||
|
| Option | Experimental? | Dangerous? | For development? |
|
||||||
|
| -------------------------- | ------------- | ---------- | ---------------- |
|
||||||
|
| `override-upstream` | | | Yes |
|
||||||
|
| `use-lfu` | Yes | | |
|
||||||
|
| `disable-token-validation` | | Yes | Yes |
|
||||||
|
| `offline-mode` | | | Yes |
|
||||||
|
| `disable-tls` | | Yes | Yes |
|
|
@ -1,10 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# This script needs to be run once in order for compile time macros to not
|
|
||||||
# complain about a missing DB
|
|
||||||
|
|
||||||
# We can trust that our program will initialize the db at runtime the same way
|
|
||||||
# as it pulls from the same file for initialization
|
|
||||||
|
|
||||||
mkdir cache
|
|
||||||
sqlite3 cache/metadata.sqlite < db_queries/init.sql
|
|
57
settings.sample.yaml
Normal file
57
settings.sample.yaml
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
# ⢸⣿⣿⣿⣿⠃⠄⢀⣴⡾⠃⠄⠄⠄⠄⠄⠈⠺⠟⠛⠛⠛⠛⠻⢿⣿⣿⣿⣿⣶⣤⡀⠄
|
||||||
|
# ⢸⣿⣿⣿⡟⢀⣴⣿⡿⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣸⣿⣿⣿⣿⣿⣿⣿⣷
|
||||||
|
# ⢸⣿⣿⠟⣴⣿⡿⡟⡼⢹⣷⢲⡶⣖⣾⣶⢄⠄⠄⠄⠄⠄⢀⣼⣿⢿⣿⣿⣿⣿⣿⣿⣿
|
||||||
|
# ⢸⣿⢫⣾⣿⡟⣾⡸⢠⡿⢳⡿⠍⣼⣿⢏⣿⣷⢄⡀⠄⢠⣾⢻⣿⣸⣿⣿⣿⣿⣿⣿⣿
|
||||||
|
# ⡿⣡⣿⣿⡟⡼⡁⠁⣰⠂⡾⠉⢨⣿⠃⣿⡿⠍⣾⣟⢤⣿⢇⣿⢇⣿⣿⢿⣿⣿⣿⣿⣿
|
||||||
|
# ⣱⣿⣿⡟⡐⣰⣧⡷⣿⣴⣧⣤⣼⣯⢸⡿⠁⣰⠟⢀⣼⠏⣲⠏⢸⣿⡟⣿⣿⣿⣿⣿⣿
|
||||||
|
# ⣿⣿⡟⠁⠄⠟⣁⠄⢡⣿⣿⣿⣿⣿⣿⣦⣼⢟⢀⡼⠃⡹⠃⡀⢸⡿⢸⣿⣿⣿⣿⣿⡟
|
||||||
|
# ⣿⣿⠃⠄⢀⣾⠋⠓⢰⣿⣿⣿⣿⣿⣿⠿⣿⣿⣾⣅⢔⣕⡇⡇⡼⢁⣿⣿⣿⣿⣿⣿⢣
|
||||||
|
# ⣿⡟⠄⠄⣾⣇⠷⣢⣿⣿⣿⣿⣿⣿⣿⣭⣀⡈⠙⢿⣿⣿⡇⡧⢁⣾⣿⣿⣿⣿⣿⢏⣾
|
||||||
|
# ⣿⡇⠄⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢻⠇⠄⠄⢿⣿⡇⢡⣾⣿⣿⣿⣿⣿⣏⣼⣿
|
||||||
|
# ⣿⣷⢰⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⢰⣧⣀⡄⢀⠘⡿⣰⣿⣿⣿⣿⣿⣿⠟⣼⣿⣿
|
||||||
|
# ⢹⣿⢸⣿⣿⠟⠻⢿⣿⣿⣿⣿⣿⣿⣿⣶⣭⣉⣤⣿⢈⣼⣿⣿⣿⣿⣿⣿⠏⣾⣹⣿⣿
|
||||||
|
# ⢸⠇⡜⣿⡟⠄⠄⠄⠈⠙⣿⣿⣿⣿⣿⣿⣿⣿⠟⣱⣻⣿⣿⣿⣿⣿⠟⠁⢳⠃⣿⣿⣿
|
||||||
|
# ⠄⣰⡗⠹⣿⣄⠄⠄⠄⢀⣿⣿⣿⣿⣿⣿⠟⣅⣥⣿⣿⣿⣿⠿⠋⠄⠄⣾⡌⢠⣿⡿⠃
|
||||||
|
# ⠜⠋⢠⣷⢻⣿⣿⣶⣾⣿⣿⣿⣿⠿⣛⣥⣾⣿⠿⠟⠛⠉⠄⠄
|
||||||
|
#
|
||||||
|
# MangaDex@Home configuration file
|
||||||
|
# We are pleased to have you here
|
||||||
|
# May fate stay the night with you!
|
||||||
|
#
|
||||||
|
# Default values are commented out.
|
||||||
|
|
||||||
|
# The size in mebibytes of the cache
|
||||||
|
# You can use megabytes instead in a pinch,
|
||||||
|
# but just know the two are **NOT** the same.
|
||||||
|
max_cache_size_in_mebibytes: 0
|
||||||
|
|
||||||
|
server_settings:
|
||||||
|
# The client secret. Keep this secret at all costs :P
|
||||||
|
secret: suichan wa kyou mo kawaii!
|
||||||
|
|
||||||
|
# The port for the webserver to listen on. 443 is recommended for max appeal.
|
||||||
|
# port: 443
|
||||||
|
|
||||||
|
# This controls the value the server receives for your upload speed.
|
||||||
|
external_max_kilobits_per_second: 0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Advanced settings
|
||||||
|
#
|
||||||
|
|
||||||
|
# The external hostname to listen on. Keep this at 0.0.0.0 unless you know
|
||||||
|
# what you're doing.
|
||||||
|
# hostname: 0.0.0.0
|
||||||
|
|
||||||
|
# The external port to broadcast to the backend. Keep this at 0 unless you
|
||||||
|
# know what you're doing. 0 means broadcast the same value as `port`.
|
||||||
|
# external_port: 0
|
||||||
|
|
||||||
|
# How long to wait at most for the graceful shutdown (Ctrl-C or SIGINT).
|
||||||
|
# graceful_shutdown_wait_seconds: 60
|
||||||
|
|
||||||
|
# The external ip to broadcast to the webserver. The default of null (~) means
|
||||||
|
# the backend will infer it from where it was sent from, which may fail in the
|
||||||
|
# presence of multiple IPs.
|
||||||
|
# external_ip: ~
|
9
src/cache/disk.rs
vendored
9
src/cache/disk.rs
vendored
|
@ -6,7 +6,6 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bytes::Bytes;
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use log::{error, warn, LevelFilter};
|
use log::{error, warn, LevelFilter};
|
||||||
use sqlx::sqlite::SqliteConnectOptions;
|
use sqlx::sqlite::SqliteConnectOptions;
|
||||||
|
@ -15,6 +14,8 @@ use tokio::fs::remove_file;
|
||||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use tokio_stream::wrappers::ReceiverStream;
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
|
||||||
|
use crate::units::Bytes;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
BoxedImageStream, Cache, CacheError, CacheKey, CacheStream, CallbackCache, ImageMetadata,
|
BoxedImageStream, Cache, CacheError, CacheKey, CacheStream, CallbackCache, ImageMetadata,
|
||||||
};
|
};
|
||||||
|
@ -34,7 +35,7 @@ impl DiskCache {
|
||||||
/// Constructs a new low memory cache at the provided path and capacity.
|
/// Constructs a new low memory cache at the provided path and capacity.
|
||||||
/// This internally spawns a task that will wait for filesystem
|
/// This internally spawns a task that will wait for filesystem
|
||||||
/// notifications when a file has been written.
|
/// notifications when a file has been written.
|
||||||
pub async fn new(disk_max_size: u64, disk_path: PathBuf) -> Arc<Self> {
|
pub async fn new(disk_max_size: Bytes, disk_path: PathBuf) -> Arc<Self> {
|
||||||
let (db_tx, db_rx) = channel(128);
|
let (db_tx, db_rx) = channel(128);
|
||||||
let db_pool = {
|
let db_pool = {
|
||||||
let db_url = format!("sqlite:{}/metadata.sqlite", disk_path.to_string_lossy());
|
let db_url = format!("sqlite:{}/metadata.sqlite", disk_path.to_string_lossy());
|
||||||
|
@ -75,7 +76,7 @@ impl DiskCache {
|
||||||
Arc::clone(&new_self),
|
Arc::clone(&new_self),
|
||||||
db_rx,
|
db_rx,
|
||||||
db_pool,
|
db_pool,
|
||||||
disk_max_size / 20 * 19,
|
disk_max_size.get() as u64 / 20 * 19,
|
||||||
));
|
));
|
||||||
|
|
||||||
new_self
|
new_self
|
||||||
|
@ -237,7 +238,7 @@ impl CallbackCache for DiskCache {
|
||||||
key: CacheKey,
|
key: CacheKey,
|
||||||
image: BoxedImageStream,
|
image: BoxedImageStream,
|
||||||
metadata: ImageMetadata,
|
metadata: ImageMetadata,
|
||||||
on_complete: Sender<(CacheKey, Bytes, ImageMetadata, u64)>,
|
on_complete: Sender<(CacheKey, bytes::Bytes, ImageMetadata, u64)>,
|
||||||
) -> Result<CacheStream, CacheError> {
|
) -> Result<CacheStream, CacheError> {
|
||||||
let channel = self.db_update_channel_sender.clone();
|
let channel = self.db_update_channel_sender.clone();
|
||||||
|
|
||||||
|
|
6
src/cache/fs.rs
vendored
6
src/cache/fs.rs
vendored
|
@ -111,21 +111,21 @@ pub(super) async fn read_file(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let header = if let Some(header) = Header::from_slice(&header_bytes) {
|
let file_header = if let Some(header) = Header::from_slice(&header_bytes) {
|
||||||
header
|
header
|
||||||
} else {
|
} else {
|
||||||
warn!("Found file, but encrypted header was invalid. Assuming corrupted!");
|
warn!("Found file, but encrypted header was invalid. Assuming corrupted!");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let secret_stream = if let Ok(stream) = SecretStream::init_pull(&header, key) {
|
let secret_stream = if let Ok(stream) = SecretStream::init_pull(&file_header, key) {
|
||||||
stream
|
stream
|
||||||
} else {
|
} else {
|
||||||
warn!("Failed to init secret stream with key and header. Assuming corrupted!");
|
warn!("Failed to init secret stream with key and header. Assuming corrupted!");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
maybe_header = Some(header);
|
maybe_header = Some(file_header);
|
||||||
|
|
||||||
reader = Some(Box::pin(EncryptedDiskReader::new(file, secret_stream)));
|
reader = Some(Box::pin(EncryptedDiskReader::new(file, secret_stream)));
|
||||||
}
|
}
|
||||||
|
|
6
src/cache/mem.rs
vendored
6
src/cache/mem.rs
vendored
|
@ -86,7 +86,7 @@ where
|
||||||
MemoryCacheImpl: 'static + InternalMemoryCache,
|
MemoryCacheImpl: 'static + InternalMemoryCache,
|
||||||
ColdCache: 'static + Cache,
|
ColdCache: 'static + Cache,
|
||||||
{
|
{
|
||||||
pub async fn new(inner: ColdCache, max_mem_size: u64) -> Arc<Self> {
|
pub async fn new(inner: ColdCache, max_mem_size: crate::units::Bytes) -> Arc<Self> {
|
||||||
let (tx, mut rx) = channel(100);
|
let (tx, mut rx) = channel(100);
|
||||||
let new_self = Arc::new(Self {
|
let new_self = Arc::new(Self {
|
||||||
inner,
|
inner,
|
||||||
|
@ -98,7 +98,7 @@ where
|
||||||
let new_self_0 = Arc::clone(&new_self);
|
let new_self_0 = Arc::clone(&new_self);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let new_self = new_self_0;
|
let new_self = new_self_0;
|
||||||
let max_mem_size = max_mem_size / 20 * 19;
|
let max_mem_size = max_mem_size.get() / 20 * 19;
|
||||||
while let Some((key, bytes, metadata, size)) = rx.recv().await {
|
while let Some((key, bytes, metadata, size)) = rx.recv().await {
|
||||||
// Add to memory cache
|
// Add to memory cache
|
||||||
// We can add first because we constrain our memory usage to 95%
|
// We can add first because we constrain our memory usage to 95%
|
||||||
|
@ -112,7 +112,7 @@ where
|
||||||
.push(key, (bytes, metadata, size));
|
.push(key, (bytes, metadata, size));
|
||||||
|
|
||||||
// Pop if too large
|
// Pop if too large
|
||||||
while new_self.cur_mem_size.load(Ordering::Acquire) >= max_mem_size {
|
while new_self.cur_mem_size.load(Ordering::Acquire) >= max_mem_size as u64 {
|
||||||
let popped = new_self
|
let popped = new_self
|
||||||
.mem_cache
|
.mem_cache
|
||||||
.lock()
|
.lock()
|
||||||
|
|
304
src/config.rs
304
src/config.rs
|
@ -1,59 +1,275 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::num::{NonZeroU16, NonZeroU64};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::path::PathBuf;
|
use std::hint::unreachable_unchecked;
|
||||||
|
use std::io::{ErrorKind, Write};
|
||||||
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use clap::{crate_authors, crate_description, crate_version, Clap};
|
use clap::{crate_authors, crate_description, crate_version, Clap};
|
||||||
|
use log::LevelFilter;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::units::{KilobitsPerSecond, Mebibytes, Port};
|
||||||
|
|
||||||
// Validate tokens is an atomic because it's faster than locking on rwlock.
|
// Validate tokens is an atomic because it's faster than locking on rwlock.
|
||||||
pub static VALIDATE_TOKENS: AtomicBool = AtomicBool::new(false);
|
pub static VALIDATE_TOKENS: AtomicBool = AtomicBool::new(false);
|
||||||
// We use an atomic here because it's better for us to not pass the config
|
|
||||||
// everywhere.
|
|
||||||
pub static SEND_SERVER_VERSION: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
pub static OFFLINE_MODE: AtomicBool = AtomicBool::new(false);
|
pub static OFFLINE_MODE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
#[error("No config found. One has been created for you to modify.")]
|
||||||
|
NotInitialized,
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
Parse(#[from] serde_yaml::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_config() -> Result<Config, ConfigError> {
|
||||||
|
// Load cli args first
|
||||||
|
let cli_args: CliArgs = CliArgs::parse();
|
||||||
|
|
||||||
|
// Load yaml file next
|
||||||
|
let config_file: Result<YamlArgs, _> = {
|
||||||
|
let config_path = cli_args
|
||||||
|
.config_path
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or_else(|| Path::new("./settings.yaml"));
|
||||||
|
match File::open(config_path) {
|
||||||
|
Ok(file) => serde_yaml::from_reader(file),
|
||||||
|
Err(e) if e.kind() == ErrorKind::NotFound => {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(config_path)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let default_config = include_str!("../settings.sample.yaml");
|
||||||
|
file.write_all(default_config.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
return Err(ConfigError::NotInitialized);
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// generate config
|
||||||
|
let config = Config::from_cli_and_file(cli_args, config_file?);
|
||||||
|
|
||||||
|
// initialize globals
|
||||||
|
OFFLINE_MODE.store(
|
||||||
|
config
|
||||||
|
.unstable_options
|
||||||
|
.contains(&UnstableOptions::OfflineMode),
|
||||||
|
Ordering::Release,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Represents a fully parsed config, from a variety of sources.
|
||||||
|
pub struct Config {
|
||||||
|
pub cache_type: CacheType,
|
||||||
|
pub cache_path: PathBuf,
|
||||||
|
pub shutdown_timeout: NonZeroU16,
|
||||||
|
pub log_level: LevelFilter,
|
||||||
|
pub client_secret: ClientSecret,
|
||||||
|
pub port: Port,
|
||||||
|
pub bind_address: SocketAddr,
|
||||||
|
pub external_address: Option<SocketAddr>,
|
||||||
|
pub ephemeral_disk_encryption: bool,
|
||||||
|
pub network_speed: KilobitsPerSecond,
|
||||||
|
pub disk_quota: Mebibytes,
|
||||||
|
pub memory_quota: Mebibytes,
|
||||||
|
pub unstable_options: Vec<UnstableOptions>,
|
||||||
|
pub override_upstream: Option<Url>,
|
||||||
|
pub enable_metrics: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
fn from_cli_and_file(cli_args: CliArgs, file_args: YamlArgs) -> Self {
|
||||||
|
let file_extended_options = file_args.extended_options.unwrap_or_default();
|
||||||
|
|
||||||
|
let log_level = match (cli_args.quiet, cli_args.verbose) {
|
||||||
|
(n, _) if n > 2 => LevelFilter::Off,
|
||||||
|
(2, _) => LevelFilter::Error,
|
||||||
|
(1, _) => LevelFilter::Warn,
|
||||||
|
// Use log level from file if no flags were provided to CLI
|
||||||
|
(0, 0) => file_extended_options
|
||||||
|
.logging_level
|
||||||
|
.unwrap_or(LevelFilter::Info),
|
||||||
|
(_, 1) => LevelFilter::Debug,
|
||||||
|
(_, n) if n > 1 => LevelFilter::Trace,
|
||||||
|
// compiler can't figure it out
|
||||||
|
_ => unsafe { unreachable_unchecked() },
|
||||||
|
};
|
||||||
|
|
||||||
|
let bind_port = cli_args
|
||||||
|
.port
|
||||||
|
.unwrap_or(file_args.server_settings.port)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
// This needs to be outside because rust isn't smart enough yet to
|
||||||
|
// realize a disjointed borrow of a moved value is ok. This will be
|
||||||
|
// fixed in Rust 2021.
|
||||||
|
let external_port = file_args
|
||||||
|
.server_settings
|
||||||
|
.external_port
|
||||||
|
.map_or(bind_port, Port::get);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
cache_type: cli_args
|
||||||
|
.cache_type
|
||||||
|
.or(file_extended_options.cache_type)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
cache_path: cli_args
|
||||||
|
.cache_path
|
||||||
|
.or(file_extended_options.cache_path)
|
||||||
|
.unwrap_or_else(|| PathBuf::from_str("./cache").unwrap()),
|
||||||
|
shutdown_timeout: file_args
|
||||||
|
.server_settings
|
||||||
|
.graceful_shutdown_wait_seconds
|
||||||
|
.unwrap_or(unsafe { NonZeroU16::new_unchecked(60) }),
|
||||||
|
log_level,
|
||||||
|
// secret should never be in CLI
|
||||||
|
client_secret: if let Ok(v) = std::env::var("CLIENT_SECRET") {
|
||||||
|
ClientSecret(v)
|
||||||
|
} else {
|
||||||
|
file_args.server_settings.secret
|
||||||
|
},
|
||||||
|
port: cli_args.port.unwrap_or(file_args.server_settings.port),
|
||||||
|
bind_address: SocketAddr::new(
|
||||||
|
file_args
|
||||||
|
.server_settings
|
||||||
|
.hostname
|
||||||
|
.unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),
|
||||||
|
bind_port,
|
||||||
|
),
|
||||||
|
external_address: file_args
|
||||||
|
.server_settings
|
||||||
|
.external_ip
|
||||||
|
.map(|ip_addr| SocketAddr::new(ip_addr, external_port)),
|
||||||
|
ephemeral_disk_encryption: cli_args
|
||||||
|
.ephemeral_disk_encryption
|
||||||
|
.or(file_extended_options.ephemeral_disk_encryption)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
network_speed: cli_args
|
||||||
|
.network_speed
|
||||||
|
.unwrap_or(file_args.server_settings.external_max_kilobits_per_second),
|
||||||
|
disk_quota: cli_args
|
||||||
|
.disk_quota
|
||||||
|
.unwrap_or(file_args.max_cache_size_in_mebibytes),
|
||||||
|
memory_quota: cli_args
|
||||||
|
.memory_quota
|
||||||
|
.or(file_extended_options.memory_quota)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
enable_metrics: file_extended_options.enable_metrics.unwrap_or_default(),
|
||||||
|
|
||||||
|
// Unstable options (and related) should never be in yaml config
|
||||||
|
unstable_options: cli_args.unstable_options,
|
||||||
|
override_upstream: cli_args.override_upstream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct YamlArgs {
|
||||||
|
// Naming is legacy
|
||||||
|
max_cache_size_in_mebibytes: Mebibytes,
|
||||||
|
server_settings: YamlServerSettings,
|
||||||
|
// This implementation custom options
|
||||||
|
extended_options: Option<YamlExtendedOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Naming is legacy
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct YamlServerSettings {
|
||||||
|
secret: ClientSecret,
|
||||||
|
#[serde(default)]
|
||||||
|
port: Port,
|
||||||
|
external_max_kilobits_per_second: KilobitsPerSecond,
|
||||||
|
external_port: Option<Port>,
|
||||||
|
graceful_shutdown_wait_seconds: Option<NonZeroU16>,
|
||||||
|
hostname: Option<IpAddr>,
|
||||||
|
external_ip: Option<IpAddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// this intentionally does not implement display
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct ClientSecret(String);
|
||||||
|
|
||||||
|
impl std::fmt::Debug for ClientSecret {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "[client secret]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
struct YamlExtendedOptions {
|
||||||
|
memory_quota: Option<Mebibytes>,
|
||||||
|
cache_type: Option<CacheType>,
|
||||||
|
ephemeral_disk_encryption: Option<bool>,
|
||||||
|
enable_metrics: Option<bool>,
|
||||||
|
logging_level: Option<LevelFilter>,
|
||||||
|
cache_path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Copy, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum CacheType {
|
||||||
|
OnDisk,
|
||||||
|
Lru,
|
||||||
|
Lfu,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CacheType {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"on_disk" => Ok(Self::OnDisk),
|
||||||
|
"lru" => Ok(Self::Lru),
|
||||||
|
"lfu" => Ok(Self::Lfu),
|
||||||
|
_ => Err(format!("Unknown option: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CacheType {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::OnDisk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clap, Clone)]
|
#[derive(Clap, Clone)]
|
||||||
#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]
|
#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]
|
||||||
pub struct CliArgs {
|
struct CliArgs {
|
||||||
/// The port to listen on.
|
/// The port to listen on.
|
||||||
#[clap(short, long, default_value = "42069", env = "PORT")]
|
#[clap(short, long)]
|
||||||
pub port: NonZeroU16,
|
pub port: Option<Port>,
|
||||||
/// How large, in bytes, the in-memory cache should be. Note that this does
|
/// How large, in mebibytes, the in-memory cache should be. Note that this
|
||||||
/// not include runtime memory usage.
|
/// does not include runtime memory usage.
|
||||||
#[clap(long, env = "MEM_CACHE_QUOTA_BYTES", conflicts_with = "low-memory")]
|
#[clap(long)]
|
||||||
pub memory_quota: Option<NonZeroU64>,
|
pub memory_quota: Option<Mebibytes>,
|
||||||
/// How large, in bytes, the on-disk cache should be. Note that actual
|
/// How large, in mebibytes, the on-disk cache should be. Note that actual
|
||||||
/// values may be larger for metadata information.
|
/// values may be larger for metadata information.
|
||||||
#[clap(long, env = "DISK_CACHE_QUOTA_BYTES")]
|
#[clap(long)]
|
||||||
pub disk_quota: u64,
|
pub disk_quota: Option<Mebibytes>,
|
||||||
/// Sets the location of the disk cache.
|
/// Sets the location of the disk cache.
|
||||||
#[clap(long, default_value = "./cache", env = "DISK_CACHE_PATH")]
|
#[clap(long)]
|
||||||
pub cache_path: PathBuf,
|
pub cache_path: Option<PathBuf>,
|
||||||
/// The network speed to advertise to Mangadex@Home control server.
|
/// The network speed to advertise to Mangadex@Home control server.
|
||||||
#[clap(long, env = "MAX_NETWORK_SPEED")]
|
#[clap(long)]
|
||||||
pub network_speed: NonZeroU64,
|
pub network_speed: Option<KilobitsPerSecond>,
|
||||||
/// Whether or not to provide the Server HTTP header to clients. This is
|
|
||||||
/// useful for debugging, but is generally not recommended for security
|
|
||||||
/// reasons.
|
|
||||||
#[clap(long, env = "ENABLE_SERVER_STRING", takes_value = false)]
|
|
||||||
pub enable_server_string: bool,
|
|
||||||
/// Changes the caching behavior to avoid buffering images in memory, and
|
|
||||||
/// instead use the filesystem as the buffer backing. This is useful for
|
|
||||||
/// clients in low (< 1GB) RAM environments.
|
|
||||||
#[clap(
|
|
||||||
short,
|
|
||||||
long,
|
|
||||||
conflicts_with("memory-quota"),
|
|
||||||
env = "LOW_MEMORY_MODE",
|
|
||||||
takes_value = false
|
|
||||||
)]
|
|
||||||
pub low_memory: bool,
|
|
||||||
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
|
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
|
||||||
/// verbose flags increases the verbosity to DEBUG and TRACE, respectively.
|
/// verbose flags increases the verbosity to DEBUG and TRACE, respectively.
|
||||||
#[clap(short, long, parse(from_occurrences))]
|
#[clap(short, long, parse(from_occurrences), conflicts_with = "quiet")]
|
||||||
pub verbose: usize,
|
pub verbose: usize,
|
||||||
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
|
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
|
||||||
/// quiet flags decreases the verbosity to WARN, ERROR, and no logs,
|
/// quiet flags decreases the verbosity to WARN, ERROR, and no logs,
|
||||||
|
@ -68,7 +284,11 @@ pub struct CliArgs {
|
||||||
/// encrypted with a key generated at runtime. There are implications to
|
/// encrypted with a key generated at runtime. There are implications to
|
||||||
/// performance, privacy, and usability with this flag enabled.
|
/// performance, privacy, and usability with this flag enabled.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pub ephemeral_disk_encryption: bool,
|
pub ephemeral_disk_encryption: Option<bool>,
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub config_path: Option<PathBuf>,
|
||||||
|
#[clap(short = 't', long)]
|
||||||
|
pub cache_type: Option<CacheType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
@ -77,10 +297,6 @@ pub enum UnstableOptions {
|
||||||
/// you know what you're dealing with.
|
/// you know what you're dealing with.
|
||||||
OverrideUpstream,
|
OverrideUpstream,
|
||||||
|
|
||||||
/// Use an LFU implementation for the in-memory cache instead of the default
|
|
||||||
/// LRU implementation.
|
|
||||||
UseLfu,
|
|
||||||
|
|
||||||
/// Disables token validation. Don't use this unless you know the
|
/// Disables token validation. Don't use this unless you know the
|
||||||
/// ramifications of this command.
|
/// ramifications of this command.
|
||||||
DisableTokenValidation,
|
DisableTokenValidation,
|
||||||
|
@ -98,7 +314,6 @@ impl FromStr for UnstableOptions {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"override-upstream" => Ok(Self::OverrideUpstream),
|
"override-upstream" => Ok(Self::OverrideUpstream),
|
||||||
"use-lfu" => Ok(Self::UseLfu),
|
|
||||||
"disable-token-validation" => Ok(Self::DisableTokenValidation),
|
"disable-token-validation" => Ok(Self::DisableTokenValidation),
|
||||||
"offline-mode" => Ok(Self::OfflineMode),
|
"offline-mode" => Ok(Self::OfflineMode),
|
||||||
"disable-tls" => Ok(Self::DisableTls),
|
"disable-tls" => Ok(Self::DisableTls),
|
||||||
|
@ -111,7 +326,6 @@ impl Display for UnstableOptions {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::OverrideUpstream => write!(f, "override-upstream"),
|
Self::OverrideUpstream => write!(f, "override-upstream"),
|
||||||
Self::UseLfu => write!(f, "use-lfu"),
|
|
||||||
Self::DisableTokenValidation => write!(f, "disable-token-validation"),
|
Self::DisableTokenValidation => write!(f, "disable-token-validation"),
|
||||||
Self::OfflineMode => write!(f, "offline-mode"),
|
Self::OfflineMode => write!(f, "offline-mode"),
|
||||||
Self::DisableTls => write!(f, "disable-tls"),
|
Self::DisableTls => write!(f, "disable-tls"),
|
||||||
|
|
121
src/main.rs
121
src/main.rs
|
@ -2,12 +2,10 @@
|
||||||
// We're end users, so these is ok
|
// We're end users, so these is ok
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
use std::env::{self, VarError};
|
use std::env::VarError;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::hint::unreachable_unchecked;
|
use std::num::ParseIntError;
|
||||||
use std::num::{NonZeroU64, ParseIntError};
|
|
||||||
use std::process;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -16,9 +14,8 @@ use actix_web::rt::{spawn, time, System};
|
||||||
use actix_web::web::{self, Data};
|
use actix_web::web::{self, Data};
|
||||||
use actix_web::{App, HttpResponse, HttpServer};
|
use actix_web::{App, HttpResponse, HttpServer};
|
||||||
use cache::{Cache, DiskCache};
|
use cache::{Cache, DiskCache};
|
||||||
use clap::Clap;
|
use config::Config;
|
||||||
use config::CliArgs;
|
use log::{debug, error, info, warn};
|
||||||
use log::{debug, error, info, warn, LevelFilter};
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use rustls::{NoClientAuth, ServerConfig};
|
use rustls::{NoClientAuth, ServerConfig};
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
@ -29,7 +26,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::cache::mem::{Lfu, Lru};
|
use crate::cache::mem::{Lfu, Lru};
|
||||||
use crate::cache::{MemoryCache, ENCRYPTION_KEY};
|
use crate::cache::{MemoryCache, ENCRYPTION_KEY};
|
||||||
use crate::config::{UnstableOptions, OFFLINE_MODE};
|
use crate::config::{CacheType, UnstableOptions, OFFLINE_MODE};
|
||||||
use crate::state::DynamicServerCert;
|
use crate::state::DynamicServerCert;
|
||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
|
@ -39,13 +36,9 @@ mod ping;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod state;
|
mod state;
|
||||||
mod stop;
|
mod stop;
|
||||||
|
mod units;
|
||||||
|
|
||||||
#[macro_export]
|
const CLIENT_API_VERSION: usize = 31;
|
||||||
macro_rules! client_api_version {
|
|
||||||
() => {
|
|
||||||
"31"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
enum ServerError {
|
enum ServerError {
|
||||||
|
@ -65,77 +58,58 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// Config loading
|
// Config loading
|
||||||
//
|
//
|
||||||
|
|
||||||
let cli_args = CliArgs::parse();
|
let config = match config::load_config() {
|
||||||
let port = cli_args.port;
|
Ok(c) => c,
|
||||||
let memory_max_size = cli_args
|
Err(e) => {
|
||||||
.memory_quota
|
eprintln!("{}", e);
|
||||||
.map(NonZeroU64::get)
|
return Err(Box::new(e) as Box<_>);
|
||||||
.unwrap_or_default();
|
}
|
||||||
let disk_quota = cli_args.disk_quota;
|
};
|
||||||
let cache_path = cli_args.cache_path.clone();
|
|
||||||
let low_mem_mode = cli_args.low_memory;
|
let memory_quota = config.memory_quota;
|
||||||
let use_lfu = cli_args.unstable_options.contains(&UnstableOptions::UseLfu);
|
let disk_quota = config.disk_quota;
|
||||||
let disable_tls = cli_args
|
let cache_type = config.cache_type;
|
||||||
|
let cache_path = config.cache_path.clone();
|
||||||
|
let disable_tls = config
|
||||||
.unstable_options
|
.unstable_options
|
||||||
.contains(&UnstableOptions::DisableTls);
|
.contains(&UnstableOptions::DisableTls);
|
||||||
OFFLINE_MODE.store(
|
let bind_address = config.bind_address;
|
||||||
cli_args
|
|
||||||
.unstable_options
|
|
||||||
.contains(&UnstableOptions::OfflineMode),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Logging and warnings
|
// Logging and warnings
|
||||||
//
|
//
|
||||||
|
|
||||||
let log_level = match (cli_args.quiet, cli_args.verbose) {
|
SimpleLogger::new().with_level(config.log_level).init()?;
|
||||||
(n, _) if n > 2 => LevelFilter::Off,
|
|
||||||
(2, _) => LevelFilter::Error,
|
|
||||||
(1, _) => LevelFilter::Warn,
|
|
||||||
(0, 0) => LevelFilter::Info,
|
|
||||||
(_, 1) => LevelFilter::Debug,
|
|
||||||
(_, n) if n > 1 => LevelFilter::Trace,
|
|
||||||
// compiler can't figure it out
|
|
||||||
_ => unsafe { unreachable_unchecked() },
|
|
||||||
};
|
|
||||||
|
|
||||||
SimpleLogger::new().with_level(log_level).init()?;
|
if let Err(e) = print_preamble_and_warnings(&config) {
|
||||||
|
|
||||||
if let Err(e) = print_preamble_and_warnings(&cli_args) {
|
|
||||||
error!("{}", e);
|
error!("{}", e);
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let client_secret = if let Ok(v) = env::var("CLIENT_SECRET") {
|
debug!("{:?}", &config);
|
||||||
v
|
|
||||||
} else {
|
|
||||||
error!("Client secret not found in ENV. Please set CLIENT_SECRET.");
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
let client_secret_1 = client_secret.clone();
|
|
||||||
|
|
||||||
if cli_args.ephemeral_disk_encryption {
|
let client_secret = config.client_secret.clone();
|
||||||
|
let client_secret_1 = config.client_secret.clone();
|
||||||
|
|
||||||
|
if config.ephemeral_disk_encryption {
|
||||||
info!("Running with at-rest encryption!");
|
info!("Running with at-rest encryption!");
|
||||||
ENCRYPTION_KEY.set(gen_key()).unwrap();
|
ENCRYPTION_KEY.set(gen_key()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics::init();
|
if config.enable_metrics {
|
||||||
|
metrics::init();
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP Server init
|
// HTTP Server init
|
||||||
|
|
||||||
let server = if OFFLINE_MODE.load(Ordering::Acquire) {
|
let server = if OFFLINE_MODE.load(Ordering::Acquire) {
|
||||||
ServerState::init_offline()
|
ServerState::init_offline()
|
||||||
} else {
|
} else {
|
||||||
ServerState::init(&client_secret, &cli_args).await?
|
ServerState::init(&client_secret, &config).await?
|
||||||
};
|
};
|
||||||
let data_0 = Arc::new(RwLockServerState(RwLock::new(server)));
|
let data_0 = Arc::new(RwLockServerState(RwLock::new(server)));
|
||||||
let data_1 = Arc::clone(&data_0);
|
let data_1 = Arc::clone(&data_0);
|
||||||
|
|
||||||
// What's nice is that Rustls only supports TLS 1.2 and 1.3.
|
|
||||||
let mut tls_config = ServerConfig::new(NoClientAuth::new());
|
|
||||||
tls_config.cert_resolver = Arc::new(DynamicServerCert);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// At this point, the server is ready to start, and starts the necessary
|
// At this point, the server is ready to start, and starts the necessary
|
||||||
// threads.
|
// threads.
|
||||||
|
@ -171,18 +145,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
debug!("Sending ping!");
|
debug!("Sending ping!");
|
||||||
ping::update_server_state(&client_secret_1, &cli_args, &mut data).await;
|
ping::update_server_state(&client_secret_1, &config, &mut data).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let cache = DiskCache::new(disk_quota, cache_path.clone()).await;
|
let memory_max_size = memory_quota.into();
|
||||||
let cache: Arc<dyn Cache> = if low_mem_mode {
|
let cache = DiskCache::new(disk_quota.into(), cache_path.clone()).await;
|
||||||
cache
|
let cache: Arc<dyn Cache> = match cache_type {
|
||||||
} else if use_lfu {
|
CacheType::OnDisk => cache,
|
||||||
MemoryCache::<Lfu, _>::new(cache, memory_max_size).await
|
CacheType::Lru => MemoryCache::<Lfu, _>::new(cache, memory_max_size).await,
|
||||||
} else {
|
CacheType::Lfu => MemoryCache::<Lru, _>::new(cache, memory_max_size).await,
|
||||||
MemoryCache::<Lru, _>::new(cache, memory_max_size).await
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let cache_0 = Arc::clone(&cache);
|
let cache_0 = Arc::clone(&cache);
|
||||||
|
@ -209,12 +182,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.shutdown_timeout(60);
|
.shutdown_timeout(60);
|
||||||
|
|
||||||
if disable_tls {
|
if disable_tls {
|
||||||
server.bind(format!("0.0.0.0:{}", port))?.run().await?;
|
server.bind(bind_address)?.run().await?;
|
||||||
} else {
|
} else {
|
||||||
server
|
// Rustls only supports TLS 1.2 and 1.3.
|
||||||
.bind_rustls(format!("0.0.0.0:{}", port), tls_config)?
|
let tls_config = {
|
||||||
.run()
|
let mut tls_config = ServerConfig::new(NoClientAuth::new());
|
||||||
.await?;
|
tls_config.cert_resolver = Arc::new(DynamicServerCert);
|
||||||
|
tls_config
|
||||||
|
};
|
||||||
|
|
||||||
|
server.bind_rustls(bind_address, tls_config)?.run().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waiting for us to finish sending stop message
|
// Waiting for us to finish sending stop message
|
||||||
|
@ -246,7 +223,7 @@ impl Display for InvalidCombination {
|
||||||
|
|
||||||
impl Error for InvalidCombination {}
|
impl Error for InvalidCombination {}
|
||||||
|
|
||||||
fn print_preamble_and_warnings(args: &CliArgs) -> Result<(), Box<dyn Error>> {
|
fn print_preamble_and_warnings(args: &Config) -> Result<(), Box<dyn Error>> {
|
||||||
println!(concat!(
|
println!(concat!(
|
||||||
env!("CARGO_PKG_NAME"),
|
env!("CARGO_PKG_NAME"),
|
||||||
" ",
|
" ",
|
||||||
|
|
43
src/ping.rs
43
src/ping.rs
|
@ -1,4 +1,3 @@
|
||||||
use std::num::{NonZeroU16, NonZeroU64};
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::{io::BufReader, sync::Arc};
|
use std::{io::BufReader, sync::Arc};
|
||||||
|
|
||||||
|
@ -12,35 +11,34 @@ use serde_repr::Deserialize_repr;
|
||||||
use sodiumoxide::crypto::box_::PrecomputedKey;
|
use sodiumoxide::crypto::box_::PrecomputedKey;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::config::{CliArgs, VALIDATE_TOKENS};
|
use crate::config::{ClientSecret, Config, UnstableOptions, VALIDATE_TOKENS};
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
RwLockServerState, PREVIOUSLY_COMPROMISED, PREVIOUSLY_PAUSED, TLS_CERTS,
|
RwLockServerState, PREVIOUSLY_COMPROMISED, PREVIOUSLY_PAUSED, TLS_CERTS,
|
||||||
TLS_PREVIOUSLY_CREATED, TLS_SIGNING_KEY,
|
TLS_PREVIOUSLY_CREATED, TLS_SIGNING_KEY,
|
||||||
};
|
};
|
||||||
use crate::{client_api_version, config::UnstableOptions};
|
use crate::units::{BytesPerSecond, Mebibytes, Port};
|
||||||
|
use crate::CLIENT_API_VERSION;
|
||||||
|
|
||||||
pub const CONTROL_CENTER_PING_URL: &str = "https://api.mangadex.network/ping";
|
pub const CONTROL_CENTER_PING_URL: &str = "https://api.mangadex.network/ping";
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize)]
|
||||||
pub struct Request<'a> {
|
pub struct Request<'a> {
|
||||||
secret: &'a str,
|
secret: &'a ClientSecret,
|
||||||
port: NonZeroU16,
|
port: Port,
|
||||||
disk_space: u64,
|
disk_space: Mebibytes,
|
||||||
network_speed: NonZeroU64,
|
network_speed: BytesPerSecond,
|
||||||
build_version: u64,
|
build_version: usize,
|
||||||
tls_created_at: Option<String>,
|
tls_created_at: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Request<'a> {
|
impl<'a> Request<'a> {
|
||||||
fn from_config_and_state(secret: &'a str, config: &CliArgs) -> Self {
|
fn from_config_and_state(secret: &'a ClientSecret, config: &Config) -> Self {
|
||||||
Self {
|
Self {
|
||||||
secret,
|
secret,
|
||||||
port: config.port,
|
port: config.port,
|
||||||
disk_space: config.disk_quota,
|
disk_space: config.disk_quota,
|
||||||
network_speed: config.network_speed,
|
network_speed: config.network_speed.into(),
|
||||||
build_version: client_api_version!()
|
build_version: CLIENT_API_VERSION,
|
||||||
.parse()
|
|
||||||
.expect("to parse the build version"),
|
|
||||||
tls_created_at: TLS_PREVIOUSLY_CREATED
|
tls_created_at: TLS_PREVIOUSLY_CREATED
|
||||||
.get()
|
.get()
|
||||||
.map(|v| v.load().as_ref().clone()),
|
.map(|v| v.load().as_ref().clone()),
|
||||||
|
@ -48,17 +46,14 @@ impl<'a> Request<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::fallible_impl_from)]
|
impl<'a> From<(&'a ClientSecret, &Config)> for Request<'a> {
|
||||||
impl<'a> From<(&'a str, &CliArgs)> for Request<'a> {
|
fn from((secret, config): (&'a ClientSecret, &Config)) -> Self {
|
||||||
fn from((secret, config): (&'a str, &CliArgs)) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
secret,
|
secret,
|
||||||
port: config.port,
|
port: config.port,
|
||||||
disk_space: config.disk_quota,
|
disk_space: config.disk_quota,
|
||||||
network_speed: config.network_speed,
|
network_speed: config.network_speed.into(),
|
||||||
build_version: client_api_version!()
|
build_version: CLIENT_API_VERSION,
|
||||||
.parse()
|
|
||||||
.expect("to parse the build version"),
|
|
||||||
tls_created_at: None,
|
tls_created_at: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +161,11 @@ impl std::fmt::Debug for Tls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_server_state(secret: &str, cli: &CliArgs, data: &mut Arc<RwLockServerState>) {
|
pub async fn update_server_state(
|
||||||
|
secret: &ClientSecret,
|
||||||
|
cli: &Config,
|
||||||
|
data: &mut Arc<RwLockServerState>,
|
||||||
|
) {
|
||||||
let req = Request::from_config_and_state(secret, cli);
|
let req = Request::from_config_and_state(secret, cli);
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let resp = client.post(CONTROL_CENTER_PING_URL).json(&req).send().await;
|
let resp = client.post(CONTROL_CENTER_PING_URL).json(&req).send().await;
|
||||||
|
|
|
@ -22,15 +22,14 @@ use sodiumoxide::crypto::box_::{open_precomputed, Nonce, PrecomputedKey, NONCEBY
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::cache::{Cache, CacheKey, ImageMetadata, UpstreamError};
|
use crate::cache::{Cache, CacheKey, ImageMetadata, UpstreamError};
|
||||||
use crate::client_api_version;
|
use crate::config::{OFFLINE_MODE, VALIDATE_TOKENS};
|
||||||
use crate::config::{OFFLINE_MODE, SEND_SERVER_VERSION, VALIDATE_TOKENS};
|
|
||||||
use crate::metrics::{
|
use crate::metrics::{
|
||||||
CACHE_HIT_COUNTER, CACHE_MISS_COUNTER, REQUESTS_DATA_COUNTER, REQUESTS_DATA_SAVER_COUNTER,
|
CACHE_HIT_COUNTER, CACHE_MISS_COUNTER, REQUESTS_DATA_COUNTER, REQUESTS_DATA_SAVER_COUNTER,
|
||||||
REQUESTS_OTHER_COUNTER, REQUESTS_TOTAL_COUNTER,
|
REQUESTS_OTHER_COUNTER, REQUESTS_TOTAL_COUNTER,
|
||||||
};
|
};
|
||||||
use crate::state::RwLockServerState;
|
use crate::state::RwLockServerState;
|
||||||
|
|
||||||
pub const BASE64_CONFIG: base64::Config = base64::Config::new(base64::CharacterSet::UrlSafe, false);
|
const BASE64_CONFIG: base64::Config = base64::Config::new(base64::CharacterSet::UrlSafe, false);
|
||||||
|
|
||||||
static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| {
|
static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| {
|
||||||
Client::builder()
|
Client::builder()
|
||||||
|
@ -41,15 +40,6 @@ static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| {
|
||||||
.expect("Client initialization to work")
|
.expect("Client initialization to work")
|
||||||
});
|
});
|
||||||
|
|
||||||
const SERVER_ID_STRING: &str = concat!(
|
|
||||||
env!("CARGO_CRATE_NAME"),
|
|
||||||
" ",
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
" (",
|
|
||||||
client_api_version!(),
|
|
||||||
") - Conforming to spec revision b82043289",
|
|
||||||
);
|
|
||||||
|
|
||||||
enum ServerResponse {
|
enum ServerResponse {
|
||||||
TokenValidationError(TokenValidationError),
|
TokenValidationError(TokenValidationError),
|
||||||
HttpResponse(HttpResponse),
|
HttpResponse(HttpResponse),
|
||||||
|
@ -226,10 +216,6 @@ fn push_headers(builder: &mut HttpResponseBuilder) -> &mut HttpResponseBuilder {
|
||||||
.insert_header((CACHE_CONTROL, "public, max-age=1209600"))
|
.insert_header((CACHE_CONTROL, "public, max-age=1209600"))
|
||||||
.insert_header(("Timing-Allow-Origin", "https://mangadex.org"));
|
.insert_header(("Timing-Allow-Origin", "https://mangadex.org"));
|
||||||
|
|
||||||
if SEND_SERVER_VERSION.load(Ordering::Acquire) {
|
|
||||||
builder.insert_header(("Server", SERVER_ID_STRING));
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use crate::config::{CliArgs, UnstableOptions, OFFLINE_MODE, SEND_SERVER_VERSION, VALIDATE_TOKENS};
|
use crate::config::{ClientSecret, Config, UnstableOptions, OFFLINE_MODE, VALIDATE_TOKENS};
|
||||||
use crate::ping::{Request, Response, CONTROL_CENTER_PING_URL};
|
use crate::ping::{Request, Response, CONTROL_CENTER_PING_URL};
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
|
@ -45,18 +45,13 @@ pub enum ServerInitError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerState {
|
impl ServerState {
|
||||||
pub async fn init(secret: &str, config: &CliArgs) -> Result<Self, ServerInitError> {
|
pub async fn init(secret: &ClientSecret, config: &Config) -> Result<Self, ServerInitError> {
|
||||||
let resp = reqwest::Client::new()
|
let resp = reqwest::Client::new()
|
||||||
.post(CONTROL_CENTER_PING_URL)
|
.post(CONTROL_CENTER_PING_URL)
|
||||||
.json(&Request::from((secret, config)))
|
.json(&Request::from((secret, config)))
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if config.enable_server_string {
|
|
||||||
warn!("Client will send Server header in responses. This is not recommended!");
|
|
||||||
SEND_SERVER_VERSION.store(true, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
match resp {
|
match resp {
|
||||||
Ok(resp) => match resp.json::<Response>().await {
|
Ok(resp) => match resp.json::<Response>().await {
|
||||||
Ok(Response::Ok(mut resp)) => {
|
Ok(Response::Ok(mut resp)) => {
|
||||||
|
|
|
@ -2,14 +2,16 @@ use log::{info, warn};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::config::ClientSecret;
|
||||||
|
|
||||||
const CONTROL_CENTER_STOP_URL: &str = "https://api.mangadex.network/ping";
|
const CONTROL_CENTER_STOP_URL: &str = "https://api.mangadex.network/ping";
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct StopRequest<'a> {
|
struct StopRequest<'a> {
|
||||||
secret: &'a str,
|
secret: &'a ClientSecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_stop(secret: &str) {
|
pub async fn send_stop(secret: &ClientSecret) {
|
||||||
let request = StopRequest { secret };
|
let request = StopRequest { secret };
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
match client
|
match client
|
||||||
|
|
80
src/units.rs
Normal file
80
src/units.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::num::{NonZeroU16, NonZeroU64, ParseIntError};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Wrapper type for a port number.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
|
pub struct Port(NonZeroU16);
|
||||||
|
|
||||||
|
impl Port {
|
||||||
|
pub const fn get(self) -> u16 {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Port {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(unsafe { NonZeroU16::new_unchecked(443) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Port {
|
||||||
|
type Err = <NonZeroU16 as FromStr>::Err;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
NonZeroU16::from_str(s).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Port {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, Hash, Eq, PartialEq)]
|
||||||
|
pub struct Mebibytes(usize);
|
||||||
|
|
||||||
|
impl FromStr for Mebibytes {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
s.parse::<usize>().map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bytes(usize);
|
||||||
|
|
||||||
|
impl Bytes {
|
||||||
|
pub const fn get(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Mebibytes> for Bytes {
|
||||||
|
fn from(mib: Mebibytes) -> Self {
|
||||||
|
Self(mib.0 << 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
|
||||||
|
pub struct KilobitsPerSecond(NonZeroU64);
|
||||||
|
|
||||||
|
impl FromStr for KilobitsPerSecond {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
s.parse::<NonZeroU64>().map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Serialize, Debug, Hash, Eq, PartialEq)]
|
||||||
|
pub struct BytesPerSecond(NonZeroU64);
|
||||||
|
|
||||||
|
impl From<KilobitsPerSecond> for BytesPerSecond {
|
||||||
|
fn from(kbps: KilobitsPerSecond) -> Self {
|
||||||
|
Self(unsafe { NonZeroU64::new_unchecked(kbps.0.get() * 125) })
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue