217 lines
6.9 KiB
Rust
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)
|
|
}
|
|
}
|