use crate::constants::closures_lock;
use crate::default;
use colored::*;
use core::time::Duration;
use std::ffi::OsStr;
use std::path::PathBuf;
#[cfg(feature = "rustls")]
use tokio_rustls::rustls::{
internal::pemfile::{certs, rsa_private_keys},
Certificate, PrivateKey,
};
#[derive(Clone)]
pub struct Ssl {
pub key: PathBuf,
pub cert: PathBuf,
pub port: u16,
}
impl Ssl {
pub fn new() -> Self {
Ssl {
key: PathBuf::new(),
cert: PathBuf::new(),
port: 443,
}
}
pub fn key(&mut self, path: &str) -> &mut Self {
self.key = PathBuf::from(path);
self
}
pub fn cert(&mut self, path: &str) -> &mut Self {
self.cert = PathBuf::from(path);
self
}
pub fn validate(&self) {
let key_ext = self
.key
.as_path()
.extension()
.and_then(OsStr::to_str)
.unwrap_or("");
let cert_ext = self
.cert
.as_path()
.extension()
.and_then(OsStr::to_str)
.unwrap_or("");
if key_ext != "pem" && cert_ext != "pem" {
panic!("Invalid key/cert file, {:?}", "bad extension")
}
}
}
pub struct OctaneConfig {
pub keep_alive: Option<Duration>,
pub ssl: Ssl,
pub file_404: Option<PathBuf>,
pub worker_threads: Option<usize>,
}
pub trait Config {
fn set_keepalive(&mut self, duration: Duration);
fn set_404_file(&mut self, dir_name: &'static str);
fn with_ssl_config(&mut self, ssl_conf: Ssl);
fn ssl(&mut self, port: u16) -> &mut Ssl;
}
impl OctaneConfig {
pub fn new() -> Self {
OctaneConfig {
ssl: Ssl::new(),
keep_alive: Some(Duration::from_secs(5)),
worker_threads: None,
file_404: None,
}
}
pub fn append(&mut self, settings: Self) {
self.ssl = settings.ssl;
self.keep_alive = settings.keep_alive;
}
pub fn worker_threads(&mut self, threads: usize) -> &mut Self {
self.worker_threads = Some(threads);
self
}
#[cfg(feature = "rustls")]
pub fn get_cert(&self) -> std::io::Result<Vec<Certificate>> {
self.ssl.validate();
let mut buf = std::io::BufReader::new(std::fs::File::open(&self.ssl.cert)?);
certs(&mut buf)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid Certs"))
}
#[cfg(feature = "rustls")]
pub fn get_key(&self) -> std::io::Result<Vec<PrivateKey>> {
self.ssl.validate();
let mut buf = std::io::BufReader::new(std::fs::File::open(&self.ssl.key)?);
rsa_private_keys(&mut buf)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid Key"))
}
pub fn startup_string(&self, ssl: bool, port: u16) -> String {
let mut final_string = String::new();
final_string.push_str(
format!(
"\n\r{} {}\n\r\n{}\n\n",
"Starting".bold().blue(),
"Octane".green().bold(),
"Configurations".red().bold()
)
.as_str(),
);
if let Some(x) = self.keep_alive {
final_string.push_str(
format!(
"{}: {}s\n",
"-> Keep-alive".blue(),
x.as_secs_f64().to_string().green(),
)
.as_str(),
);
} else {
final_string.push_str(
format!("{}: {}\n", "-> Keep-alive".blue(), "Disabled".green(),).as_str(),
);
}
if let Some(x) = self.worker_threads {
final_string.push_str(
format!(
"{}: {}\n",
"-> Worker-threads".blue(),
x.to_string().green(),
)
.as_str(),
);
} else {
final_string.push_str(
format!(
"{}: {}\n",
"-> Worker-threads".blue(),
"Number of cores available in the CPU".green(),
)
.as_str(),
);
}
if ssl {
final_string.push_str(
format!(
"{}: {} {}\n",
"-> TLS".blue(),
"enabled at".green(),
self.ssl.port.to_string().red().bold()
)
.as_str(),
);
} else {
final_string.push_str(format!("{}: {} \n", "TLS".red(), "disabled".green()).as_str());
}
closures_lock(|map| {
final_string.push_str(
format!(
"{}: {} paths\n",
"-> Serving".blue(),
map.len().to_string().red().bold()
)
.as_str(),
);
});
final_string.push_str(
format!(
"\n{} at {}:{}\n",
"Listening".red(),
"localhost".blue(),
port.to_string().red().bold()
)
.as_str(),
);
final_string
}
}
default!(OctaneConfig);
default!(Ssl);
impl Config for OctaneConfig {
fn set_keepalive(&mut self, duration: Duration) {
self.keep_alive = Some(duration);
}
fn set_404_file(&mut self, dir_name: &'static str) {
self.file_404 = Some(PathBuf::from(dir_name));
}
fn with_ssl_config(&mut self, ssl_conf: Ssl) {
self.ssl.key = ssl_conf.key;
self.ssl.cert = ssl_conf.cert;
}
fn ssl(&mut self, port: u16) -> &mut Ssl {
self.ssl.port = port;
&mut self.ssl
}
}