#![allow(dead_code)]
use std::cmp;
use std::fs::File;
use std::io::Read;
use std::ops::Div;
use std::sync::Arc;
use std::time::Duration;
use crate::hashbrown::HashMap;
use crate::num_cpus;
use crate::parking_lot::RwLock;
use crate::support::common::*;
use native_tls::{Identity, TlsAcceptor};
lazy_static! {
static ref VIEW_ENGINES: RwLock<HashMap<String, Box<ViewEngine>>> = RwLock::new(HashMap::new());
static ref METADATA_STORE: RwLock<ConnMetadata> = RwLock::new(ConnMetadata::new());
}
pub struct ServerConfig {
pool_size: usize,
read_timeout: u16,
write_timeout: u16,
read_limit: usize,
tls_path: &'static str,
use_session_autoclean: bool,
session_auto_clean_period: Option<Duration>,
}
impl ServerConfig {
#[inline]
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn get_pool_size(&self) -> usize {
self.pool_size
}
#[inline]
pub fn set_pool_size(&mut self, size: usize) {
self.pool_size = size;
}
#[inline]
pub fn get_read_timeout(&self) -> u16 {
self.read_timeout
}
#[inline]
pub fn set_read_timeout(&mut self, timeout: u16) {
self.read_timeout = timeout;
}
#[inline]
pub fn get_write_timeout(&self) -> u16 {
self.write_timeout
}
#[inline]
pub fn set_write_timeout(&mut self, timeout: u16) {
self.write_timeout = timeout;
}
#[inline]
pub fn set_read_limit(&mut self, limit: usize) {
self.read_limit = limit;
}
#[inline]
pub fn get_read_limit(&self) -> usize {
self.read_limit
}
#[inline]
pub fn set_session_auto_clean(&mut self, auto_clean: bool) {
self.use_session_autoclean = auto_clean;
}
#[inline]
pub fn get_session_auto_clean(&self) -> bool {
self.use_session_autoclean
}
#[inline]
pub fn set_tls_path(&mut self, path: &'static str) {
self.tls_path = path;
}
#[inline]
pub fn tls_path(&self) -> &str {
self.tls_path
}
#[inline]
pub(crate) fn build_tls_acceptor(&mut self) -> Option<Arc<TlsAcceptor>> {
if self.tls_path.is_empty() {
return None;
}
let mut file = File::open(self.tls_path).unwrap();
let mut content = vec![];
file.read_to_end(&mut content).unwrap();
let identity = Identity::from_pkcs12(&content, "hunter2").unwrap();
let acceptor = Arc::new(TlsAcceptor::new(identity).unwrap());
self.tls_path = "";
Some(acceptor)
}
#[inline]
pub fn clear_session_auto_clean(&mut self) {
self.session_auto_clean_period = None;
}
pub fn get_session_auto_clean_period(&self) -> Option<Duration> {
self.session_auto_clean_period
}
#[inline]
pub fn set_session_auto_clean_period(&mut self, auto_clean_sec: Duration) {
self.session_auto_clean_period = Some(auto_clean_sec);
}
pub fn use_default_header(header: HashMap<String, String>) {
let mut store = METADATA_STORE.write();
(*store).header = header;
}
pub fn set_default_header(field: String, value: String, replace: bool) {
let mut store = METADATA_STORE.write();
(*store).header.add(&field[..], value, replace, false);
}
pub fn set_status_page_generator(status: u16, generator: PageGenerator) {
if status > 0 {
let mut store = METADATA_STORE.write();
(*store).status_page_generators.insert(status, generator);
}
}
pub(crate) fn load_server_params(&self) -> (u64, u64, usize) {
(
u64::from(self.get_read_timeout()),
u64::from(self.get_write_timeout()),
self.get_read_limit().div(512),
)
}
}
impl Default for ServerConfig {
fn default() -> Self {
let path = option_env!("TLS_PATH").unwrap_or("");
ServerConfig {
pool_size: cmp::max(4 * num_cpus::get(), 8),
read_timeout: 512,
write_timeout: 0,
read_limit: 0,
tls_path: path,
use_session_autoclean: false,
session_auto_clean_period: Some(Duration::from_secs(3600)),
}
}
}
pub type ViewEngine = fn(&mut String, Box<dyn EngineContext + Send + Sync>) -> u16;
pub trait EngineContext {
fn display(&self, field: &str) -> Result<String, String>;
}
pub trait ViewEngineDefinition {
fn view_engine(extension: &str, engine: ViewEngine);
}
impl ViewEngineDefinition for ServerConfig {
fn view_engine(extension: &str, engine: ViewEngine) {
if extension.is_empty() {
return;
}
let mut engines = VIEW_ENGINES.write();
(*engines).insert(extension.to_owned(), Box::new(engine));
}
}
pub trait ViewEngineParser {
fn template_parser<T: EngineContext + Send + Sync + 'static>(
extension: &str,
source: Vec<u8>,
context: Box<T>,
) -> (u16, Vec<u8>);
}
impl ViewEngineParser for ServerConfig {
fn template_parser<T: EngineContext + Send + Sync + 'static>(
extension: &str,
source: Vec<u8>,
context: Box<T>,
) -> (u16, Vec<u8>) {
if extension.is_empty() {
return (0, Vec::new());
}
match String::from_utf8(source) {
Ok(mut s) => {
let template_engines = VIEW_ENGINES.read();
if let Some(engine) = template_engines.get(extension) {
let code = engine(&mut s, context);
return (code, Vec::from(s.as_bytes()));
}
(404, Vec::new())
}
Err(err) => {
(404, Vec::new())
}
}
}
}
pub type PageGenerator = fn() -> String;
pub struct ConnMetadata {
header: HashMap<String, String>,
status_page_generators: HashMap<u16, PageGenerator>,
}
impl ConnMetadata {
pub fn new() -> Self {
ConnMetadata {
header: HashMap::new(),
status_page_generators: HashMap::new(),
}
}
#[inline]
pub fn get_default_header() -> Option<HashMap<String, String>> {
let store = METADATA_STORE.read();
if !store.header.is_empty() {
return Some(store.header.clone());
}
None
}
#[inline]
pub(crate) fn get_status_pages(status: u16) -> Option<PageGenerator> {
let store = METADATA_STORE.read();
if store.status_page_generators.is_empty() {
return None;
}
store.status_page_generators.get(&status).cloned()
}
}