junobuild_storage/http/
headers.rs1use crate::constants::ASSET_ENCODING_NO_COMPRESSION;
2use crate::http::types::HeaderField;
3use crate::types::config::{StorageConfig, StorageConfigIFrame};
4use crate::types::store::{Asset, AssetEncoding, EncodingType};
5use crate::url::matching_urls;
6use hex::encode;
7use std::collections::HashMap;
8
9pub fn build_headers(
10 asset: &Asset,
11 encoding: &AssetEncoding,
12 encoding_type: &EncodingType,
13 config: &StorageConfig,
14) -> Vec<HeaderField> {
15 let mut headers: HashMap<String, String> = build_config_headers(&asset.key.full_path, config)
17 .into_iter()
18 .map(|HeaderField(key, value)| (key.to_lowercase(), value))
19 .collect();
20
21 for HeaderField(key, value) in &asset.headers {
23 headers.insert(key.to_lowercase(), value.clone());
24 }
25
26 if asset.key.token.is_some() {
29 for HeaderField(key, value) in token_headers() {
30 headers.insert(key.to_lowercase(), value);
31 }
32 }
33
34 headers.insert("accept-ranges".to_string(), "bytes".to_string());
36
37 headers.insert(
38 "etag".to_string(),
39 format!("\"{}\"", encode(encoding.sha256)),
40 );
41
42 for HeaderField(key, value) in security_headers() {
44 headers.insert(key.to_lowercase(), value);
45 }
46
47 if let Some(HeaderField(key, value)) = iframe_headers(&config.unwrap_iframe()) {
49 headers.insert(key.to_lowercase(), value);
50 }
51
52 if encoding_type.clone() != *ASSET_ENCODING_NO_COMPRESSION {
53 headers.insert("content-encoding".to_string(), encoding_type.to_string());
54 }
55
56 headers
57 .into_iter()
58 .map(|(key, value)| HeaderField(key, value))
59 .collect()
60}
61
62pub fn build_redirect_headers(location: &str, iframe: &StorageConfigIFrame) -> Vec<HeaderField> {
63 let mut headers = Vec::new();
64
65 headers.extend(security_headers());
67
68 if let Some(iframe_header) = iframe_headers(iframe) {
70 headers.push(iframe_header);
71 }
72
73 headers.push(HeaderField("Location".to_string(), location.to_string()));
74
75 headers
76}
77
78fn security_headers() -> Vec<HeaderField> {
83 vec![
84 HeaderField("X-Content-Type-Options".to_string(), "nosniff".to_string()),
85 HeaderField(
86 "Strict-Transport-Security".to_string(),
87 "max-age=31536000 ; includeSubDomains".to_string(),
88 ),
89 HeaderField("Referrer-Policy".to_string(), "same-origin".to_string()),
92 ]
93}
94
95fn iframe_headers(iframe: &StorageConfigIFrame) -> Option<HeaderField> {
96 match iframe {
97 StorageConfigIFrame::Deny => Some(HeaderField(
98 "X-Frame-Options".to_string(),
99 "DENY".to_string(),
100 )),
101 StorageConfigIFrame::SameOrigin => Some(HeaderField(
102 "X-Frame-Options".to_string(),
103 "SAMEORIGIN".to_string(),
104 )),
105 StorageConfigIFrame::AllowAny => None,
106 }
107}
108
109fn token_headers() -> Vec<HeaderField> {
110 vec![
111 HeaderField("X-Robots-Tag".to_string(), "noindex, nofollow".to_string()),
113 HeaderField("Cache-Control".to_string(), "private, no-store".to_string()),
116 ]
117}
118
119pub fn build_config_headers(
120 requested_path: &str,
121 StorageConfig {
122 headers: config_headers,
123 ..
124 }: &StorageConfig,
125) -> Vec<HeaderField> {
126 matching_urls(requested_path, config_headers)
127 .iter()
128 .flat_map(|(_, headers)| headers.clone())
129 .collect()
130}