diff --git a/examples/document.rs b/examples/document.rs index 9d4bdc2..5ea2678 100644 --- a/examples/document.rs +++ b/examples/document.rs @@ -1,6 +1,6 @@ use anyhow::*; use log::LevelFilter; -use northstar::{Server, Request, Response, GEMINI_PORT, Document}; +use northstar::{Server, Response, GEMINI_PORT, Document}; use northstar::document::HeadingLevel::*; #[tokio::main] @@ -9,14 +9,7 @@ async fn main() -> Result<()> { .filter_module("northstar", LevelFilter::Debug) .init(); - Server::bind(("localhost", GEMINI_PORT)) - .add_route("/",handle_request) - .serve() - .await -} - -async fn handle_request(_request: Request) -> Result { - let response = Document::new() + let response: Response = Document::new() .add_preformatted(include_str!("northstar_logo.txt")) .add_blank_line() .add_link("https://docs.rs/northstar", "Documentation") @@ -41,5 +34,9 @@ async fn handle_request(_request: Request) -> Result { "openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365", )) .into(); - Ok(response) + + Server::bind(("localhost", GEMINI_PORT)) + .add_route("/", response) + .serve() + .await } diff --git a/src/handling.rs b/src/handling.rs index 244efa0..b8bb811 100644 --- a/src/handling.rs +++ b/src/handling.rs @@ -15,7 +15,7 @@ use std::{ panic::{catch_unwind, AssertUnwindSafe}, }; -use crate::types::{Response, Request}; +use crate::{Document, types::{Body, Response, Request}}; /// A struct representing something capable of handling a request. /// @@ -29,6 +29,7 @@ use crate::types::{Response, Request}; /// response is produced. pub enum Handler { FnHandler(HandlerInner), + StaticHandler(Response), } /// Since we can't store train objects, we need to wrap fn handlers in a box @@ -57,6 +58,26 @@ impl Handler { Response::server_error("").unwrap() }) }, + Self::StaticHandler(response) => { + let body = response.as_ref(); + match body { + None => Response::new(response.header().clone()), + Some(Body::Bytes(bytes)) => { + Response::new(response.header().clone()) + .with_body(bytes.clone()) + }, + _ => { + error!(concat!( + "Cannot construct a static handler with a reader-based body! ", + " We're sending a response so that the client doesn't crash, but", + " given that this is a release build you should really fix this." + )); + Response::server_error( + "Very bad server error, go tell the sysadmin to look at the logs." + ).unwrap() + } + } + } } } } @@ -81,6 +102,44 @@ where } } +// We tolerate a fallible `impl From` because this is *really* not the kind of thing the +// user should be catching in runtime. +#[allow(clippy::fallible_impl_from)] +impl From for Handler { + /// Serve an unchanging response + /// + /// Any and all requests to this handler will be responded to with the same response, + /// no matter what. This is good for static content that is provided by your app. + /// For serving files & directories, try looking at creating a handler from a path + /// + /// ## Panics + /// This response type **CANNOT** be created using Responses with [`Reader`] bodies. + /// Attempting to do this will cause a panic. Don't. + /// + /// [`Reader`]: Body::Reader + fn from(response: Response) -> Self { + #[cfg(debug_assertions)] { + // We have another check once the handler is actually called that is not + // disabled for release builds + if let Some(Body::Reader(_)) = response.as_ref() { + panic!("Cannot construct a static handler with a reader-based body"); + } + } + Self::StaticHandler(response) + } +} + +impl From<&Document> for Handler { + /// Serve an unchanging response, shorthand for From + /// + /// This document will be sent in response to any requests that arrive at this + /// handler. As with all documents, this will be a successful response with a + /// `text/gemini` MIME. + fn from(doc: &Document) -> Self { + Self::StaticHandler(doc.into()) + } +} + /// A utility for catching unwinds on Futures. /// /// This is adapted from the futures-rs CatchUnwind, in an effort to reduce the large diff --git a/src/types/response.rs b/src/types/response.rs index dceec4e..3964ae1 100644 --- a/src/types/response.rs +++ b/src/types/response.rs @@ -96,6 +96,18 @@ impl Response { } } +impl AsRef> for Response { + fn as_ref(&self) -> &Option { + &self.body + } +} + +impl AsMut> for Response { + fn as_mut(&mut self) -> &mut Option { + &mut self.body + } +} + impl> From for Response { fn from(doc: D) -> Self { Self::document(doc) diff --git a/src/util.rs b/src/util.rs index 945e2f7..1fe5591 100644 --- a/src/util.rs +++ b/src/util.rs @@ -10,6 +10,7 @@ use tokio::{ }; #[cfg(feature="serve_dir")] use crate::types::{Document, document::HeadingLevel::*}; +#[cfg(feature="serve_dir")] use crate::types::Response; use std::future::Future; use tokio::time;