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}