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