kochab/src/types/response.rs

217 lines
6.9 KiB
Rust

use std::borrow::Borrow;
use crate::types::{Body, Document};
pub struct Response {
pub status: u8,
pub meta: String,
pub body: Option<Body>,
}
impl Response {
/// Create a response with a given status and meta
pub fn new(status: u8, meta: impl ToString) -> Self {
Self {
status,
meta: meta.to_string(),
body: None,
}
}
/// Create a INPUT (10) response with a given prompt
///
/// Use [`Response::sensitive_input()`] for collecting any sensitive input, as input
/// collected by this request may be logged.
pub fn input(prompt: impl ToString) -> Self {
Self::new(10, prompt)
}
/// Create a SENSITIVE INPUT (11) response with a given prompt
///
/// See also [`Response::input()`] for unsensitive inputs
pub fn sensitive_input(prompt: impl ToString) -> Self {
Self::new(11, prompt)
}
/// Create a SUCCESS (20) response with a given body and MIME
pub fn success(mime: impl ToString, body: impl Into<Body>) -> Self {
Self {
status: 20,
meta: mime.to_string(),
body: Some(body.into()),
}
}
/// Create a SUCCESS (20) response with a `text/gemini` MIME
pub fn success_gemini(body: impl Into<Body>) -> Self {
Self::success("text/gemini", body)
}
/// Create a SUCCESS (20) response with a `text/plain` MIME
pub fn success_plain(body: impl Into<Body>) -> Self {
Self::success("text/plain", body)
}
/// Create a REDIRECT - TEMPORARY (30) response with a destination
pub fn redirect_temporary(dest: impl ToString) -> Self {
Self::new(30, dest)
}
/// Create a REDIRECT - PERMANENT (31) response with a destination
pub fn redirect_permanent(dest: impl ToString) -> Self {
Self::new(31, dest)
}
/// Create a TEMPORARY FAILURE (40) response with a human readable error
pub fn temporary_failure(reason: impl ToString) -> Self {
Self::new(40, reason)
}
/// Create a SERVER UNAVAILABLE (41) response with a human readable error
///
/// Used to denote that the server is temporarily unavailable, for example due to
/// heavy load, or maintenance
pub fn server_unavailable(reason: impl ToString) -> Self {
Self::new(41, reason)
}
/// Create a CGI ERROR (42) response with a human readable error
pub fn cgi_error(reason: impl ToString) -> Self {
Self::new(42, reason)
}
/// Create a PROXY ERROR (43) response with a human readable error
pub fn proxy_error(reason: impl ToString) -> Self {
Self::new(43, reason)
}
/// Create a SLOW DOWN (44) response with a wait time in seconds
///
/// Used to denote that the user should wait a certain number of seconds before
/// sending another request, often for ratelimiting purposes
pub fn slow_down(wait: u64) -> Self {
Self::new(44, wait)
}
/// Create a PERMANENT FAILURE (50) response with a human readable error
pub fn permanent_failure(reason: impl ToString) -> Self {
Self::new(50, reason)
}
/// Create a NOT FOUND (51) response with no further information
///
/// Essentially a 404
pub fn not_found() -> Self {
Self::new(51, String::new())
}
/// Create a GONE (52) response with a human readable error
///
/// For when a resource used to be here, but never will be again
pub fn gone(reason: impl ToString) -> Self {
Self::new(52, reason)
}
/// Create a PROXY REQUEST REFUSED (53) response with a human readable error
///
/// The server does not serve content on this domain
pub fn proxy_request_refused(reason: impl ToString) -> Self {
Self::new(53, reason)
}
/// Create a BAD REQUEST (59) response with a human readable error
pub fn bad_request(reason: impl ToString) -> Self {
Self::new(59, reason)
}
/// Create a CLIENT CERTIFICATE REQUIRED (60) response with a human readable error
pub fn client_certificate_required(reason: impl ToString) -> Self {
Self::new(60, reason)
}
/// Create a CERTIFICATE NOT AUTHORIZED (61) response with a human readable error
pub fn certificate_not_authorized(reason: impl ToString) -> Self {
Self::new(61, reason)
}
/// Create a CERTIFICATE NOT VALID (62) response with a human readable error
pub fn certificate_not_valid(reason: impl ToString) -> Self {
Self::new(62, reason)
}
/// True if the response is a SUCCESS (10) response
pub const fn is_success(&self) -> bool {
self.status == 10
}
#[cfg_attr(feature="gemini_srv",allow(unused_variables))]
/// Rewrite any links in this response based on the path identified by a request
///
/// Currently, this rewrites any links in:
/// * SUCCESS (10) requests with a `text/gemini` MIME
/// * REDIRECT (3X) requests
///
/// For all other responses, and for any responses without links, this method has no
/// effect.
///
/// If this response contains a reader-based body, this **MAY** load the reader's
/// contents into memory if the mime is "text/gemini". If an IO error occurs during
/// this process, this error will be raised
///
/// If the request does not contain enough information to rewrite a link (in other
/// words, if [`Request::rewrite_path()`] returns [`None`]), then false is returned.
/// In all other cases, this method returns true.
///
/// Panics if a "text/gemini" response is not UTF-8 formatted
///
/// For an overview of methods for rewriting links, see
/// [`Server::set_autorewrite()`].\
/// For more information about how rewritten paths are calculated, see
/// [`Request::rewrite_path()`].
///
/// [`Server::set_autorewrite()`]: crate::Server::set_autorewrite()
/// [`Request::rewrite_path()`]: crate::Server::set_autorewrite()
pub async fn rewrite_all(&mut self, based_on: &crate::Request) -> std::io::Result<bool> {
#[cfg(feature = "scgi_srv")]
match self.status {
20 if self.meta == "text/gemini" => {
if let Some(body) = self.body.as_mut() {
body.rewrite_all(based_on).await
} else {
Ok(false)
}
},
30 | 31 => {
if let Some(path) = based_on.rewrite_path(&self.meta) {
self.meta = path;
Ok(true)
} else {
Ok(false)
}
},
_ => Ok(true),
}
#[cfg(feature = "gemini_srv")]
Ok(true)
}
}
impl AsRef<Option<Body>> for Response {
fn as_ref(&self) -> &Option<Body> {
&self.body
}
}
impl AsMut<Option<Body>> for Response {
fn as_mut(&mut self) -> &mut Option<Body> {
&mut self.body
}
}
impl<D: Borrow<Document>> From<D> for Response {
fn from(doc: D) -> Self {
Self::success_gemini(doc)
}
}