Skip to main content

trillium_head/
lib.rs

1//! # Trillium handler for HEAD requests
2//!
3//! This simple handler rewrites HEAD requests to be GET requests, and
4//! then before sending the response, removes the outbound body but sets a
5//! content length header. Any handlers subsequent to this one see a GET
6//! request.
7#![forbid(unsafe_code)]
8#![deny(
9    missing_copy_implementations,
10    rustdoc::missing_crate_level_docs,
11    missing_debug_implementations,
12    missing_docs,
13    nonstandard_style,
14    unused_qualifications
15)]
16
17#[cfg(test)]
18#[doc = include_str!("../README.md")]
19mod readme {}
20
21use trillium::{Conn, Handler, KnownHeaderName::ContentLength, Method, Transport};
22
23/// Trillium handler for HEAD requests
24///
25/// See crate-level docs for an explanation
26#[derive(Default, Clone, Copy, Debug)]
27pub struct Head {
28    _my_private_things: (),
29}
30
31impl Head {
32    /// constructs a new Head handler
33    pub fn new() -> Self {
34        Self::default()
35    }
36}
37
38#[derive(Clone, Copy, Debug)]
39struct RequestWasHead;
40
41impl Handler for Head {
42    async fn run(&self, mut conn: Conn) -> Conn {
43        if conn.method() == Method::Head {
44            AsMut::<trillium_http::Conn<Box<dyn Transport>>>::as_mut(&mut conn)
45                .set_method(Method::Get);
46            conn.insert_state(RequestWasHead);
47        }
48
49        conn
50    }
51
52    async fn before_send(&self, mut conn: Conn) -> Conn {
53        if conn.state::<RequestWasHead>().is_none() {
54            return conn;
55        }
56        AsMut::<trillium_http::Conn<Box<dyn Transport>>>::as_mut(&mut conn)
57            .set_method(Method::Head);
58        if let Some(len) = conn.take_response_body().and_then(|body| body.len()) {
59            conn.response_headers_mut().insert(ContentLength, len);
60        }
61
62        conn
63    }
64}
65
66/// Alias for [`Head::new`]
67pub fn head() -> Head {
68    Head::new()
69}