Skip to content

Commit

Permalink
feat(ic-asset-certification): allow overriding asset response status …
Browse files Browse the repository at this point in the history
…code (#394)

Needs #393 to be merged first for the CI to pass
  • Loading branch information
nathanosdev authored Nov 27, 2024
1 parent af036cd commit 7e3f70d
Show file tree
Hide file tree
Showing 25 changed files with 287 additions and 177 deletions.
1 change: 1 addition & 0 deletions examples/http-certification/assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ fn certify_all_assets() {
)]),
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: encodings.clone(),
Expand Down
5 changes: 4 additions & 1 deletion examples/http-certification/assets/src/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use ic_cdk::{
api::{data_certificate, set_certified_data},
*,
};
use ic_http_certification::{HeaderField, HttpCertificationTree, HttpRequest, HttpResponse};
use ic_http_certification::{
HeaderField, HttpCertificationTree, HttpRequest, HttpResponse, StatusCode,
};
use include_dir::{include_dir, Dir};
use std::{cell::RefCell, rc::Rc};

Expand Down Expand Up @@ -69,6 +71,7 @@ fn certify_all_assets() {
)]),
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: encodings.clone(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ic_cdk::{
use ic_http_certification::{
utils::add_v2_certificate_header, DefaultCelBuilder, DefaultResponseCertification,
DefaultResponseOnlyCelExpression, HeaderField, HttpCertification, HttpCertificationPath,
HttpCertificationTree, HttpCertificationTreeEntry, HttpRequest, HttpResponse,
HttpCertificationTree, HttpCertificationTreeEntry, HttpRequest, HttpResponse, StatusCode,
CERTIFICATE_EXPRESSION_HEADER_NAME,
};
use include_dir::{include_dir, Dir};
Expand Down Expand Up @@ -399,7 +399,7 @@ fn create_uncertified_response() -> HttpResponse<'static> {
);

HttpResponse::builder()
.with_status_code(200)
.with_status_code(StatusCode::OK)
.with_headers(headers)
.with_body(body)
.build()
Expand Down Expand Up @@ -436,7 +436,7 @@ fn create_asset_response(
let headers = get_asset_headers(additional_headers, body.len(), cel_expr);

HttpResponse::builder()
.with_status_code(200)
.with_status_code(StatusCode::OK)
.with_headers(headers)
.with_body(body)
.build()
Expand Down
18 changes: 9 additions & 9 deletions examples/http-certification/json-api/src/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ic_http_certification::{
utils::add_v2_certificate_header, DefaultCelBuilder, DefaultFullCelExpression,
DefaultResponseCertification, DefaultResponseOnlyCelExpression, HttpCertification,
HttpCertificationPath, HttpCertificationTree, HttpCertificationTreeEntry, HttpRequest,
HttpResponse, CERTIFICATE_EXPRESSION_HEADER_NAME,
HttpResponse, StatusCode, CERTIFICATE_EXPRESSION_HEADER_NAME,
};
use lazy_static::lazy_static;
use matchit::{Params, Router};
Expand Down Expand Up @@ -176,7 +176,7 @@ fn certify_list_todos_response() {
)
.encode()
});
let mut response = create_response(200, body);
let mut response = create_response(StatusCode::OK, body);

certify_response(request, &mut response, &TODOS_TREE_PATH);
}
Expand All @@ -191,15 +191,15 @@ fn certify_not_allowed_todo_responses() {
.build();

let body = ErrorResponse::not_allowed().encode();
let mut response = create_response(405, body);
let mut response = create_response(StatusCode::METHOD_NOT_ALLOWED, body);

certify_response(request, &mut response, &TODOS_TREE_PATH);
});
}

fn certify_not_found_response() {
let body = ErrorResponse::not_found().encode();
let mut response = create_response(404, body);
let mut response = create_response(StatusCode::NOT_FOUND, body);

let tree_path = HttpCertificationPath::wildcard(NOT_FOUND_PATH);

Expand Down Expand Up @@ -405,7 +405,7 @@ fn create_todo_item_handler(req: &HttpRequest, _params: &Params) -> HttpResponse
certify_list_todos_response();

let body = CreateTodoItemResponse::ok(&todo_item).encode();
create_response(201, body)
create_response(StatusCode::CREATED, body)
}

fn update_todo_item_handler(req: &HttpRequest, params: &Params) -> HttpResponse<'static> {
Expand All @@ -427,7 +427,7 @@ fn update_todo_item_handler(req: &HttpRequest, params: &Params) -> HttpResponse<
certify_list_todos_response();

let body = UpdateTodoItemResponse::ok(&()).encode();
create_response(200, body)
create_response(StatusCode::OK, body)
}

fn delete_todo_item_handler(_req: &HttpRequest, params: &Params) -> HttpResponse<'static> {
Expand All @@ -440,7 +440,7 @@ fn delete_todo_item_handler(_req: &HttpRequest, params: &Params) -> HttpResponse
certify_list_todos_response();

let body = DeleteTodoItemResponse::ok(&()).encode();
create_response(204, body)
create_response(StatusCode::NO_CONTENT, body)
}

fn upgrade_to_update_call_handler(
Expand All @@ -451,7 +451,7 @@ fn upgrade_to_update_call_handler(
}

fn no_update_call_handler(_http_request: &HttpRequest, _params: &Params) -> HttpResponse<'static> {
create_response(400, vec![])
create_response(StatusCode::BAD_REQUEST, vec![])
}

// Encoding
Expand All @@ -463,7 +463,7 @@ where
serde_json::from_slice(value).expect("Failed to deserialize value")
}

fn create_response(status_code: u16, body: Vec<u8>) -> HttpResponse<'static> {
fn create_response(status_code: StatusCode, body: Vec<u8>) -> HttpResponse<'static> {
HttpResponse::builder()
.with_status_code(status_code)
.with_headers(vec![
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ic_cdk::{
};
use ic_http_certification::{
utils::{add_skip_certification_header, skip_certification_certified_data},
HttpResponse,
HttpResponse, StatusCode,
};

#[init]
Expand Down Expand Up @@ -55,7 +55,7 @@ fn create_response() -> HttpResponse<'static> {
];

HttpResponse::builder()
.with_status_code(200)
.with_status_code(StatusCode::OK)
.with_headers(headers)
.with_body(body)
.build()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ic_cdk::*;
use ic_http_certification::{HttpResponse, HttpUpdateResponse};
use ic_http_certification::{HttpResponse, HttpUpdateResponse, StatusCode};

#[query]
fn http_request() -> HttpResponse<'static> {
Expand All @@ -9,7 +9,7 @@ fn http_request() -> HttpResponse<'static> {
#[update]
fn http_request_update() -> HttpUpdateResponse<'static> {
HttpResponse::builder()
.with_status_code(418)
.with_status_code(StatusCode::IM_A_TEAPOT)
.with_body(b"I'm a teapot")
.build_update()
}
28 changes: 15 additions & 13 deletions packages/ic-asset-certification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ This is implemented in the following steps:
2. [Configuring asset certification](#configuring-asset-certification)
3. [Inserting assets into the asset router](#inserting-assets-into-the-asset-router)
4. [Serving assets](#serving-assets)
5. [Deleting assets](#deleting-assets)
6. [Querying assets](#querying-assets)

For canisters that need it, it's also possible to [delete assets](#deleting-assets).

Expand Down Expand Up @@ -167,6 +169,7 @@ on the `/index.html` path, in addition to serving as the fallback for the `/`
scope and setting `/` as an alias for this asset.

```rust
use ic_http_certification::StatusCode;
use ic_asset_certification::{AssetConfig, AssetFallbackConfig};

let config = AssetConfig::File {
Expand All @@ -177,6 +180,7 @@ let config = AssetConfig::File {
],
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: vec![
Expand Down Expand Up @@ -206,6 +210,7 @@ Multiple aliases are also configured for this asset, namely:
Requests to any of those aliases will serve the `/404.html` asset.

```rust
use ic_http_certification::StatusCode;
use ic_asset_certification::{AssetConfig, AssetFallbackConfig};

let config = AssetConfig::File {
Expand All @@ -217,9 +222,11 @@ let config = AssetConfig::File {
fallback_for: vec![
AssetFallbackConfig {
scope: "/css".to_string(),
status_code: Some(StatusCode::NOT_FOUND),
},
AssetFallbackConfig {
scope: "/js".to_string(),
status_code: Some(StatusCode::NOT_FOUND),
},
],
aliased_by: vec![
Expand Down Expand Up @@ -269,6 +276,7 @@ For example, the following pattern will match all `.js` files in the `js`
directory:

```rust
use ic_http_certification::StatusCode;
use ic_asset_certification::AssetConfig;

let config = AssetConfig::Pattern {
Expand Down Expand Up @@ -331,6 +339,7 @@ the appropriate response.
Assets can be inserted using the `certify_assets` method:

```rust
use ic_http_certification::StatusCode;
use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind};

let mut asset_router = AssetRouter::default();
Expand Down Expand Up @@ -384,6 +393,7 @@ let asset_configs = vec![
)],
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: vec![
Expand Down Expand Up @@ -460,7 +470,7 @@ This method will return a response, a witness and an expression path, which can
alongside the canister's data certificate to add the required certificate header to the response.

```rust
use ic_http_certification::{HttpRequest, utils::add_v2_certificate_header};
use ic_http_certification::{HttpRequest, utils::add_v2_certificate_header, StatusCode};
use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter};

let mut asset_router = AssetRouter::default();
Expand All @@ -478,6 +488,7 @@ let asset_config = AssetConfig::File {
],
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: vec![],
Expand Down Expand Up @@ -522,6 +533,7 @@ only includes Brotli, then the Gzip file will not be deleted.
Using the same base example used to demonstrate certifying assets:

```rust
use ic_http_certification::StatusCode;
use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind, AssetEncoding};

let mut asset_router = AssetRouter::default();
Expand Down Expand Up @@ -575,6 +587,7 @@ let asset_configs = vec![
)],
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: vec![
Expand Down Expand Up @@ -619,10 +632,6 @@ asset_router.certify_assets(assets, asset_configs).unwrap();
To delete the `index.html` asset, along with the fallback configuration for the `/` scope, the alias `/` and the alternative encodings:

```rust
# use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind, AssetEncoding};

# let mut asset_router = AssetRouter::default();

asset_router
.delete_assets(
vec![
Expand All @@ -642,6 +651,7 @@ asset_router
)],
fallback_for: vec![AssetFallbackConfig {
scope: "/".to_string(),
status_code: Some(StatusCode::OK),
}],
aliased_by: vec!["/".to_string()],
encodings: vec![
Expand All @@ -656,10 +666,6 @@ asset_router
To delete the `app.js` asset, along with the alternative encodings:

```rust
# use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind, AssetEncoding};

# let mut asset_router = AssetRouter::default();

asset_router
.delete_assets(
vec![
Expand All @@ -686,10 +692,6 @@ asset_router
To delete the `css/app-ba74b708.css` asset, along with the alternative encodings:

```rust
# use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind, AssetEncoding};

# let mut asset_router = AssetRouter::default();

asset_router.delete_assets(
vec![
Asset::new(
Expand Down
12 changes: 12 additions & 0 deletions packages/ic-asset-certification/src/asset_config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{Asset, AssetCertificationError};
use globset::{Glob, GlobMatcher};
use ic_http_certification::StatusCode;
use std::fmt::{Display, Formatter};

/// Certification configuration for [assets](Asset). This configuration
Expand All @@ -22,6 +23,7 @@ use std::fmt::{Display, Formatter};
/// set to `text/javascript` and a `cache-control` header is added.
///
/// ```
/// use ic_http_certification::StatusCode;
/// use ic_asset_certification::{AssetConfig, AssetEncoding};
///
/// let config = AssetConfig::File {
Expand All @@ -48,6 +50,7 @@ use std::fmt::{Display, Formatter};
/// The content type is set to `text/html` and a `cache-control` header is added.
///
/// ```
/// use ic_http_certification::StatusCode;
/// use ic_asset_certification::{AssetConfig, AssetFallbackConfig, AssetEncoding};
///
/// let config = AssetConfig::File {
Expand All @@ -58,6 +61,7 @@ use std::fmt::{Display, Formatter};
/// ],
/// fallback_for: vec![AssetFallbackConfig {
/// scope: "/".to_string(),
/// status_code: Some(StatusCode::OK),
/// }],
/// aliased_by: vec!["/".to_string()],
/// encodings: vec![
Expand Down Expand Up @@ -88,6 +92,7 @@ use std::fmt::{Display, Formatter};
/// - `/not-found/index.html`
///
/// ```
/// use ic_http_certification::StatusCode;
/// use ic_asset_certification::{AssetConfig, AssetFallbackConfig, AssetEncoding};
///
/// let config = AssetConfig::File {
Expand All @@ -99,9 +104,11 @@ use std::fmt::{Display, Formatter};
/// fallback_for: vec![
/// AssetFallbackConfig {
/// scope: "/css".to_string(),
/// status_code: Some(StatusCode::NOT_FOUND),
/// },
/// AssetFallbackConfig {
/// scope: "/js".to_string(),
/// status_code: Some(StatusCode::NOT_FOUND),
/// },
/// ],
/// aliased_by: vec![
Expand All @@ -126,6 +133,7 @@ use std::fmt::{Display, Formatter};
/// set to `text/css` and a `cache-control` header is added.
///
/// ```
/// use ic_http_certification::StatusCode;
/// use ic_asset_certification::{AssetConfig, AssetEncoding};
///
/// let config = AssetConfig::Pattern {
Expand Down Expand Up @@ -371,6 +379,10 @@ pub struct AssetFallbackConfig {
/// See the [fallback_for](AssetConfig::File::fallback_for)
/// configuration of the [AssetConfig] interface for more information.
pub scope: String,

/// The HTTP status code to return when serving the asset.
/// If this value is not provided, the default status code will be 200.
pub status_code: Option<StatusCode>,
}

/// The type of redirect to use. Redirects can be either
Expand Down
Loading

0 comments on commit 7e3f70d

Please sign in to comment.