2021-03-17 18:45:16 -07:00
|
|
|
#![warn(clippy::pedantic, clippy::nursery)]
|
2021-03-22 17:00:21 -07:00
|
|
|
// We're end users, so these is ok
|
2021-04-17 20:19:27 -07:00
|
|
|
#![allow(clippy::module_name_repetitions)]
|
2021-03-17 18:45:16 -07:00
|
|
|
|
2021-03-25 19:58:07 -07:00
|
|
|
use std::env::{self, VarError};
|
|
|
|
use std::process;
|
2021-03-22 14:47:56 -07:00
|
|
|
use std::sync::atomic::AtomicBool;
|
|
|
|
use std::sync::Arc;
|
2021-03-17 18:45:16 -07:00
|
|
|
use std::time::Duration;
|
2021-03-22 14:47:56 -07:00
|
|
|
use std::{num::ParseIntError, sync::atomic::Ordering};
|
2021-03-17 18:45:16 -07:00
|
|
|
|
2021-03-22 14:47:56 -07:00
|
|
|
use actix_web::rt::{spawn, time, System};
|
2021-03-22 20:04:54 -07:00
|
|
|
use actix_web::web::{self, Data};
|
2021-03-17 18:45:16 -07:00
|
|
|
use actix_web::{App, HttpServer};
|
2021-04-14 20:44:13 -07:00
|
|
|
use cache::{Cache, GenerationalCache, LowMemCache};
|
2021-03-25 18:06:54 -07:00
|
|
|
use clap::Clap;
|
|
|
|
use config::CliArgs;
|
2021-03-22 20:19:56 -07:00
|
|
|
use log::{debug, error, warn, LevelFilter};
|
2021-03-25 21:07:32 -07:00
|
|
|
use parking_lot::{Mutex, RwLock};
|
2021-03-22 14:47:56 -07:00
|
|
|
use rustls::{NoClientAuth, ServerConfig};
|
2021-03-17 18:45:16 -07:00
|
|
|
use simple_logger::SimpleLogger;
|
2021-03-22 14:47:56 -07:00
|
|
|
use state::{RwLockServerState, ServerState};
|
|
|
|
use stop::send_stop;
|
2021-03-17 18:45:16 -07:00
|
|
|
use thiserror::Error;
|
|
|
|
|
2021-03-22 14:47:56 -07:00
|
|
|
mod cache;
|
2021-03-25 18:06:54 -07:00
|
|
|
mod config;
|
2021-03-17 18:45:16 -07:00
|
|
|
mod ping;
|
|
|
|
mod routes;
|
2021-03-22 14:47:56 -07:00
|
|
|
mod state;
|
2021-03-17 18:45:16 -07:00
|
|
|
mod stop;
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! client_api_version {
|
|
|
|
() => {
|
|
|
|
"30"
|
|
|
|
};
|
|
|
|
}
|
2021-03-25 19:58:07 -07:00
|
|
|
|
2021-03-17 18:45:16 -07:00
|
|
|
#[derive(Error, Debug)]
|
|
|
|
enum ServerError {
|
|
|
|
#[error("There was a failure parsing config")]
|
|
|
|
Config(#[from] VarError),
|
|
|
|
#[error("Failed to parse an int")]
|
|
|
|
ParseInt(#[from] ParseIntError),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_web::main]
|
|
|
|
async fn main() -> Result<(), std::io::Error> {
|
2021-03-22 14:47:56 -07:00
|
|
|
// It's ok to fail early here, it would imply we have a invalid config.
|
2021-03-17 18:45:16 -07:00
|
|
|
dotenv::dotenv().ok();
|
2021-03-25 18:06:54 -07:00
|
|
|
let cli_args = CliArgs::parse();
|
2021-04-17 19:12:02 -07:00
|
|
|
|
|
|
|
println!(concat!(
|
|
|
|
env!("CARGO_PKG_NAME"),
|
2021-04-17 20:19:27 -07:00
|
|
|
" Copyright (C) 2021 ",
|
|
|
|
env!("CARGO_PKG_AUTHORS"),
|
|
|
|
"\n\n",
|
2021-04-17 19:12:02 -07:00
|
|
|
env!("CARGO_PKG_NAME"),
|
|
|
|
" is free software: you can redistribute it and/or modify\n\
|
|
|
|
it under the terms of the GNU General Public License as published by\n\
|
|
|
|
the Free Software Foundation, either version 3 of the License, or\n\
|
|
|
|
(at your option) any later version.\n\n",
|
|
|
|
env!("CARGO_PKG_NAME"),
|
|
|
|
" is distributed in the hope that it will be useful,\n\
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
|
|
|
|
GNU General Public License for more details.\n\n\
|
|
|
|
You should have received a copy of the GNU General Public License\n\
|
|
|
|
along with ",
|
|
|
|
env!("CARGO_PKG_NAME"),
|
|
|
|
". If not, see <https://www.gnu.org/licenses/>.\n"
|
|
|
|
));
|
|
|
|
|
2021-03-25 19:58:07 -07:00
|
|
|
let port = cli_args.port;
|
2021-03-25 21:07:32 -07:00
|
|
|
let memory_max_size = cli_args.memory_quota.get();
|
|
|
|
let disk_quota = cli_args.disk_quota;
|
|
|
|
let cache_path = cli_args.cache_path.clone();
|
2021-04-14 20:44:13 -07:00
|
|
|
let low_mem_mode = cli_args.low_memory;
|
2021-03-25 18:06:54 -07:00
|
|
|
|
2021-04-17 20:19:27 -07:00
|
|
|
match cli_args.verbose {
|
|
|
|
0 => SimpleLogger::new().with_level(LevelFilter::Info),
|
|
|
|
1 => SimpleLogger::new().with_level(LevelFilter::Debug),
|
|
|
|
_ => SimpleLogger::new().with_level(LevelFilter::Trace),
|
|
|
|
}
|
|
|
|
.init()
|
|
|
|
.unwrap();
|
2021-03-25 18:06:54 -07:00
|
|
|
|
|
|
|
let client_secret = if let Ok(v) = env::var("CLIENT_SECRET") {
|
|
|
|
v
|
|
|
|
} else {
|
2021-03-25 19:58:07 -07:00
|
|
|
error!("Client secret not found in ENV. Please set CLIENT_SECRET.");
|
2021-03-25 18:06:54 -07:00
|
|
|
process::exit(1);
|
|
|
|
};
|
|
|
|
let client_secret_1 = client_secret.clone();
|
|
|
|
|
|
|
|
let server = ServerState::init(&client_secret, &cli_args).await.unwrap();
|
2021-03-25 19:58:07 -07:00
|
|
|
let data_0 = Arc::new(RwLockServerState(RwLock::new(server)));
|
|
|
|
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 = data_0.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// At this point, the server is ready to start, and starts the necessary
|
|
|
|
// threads.
|
|
|
|
//
|
2021-03-22 14:47:56 -07:00
|
|
|
|
|
|
|
// Set ctrl+c to send a stop message
|
|
|
|
let running = Arc::new(AtomicBool::new(true));
|
|
|
|
let r = running.clone();
|
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
let client_secret = client_secret.clone();
|
|
|
|
System::new().block_on(async move {
|
|
|
|
send_stop(&client_secret).await;
|
|
|
|
});
|
|
|
|
r.store(false, Ordering::SeqCst);
|
|
|
|
})
|
|
|
|
.expect("Error setting Ctrl-C handler");
|
|
|
|
|
2021-03-25 19:58:07 -07:00
|
|
|
// Spawn ping task
|
2021-03-17 18:45:16 -07:00
|
|
|
spawn(async move {
|
|
|
|
let mut interval = time::interval(Duration::from_secs(90));
|
|
|
|
let mut data = Arc::clone(&data_0);
|
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
2021-03-22 20:19:56 -07:00
|
|
|
debug!("Sending ping!");
|
2021-03-25 18:06:54 -07:00
|
|
|
ping::update_server_state(&client_secret_1, &cli_args, &mut data).await;
|
2021-03-17 18:45:16 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-04-18 14:38:33 -07:00
|
|
|
let cache: Box<dyn Cache> = if low_mem_mode {
|
|
|
|
Box::new(LowMemCache::new(disk_quota, cache_path.clone()))
|
|
|
|
} else {
|
|
|
|
Box::new(GenerationalCache::new(
|
|
|
|
memory_max_size,
|
|
|
|
disk_quota,
|
|
|
|
cache_path.clone(),
|
|
|
|
))
|
|
|
|
};
|
|
|
|
let cache = Arc::new(Mutex::new(cache));
|
|
|
|
let cache1 = Arc::clone(&cache);
|
|
|
|
|
|
|
|
// Spawn periodic cache trimming
|
|
|
|
spawn(async move {
|
|
|
|
let mut interval = time::interval(Duration::from_secs(3 * 60));
|
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
cache.lock().prune().await;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-03-25 19:58:07 -07:00
|
|
|
// Start HTTPS server
|
2021-03-17 18:45:16 -07:00
|
|
|
HttpServer::new(move || {
|
|
|
|
App::new()
|
|
|
|
.service(routes::token_data)
|
2021-03-22 14:47:56 -07:00
|
|
|
.service(routes::token_data_saver)
|
|
|
|
.route("{tail:.*}", web::get().to(routes::default))
|
2021-03-17 18:45:16 -07:00
|
|
|
.app_data(Data::from(Arc::clone(&data_1)))
|
2021-04-18 14:38:33 -07:00
|
|
|
.app_data(Data::from(Arc::clone(&cache1)))
|
2021-03-17 18:45:16 -07:00
|
|
|
})
|
|
|
|
.shutdown_timeout(60)
|
|
|
|
.bind_rustls(format!("0.0.0.0:{}", port), tls_config)?
|
|
|
|
.run()
|
2021-03-22 14:47:56 -07:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
// Waiting for us to finish sending stop message
|
|
|
|
while running.load(Ordering::SeqCst) {
|
|
|
|
std::thread::sleep(Duration::from_millis(250));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2021-03-17 18:45:16 -07:00
|
|
|
}
|