static_web_server/exts/
http.rs1use hyper::{Method, Response, header::HeaderValue};
9
10pub const HTTP_SUPPORTED_METHODS: &[Method; 3] = &[Method::OPTIONS, Method::HEAD, Method::GET];
12
13pub trait MethodExt {
15 fn is_allowed(&self) -> bool;
17 #[allow(unused)]
19 fn is_get(&self) -> bool;
20 fn is_head(&self) -> bool;
22 fn is_options(&self) -> bool;
24}
25
26impl MethodExt for Method {
27 #[inline(always)]
29 fn is_allowed(&self) -> bool {
30 for method in HTTP_SUPPORTED_METHODS {
31 if method == self {
32 return true;
33 }
34 }
35 false
36 }
37
38 #[inline(always)]
40 fn is_get(&self) -> bool {
41 self == Method::GET
42 }
43
44 #[inline(always)]
46 fn is_head(&self) -> bool {
47 self == Method::HEAD
48 }
49
50 #[inline(always)]
52 fn is_options(&self) -> bool {
53 self == Method::OPTIONS
54 }
55}
56
57static VARY_ACCEPT_ENCODING: HeaderValue = HeaderValue::from_static("accept-encoding");
59
60pub(crate) fn append_vary_accept_encoding<B>(resp: &mut Response<B>) {
63 let accept_enc = hyper::header::ACCEPT_ENCODING.as_str();
64 match resp.headers().get(hyper::header::VARY) {
65 None => {
66 resp.headers_mut()
67 .insert(hyper::header::VARY, VARY_ACCEPT_ENCODING.clone());
68 }
69 Some(existing) => {
70 let s = existing.to_str().unwrap_or_default();
71 if s.contains(accept_enc) {
72 return;
73 }
74 let mut new_val = String::with_capacity(s.len() + 2 + accept_enc.len());
76 new_val.push_str(s);
77 if !s.is_empty() {
78 new_val.push_str(", ");
79 }
80 new_val.push_str(accept_enc);
81 if let Ok(val) = HeaderValue::from_str(&new_val) {
82 resp.headers_mut().insert(hyper::header::VARY, val);
83 }
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use hyper::{Method, Response, StatusCode};
91
92 use super::{MethodExt, append_vary_accept_encoding};
93
94 #[test]
95 fn method_get_is_allowed() {
96 assert!(Method::GET.is_allowed());
97 assert!(Method::GET.is_get());
98 assert!(!Method::GET.is_head());
99 assert!(!Method::GET.is_options());
100 }
101
102 #[test]
103 fn method_head_is_allowed() {
104 assert!(Method::HEAD.is_allowed());
105 assert!(Method::HEAD.is_head());
106 assert!(!Method::HEAD.is_get());
107 assert!(!Method::HEAD.is_options());
108 }
109
110 #[test]
111 fn method_options_is_allowed() {
112 assert!(Method::OPTIONS.is_allowed());
113 assert!(Method::OPTIONS.is_options());
114 assert!(!Method::OPTIONS.is_get());
115 assert!(!Method::OPTIONS.is_head());
116 }
117
118 #[test]
119 fn method_post_is_not_allowed() {
120 assert!(!Method::POST.is_allowed());
121 assert!(!Method::POST.is_get());
122 assert!(!Method::POST.is_head());
123 assert!(!Method::POST.is_options());
124 }
125
126 #[test]
127 fn method_put_delete_patch_are_not_allowed() {
128 for method in [Method::PUT, Method::DELETE, Method::PATCH] {
129 assert!(!method.is_allowed(), "{method} should not be allowed");
130 }
131 }
132
133 #[test]
134 fn vary_added_when_absent() {
135 let mut resp = Response::new(crate::body::empty());
136 *resp.status_mut() = StatusCode::OK;
137 append_vary_accept_encoding(&mut resp);
138 let vary = resp.headers().get(hyper::header::VARY).unwrap();
139 assert_eq!(vary.to_str().unwrap(), "accept-encoding");
140 }
141
142 #[test]
143 fn vary_not_duplicated_when_already_present() {
144 let mut resp = Response::new(crate::body::empty());
145 *resp.status_mut() = StatusCode::OK;
146 resp.headers_mut()
147 .insert(hyper::header::VARY, "accept-encoding".parse().unwrap());
148 append_vary_accept_encoding(&mut resp);
149 let vary = resp.headers().get(hyper::header::VARY).unwrap();
150 assert_eq!(vary.to_str().unwrap(), "accept-encoding");
151 }
152
153 #[test]
154 fn vary_appended_to_existing_value() {
155 let mut resp = Response::new(crate::body::empty());
156 *resp.status_mut() = StatusCode::OK;
157 resp.headers_mut()
158 .insert(hyper::header::VARY, "origin".parse().unwrap());
159 append_vary_accept_encoding(&mut resp);
160 let vary = resp.headers().get(hyper::header::VARY).unwrap();
161 assert_eq!(vary.to_str().unwrap(), "origin, accept-encoding");
162 }
163
164 #[test]
165 fn vary_not_duplicated_when_mixed_with_others() {
166 let mut resp = Response::new(crate::body::empty());
167 *resp.status_mut() = StatusCode::OK;
168 resp.headers_mut().insert(
169 hyper::header::VARY,
170 "origin, accept-encoding".parse().unwrap(),
171 );
172 append_vary_accept_encoding(&mut resp);
173 let vary = resp.headers().get(hyper::header::VARY).unwrap();
174 assert_eq!(vary.to_str().unwrap(), "origin, accept-encoding");
175 }
176}