rust_web_server/header/
mod.rs1use crate::client_hint::ClientHint;
2use crate::cors::Cors;
3use crate::ext::date_time_ext::DateTimeExt;
4use crate::ext::string_ext::StringExt;
5use crate::range::Range;
6use crate::request::Request;
7use crate::symbol::SYMBOL;
8
9#[cfg(test)]
10mod tests;
11
12pub mod content_disposition;
13
14#[cfg(test)]
15mod example;
16
17#[derive(PartialEq, Eq, Clone, Debug)]
23pub struct Header {
24 pub name: String,
26 pub value: String,
28}
29
30impl Header {
31
32 pub fn as_string(&self) -> String {
33 let formatted = format!("{}: {}", self.name, self.value);
34 formatted
35 }
36
37 pub const _ACCESS_CONTROL_REQUEST_METHOD: &'static str = "Access-Control-Request-Method";
38 pub const _ACCESS_CONTROL_REQUEST_HEADERS: &'static str = "Access-Control-Request-Headers";
39 pub const _ACCESS_CONTROL_ALLOW_ORIGIN: &'static str = "Access-Control-Allow-Origin";
40 pub const _ACCESS_CONTROL_ALLOW_METHODS: &'static str = "Access-Control-Allow-Methods";
41 pub const _ACCESS_CONTROL_ALLOW_HEADERS: &'static str = "Access-Control-Allow-Headers";
42 pub const _ACCESS_CONTROL_ALLOW_CREDENTIALS: &'static str = "Access-Control-Allow-Credentials";
43 pub const _ACCESS_CONTROL_MAX_AGE: &'static str = "Access-Control-Max-Age";
44 pub const _ACCESS_CONTROL_EXPOSE_HEADERS: &'static str = "Access-Control-Expose-Headers";
45
46 pub const _ACCEPT: &'static str = "Accept";
47 pub const _ACCEPT_RANGES: &'static str = "Accept-Ranges";
48 pub const _ACCEPT_CH: &'static str = "Accept-CH";
49 pub const _ACCEPT_ENCODING: &'static str = "Accept-Encoding";
50 pub const _ACCEPT_LANGUAGE: &'static str = "Accept-Language";
51 pub const _ACCEPT_PATCH: &'static str = "Accept-Patch";
52 pub const _ACCEPT_POST: &'static str = "Accept-Post";
53 pub const _AGE: &'static str = "Age";
54 pub const _ALLOW: &'static str = "Allow";
55 pub const _ALT_SVC: &'static str = "Alt-Svc";
56 pub const _AUTHORIZATION: &'static str = "Authorization";
57 pub const _CACHE_CONTROL: &'static str = "Cache-Control";
58 pub const _CONNECTION: &'static str = "Connection";
59 pub const _CLEAR_SITE_DATA: &'static str = "Clear-Site-Data";
60 pub const _CONTENT_TYPE: &'static str = "Content-Type";
61 pub const _CONTENT_LENGTH: &'static str = "Content-Length";
62 pub const _CONTENT_RANGE: &'static str = "Content-Range";
63 pub const _CONTENT_DISPOSITION: &'static str = "Content-Disposition";
64 pub const _CONTENT_ENCODING: &'static str = "Content-Encoding";
65 pub const _CONTENT_LANGUAGE: &'static str = "Content-Language";
66 pub const _CONTENT_LOCATION: &'static str = "Content-Location";
67 pub const _CONTENT_SECURITY_POLICY: &'static str = "Content-Security-Policy";
68 pub const _CONTENT_SECURITY_POLICY_REPORT_ONLY: &'static str = "Content-Security-Policy-Report-Only";
69 pub const _COOKIE: &'static str = "Cookie";
70 pub const _CROSS_ORIGIN_EMBEDDER_POLICY: &'static str = "Cross-Origin-Embedder-Policy";
71 pub const _CROSS_ORIGIN_OPENER_POLICY: &'static str = "Cross-Origin-Opener-Policy";
72 pub const _CROSS_ORIGIN_RESOURCE_POLICY: &'static str = "Cross-Origin-Resource-Policy";
73 pub const _DATE: &'static str = "Date";
74 pub const _DATE_UNIX_EPOCH_NANOS: &'static str = "Date-Unix-Epoch-Nanos";
75 pub const _DEVICE_MEMORY: &'static str = "Device-Memory";
76 pub const _DIGEST: &'static str = "Digest";
77 pub const _DOWNLINK: &'static str = "Downlink";
78 pub const _EARLY_DATA: &'static str = "Early-Data";
79 pub const _ECT: &'static str = "ECT";
80 pub const _ETAG: &'static str = "ETag";
81 pub const _EXPECT: &'static str = "Expect";
82 pub const _EXPIRES: &'static str = "Expires";
83 pub const _FEATURE_POLICY: &'static str = "Feature-Policy";
84 pub const _PERMISSIONS_POLICY: &'static str = "Permissions-Policy";
85 pub const _FORWARDED: &'static str = "Forwarded";
86 pub const _FROM: &'static str = "From";
87 pub const _HOST: &'static str = "Host";
88 pub const _IF_MATCH: &'static str = "If-Match";
89 pub const _IF_MODIFIED_SINCE: &'static str = "If-Modified-Since";
90 pub const _IF_NONE_MATCH: &'static str = "If-None-Match";
91 pub const _IF_RANGE: &'static str = "If-Range";
92 pub const _IF_UNMODIFIED_SINCE: &'static str = "If-Unmodified-Since";
93 pub const _LAST_MODIFIED: &'static str = "Last-Modified";
94 pub const _LAST_MODIFIED_UNIX_EPOCH_NANOS: &'static str = "Last-Modified-Unix-Epoch-Nanos";
95 pub const _LINK: &'static str = "Link";
96 pub const _LOCATION: &'static str = "Location";
97 pub const _MAX_FORWARDS: &'static str = "Max-Forwards";
98 pub const _NEL: &'static str = "NEL";
99 pub const _ORIGIN: &'static str = "Origin";
100 pub const _PROXY_AUTHENTICATE: &'static str = "Proxy-Authenticate";
101 pub const _PROXY_AUTHORIZATION: &'static str = "Proxy-Authorization";
102 pub const _RANGE: &'static str = "Range";
103 pub const _REFERER: &'static str = "Referer";
104 pub const _REFERRER_POLICY: &'static str = "Referrer-Policy";
105 pub const _RETRY_AFTER: &'static str = "Retry-After";
106 pub const _RTT: &'static str = "RTT";
107 pub const _SAVE_DATA: &'static str = "Save-Data";
108 pub const _SEC_CH_UA: &'static str = "Sec-CH-UA";
109 pub const _SEC_CH_UA_ARCH: &'static str = "Sec-CH-UA-Arch";
110 pub const _SEC_CH_UA_BITNESS: &'static str = "Sec-CH-UA-Bitness";
111 pub const _SEC_CH_UA_FULL_VERSION_LIST: &'static str = "Sec-CH-UA-Full-Version-List";
112 pub const _SEC_CH_UA_MOBILE: &'static str = "Sec-CH-UA-Mobile";
113 pub const _SEC_CH_UA_MODEL: &'static str = "Sec-CH-UA-Model";
114 pub const _SEC_CH_UA_PLATFORM: &'static str = "Sec-CH-UA-Platform";
115 pub const _SEC_CH_UA_PLATFORM_VERSION: &'static str = "Sec-CH-UA-Platform-Version";
116 pub const _SEC_FETCH_DEST: &'static str = "Sec-Fetch-Dest";
117 pub const _SEC_FETCH_MODE: &'static str = "Sec-Fetch-Mode";
118 pub const _SEC_FETCH_SITE: &'static str = "Sec-Fetch-Site";
119 pub const _SEC_FETCH_USER: &'static str = "Sec-Fetch-User";
120 pub const _SEC_GPC: &'static str = "Sec-GPC";
121 pub const _SERVER: &'static str = "Server";
122 pub const _SERVER_TIMING: &'static str = "Server-Timing";
123 pub const _SERVICE_WORKER_NAVIGATION_PRELOAD: &'static str = "Service-Worker-Navigation-Preload";
124 pub const _SET_COOKIE: &'static str = "Set-Cookie";
125 pub const _SOURCE_MAP: &'static str = "SourceMap";
126 pub const _STRICT_TRANSPORT_SECURITY: &'static str = "Strict-Transport-Security";
127 pub const _TE: &'static str = "TE";
128 pub const _TIMING_ALLOW_ORIGIN: &'static str = "Timing-Allow-Origin";
129 pub const _TRAILER: &'static str = "Trailer";
130 pub const _TRANSFER_ENCODING: &'static str = "Transfer-Encoding";
131 pub const _UPGRADE: &'static str = "Upgrade";
132 pub const _UPGRADE_INSECURE_REQUESTS: &'static str = "Upgrade-Insecure-Requests";
133 pub const _USER_AGENT: &'static str = "User-Agent";
134 pub const _VARY: &'static str = "Vary";
135 pub const _VIA: &'static str = "Via";
136 pub const _WANT_DIGEST: &'static str = "Want-Digest";
137 pub const _WWW_AUTHENTICATE: &'static str = "WWW-Authenticate";
138 pub const _X_CONTENT_TYPE_OPTIONS: &'static str = "X-Content-Type-Options";
139 pub const _X_CONTENT_TYPE_OPTIONS_VALUE_NOSNIFF: &'static str = "nosniff";
140 pub const _X_FRAME_OPTIONS: &'static str = "X-Frame-Options";
141 pub const _X_FRAME_OPTIONS_VALUE_DENY: &'static str = "DENY";
142 pub const _X_FRAME_OPTIONS_VALUE_SAME_ORIGIN: &'static str = "SAMEORIGIN";
143
144 pub const _STRICT_TRANSPORT_SECURITY_VALUE_DEFAULT: &'static str = "max-age=31536000; includeSubDomains";
145 pub const _REFERRER_POLICY_VALUE_DEFAULT: &'static str = "strict-origin-when-cross-origin";
146 pub const _PERMISSIONS_POLICY_VALUE_DEFAULT: &'static str = "geolocation=(), microphone=(), camera=()";
147 pub const _CONTENT_SECURITY_POLICY_VALUE_DEFAULT: &'static str = "default-src 'self'";
148
149
150
151
152 pub const NAME_VALUE_SEPARATOR: &'static str = ": ";
153
154 pub fn single_line(s: &str) -> std::borrow::Cow<'_, str> {
160 if s.contains(['\r', '\n']) {
161 std::borrow::Cow::Owned(s.replace(['\r', '\n'], " "))
162 } else {
163 std::borrow::Cow::Borrowed(s)
164 }
165 }
166
167 pub const _DO_NOT_STORE_CACHE: &'static str = "no-store, no-cache, private, max-age=0, must-revalidate, proxy-revalidate";
168
169
170
171 pub fn get_header_list_with_config(
179 request: &Request,
180 config: &crate::server_config::ServerConfig,
181 ) -> Vec<Header> {
182 let cors_vary = Cors::get_vary_header_value();
183 let mut vary_value = vec![cors_vary];
184
185 let mut header_list = Cors::get_headers_from_config(request, config);
186
187 let client_hint_header = ClientHint::get_accept_client_hints_header();
188 header_list.push(client_hint_header);
189
190 let critical_client_hint_header = ClientHint::get_critical_client_hints_header();
191 header_list.push(critical_client_hint_header);
192
193 let client_hint_vary = ClientHint::get_vary_header_value();
194 vary_value.push(client_hint_vary);
195
196 header_list.push(Header {
197 name: Header::_VARY.to_string(),
198 value: vary_value.join(", "),
199 });
200
201 header_list.push(Header::get_x_content_type_options_header());
202 header_list.push(Header::get_accept_ranges_header());
203 header_list.push(Header::get_x_frame_options_header());
204 header_list.push(Header::get_date_iso_8601_header());
205 header_list.push(Header::get_no_cache_header());
206
207 header_list.push(Header {
208 name: Header::_REFERRER_POLICY.to_string(),
209 value: Header::_REFERRER_POLICY_VALUE_DEFAULT.to_string(),
210 });
211 header_list.push(Header {
212 name: Header::_PERMISSIONS_POLICY.to_string(),
213 value: Header::_PERMISSIONS_POLICY_VALUE_DEFAULT.to_string(),
214 });
215
216 if !config.csp.is_empty() {
217 header_list.push(Header {
218 name: Header::_CONTENT_SECURITY_POLICY.to_string(),
219 value: config.csp.clone(),
220 });
221 }
222
223 header_list
224 }
225
226 pub fn get_header_list(request: &Request) -> Vec<Header> {
234 Self::get_header_list_with_config(request, &crate::server_config::ServerConfig::current())
235 }
236
237
238 pub fn get_x_content_type_options_header() -> Header {
239 Header {
240 name: Header::_X_CONTENT_TYPE_OPTIONS.to_string(),
241 value: Header::_X_CONTENT_TYPE_OPTIONS_VALUE_NOSNIFF.to_string(),
242 }
243 }
244
245 pub fn get_accept_ranges_header() -> Header {
246 Header {
247 name: Header::_ACCEPT_RANGES.to_string(),
248 value: Range::BYTES.to_string(),
249 }
250 }
251
252 pub fn get_x_frame_options_header() -> Header {
253 Header {
254 name: Header::_X_FRAME_OPTIONS.to_string(),
255 value: Header::_X_FRAME_OPTIONS_VALUE_SAME_ORIGIN.to_string(),
256 }
257 }
258
259 pub fn get_date_iso_8601_header() -> Header {
260 Header {
261 name: Header::_DATE_UNIX_EPOCH_NANOS.to_string(),
262 value: DateTimeExt::_now_unix_epoch_nanos().to_string(),
263 }
264 }
265
266 pub fn get_no_cache_header() -> Header {
267 Header {
268 name: Header::_CACHE_CONTROL.to_string(),
269 value: Header::_DO_NOT_STORE_CACHE.to_string(),
270 }
271 }
272
273 pub fn get_hsts_header() -> Header {
274 Header {
275 name: Header::_STRICT_TRANSPORT_SECURITY.to_string(),
276 value: Header::_STRICT_TRANSPORT_SECURITY_VALUE_DEFAULT.to_string(),
277 }
278 }
279
280 pub fn parse_header(raw_header: &str) -> Result<Header, String> {
281 let escaped_header = StringExt::filter_ascii_control_characters(raw_header);
282 let escaped_header = StringExt::truncate_new_line_carriage_return(&escaped_header);
283
284 let boxed_split = escaped_header.split_once(SYMBOL.colon);
285 if boxed_split.is_none() {
286 let message = format!("Unable to parse header: {}", escaped_header);
287 return Err(message)
288 }
289
290 let (name, value) = boxed_split.unwrap();
291
292 let header = Header {
293 name: name.trim().to_string(),
294 value: value.trim().to_string(),
295 };
296
297 Ok(header)
298 }
299
300 pub fn parse(raw_header: &str) -> Result<Header, String> {
301 Header::parse_header(raw_header)
302 }
303
304 pub fn generate(&self) -> String {
305 self.as_string()
306 }
307
308}
309