Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable serving of well-known assets that don't have extensions #2398

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/asset_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use lazy_static::lazy_static;
use serde::Serialize;
use sha2::Digest;
use std::collections::HashMap;
use std::path::Path;
use std::path::{Path, PathBuf};

pub const IC_CERTIFICATE_HEADER: &str = "IC-Certificate";
pub const IC_CERTIFICATE_EXPRESSION_HEADER: &str = "IC-CertificateExpression";
Expand Down Expand Up @@ -380,6 +380,14 @@ impl ContentType {

lazy_static! {
pub static ref EXPR_HASH: Hash = sha2::Sha256::digest(IC_CERTIFICATE_EXPRESSION).into();
// Some files served by a canister are by definitions required not to have extensions,
// so we white-list them and their predefined type & encoding.
static ref KNOWN_FILES: HashMap<PathBuf, (ContentType, ContentEncoding)> = {
let mut map = HashMap::new();
map.insert(Path::new(".well-known/ic-domains").to_owned(), (ContentType::JSON, ContentEncoding::Identity));
map.insert(Path::new(".well-known/ii-alternative-origins").to_owned(), (ContentType::JSON, ContentEncoding::Identity));
map
};
}

fn response_hash(status_code: u16, headers: &[HeaderField], body_hash: &Hash) -> Hash {
Expand Down Expand Up @@ -456,13 +464,17 @@ fn collect_assets_rec(dir: &Dir, assets: &mut Vec<Asset>) {
}
}

/// Returns the content type and the encoding type of the given file, based on the extension(s).
/// Returns the content type and the encoding type of the given file, either
/// because the file is on a "white-list" of known files, or based on the extension(s),
/// If the text after the last dot is "gz" (i.e. this is a gzipped file), then content type
/// is determined by the text after the second to last dot and the last dot in the file name,
/// e.g. `ContentType::JS` for "some.gzipped.file.js.gz", and the encoding is `ContentEncoding::GZip`.
/// Otherwise the content type is determined by the text after the last dot in the file name,
/// and the encoding is `ContentEncoding::Identity`.
fn content_type_and_encoding(asset_path: &Path) -> (ContentType, ContentEncoding) {
if let Some((content_type, content_encoding)) = KNOWN_FILES.get(asset_path) {
return (*content_type, *content_encoding);
}
let extension = asset_path
.extension()
.unwrap_or_else(|| panic!("Unsupported file without extension: {:?}", asset_path))
Expand Down Expand Up @@ -627,6 +639,16 @@ fn test_filepath_urlpaths() {
#[test]
fn should_return_correct_extension() {
let path_extension_encoding = [
(
".well-known/ic-domains",
ContentType::JSON,
ContentEncoding::Identity,
),
(
".well-known/ii-alternative-origins",
ContentType::JSON,
ContentEncoding::Identity,
),
(
"path1/some_css_file.css",
ContentType::CSS,
Expand Down
Loading