about summary refs log tree commit diff
path: root/kittybox-rs/src/lib.rs
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2022-05-24 17:18:30 +0300
committerVika <vika@fireburn.ru>2022-05-24 17:18:30 +0300
commit5610a5f0bf1a9df02bd3d5b55e2cdebef2440360 (patch)
tree8394bcf1dcc204043d7adeb8dde2e2746977606e /kittybox-rs/src/lib.rs
parent2f93873122b47e42f7ee1c38f1f04d052a63599c (diff)
flake.nix: reorganize
 - Kittybox's source code is moved to a subfolder
   - This improves build caching by Nix since it doesn't take changes
     to other files into account
 - Package and test definitions were spun into separate files
   - This makes my flake.nix much easier to navigate
   - This also makes it somewhat possible to use without flakes (but
     it is still not easy, so use flakes!)
 - Some attributes were moved in compliance with Nix 2.8's changes to
   flake schema
Diffstat (limited to 'kittybox-rs/src/lib.rs')
-rw-r--r--kittybox-rs/src/lib.rs103
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());
+        }
+    }
+}