1use crate::certification::cert::{build_asset_certificate_header, build_certified_expression};
2use crate::constants::ASSET_ENCODING_NO_COMPRESSION;
3use crate::http::headers::{build_headers, build_redirect_headers};
4use crate::http::types::{CallbackFunc, HeaderField, StreamingCallbackToken, StreamingStrategy};
5use crate::memory::STATE;
6use crate::types::config::{StorageConfig, StorageConfigIFrame};
7use crate::types::runtime_state::StorageRuntimeState;
8use crate::types::store::{Asset, AssetEncoding, AssetKey, EncodingType};
9use ic_certification::HashTree;
10use junobuild_collections::types::rules::Memory;
11use junobuild_shared::ic::api::id;
12use serde_bytes::ByteBuf;
13
14pub fn streaming_strategy(
15 key: &AssetKey,
16 encoding: &AssetEncoding,
17 encoding_type: &str,
18 headers: &[HeaderField],
19 memory: &Memory,
20) -> Option<StreamingStrategy> {
21 let streaming_token: Option<StreamingCallbackToken> =
22 create_token(key, 0, encoding, encoding_type, headers, memory);
23
24 streaming_token.map(|streaming_token| StreamingStrategy::Callback {
25 callback: CallbackFunc::new(id(), "http_request_streaming_callback".to_string()),
26 token: streaming_token,
27 })
28}
29
30pub fn create_token(
31 key: &AssetKey,
32 chunk_index: usize,
33 encoding: &AssetEncoding,
34 encoding_type: &str,
35 headers: &[HeaderField],
36 memory: &Memory,
37) -> Option<StreamingCallbackToken> {
38 if chunk_index + 1 >= encoding.content_chunks.len() {
39 return None;
40 }
41
42 Some(StreamingCallbackToken {
43 full_path: key.full_path.clone(),
44 token: key.token.clone(),
45 headers: headers.to_owned(),
46 index: chunk_index + 1,
47 sha256: Some(ByteBuf::from(encoding.sha256)),
48 encoding_type: encoding_type.to_owned(),
49 memory: memory.clone(),
50 })
51}
52
53#[allow(clippy::too_many_arguments)]
54pub fn build_response_headers(
55 url: &str,
56 asset: &Asset,
57 encoding: &AssetEncoding,
58 encoding_type: &EncodingType,
59 certificate_version: &Option<u16>,
60 rewrite_source: &Option<String>,
61 config: &StorageConfig,
62 sigs_tree: HashTree,
63) -> Result<Vec<HeaderField>, &'static str> {
64 let asset_headers = build_headers(asset, encoding, encoding_type, config);
65
66 extend_headers_with_certification(
67 asset_headers,
68 url,
69 certificate_version,
70 rewrite_source,
71 sigs_tree,
72 )
73}
74
75pub fn build_response_redirect_headers(
76 url: &str,
77 location: &str,
78 iframe: &StorageConfigIFrame,
79 certificate_version: &Option<u16>,
80 sigs_tree: HashTree,
81) -> Result<Vec<HeaderField>, &'static str> {
82 let asset_headers = build_redirect_headers(location, iframe);
83
84 extend_headers_with_certification(asset_headers, url, certificate_version, &None, sigs_tree)
85}
86
87fn extend_headers_with_certification(
88 asset_headers: Vec<HeaderField>,
89 url: &str,
90 certificate_version: &Option<u16>,
91 rewrite_source: &Option<String>,
92 sigs_tree: HashTree,
93) -> Result<Vec<HeaderField>, &'static str> {
94 let certified_header =
95 build_certified_headers(url, certificate_version, rewrite_source, sigs_tree)?;
96 let certified_expression = build_certified_expression(&asset_headers, certificate_version)?;
97
98 match certified_expression {
99 None => Ok([asset_headers, vec![certified_header]].concat()),
100 Some(certified_expression) => {
101 Ok([asset_headers, vec![certified_header, certified_expression]].concat())
102 }
103 }
104}
105
106fn build_certified_headers(
107 url: &str,
108 certificate_version: &Option<u16>,
109 rewrite_source: &Option<String>,
110 sigs_tree: HashTree,
111) -> Result<HeaderField, &'static str> {
112 STATE.with(|state| {
113 build_certified_headers_impl(
114 url,
115 certificate_version,
116 rewrite_source,
117 sigs_tree,
118 &state.borrow().runtime.storage,
119 )
120 })
121}
122
123fn build_certified_headers_impl(
124 url: &str,
125 certificate_version: &Option<u16>,
126 rewrite_source: &Option<String>,
127 sigs_tree: HashTree,
128 state: &StorageRuntimeState,
129) -> Result<HeaderField, &'static str> {
130 build_asset_certificate_header(
131 &state.asset_hashes,
132 url.to_owned(),
133 certificate_version,
134 rewrite_source,
135 sigs_tree,
136 )
137}
138
139pub fn build_encodings(headers: Vec<HeaderField>) -> Vec<String> {
140 let mut encodings: Vec<String> = vec![];
141 for HeaderField(name, value) in headers.iter() {
142 if name.eq_ignore_ascii_case("Accept-Encoding") {
143 for v in value.split(',') {
144 encodings.push(v.trim().to_string());
145 }
146 }
147 }
148 encodings.push(ASSET_ENCODING_NO_COMPRESSION.to_string());
149
150 encodings
151}