Compare commits

...

4 commits

Author SHA1 Message Date
c5383639f5
ignore default config value 2021-07-09 19:18:09 -04:00
88561f7c2c
Fix short arg conflict 2021-07-09 19:17:56 -04:00
ce03ce0baf
add support for yaml files 2021-07-09 19:14:53 -04:00
e78315025d
Use build script 2021-07-09 17:32:00 -04:00
8 changed files with 164 additions and 78 deletions

3
.gitignore vendored
View file

@ -3,4 +3,5 @@
/cache /cache
flamegraph*.svg flamegraph*.svg
perf.data* perf.data*
dhat.out.* dhat.out.*
settings.yaml

View file

@ -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(())
} }

View file

@ -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

View file

@ -18,6 +18,8 @@
# MangaDex@Home configuration file # MangaDex@Home configuration file
# We are pleased to have you here # We are pleased to have you here
# May fate stay the night with you! # May fate stay the night with you!
#
# Default values are commented out.
# The size in mebibytes of the cache # The size in mebibytes of the cache
# You can use megabytes instead in a pinch, # You can use megabytes instead in a pinch,
@ -25,37 +27,31 @@
max_cache_size_in_mebibytes: 0 max_cache_size_in_mebibytes: 0
server_settings: server_settings:
# The client secret # The client secret. Keep this secret at all costs :P
# Keep this secret at all costs :P secret: suichan wa kyou mo kawaii!
secret: ina_is_the_cutest
# The port for the webserver to listen on # The port for the webserver to listen on. 443 is recommended for max appeal.
# 443 is recommended for maximum appeal # port: 443
port: 443
# This controls the value the server receives # This controls the value the server receives for your upload speed.
# for your upload speed
external_max_kilobits_per_second: 0 external_max_kilobits_per_second: 0
# #
# Stuff that you probably don't need to change # Advanced settings
# #
# The external port to broadcast to the backend # The external hostname to listen on. Keep this at 0.0.0.0 unless you know
# Keep this at 0 unless you know what you're doing # what you're doing.
# 0 means broadcast the same value as `port` # hostname: 0.0.0.0
external_port: 0
# How long to wait for the graceful shutdown (Ctrl-C or SIGINT) # The external port to broadcast to the backend. Keep this at 0 unless you
# This is rounded to a multiple of 5 seconds # know what you're doing. 0 means broadcast the same value as `port`.
graceful_shutdown_wait_seconds: 60 # external_port: 0
# The external hostname to listen on # How long to wait at most for the graceful shutdown (Ctrl-C or SIGINT).
# Keep this at 0.0.0.0 unless you know what you're doing # graceful_shutdown_wait_seconds: 60
hostname: 0.0.0.0
# The external ip to broadcast to the webserver # The external ip to broadcast to the webserver. The default of null (~) means
# The default of null means the backend will infer it # the backend will infer it from where it was sent from, which may fail in the
# from where it was sent from, which may fail in the # presence of multiple IPs.
# presence of multiple IPs # external_ip: ~
external_ip: ~

6
src/cache/fs.rs vendored
View file

@ -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)));
} }

View file

@ -2,8 +2,8 @@ use std::fmt::{Display, Formatter};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::hint::unreachable_unchecked; use std::hint::unreachable_unchecked;
use std::io::{ErrorKind, Write}; use std::io::{ErrorKind, Write};
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::num::{NonZeroU16, NonZeroU64}; use std::num::NonZeroU16;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -64,7 +64,7 @@ pub fn load_config() -> Result<Config, serde_yaml::Error> {
Ok(config) Ok(config)
} }
/// Represents a fully parsed config file. /// Represents a fully parsed config, from a variety of sources.
pub struct Config { pub struct Config {
pub cache_type: CacheType, pub cache_type: CacheType,
pub cache_path: PathBuf, pub cache_path: PathBuf,
@ -75,27 +75,93 @@ pub struct Config {
pub bind_address: SocketAddr, pub bind_address: SocketAddr,
pub external_address: Option<SocketAddr>, pub external_address: Option<SocketAddr>,
pub ephemeral_disk_encryption: bool, pub ephemeral_disk_encryption: bool,
pub unstable_options: Vec<UnstableOptions>,
pub network_speed: KilobitsPerSecond, pub network_speed: KilobitsPerSecond,
pub disk_quota: Mebibytes, pub disk_quota: Mebibytes,
pub memory_quota: Mebibytes, pub memory_quota: Mebibytes,
pub unstable_options: Vec<UnstableOptions>,
pub override_upstream: Option<Url>, pub override_upstream: Option<Url>,
pub enable_metrics: bool,
} }
impl Config { impl Config {
fn from_cli_and_file(cli_args: CliArgs, file_args: YamlArgs) -> Self { 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) { let log_level = match (cli_args.quiet, cli_args.verbose) {
(n, _) if n > 2 => LevelFilter::Off, (n, _) if n > 2 => LevelFilter::Off,
(2, _) => LevelFilter::Error, (2, _) => LevelFilter::Error,
(1, _) => LevelFilter::Warn, (1, _) => LevelFilter::Warn,
// Use log level from file if no flags were provided to CLI // Use log level from file if no flags were provided to CLI
(0, 0) => file_args.extended_options.logging_level, (0, 0) => file_extended_options
.logging_level
.unwrap_or(LevelFilter::Info),
(_, 1) => LevelFilter::Debug, (_, 1) => LevelFilter::Debug,
(_, n) if n > 1 => LevelFilter::Trace, (_, n) if n > 1 => LevelFilter::Trace,
// compiler can't figure it out // compiler can't figure it out
_ => unsafe { unreachable_unchecked() }, _ => unsafe { unreachable_unchecked() },
}; };
todo!()
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: 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,
}
} }
} }
@ -105,13 +171,14 @@ struct YamlArgs {
max_cache_size_in_mebibytes: Mebibytes, max_cache_size_in_mebibytes: Mebibytes,
server_settings: YamlServerSettings, server_settings: YamlServerSettings,
// This implementation custom options // This implementation custom options
extended_options: YamlExtendedOptions, extended_options: Option<YamlExtendedOptions>,
} }
// Naming is legacy // Naming is legacy
#[derive(Deserialize)] #[derive(Deserialize)]
struct YamlServerSettings { struct YamlServerSettings {
secret: ClientSecret, secret: ClientSecret,
#[serde(default)]
port: Port, port: Port,
external_max_kilobits_per_second: KilobitsPerSecond, external_max_kilobits_per_second: KilobitsPerSecond,
external_port: Option<Port>, external_port: Option<Port>,
@ -124,21 +191,14 @@ struct YamlServerSettings {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct ClientSecret(String); pub struct ClientSecret(String);
#[derive(Deserialize)] #[derive(Deserialize, Default)]
struct YamlExtendedOptions { struct YamlExtendedOptions {
memory_quota: Option<NonZeroU64>, memory_quota: Option<Mebibytes>,
#[serde(default)] cache_type: Option<CacheType>,
cache_type: CacheType, ephemeral_disk_encryption: Option<bool>,
#[serde(default)] enable_metrics: Option<bool>,
ephemeral_disk_encryption: bool, logging_level: Option<LevelFilter>,
#[serde(default)] cache_path: Option<PathBuf>,
enable_metrics: bool,
#[serde(default = "default_logging_level")]
logging_level: LevelFilter,
}
const fn default_logging_level() -> LevelFilter {
LevelFilter::Info
} }
#[derive(Deserialize, Copy, Clone)] #[derive(Deserialize, Copy, Clone)]
@ -172,25 +232,25 @@ impl Default for CacheType {
#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())] #[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]
struct CliArgs { struct CliArgs {
/// The port to listen on. /// The port to listen on.
#[clap(short, long, default_value = "42069")] #[clap(short, long)]
pub port: Port, 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)] #[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)] #[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")] #[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)] #[clap(long)]
pub network_speed: NonZeroU64, pub network_speed: Option<KilobitsPerSecond>,
/// 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,
@ -205,11 +265,11 @@ 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)] #[clap(short, long)]
pub config_path: Option<PathBuf>, pub config_path: Option<PathBuf>,
#[clap(default_value = "on_disk")] #[clap(short = 't', long)]
pub cache_type: CacheType, pub cache_type: Option<CacheType>,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]

View file

@ -93,7 +93,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
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

View file

@ -1,5 +1,5 @@
use std::fmt::Display; use std::fmt::Display;
use std::num::{NonZeroU16, NonZeroU64}; use std::num::{NonZeroU16, NonZeroU64, ParseIntError};
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -8,6 +8,18 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct Port(NonZeroU16); 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 { impl FromStr for Port {
type Err = <NonZeroU16 as FromStr>::Err; type Err = <NonZeroU16 as FromStr>::Err;
@ -25,9 +37,11 @@ impl Display for Port {
#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, Hash, Eq, PartialEq)] #[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, Hash, Eq, PartialEq)]
pub struct Mebibytes(usize); pub struct Mebibytes(usize);
impl Mebibytes { impl FromStr for Mebibytes {
pub const fn get(self) -> usize { type Err = ParseIntError;
self.0
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<usize>().map(Self)
} }
} }
@ -48,6 +62,14 @@ impl From<Mebibytes> for Bytes {
#[derive(Copy, Clone, Deserialize, Debug, Hash, Eq, PartialEq)] #[derive(Copy, Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
pub struct KilobitsPerSecond(NonZeroU64); 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)] #[derive(Copy, Clone, Serialize, Debug, Hash, Eq, PartialEq)]
pub struct BytesPerSecond(NonZeroU64); pub struct BytesPerSecond(NonZeroU64);