Skip to main content

rust_web_server/header/
mod.rs

1use 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/// An HTTP header name/value pair.
18///
19/// Use the associated string constants (e.g. [`Header::_CONTENT_TYPE`]) for header names.
20/// [`Header::get_header_list`] returns the standard set of response headers (client hints,
21/// CORS, cache control, security headers) that should be included on every response.
22#[derive(PartialEq, Eq, Clone, Debug)]
23pub struct Header {
24    /// Header name, e.g. `"Content-Type"`.
25    pub name: String,
26    /// Header value, e.g. `"application/json"`.
27    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 _CLEAR_SITE_DATA: &'static str = "Clear-Site-Data";
59    pub const _CONTENT_TYPE: &'static str = "Content-Type";
60    pub const _CONTENT_LENGTH: &'static str = "Content-Length";
61    pub const _CONTENT_RANGE: &'static str = "Content-Range";
62    pub const _CONTENT_DISPOSITION: &'static str = "Content-Disposition";
63    pub const _CONTENT_ENCODING: &'static str = "Content-Encoding";
64    pub const _CONTENT_LANGUAGE: &'static str = "Content-Language";
65    pub const _CONTENT_LOCATION: &'static str = "Content-Location";
66    pub const _CONTENT_SECURITY_POLICY: &'static str = "Content-Security-Policy";
67    pub const _CONTENT_SECURITY_POLICY_REPORT_ONLY: &'static str = "Content-Security-Policy-Report-Only";
68    pub const _COOKIE: &'static str = "Cookie";
69    pub const _CROSS_ORIGIN_EMBEDDER_POLICY: &'static str = "Cross-Origin-Embedder-Policy";
70    pub const _CROSS_ORIGIN_OPENER_POLICY: &'static str = "Cross-Origin-Opener-Policy";
71    pub const _CROSS_ORIGIN_RESOURCE_POLICY: &'static str = "Cross-Origin-Resource-Policy";
72    pub const _DATE: &'static str = "Date";
73    pub const _DATE_UNIX_EPOCH_NANOS: &'static str = "Date-Unix-Epoch-Nanos";
74    pub const _DEVICE_MEMORY: &'static str = "Device-Memory";
75    pub const _DIGEST: &'static str = "Digest";
76    pub const _DOWNLINK: &'static str = "Downlink";
77    pub const _EARLY_DATA: &'static str = "Early-Data";
78    pub const _ECT: &'static str = "ECT";
79    pub const _ETAG: &'static str = "ETag";
80    pub const _EXPECT: &'static str = "Expect";
81    pub const _EXPIRES: &'static str = "Expires";
82    pub const _FEATURE_POLICY: &'static str = "Feature-Policy";
83    pub const _PERMISSIONS_POLICY: &'static str = "Permissions-Policy";
84    pub const _FORWARDED: &'static str = "Forwarded";
85    pub const _FROM: &'static str = "From";
86    pub const _HOST: &'static str = "Host";
87    pub const _IF_MATCH: &'static str = "If-Match";
88    pub const _IF_MODIFIED_SINCE: &'static str = "If-Modified-Since";
89    pub const _IF_NONE_MATCH: &'static str = "If-None-Match";
90    pub const _IF_RANGE: &'static str = "If-Range";
91    pub const _IF_UNMODIFIED_SINCE: &'static str = "If-Unmodified-Since";
92    pub const _LAST_MODIFIED: &'static str = "Last-Modified";
93    pub const _LAST_MODIFIED_UNIX_EPOCH_NANOS: &'static str = "Last-Modified-Unix-Epoch-Nanos";
94    pub const _LINK: &'static str = "Link";
95    pub const _LOCATION: &'static str = "Location";
96    pub const _MAX_FORWARDS: &'static str = "Max-Forwards";
97    pub const _NEL: &'static str = "NEL";
98    pub const _ORIGIN: &'static str = "Origin";
99    pub const _PROXY_AUTHENTICATE: &'static str = "Proxy-Authenticate";
100    pub const _PROXY_AUTHORIZATION: &'static str = "Proxy-Authorization";
101    pub const _RANGE: &'static str = "Range";
102    pub const _REFERER: &'static str = "Referer";
103    pub const _REFERRER_POLICY: &'static str = "Referrer-Policy";
104    pub const _RETRY_AFTER: &'static str = "Retry-After";
105    pub const _RTT: &'static str = "RTT";
106    pub const _SAVE_DATA: &'static str = "Save-Data";
107    pub const _SEC_CH_UA: &'static str = "Sec-CH-UA";
108    pub const _SEC_CH_UA_ARCH: &'static str = "Sec-CH-UA-Arch";
109    pub const _SEC_CH_UA_BITNESS: &'static str = "Sec-CH-UA-Bitness";
110    pub const _SEC_CH_UA_FULL_VERSION_LIST: &'static str = "Sec-CH-UA-Full-Version-List";
111    pub const _SEC_CH_UA_MOBILE: &'static str = "Sec-CH-UA-Mobile";
112    pub const _SEC_CH_UA_MODEL: &'static str = "Sec-CH-UA-Model";
113    pub const _SEC_CH_UA_PLATFORM: &'static str = "Sec-CH-UA-Platform";
114    pub const _SEC_CH_UA_PLATFORM_VERSION: &'static str = "Sec-CH-UA-Platform-Version";
115    pub const _SEC_FETCH_DEST: &'static str = "Sec-Fetch-Dest";
116    pub const _SEC_FETCH_MODE: &'static str = "Sec-Fetch-Mode";
117    pub const _SEC_FETCH_SITE: &'static str = "Sec-Fetch-Site";
118    pub const _SEC_FETCH_USER: &'static str = "Sec-Fetch-User";
119    pub const _SEC_GPC: &'static str = "Sec-GPC";
120    pub const _SERVER: &'static str = "Server";
121    pub const _SERVER_TIMING: &'static str = "Server-Timing";
122    pub const _SERVICE_WORKER_NAVIGATION_PRELOAD: &'static str = "Service-Worker-Navigation-Preload";
123    pub const _SET_COOKIE: &'static str = "Set-Cookie";
124    pub const _SOURCE_MAP: &'static str = "SourceMap";
125    pub const _STRICT_TRANSPORT_SECURITY: &'static str = "Strict-Transport-Security";
126    pub const _TE: &'static str = "TE";
127    pub const _TIMING_ALLOW_ORIGIN: &'static str = "Timing-Allow-Origin";
128    pub const _TRAILER: &'static str = "Trailer";
129    pub const _TRANSFER_ENCODING: &'static str = "Transfer-Encoding";
130    pub const _UPGRADE: &'static str = "Upgrade";
131    pub const _UPGRADE_INSECURE_REQUESTS: &'static str = "Upgrade-Insecure-Requests";
132    pub const _USER_AGENT: &'static str = "User-Agent";
133    pub const _VARY: &'static str = "Vary";
134    pub const _VIA: &'static str = "Via";
135    pub const _WANT_DIGEST: &'static str = "Want-Digest";
136    pub const _WWW_AUTHENTICATE: &'static str = "WWW-Authenticate";
137    pub const _X_CONTENT_TYPE_OPTIONS: &'static str = "X-Content-Type-Options";
138    pub const _X_CONTENT_TYPE_OPTIONS_VALUE_NOSNIFF: &'static str = "nosniff";
139    pub const _X_FRAME_OPTIONS: &'static str = "X-Frame-Options";
140    pub const _X_FRAME_OPTIONS_VALUE_DENY: &'static str = "DENY";
141    pub const _X_FRAME_OPTIONS_VALUE_SAME_ORIGIN: &'static str = "SAMEORIGIN";
142
143    pub const _STRICT_TRANSPORT_SECURITY_VALUE_DEFAULT: &'static str = "max-age=31536000; includeSubDomains";
144    pub const _REFERRER_POLICY_VALUE_DEFAULT: &'static str = "strict-origin-when-cross-origin";
145    pub const _PERMISSIONS_POLICY_VALUE_DEFAULT: &'static str = "geolocation=(), microphone=(), camera=()";
146    pub const _CONTENT_SECURITY_POLICY_VALUE_DEFAULT: &'static str = "default-src 'self'";
147
148
149
150
151    pub const NAME_VALUE_SEPARATOR: &'static str = ": ";
152
153    pub const _DO_NOT_STORE_CACHE: &'static str = "no-store, no-cache, private, max-age=0, must-revalidate, proxy-revalidate";
154
155
156
157    pub fn get_header_list(request: &Request) -> Vec<Header> {
158        let mut header_list : Vec<Header>;
159        let mut vary_value : Vec<String>;
160
161        let cors_vary = Cors::get_vary_header_value();
162        vary_value = vec![cors_vary];
163        let cors_header_list: Vec<Header> = Cors::get_headers(&request);
164        header_list = cors_header_list;
165
166        let client_hint_header = ClientHint::get_accept_client_hints_header();
167        header_list.push(client_hint_header);
168
169        let critical_client_hint_header = ClientHint::get_critical_client_hints_header();
170        header_list.push(critical_client_hint_header);
171
172        let client_hint_vary = ClientHint::get_vary_header_value();
173        vary_value.push(client_hint_vary);
174
175        let vary_header = Header { name: Header::_VARY.to_string(), value: vary_value.join(", ") };
176        header_list.push(vary_header);
177
178        let x_content_type_options_header = Header::get_x_content_type_options_header();
179        header_list.push(x_content_type_options_header);
180
181        let accept_ranges_header = Header::get_accept_ranges_header();
182        header_list.push(accept_ranges_header);
183
184        let x_frame_options_header = Header::get_x_frame_options_header();
185        header_list.push(x_frame_options_header);
186
187        let date_iso_8601_header = Header::get_date_iso_8601_header();
188        header_list.push(date_iso_8601_header);
189
190        let no_cache = Header::get_no_cache_header();
191        header_list.push(no_cache);
192
193        let referrer_policy = Header {
194            name: Header::_REFERRER_POLICY.to_string(),
195            value: Header::_REFERRER_POLICY_VALUE_DEFAULT.to_string(),
196        };
197        header_list.push(referrer_policy);
198
199        let permissions_policy = Header {
200            name: Header::_PERMISSIONS_POLICY.to_string(),
201            value: Header::_PERMISSIONS_POLICY_VALUE_DEFAULT.to_string(),
202        };
203        header_list.push(permissions_policy);
204
205        let csp_value = std::env::var("RWS_CONFIG_CSP")
206            .unwrap_or_else(|_| Header::_CONTENT_SECURITY_POLICY_VALUE_DEFAULT.to_string());
207        if !csp_value.is_empty() {
208            header_list.push(Header {
209                name: Header::_CONTENT_SECURITY_POLICY.to_string(),
210                value: csp_value,
211            });
212        }
213
214        header_list
215    }
216
217
218    pub fn get_x_content_type_options_header() -> Header {
219        Header {
220            name: Header::_X_CONTENT_TYPE_OPTIONS.to_string(),
221            value: Header::_X_CONTENT_TYPE_OPTIONS_VALUE_NOSNIFF.to_string(),
222        }
223    }
224
225    pub fn get_accept_ranges_header() -> Header {
226        Header {
227            name: Header::_ACCEPT_RANGES.to_string(),
228            value: Range::BYTES.to_string(),
229        }
230    }
231
232    pub fn get_x_frame_options_header() -> Header {
233        Header {
234            name: Header::_X_FRAME_OPTIONS.to_string(),
235            value: Header::_X_FRAME_OPTIONS_VALUE_SAME_ORIGIN.to_string(),
236        }
237    }
238
239    pub fn get_date_iso_8601_header() -> Header {
240        Header {
241            name: Header::_DATE_UNIX_EPOCH_NANOS.to_string(),
242            value: DateTimeExt::_now_unix_epoch_nanos().to_string(),
243        }
244    }
245
246    pub fn get_no_cache_header() -> Header {
247        Header {
248            name: Header::_CACHE_CONTROL.to_string(),
249            value: Header::_DO_NOT_STORE_CACHE.to_string(),
250        }
251    }
252
253    pub fn get_hsts_header() -> Header {
254        Header {
255            name: Header::_STRICT_TRANSPORT_SECURITY.to_string(),
256            value: Header::_STRICT_TRANSPORT_SECURITY_VALUE_DEFAULT.to_string(),
257        }
258    }
259
260    pub fn parse_header(raw_header: &str) -> Result<Header, String> {
261        let escaped_header = StringExt::filter_ascii_control_characters(raw_header);
262        let escaped_header = StringExt::truncate_new_line_carriage_return(&escaped_header);
263
264        let boxed_split = escaped_header.split_once(SYMBOL.colon);
265        if boxed_split.is_none() {
266            let message = format!("Unable to parse header: {}", escaped_header);
267            return Err(message)
268        }
269
270        let (name, value) = boxed_split.unwrap();
271
272        let header = Header {
273            name: name.trim().to_string(),
274            value: value.trim().to_string(),
275        };
276
277        Ok(header)
278    }
279
280    pub fn parse(raw_header: &str) -> Result<Header, String> {
281        Header::parse_header(raw_header)
282    }
283
284    pub fn generate(&self) -> String {
285        self.as_string()
286    }
287
288}
289