From 3fb864b1111b47cc4d72893d09b74af4c264aa52 Mon Sep 17 00:00:00 2001 From: Ririsoft Date: Wed, 26 Aug 2020 09:19:02 +0200 Subject: [PATCH 1/2] split utils module into a folder This will allow to separate non related utilities in the future. --- src/{utils.rs => utils/middleware.rs} | 4 ++-- src/utils/mod.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) rename src/{utils.rs => utils/middleware.rs} (96%) create mode 100644 src/utils/mod.rs diff --git a/src/utils.rs b/src/utils/middleware.rs similarity index 96% rename from src/utils.rs rename to src/utils/middleware.rs index fde11b3ea..f7a1731fd 100644 --- a/src/utils.rs +++ b/src/utils/middleware.rs @@ -1,7 +1,7 @@ -//! Miscellaneous utilities. +//! Miscellaneous middleware utilities. +use crate::utils::async_trait; use crate::{Middleware, Next, Request, Response}; -pub use async_trait::async_trait; use std::future::Future; /// Define a middleware that operates on incoming requests. diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 000000000..c5665897d --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,6 @@ +//! Miscellaneous utilities. + +mod middleware; + +pub use async_trait::async_trait; +pub use middleware::{After, Before}; From 95585a1f805f1059298af355419f6c79537f17cd Mon Sep 17 00:00:00 2001 From: Ririsoft Date: Wed, 26 Aug 2020 10:07:31 +0200 Subject: [PATCH 2/2] add serve_content utilities Allows to serve content handling HTTP conditional requests as defined in RFC7232. --- src/utils/mod.rs | 2 + src/utils/serve_content.rs | 98 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/utils/serve_content.rs diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c5665897d..ec6098d03 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,6 +1,8 @@ //! Miscellaneous utilities. mod middleware; +mod serve_content; pub use async_trait::async_trait; pub use middleware::{After, Before}; +pub use serve_content::{serve_content, serve_content_with, ModState}; diff --git a/src/utils/serve_content.rs b/src/utils/serve_content.rs new file mode 100644 index 000000000..9f35e3a8e --- /dev/null +++ b/src/utils/serve_content.rs @@ -0,0 +1,98 @@ +//! Content serving utilities with support for conditional requests +//! as defined in [RFC 7232](https://tools.ietf.org/html/rfc7232) + +use async_std::io::{BufRead as AsyncBufRead, Read as AsyncRead, Seek as AsyncSeek}; + +use crate::{Request, Response, Result, StatusCode}; + +/// A HTTP ressource modification state. +/// +/// The modification state can be verified +/// against a `Request` with conditional headers to eventually serve a +/// `304 Not modified` or `206 Partial Content` response. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum ModState { + /// The ressource has been modified. + Modified, + /// The ressource last modification date. + /// Used with `If-Modified-since`, `If-Unmodified-Since` + /// and `If-Range` conditional headers. + /// + /// It is considered as a strong validator for `If-Range` requests. + Date(http_types::utils::HttpDate), + /// A ressource's version identifier. + /// Used with `If-None-Match`, `If-Match` and `If-Range` conditional headers. + Etag(http_types::conditional::ETag), +} + +/// Serve content according to its modification state and the request's conditional headers: +/// If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since, Range, If-Range. +/// +/// The few first bytes of the content is read to guess the MIME Type. +/// +/// # Examples +/// +/// Serve a static file returning `304 Not Modified` if the file's modification date +/// is earlier than the request's `If-Modified-Since` header. +/// +/// ``` +/// # #[allow(dead_code)] +/// async fn route_handler(request: tide::Request<()>) -> tide::Result { +/// use async_std::io::BufReader; +/// use async_std::fs::File; +/// use tide::utils::{serve_content, ModState}; +/// +/// let file = File::open("/foo/bar").await?; +/// let metadata = file.metadata().await?; +/// let mod_time = metadata.modified()?; +/// let content = BufReader::new(file); +/// +/// serve_content(request, content, ModState::from(mod_time)).await +/// } +/// ``` +pub async fn serve_content(req: Request, content: T, mod_state: ModState) -> Result +where + T: AsyncRead + AsyncBufRead + AsyncSeek + Send + Sync + Unpin + 'static, +{ + let res = Response::new(StatusCode::Ok); + serve_content_with(req, res, content, mod_state).await +} + +/// Similar than `serve_content` but allows to use a predefined `Response`. +/// +/// `serve_content_with` only modifies the response content, status and headers +/// related to conditional requests. The MIME type is not guessed from the content data. +/// +/// # Examples +/// +/// Serve a static file returning `304 Not Modified` if the file's modification date +/// is earlier than the request's `If-Modified-Since` header, with HTML MIME type. +/// +/// ``` +/// # use tide::{Response, Redirect, Request, StatusCode}; +/// # use async_std::io::BufReader; +/// # #[allow(dead_code)] +/// async fn route_handler(request: Request<()>) -> tide::Result { +/// use tide::utils::{serve_content, ModState}; +/// +/// let file = async_std::fs::File::open("/foo/bar").await?; +/// let metadata = file.metadata().await?; +/// let mod_time = metadata.modified()?; +/// let content = BufReader::new(file); +/// +/// let response = tide::Response::builder(200).content_type(tide::mime::HTML); +/// +/// serve_content_with(request, response, content, ModState::from(mod_time)).await +/// } +/// ``` +pub async fn serve_content_with( + req: Request, + res: Response, + content: T, + mod_state: ModState, +) -> Result +where + T: AsyncRead + AsyncBufRead + AsyncSeek + Send + Sync + Unpin + 'static, +{ + todo!(); +}