#![deny(clippy::all)]
use kvarn::{extensions::*, prelude::*};
use wrappers::*;
#[cfg(feature = "reverse-proxy")]
#[path = "reverse-proxy.rs"]
pub mod reverse_proxy;
#[cfg(feature = "reverse-proxy")]
pub use reverse_proxy::{
localhost, static_connection, Connection as ReverseProxyConnection, Manager as ReverseProxy,
};
#[cfg(feature = "push")]
pub mod push;
#[cfg(feature = "push")]
pub use push::mount as mount_push;
#[cfg(feature = "kvarn-fastcgi-client")]
pub mod fastcgi;
#[cfg(feature = "php")]
pub mod php;
#[cfg(feature = "php")]
pub use php::mount_php as php;
#[cfg(feature = "templates")]
pub mod templates;
#[cfg(feature = "templates")]
pub use templates::templates;
pub fn new() -> Extensions {
let mut e = Extensions::new();
mount_all(&mut e);
e
}
pub fn mount_all(extensions: &mut Extensions) {
extensions.add_present_internal("download".to_string(), Box::new(download));
extensions.add_present_internal("cache".to_string(), Box::new(cache));
extensions.add_present_internal("hide".to_string(), Box::new(hide));
extensions.add_present_file("private".to_string(), Box::new(hide));
extensions.add_present_internal("allow-ips".to_string(), Box::new(ip_allow));
#[cfg(feature = "php")]
php(extensions);
#[cfg(feature = "templates")]
extensions.add_present_internal("tmpl".to_string(), Box::new(templates));
#[cfg(feature = "push")]
push::mount(extensions);
}
#[allow(dead_code)]
pub mod parse {
use super::*;
pub fn format_file_name<P: AsRef<Path>>(path: &P) -> Option<&str> {
path.as_ref().file_name().and_then(std::ffi::OsStr::to_str)
}
pub fn format_file_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf, io::Error> {
let mut file_path = std::env::current_dir()?;
file_path.push(path);
Ok(file_path)
}
}
pub fn download(mut data: PresentDataWrapper) -> RetFut<()> {
let data = unsafe { data.get_inner() };
let headers = data.response_mut().headers_mut();
utils::replace_header_static(headers, "content-type", "application/octet-stream");
ready(())
}
pub fn cache(mut data: PresentDataWrapper) -> RetFut<()> {
fn parse<'a, I: Iterator<Item = &'a str>>(
iter: I,
) -> (Option<comprash::ClientCachePreference>, Option<comprash::ServerCachePreference>) {
let mut c = None;
let mut s = None;
for arg in iter {
let mut parts = arg.split(':');
let domain = parts.next();
let cache = parts.next();
if let (Some(domain), Some(cache)) = (domain, cache) {
match domain {
"client" => {
if let Ok(preference) = cache.parse() {
c = Some(preference)
}
}
"server" => {
if let Ok(preference) = cache.parse() {
s = Some(preference)
}
}
_ => {}
}
}
}
(c, s)
}
let data = unsafe { data.get_inner() };
let preference = parse(data.args().iter());
if let Some(c) = preference.0 {
*data.client_cache_preference() = c;
}
if let Some(s) = preference.1 {
*data.server_cache_preference() = s;
}
ready(())
}
pub fn hide(mut data: PresentDataWrapper) -> RetFut<()> {
box_fut!({
let data = unsafe { data.get_inner() };
let error = default_error(StatusCode::NOT_FOUND, Some(data.host()), None).await;
*data.response_mut() = error;
})
}
pub fn ip_allow(mut data: PresentDataWrapper) -> RetFut<()> {
box_fut!({
let data = unsafe { data.get_inner() };
let mut matched = false;
for denied in data.args().iter() {
if let Ok(ip) = denied.parse::<IpAddr>() {
if data.address().ip() == ip {
matched = true;
break;
}
}
}
*data.server_cache_preference() = comprash::ServerCachePreference::None;
*data.client_cache_preference() = comprash::ClientCachePreference::Changing;
if !matched {
let error = default_error(StatusCode::NOT_FOUND, Some(data.host()), None).await;
*data.response_mut() = error;
}
})
}
pub fn force_cache(
extensions: &mut Extensions,
rules: &'static [(&'static str, comprash::ClientCachePreference)],
) {
extensions.add_package(package!(response, req, _host {
let extension = req.uri().path().split('.').last();
let path = req.uri().path();
if let Some(extension) = extension {
for (rule, preference) in rules {
let replace = (rule.starts_with('/') && path.starts_with(rule))
|| rule.strip_prefix('.').map_or(false, |ext| ext == extension)
|| rule.strip_prefix('*').and_then(|rule| rule.strip_suffix('*')).map_or(false, |rule| path.contains(rule));
if replace {
utils::replace_header(response.headers_mut(), "cache-control", preference.as_header());
}
}
}
}), extensions::Id::new(16, "Adding cache-control header (force-cache)"));
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn all() {
let extensions = new();
let _server = kvarn_testing::ServerBuilder::from(extensions).run().await;
}
}