diff options
Diffstat (limited to 'kittybox-rs/src/lib.rs')
-rw-r--r-- | kittybox-rs/src/lib.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/kittybox-rs/src/lib.rs b/kittybox-rs/src/lib.rs new file mode 100644 index 0000000..1800b5b --- /dev/null +++ b/kittybox-rs/src/lib.rs @@ -0,0 +1,103 @@ +#![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<Extract = (Authority,), Error = warp::Rejection> + Copy { + warp::host::optional() + .and_then(|authority: Option<Authority>| async move { + authority.ok_or_else(|| warp::reject::custom(rejections::HostHeaderUnset)) + }) + } + + pub fn parse_accept() -> impl Filter<Extract = (http_types::Mime,), Error = warp::Rejection> + 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::<http_types::headers::HeaderName>().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()); + } + } +} |