use crate::certification::constants::{
EXACT_MATCH_TERMINATOR, LABEL_ASSETS_V1, LABEL_ASSETS_V2, WILDCARD_MATCH_TERMINATOR,
};
use crate::certification::tree_utils::{
fallback_paths, nested_tree_expr_path, nested_tree_key, nested_tree_path,
};
use crate::certification::types::certified::CertifiedAssetHashes;
use crate::constants::{
ENCODING_CERTIFICATION_ORDER, RESPONSE_STATUS_CODE_200, RESPONSE_STATUS_CODE_404,
ROOT_404_HTML, ROOT_INDEX_HTML, ROOT_PATH,
};
use crate::http::headers::{build_headers, build_redirect_headers};
use crate::http::types::{HeaderField, StatusCode};
use crate::types::config::{StorageConfig, StorageConfigIFrame};
use crate::types::state::FullPath;
use crate::types::store::Asset;
use crate::url::alternative_paths;
use ic_certification::{
fork, fork_hash, labeled, labeled_hash, merge_hash_trees, pruned, AsHashTree, Hash, HashTree,
};
use sha2::{Digest, Sha256};
impl CertifiedAssetHashes {
pub fn root_hash(&self) -> Hash {
fork_hash(
&labeled_hash(LABEL_ASSETS_V1, &self.tree_v1.root_hash()),
&labeled_hash(LABEL_ASSETS_V2, &self.tree_v2.root_hash()),
)
}
pub fn witness_v1(&self, path: &str) -> HashTree {
let witness = self.tree_v1.witness(path.as_bytes());
fork(
labeled(LABEL_ASSETS_V1, witness),
pruned(labeled_hash(LABEL_ASSETS_V2, &self.tree_v2.root_hash())),
)
}
pub fn witness_v2(&self, absolute_path: &str) -> HashTree {
assert!(absolute_path.starts_with('/'));
let segments = nested_tree_path(absolute_path, EXACT_MATCH_TERMINATOR);
let witness = self.tree_v2.witness(&segments);
fork(
pruned(labeled_hash(LABEL_ASSETS_V1, &self.tree_v1.root_hash())),
labeled(LABEL_ASSETS_V2, witness),
)
}
pub fn witness_rewrite_v2(&self, absolute_path: &str) -> HashTree {
assert!(absolute_path.starts_with('/'));
let segments = nested_tree_path(absolute_path, EXACT_MATCH_TERMINATOR);
let absence_proof = self.tree_v2.witness(&segments);
let fallback_paths = fallback_paths(segments.clone());
let combined_proof = fallback_paths
.into_iter()
.fold(absence_proof, |accumulator, path| {
let new_proof = self.tree_v2.witness(&path);
merge_hash_trees(accumulator, new_proof)
});
fork(
pruned(labeled_hash(LABEL_ASSETS_V1, &self.tree_v1.root_hash())),
labeled(LABEL_ASSETS_V2, combined_proof),
)
}
pub fn expr_path_v2(
&self,
absolute_path: &str,
rewrite_source: &Option<String>,
) -> Vec<String> {
match rewrite_source {
None => nested_tree_expr_path(absolute_path, EXACT_MATCH_TERMINATOR),
Some(rewrite_source) => {
nested_tree_expr_path(rewrite_source, WILDCARD_MATCH_TERMINATOR)
}
}
}
pub fn insert(&mut self, asset: &Asset, config: &StorageConfig) {
let full_path = asset.key.full_path.clone();
self.insert_most_important_v1(asset, &full_path);
self.insert_all_v2(asset, &full_path, config);
}
fn insert_most_important_v1(&mut self, asset: &Asset, full_path: &FullPath) {
for encoding_type in ENCODING_CERTIFICATION_ORDER.iter().rev() {
if let Some(encoding) = asset.encodings.get(*encoding_type) {
self.insert_v1(full_path, encoding.sha256);
return;
}
}
}
fn insert_all_v2(&mut self, asset: &Asset, full_path: &FullPath, config: &StorageConfig) {
for (encoding_type, encoding) in &asset.encodings {
self.insert_v2(
full_path,
&build_headers(asset, encoding, encoding_type, config),
RESPONSE_STATUS_CODE_200,
encoding.sha256,
);
}
}
fn insert_v1(&mut self, full_path: &FullPath, sha256: Hash) {
self.tree_v1.insert(full_path.clone(), sha256);
let alt_paths = alternative_paths(full_path);
match alt_paths {
None => (),
Some(alt_paths) => {
for alt_path in alt_paths {
self.tree_v1.insert(alt_path, sha256);
}
}
}
}
fn insert_v2(
&mut self,
full_path: &FullPath,
headers: &[HeaderField],
status_code: StatusCode,
sha256: Hash,
) {
self.tree_v2.insert(
&nested_tree_key(
full_path,
headers,
sha256,
EXACT_MATCH_TERMINATOR,
status_code,
),
vec![],
);
let alt_paths = alternative_paths(full_path);
match alt_paths {
None => (),
Some(alt_paths) => {
for alt_path in alt_paths {
self.tree_v2.insert(
&nested_tree_key(
&alt_path,
headers,
sha256,
EXACT_MATCH_TERMINATOR,
RESPONSE_STATUS_CODE_200,
),
vec![],
);
}
}
}
if *full_path == *ROOT_INDEX_HTML {
let has_404 = self
.tree_v2
.contains_path(&nested_tree_path(ROOT_404_HTML, WILDCARD_MATCH_TERMINATOR));
if !has_404 {
self.insert_rewrite_into_tree_v2(
&ROOT_PATH.to_string(),
headers,
sha256,
RESPONSE_STATUS_CODE_200,
);
}
}
if *full_path == *ROOT_404_HTML {
let index_tree_path = nested_tree_path(ROOT_INDEX_HTML, WILDCARD_MATCH_TERMINATOR);
let has_index = self.tree_v2.contains_path(&index_tree_path);
if has_index {
self.delete_from_tree_v2(&ROOT_PATH.to_string(), WILDCARD_MATCH_TERMINATOR);
}
self.insert_rewrite_into_tree_v2(
&ROOT_PATH.to_string(),
headers,
sha256,
RESPONSE_STATUS_CODE_404,
);
}
}
pub fn insert_redirect_v2(
&mut self,
full_path: &FullPath,
status_code: StatusCode,
location: &str,
iframe: &StorageConfigIFrame,
) {
let headers = build_redirect_headers(location, iframe);
let sha256 = Sha256::digest(Vec::new().clone()).into();
self.insert_v2(full_path, &headers, status_code, sha256);
}
pub fn insert_rewrite_v2(
&mut self,
full_path: &FullPath,
asset: &Asset,
config: &StorageConfig,
) {
for encoding_type in ENCODING_CERTIFICATION_ORDER.iter().rev() {
if let Some(encoding) = asset.encodings.get(*encoding_type) {
self.insert_rewrite_into_tree_v2(
full_path,
&build_headers(asset, encoding, &encoding_type.to_string(), config),
encoding.sha256,
RESPONSE_STATUS_CODE_200,
);
return;
}
}
}
fn insert_rewrite_into_tree_v2(
&mut self,
full_path: &FullPath,
headers: &[HeaderField],
sha256: Hash,
status_code: StatusCode,
) {
self.tree_v2.insert(
&nested_tree_key(
full_path,
headers,
sha256,
WILDCARD_MATCH_TERMINATOR,
status_code,
),
vec![],
);
}
pub fn delete(&mut self, asset: &Asset) {
let full_path = asset.key.full_path.clone();
self.delete_v1(&full_path);
self.delete_v2(&full_path);
}
fn delete_v1(&mut self, full_path: &String) {
self.tree_v1.delete(full_path.clone().as_bytes());
let alt_paths = alternative_paths(full_path);
match alt_paths {
None => (),
Some(alt_paths) => {
for alt_path in alt_paths {
self.tree_v1.delete(alt_path.as_bytes());
}
}
}
}
fn delete_v2(&mut self, full_path: &FullPath) {
self.delete_from_tree_v2(full_path, EXACT_MATCH_TERMINATOR);
let alt_paths = alternative_paths(full_path);
match alt_paths {
None => (),
Some(alt_paths) => {
for alt_path in alt_paths {
self.delete_from_tree_v2(&alt_path, EXACT_MATCH_TERMINATOR);
}
}
}
if *full_path == *ROOT_404_HTML {
self.delete_from_tree_v2(&ROOT_PATH.to_string(), WILDCARD_MATCH_TERMINATOR);
}
if *full_path == *ROOT_INDEX_HTML {
let has_404 = self
.tree_v2
.contains_path(&nested_tree_path(ROOT_404_HTML, WILDCARD_MATCH_TERMINATOR));
if !has_404 {
self.delete_from_tree_v2(&ROOT_PATH.to_string(), WILDCARD_MATCH_TERMINATOR);
}
}
}
fn delete_from_tree_v2(&mut self, full_path: &FullPath, terminator: &str) {
self.tree_v2
.delete(&nested_tree_path(full_path, terminator));
}
}