#![forbid(unsafe_code)] #![warn(clippy::todo)] pub mod metrics; /// Database abstraction layer for Kittybox, allowing the CMS to work with any kind of database. pub mod database; pub mod micropub; pub mod media; pub mod indieauth; pub mod frontend; pub(crate) mod rejections { #[derive(Debug)] pub(crate) struct UnacceptableContentType; impl warp::reject::Reject for UnacceptableContentType {} #[derive(Debug)] pub(crate) struct HostHeaderUnset; impl warp::reject::Reject for HostHeaderUnset {} } pub static MICROPUB_CLIENT: &[u8] = include_bytes!("./index.html"); pub mod util { use warp::{Filter, host::Authority}; use super::rejections; pub fn require_host() -> impl Filter + Copy { warp::host::optional() .and_then(|authority: Option| async move { authority.ok_or_else(|| warp::reject::custom(rejections::HostHeaderUnset)) }) } pub fn parse_accept() -> impl Filter + Copy { warp::header::value("Accept").and_then(|accept: warp::http::HeaderValue| async move { let mut accept: http_types::content::Accept = { // This is unneccesarily complicated because I want to reuse some http-types parsing // and http-types has constructor for Headers private so I need to construct // a mock Request to reason about headers... this is so dumb wtf // so much for zero-cost abstractions, huh let bytes: &[u8] = accept.as_bytes(); let value = http_types::headers::HeaderValue::from_bytes(bytes.to_vec()).unwrap(); let values: http_types::headers::HeaderValues = vec![value].into(); let mut request = http_types::Request::new(http_types::Method::Get, "http://example.com/"); request.append_header("Accept".parse::().unwrap(), &values); http_types::content::Accept::from_headers(&request).unwrap().unwrap() }; // This code is INCREDIBLY dumb, honestly... // why did I even try to use it? // TODO vendor this stuff in so I can customize it match accept.negotiate(&[ "text/html; encoding=\"utf-8\"".into(), "application/json; encoding=\"utf-8\"".into(), "text/html".into(), "application/json".into(), ]) { Ok(mime) => { Ok(http_types::Mime::from(mime.value().as_str())) }, Err(err) => { log::error!("Content-Type negotiation error: {:?}, accepting: {:?}", err, accept); Err(warp::reject::custom(rejections::UnacceptableContentType)) } } }) } mod tests { #[tokio::test] async fn test_require_host_with_host() { use super::require_host; let filter = require_host(); let res = warp::test::request() .path("/") .header("Host", "localhost:8080") .filter(&filter) .await .unwrap(); assert_eq!(res, "localhost:8080"); } #[tokio::test] async fn test_require_host_no_host() { use super::require_host; let filter = require_host(); let res = warp::test::request() .path("/") .filter(&filter) .await; assert!(res.is_err()); } } }