http_file/
date.rs

1use core::str::FromStr;
2
3use http::{
4    header::{HeaderValue, IF_MODIFIED_SINCE, IF_UNMODIFIED_SINCE},
5    Request,
6};
7use httpdate::HttpDate;
8
9use super::{buf::buf_write_header, error::ServeError, runtime::Meta};
10
11pub(super) fn mod_date_check<Ext, M>(req: &Request<Ext>, meta: &mut M) -> Result<Option<HttpDate>, ServeError>
12where
13    M: Meta,
14{
15    let mod_date = match meta.modified() {
16        Some(modified) => HttpDate::from(modified),
17        None => {
18            #[cold]
19            #[inline(never)]
20            fn precondition_check<Ext>(req: &Request<Ext>) -> Result<Option<HttpDate>, ServeError> {
21                if req.headers().contains_key(IF_UNMODIFIED_SINCE) {
22                    Err(ServeError::PreconditionFailed)
23                } else {
24                    Ok(None)
25                }
26            }
27
28            return precondition_check(req);
29        }
30    };
31
32    if let Some(ref date) = to_http_date(req.headers().get(IF_UNMODIFIED_SINCE)) {
33        if date < &mod_date {
34            return Err(ServeError::PreconditionFailed);
35        }
36    }
37
38    if let Some(ref date) = to_http_date(req.headers().get(IF_MODIFIED_SINCE)) {
39        if date >= &mod_date {
40            return Err(ServeError::NotModified);
41        }
42    }
43
44    Ok(Some(mod_date))
45}
46
47fn to_http_date(header: Option<&HeaderValue>) -> Option<HttpDate> {
48    header.and_then(|v| {
49        std::str::from_utf8(v.as_ref())
50            .ok()
51            .map(<HttpDate as FromStr>::from_str)
52            .and_then(Result::ok)
53    })
54}
55
56pub(super) fn date_to_header(date: HttpDate) -> HeaderValue {
57    buf_write_header!(29, "{date}")
58}