junobuild_storage/http/
utils.rs

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}