2020-12-02 04:32:03 +00:00
|
|
|
use tokio::io::AsyncRead;
|
|
|
|
#[cfg(feature="scgi_srv")]
|
|
|
|
use tokio::io::AsyncReadExt;
|
2020-11-19 15:21:32 +00:00
|
|
|
#[cfg(feature="serve_dir")]
|
|
|
|
use tokio::fs::File;
|
2020-11-14 03:43:29 +00:00
|
|
|
|
2020-11-20 14:40:57 +00:00
|
|
|
use std::borrow::Borrow;
|
|
|
|
|
2020-11-14 08:55:21 +00:00
|
|
|
use crate::types::Document;
|
|
|
|
|
2020-12-05 19:09:16 +00:00
|
|
|
/// The body of a response
|
|
|
|
///
|
|
|
|
/// The content of a successful response to be sent back to the user. This can be either
|
|
|
|
/// some bytes which will be sent directly to the user, or a reader which will be read at
|
|
|
|
/// some point before sending to the user.
|
2020-11-14 03:43:29 +00:00
|
|
|
pub enum Body {
|
2020-12-05 19:09:16 +00:00
|
|
|
/// In-memory bytes that may be sent back to the user
|
2020-11-14 03:43:29 +00:00
|
|
|
Bytes(Vec<u8>),
|
2020-12-05 19:09:16 +00:00
|
|
|
|
|
|
|
/// A reader which will be streamed to the user
|
|
|
|
///
|
|
|
|
/// If a reader blocks for too long, it MAY be killed before finishing, which results
|
|
|
|
/// in the user receiving a malformed response or timing out.
|
2020-11-14 03:43:29 +00:00
|
|
|
Reader(Box<dyn AsyncRead + Send + Sync + Unpin>),
|
|
|
|
}
|
|
|
|
|
2020-12-02 02:41:00 +00:00
|
|
|
impl Body {
|
|
|
|
/// Called by [`Response::rewrite_all`]
|
2020-12-02 04:32:03 +00:00
|
|
|
#[cfg(feature="scgi_srv")]
|
2020-12-02 02:41:00 +00:00
|
|
|
pub (crate) async fn rewrite_all(&mut self, based_on: &crate::Request) -> std::io::Result<bool> {
|
|
|
|
let bytes = match self {
|
|
|
|
Self::Bytes(bytes) => {
|
|
|
|
let mut newbytes = Vec::new();
|
|
|
|
std::mem::swap(bytes, &mut newbytes);
|
|
|
|
newbytes
|
|
|
|
}
|
|
|
|
Self::Reader(reader) => {
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
reader.read_to_end(&mut bytes).await?;
|
|
|
|
bytes
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut body = String::from_utf8(bytes).expect("text/gemini wasn't UTF8");
|
|
|
|
|
|
|
|
let mut maybe_indx = if body.starts_with("=> ") {
|
|
|
|
Some(3)
|
|
|
|
} else {
|
|
|
|
body.find("\n=> ").map(|offset| offset + 4)
|
|
|
|
};
|
|
|
|
while let Some(indx) = maybe_indx {
|
|
|
|
|
|
|
|
// Find the end of the link part
|
|
|
|
let end = (&body[indx..]).find(&[' ', '\n', '\r'][..])
|
|
|
|
.map(|offset| indx + offset )
|
|
|
|
.unwrap_or(body.len());
|
|
|
|
|
|
|
|
// Perform replacement
|
|
|
|
if let Some(replacement) = based_on.rewrite_path(&body[indx..end]) {
|
|
|
|
body.replace_range(indx..end, replacement.as_str());
|
|
|
|
} else {
|
|
|
|
return Ok(false)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Find next match
|
|
|
|
maybe_indx = (&body[indx..]).find("\n=> ").map(|offset| offset + 4 + indx);
|
|
|
|
}
|
|
|
|
|
|
|
|
*self = Self::Bytes(body.into_bytes());
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-20 14:40:57 +00:00
|
|
|
impl<D: Borrow<Document>> From<D> for Body {
|
|
|
|
fn from(document: D) -> Self {
|
|
|
|
Self::from(document.borrow().to_string())
|
2020-11-14 08:55:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-14 03:43:29 +00:00
|
|
|
impl From<Vec<u8>> for Body {
|
|
|
|
fn from(bytes: Vec<u8>) -> Self {
|
|
|
|
Self::Bytes(bytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a [u8]> for Body {
|
|
|
|
fn from(bytes: &[u8]) -> Self {
|
|
|
|
Self::Bytes(bytes.to_owned())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<String> for Body {
|
|
|
|
fn from(text: String) -> Self {
|
|
|
|
Self::Bytes(text.into_bytes())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a str> for Body {
|
|
|
|
fn from(text: &str) -> Self {
|
|
|
|
Self::Bytes(text.to_owned().into_bytes())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-19 15:21:32 +00:00
|
|
|
#[cfg(feature="serve_dir")]
|
2020-11-14 03:43:29 +00:00
|
|
|
impl From<File> for Body {
|
|
|
|
fn from(file: File) -> Self {
|
|
|
|
Self::Reader(Box::new(file))
|
|
|
|
}
|
|
|
|
}
|