static_web_server/
conditional_headers.rs1use headers::{
10 HeaderMap, HeaderMapExt, HeaderValue, IfModifiedSince, IfRange, IfUnmodifiedSince,
11 LastModified, Range,
12};
13use hyper::{Body, Response, StatusCode};
14
15#[derive(Debug)]
16pub(crate) struct ConditionalHeaders {
17 pub(crate) if_modified_since: Option<IfModifiedSince>,
18 pub(crate) if_unmodified_since: Option<IfUnmodifiedSince>,
19 pub(crate) if_range: Option<IfRange>,
20 pub(crate) range: Option<Range>,
21}
22
23impl ConditionalHeaders {
24 pub(crate) fn new(headers: &HeaderMap<HeaderValue>) -> Self {
25 let if_modified_since = headers.typed_get::<IfModifiedSince>();
26 let if_unmodified_since = headers.typed_get::<IfUnmodifiedSince>();
27 let if_range = headers.typed_get::<IfRange>();
28 let range = headers.typed_get::<Range>();
29
30 Self {
31 if_modified_since,
32 if_unmodified_since,
33 if_range,
34 range,
35 }
36 }
37}
38
39impl ConditionalHeaders {
40 pub(crate) fn check(self, last_modified: Option<LastModified>) -> ConditionalBody {
41 if let Some(since) = self.if_unmodified_since {
42 let precondition = last_modified
43 .map(|time| since.precondition_passes(time.into()))
44 .unwrap_or(false);
45
46 tracing::trace!(
47 "if-unmodified-since? {:?} vs {:?} = {}",
48 since,
49 last_modified,
50 precondition
51 );
52 if !precondition {
53 let mut res = Response::new(Body::empty());
54 *res.status_mut() = StatusCode::PRECONDITION_FAILED;
55 return ConditionalBody::NoBody(res);
56 }
57 }
58
59 if let Some(since) = self.if_modified_since {
60 tracing::trace!(
61 "if-modified-since? header = {:?}, file = {:?}",
62 since,
63 last_modified
64 );
65 let unmodified = last_modified
66 .map(|time| !since.is_modified(time.into()))
67 .unwrap_or(false);
69 if unmodified {
70 let mut res = Response::new(Body::empty());
71 *res.status_mut() = StatusCode::NOT_MODIFIED;
72 return ConditionalBody::NoBody(res);
73 }
74 }
75
76 if let Some(if_range) = self.if_range {
77 tracing::trace!("if-range? {:?} vs {:?}", if_range, last_modified);
78
79 let can_range = !if_range.is_modified(None, last_modified.as_ref());
80 if !can_range {
81 return ConditionalBody::WithBody(None);
82 }
83 }
84
85 ConditionalBody::WithBody(self.range)
86 }
87}
88
89pub(crate) enum ConditionalBody {
90 NoBody(Response<Body>),
91 WithBody(Option<Range>),
92}