diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/handler.rs | 6 | ||||
| -rw-r--r-- | src/response.rs | 199 | ||||
| -rw-r--r-- | src/router.rs | 72 |
3 files changed, 116 insertions, 161 deletions
diff --git a/src/handler.rs b/src/handler.rs index c5e23e8..bd64f03 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -17,15 +17,15 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ + response::Response, returnable, returnable::{CallbackContext, RouteContext}, - Response, }; pub type RouteResponse = - Box<dyn FnMut(RouteContext<'_>) -> Response<'_> + Send + Sync>; + Box<dyn FnMut(RouteContext<'_>) -> Response + Send + Sync>; pub type ErrorResponse = - Box<dyn FnMut(returnable::ErrorContext<'_>) -> Response<'_> + Send + Sync>; + Box<dyn FnMut(returnable::ErrorContext<'_>) -> Response + Send + Sync>; pub type Callback = Box<dyn FnMut(CallbackContext<'_>) + Send + Sync>; pub type CleanupCallback = Box<dyn FnMut(CallbackContext<'_>, &mut String) + Send + Sync>; diff --git a/src/response.rs b/src/response.rs index 806eebb..4ab513e 100644 --- a/src/response.rs +++ b/src/response.rs @@ -18,149 +18,110 @@ //! Content and response handlers +macro_rules! response { + ($name:ident, $status:expr) => { + pub fn $name<S>(content: S) -> Self + where S: AsRef<str> { + Self::new($status, content.as_ref()) + } + }; +} + /// The content and response type a handler should reply with. -pub enum Response<'a> { - Input(String), - SensitiveInput(String), - Success(String), - /// A successful response where the MIME type of the response is manually - /// specific by the user - SuccessWithMime(String, String), - #[cfg(feature = "auto-deduce-mime")] - /// A successful response where the MIME type of the response is - /// automatically deduced from the provided bytes - SuccessFileAuto(&'a [u8]), - SuccessFile(&'a [u8], String), - TemporaryRedirect(String), - PermanentRedirect(String), - TemporaryFailure(String), - ServerUnavailable(String), - CGIError(String), - ProxyError(String), - SlowDown(String), - PermanentFailure(String), - NotFound(String), - Gone(String), - ProxyRefused(String), - BadRequest(String), - ClientCertificateRequired(String), - CertificateNotAuthorised(String), - CertificateNotValid(String), +#[derive(Clone)] +pub struct Response { + pub status: i32, + pub mime: Option<String>, + pub content: String, + pub character_set: Option<String>, + pub language: Option<String>, } -pub(crate) fn to_value_set_status( - response: Response<'_>, - status: &mut i32, - mime: &mut String, -) -> String { - match response { - Response::Input(value) => { - *status = 10; +impl Response { + response!(input, 10); - value - } - Response::SensitiveInput(value) => { - *status = 11; + response!(sensitive_input, 11); - value - } - Response::Success(value) => { - *status = 20; + response!(temporary_redirect, 30); - value - } - Response::SuccessWithMime(value, value_mime) => { - *status = 23; - *mime = value_mime; + response!(permanent_redirect, 31); - value - } - Response::SuccessFile(value, value_mime) => { - *status = 21; // Internal status code, not real. - *mime = value_mime; + response!(temporary_failure, 40); - String::from_utf8(value.to_vec()).unwrap() - } - #[cfg(feature = "auto-deduce-mime")] - Response::SuccessFileAuto(value) => { - *status = 22; // Internal status code, not real. + response!(server_unavailable, 41); - String::from_utf8(value.to_vec()).unwrap() - } - Response::TemporaryRedirect(value) => { - *status = 30; + response!(cgi_error, 42); - value - } - Response::PermanentRedirect(value) => { - *status = 31; + response!(proxy_error, 43); - value - } - Response::TemporaryFailure(value) => { - *status = 40; + response!(slow_down, 44); - value - } - Response::ServerUnavailable(value) => { - *status = 41; + response!(permanent_failure, 50); - value - } - Response::CGIError(value) => { - *status = 42; + response!(not_found, 51); - value - } - Response::ProxyError(value) => { - *status = 43; + response!(gone, 52); - value - } - Response::SlowDown(value) => { - *status = 44; + response!(proxy_refused, 53); - value - } - Response::PermanentFailure(value) => { - *status = 50; + response!(bad_request, 59); - value - } - Response::NotFound(value) => { - *status = 51; + response!(client_certificate_required, 60); - value - } - Response::Gone(value) => { - *status = 52; + response!(certificate_not_authorised, 61); - value - } - Response::ProxyRefused(value) => { - *status = 53; + response!(certificate_not_valid, 62); - value - } - Response::BadRequest(value) => { - *status = 59; + pub fn success<S>(content: S) -> Self + where S: AsRef<str> { + Self::new(20, content.as_ref()) + .with_mime("text/gemini") + .with_language("en") + .with_character_set("utf-8") + .clone() + } - value - } - Response::ClientCertificateRequired(value) => { - *status = 60; + #[must_use] + pub fn binary_success(content: &[u8], mime: &str) -> Self { + Self::new(21, &String::from_utf8_lossy(content)) + .with_mime(mime) + .clone() + } - value - } - Response::CertificateNotAuthorised(value) => { - *status = 61; + #[cfg(feature = "auto-deduce-mime")] + #[must_use] + pub fn binary_success_auto(content: &[u8]) -> Self { + Self::new(22, &String::from_utf8_lossy(content)) + .with_mime(&tree_magic::from_u8(&*content)) + .clone() + } - value + #[must_use] + pub fn new(status: i32, content: &str) -> Self { + Self { + status, + mime: None, + content: content.to_string(), + character_set: None, + language: None, } - Response::CertificateNotValid(value) => { - *status = 62; + } - value - } + pub fn with_mime(&mut self, mime: &str) -> &mut Self { + self.mime = Some(mime.to_string()); + + self + } + + pub fn with_character_set(&mut self, character_set: &str) -> &mut Self { + self.character_set = Some(character_set.to_string()); + + self + } + + pub fn with_language(&mut self, language: &str) -> &mut Self { + self.language = Some(language.to_string()); + + self } } diff --git a/src/router.rs b/src/router.rs index a28eb67..e1e7a11 100644 --- a/src/router.rs +++ b/src/router.rs @@ -29,7 +29,7 @@ use url::Url; use crate::{ handler::{Callback, CleanupCallback, ErrorResponse, Partial, RouteResponse}, module::Module, - response::{to_value_set_status, Response}, + response::Response, returnable::{CallbackContext, ErrorContext, RouteContext}, }; @@ -271,8 +271,6 @@ impl Router { ) -> Result<(), Box<dyn Error>> { let mut buffer = [0u8; 1024]; let mut url = Url::parse("gemini://fuwn.me/")?; - let mut response_status = 0; - let mut response_mime_type = String::new(); let mut footer = String::new(); let mut header = String::new(); @@ -356,26 +354,19 @@ impl Router { }, )); } - to_value_set_status( - (*route.value).lock().unwrap()(RouteContext::new( - stream.get_ref(), - &url, - &route.params, - &stream.ssl().peer_certificate(), - )), - &mut response_status, - &mut response_mime_type, - ) + + (*route.value).lock().unwrap()(RouteContext::new( + stream.get_ref(), + &url, + &route.params, + &stream.ssl().peer_certificate(), + )) } else { - to_value_set_status( - (*self.error_handler).lock().unwrap()(ErrorContext::new( - stream.get_ref(), - &url, - &stream.ssl().peer_certificate(), - )), - &mut response_status, - &mut response_mime_type, - ) + (*self.error_handler).lock().unwrap()(ErrorContext::new( + stream.get_ref(), + &url, + &stream.ssl().peer_certificate(), + )) }; for module in &mut *self.modules.lock().unwrap() { @@ -394,36 +385,39 @@ impl Router { route.as_ref().map_or(None, |route| Some(&route.params)), &stream.ssl().peer_certificate(), ), - &mut content, + &mut content.content, ); stream .write_all( format!( "{}{}\r\n{}", - if response_status == 21 - || response_status == 22 - || response_status == 23 + if content.status == 21 + || content.status == 22 + || content.status == 23 { 20 } else { - response_status + content.status }, - match response_status { + match content.status { 20 => format!( - " text/gemini; charset={}; lang={}", - self.charset, self.language + " {}; charset={}; lang={}", + content.mime.unwrap_or_else(|| "text/gemini".to_string()), + content + .character_set + .unwrap_or_else(|| self.charset.clone()), + content.language.unwrap_or_else(|| self.language.clone()) ), - 21 => response_mime_type, + 21 => content.mime.unwrap_or_default(), #[cfg(feature = "auto-deduce-mime")] - 22 => format!(" {}", tree_magic::from_u8(&*content.as_bytes())), - 23 => response_mime_type, - _ => format!(" {content}"), + 22 => format!(" {}", content.mime.unwrap_or_default()), + _ => format!(" {}", content.content), }, - match response_status { - 20 => format!("{header}{content}\n{footer}"), - 21 | 22 | 23 => content.to_string(), + match content.status { + 20 => format!("{header}{}\n{footer}", content.content), + 21 | 22 => content.content, _ => String::new(), } ) @@ -749,8 +743,8 @@ impl Default for Router { Self { routes: matchit::Router::new(), error_handler: Arc::new(Mutex::new(Box::new(|_| { - Response::NotFound( - "This capsule has not implemented an error handler...".to_string(), + Response::not_found( + "This capsule has not implemented an error handler...", ) }))), private_key_file_name: String::new(), |