From bb1aef1364c822aeeac4fcb94e0c266e91b7c7f2 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 09:21:35 -0600
Subject: [PATCH 01/31] Update README.md
---
examples/http-certification/assets/README.md | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/examples/http-certification/assets/README.md b/examples/http-certification/assets/README.md
index 2d2c3c5..a0b9c74 100644
--- a/examples/http-certification/assets/README.md
+++ b/examples/http-certification/assets/README.md
@@ -1,7 +1,5 @@
# Serving static assets over HTTP
-## Overview
-
This guide walks through an example project that demonstrates how to create a canister that can serve certified static assets (HTML, CSS, JS) over HTTP. The example project presents a very simple single-page JavaScript application. Assets are embedded into the canister when it is compiled.
This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to asset certification will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/assets).
@@ -269,7 +267,7 @@ fn certify_all_assets() {
## Serving assets
-The `serve_asset` function is responsible for serving assets. It uses the `serve_asset` function from the `AssetRouter` to serve the assets. This function returns an `HttpResponse` that can be returned to the caller.
+The `serve_asset` function from the `AssetRouter` is responsible for serving assets. This function returns an `HttpResponse` that can be returned to the caller.
```rust
fn serve_asset(req: &HttpRequest) -> HttpResponse<'static> {
@@ -342,15 +340,10 @@ fn serve_metrics() -> HttpResponse<'static> {
## Testing the canister
-To test the canister, you can use the `dfx` command-line tool. First, run DFX:
+To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-```
-
-Then, deploy the canister:
-
-```shell
dfx deploy http_certification_assets_backend
```
From a9f75e9170492157a44b39bd0d20481d276d7187 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 09:44:42 -0600
Subject: [PATCH 02/31] Update README.md
---
.../http-certification/custom-assets/README.md | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md
index 850b5cb..8fa7686 100644
--- a/examples/http-certification/custom-assets/README.md
+++ b/examples/http-certification/custom-assets/README.md
@@ -1,7 +1,5 @@
# Serving static assets over HTTP (custom)
-## Overview
-
This guide walks through an example project that demonstrates how to create a canister that can serve certified static assets (HTML, CSS, JS) over HTTP. The example project presents a very simple single-page JavaScript application. Assets are embedded into the canister when it is compiled.
This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to HTTP Certification will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/custom-assets).
@@ -76,9 +74,9 @@ fn post_upgrade() {
}
```
-## CEL Expressions
+## CEL expressions
-The CEL expression definition is simpler in the case of assets compared to the [JSON API example](https://internetcomputer.org/docs/current/developer-docs/http-compatible-canisters/serving-json-over-http) as the same CEL expression is used for every asset, including the fallback response.
+The CEL expression definition is simpler in the case of assets compared to the [JSON API example](https://internetcomputer.org/docs/current/developer-docs/http-compatible-canisters/serving-json-over-http) as the same CEL expression is used for every asset including the fallback response.
```rust
lazy_static! {
@@ -199,7 +197,7 @@ fn certify_asset_response(
}
```
-The next function to look at is another reusable function to certify an asset with a specific encoding. This function will check for a file with an additional file extension matching the requested encoding in the statically included asset directory.
+Next is a reusable function to certify an asset with a specific encoding. This function will check for a file with an additional file extension matching the requested encoding in the statically included asset directory.
For example, when certifying `index.html` with `gzip` encoding, this function will check for `index.html.gzip`. If the encoded asset exists, then it is certified using a procedure similar to the previously defined `certify_asset_response` function. The primary difference in this function is where the encoded asset response is stored.
@@ -550,16 +548,11 @@ fn http_request(req: HttpRequest) -> HttpResponse {
## Testing the canister
-To test the canister, you can use the `dfx` command-line tool. First, run DFX:
+To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-```
-
-Then, deploy the canister:
-
-```shell
-dfx deploy http_certification_custom_assets_backend
+dfx deploy http_certification_assets_backend
```
You can now access the canister's assets by navigating to the canister's URL in a web browser. The URL can also be found using the following command:
From 396d50ff5153808009dc5ea3c114e88f209eb812 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 09:53:26 -0600
Subject: [PATCH 03/31] Update README.md
---
.../http-certification/json-api/README.md | 25 ++++++++-----------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/examples/http-certification/json-api/README.md b/examples/http-certification/json-api/README.md
index 12f7486..59f1de2 100644
--- a/examples/http-certification/json-api/README.md
+++ b/examples/http-certification/json-api/README.md
@@ -42,7 +42,7 @@ fn post_upgrade() {
CEL expressions only need to be set up once and can then be reused until the next canister upgrade. Responses can also be set up once and reused. If the response is static and will not change throughout the canister's lifetime, then it only needs to be certified once. If the response can change, however, then it will need to be re-certified every time it changes.
-`DefaultResponseOnlyCelExpression` is used when only the response is to be certified. If the request is also to be certified, then `DefaultFullCelExpression` should be used. Alternatively, the higher-level `DefaultCelExpression` can hold any type of CEL expression using the "Default" scheme. In the future, there may be more schemes, and the higher-level `CelExpression` will be able to hold CEL expressions from those different schemes. It is up to the developers to decide how they want to store and organize their CEL expressions.
+`DefaultResponseOnlyCelExpression` is used when only the response is to be certified. If the request is also to be certified, then `DefaultFullCelExpression` should be used. Alternatively, the higher-level `DefaultCelExpression` can hold any type of CEL expression using the "Default" scheme. In the future, there may be more schemes and the higher-level `CelExpression` will be able to hold CEL expressions from those different schemes. It is up to the developers to decide how they want to store and organize their CEL expressions.
In this example, there are two different CEL expressions used, a "full" CEL expression and a "response-only" CEL expression. The "full" CEL expression is used for the certified "todos" and the "response-only" CEL expression for the "Not found" response. For more information on defining CEL expressions, see the relevant section in the [`ic-http-certification` docs](https://docs.rs/ic-http-certification/latest/ic_http_certification/#defining-cel-expressions).
@@ -83,7 +83,7 @@ lazy_static! {
}
```
-## Response Headers
+## Response headers
The security headers added to responses are based on the [OWASP Secure Headers project](https://owasp.org/www-project-secure-headers/index.html).
@@ -380,7 +380,7 @@ fn upgrade_to_update_call_handler(
Upgrading to an `update` call will instruct the HTTP gateway to remake the request as an [`update` call](https://internetcomputer.org/docs/current/references/ic-interface-spec/#http-call). As an update call, the response to this request does not need to be certified. Since the canister's state has changed, however, the static `query` call responses will need to be re-certified. The same functions that certified these responses in the first place can be reused to achieve this.
-For creating todo items:
+For creating to-do items:
```rust
fn create_todo_item_handler(req: &HttpRequest, _params: &Params) -> HttpResponse<'static> {
@@ -411,7 +411,7 @@ fn create_todo_item_handler(req: &HttpRequest, _params: &Params) -> HttpResponse
}
```
-For updating todo items:
+For updating to-do items:
```rust
fn update_todo_item_handler(req: &HttpRequest, params: &Params) -> HttpResponse<'static> {
@@ -437,7 +437,7 @@ fn update_todo_item_handler(req: &HttpRequest, params: &Params) -> HttpResponse<
}
```
-And, finally, for deleting todo items:
+And, finally, for deleting to-do items:
```rust
fn delete_todo_item_handler(_req: &HttpRequest, params: &Params) -> HttpResponse<'static> {
@@ -524,19 +524,14 @@ fn insert_update_route(method: &str, path: &str, route_handler: RouteHandler) {
## Testing the canister
-To test the canister, you can use the `dfx` command-line tool. First, run DFX:
+To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-```
-
-Then, deploy the canister:
-
-```shell
dfx deploy
```
-To fetch TODO items:
+To fetch to-do items:
```shell
curl -s \
@@ -544,7 +539,7 @@ curl -s \
--resolve "$(dfx canister id http_certification_json_api_backend).localhost:$(dfx info webserver-port):127.0.0.1" | jq
```
-To add a TODO item:
+To add a to-do item:
```shell
curl -s -X POST \
@@ -554,7 +549,7 @@ curl -s -X POST \
-d '{ "title": "Learn Motoko" }' | jq
```
-To update a TODO item:
+To update a to-do item:
```shell
curl -s -X PATCH \
@@ -564,7 +559,7 @@ curl -s -X PATCH \
-d '{ "completed": true }' | jq
```
-To delete a TODO item:
+To delete a to-do item:
```shell
curl -s -X DELETE \
From d197e02f637c86e8aa3cedbfc15dddb0d7d78196 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 09:53:42 -0600
Subject: [PATCH 04/31] Update README.md
---
examples/http-certification/custom-assets/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md
index 8fa7686..e5fbb26 100644
--- a/examples/http-certification/custom-assets/README.md
+++ b/examples/http-certification/custom-assets/README.md
@@ -552,7 +552,7 @@ To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-
```shell
dfx start --background --clean
-dfx deploy http_certification_assets_backend
+dfx deploy
```
You can now access the canister's assets by navigating to the canister's URL in a web browser. The URL can also be found using the following command:
From ea827eb4640637e9d253f095cb2f3d20700f2281 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 09:53:56 -0600
Subject: [PATCH 05/31] Update README.md
---
examples/http-certification/assets/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/assets/README.md b/examples/http-certification/assets/README.md
index a0b9c74..edcb73e 100644
--- a/examples/http-certification/assets/README.md
+++ b/examples/http-certification/assets/README.md
@@ -344,7 +344,7 @@ To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-
```shell
dfx start --background --clean
-dfx deploy http_certification_assets_backend
+dfx deploy
```
You can now access the canister's assets by navigating to the canister's URL in a web browser. The URL can also be found using the following command:
From aca83d0b633d107e5472f2825b2fc093f0ad41ea Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 09:55:59 -0600
Subject: [PATCH 06/31] Update README.md
---
.../skip-certification/README.md | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index 21c807d..707d21c 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -1,10 +1,8 @@
# Skipping certification for HTTP responses
-## Overview
-
This guide walks through an example project that demonstrates how to skip HTTP certification for all possible responses from a canister.
-WARNING!!! This means that a malicious replica can return whatever data it wants in response to requests directed towards the canister. Think carefully about whether or not this is the right fit for the canister. If certification should only be skipped for certain paths, then check out the ["Serving static assets over HTTP"](https://internetcomputer.org/docs/current/developer-docs/web-apps/http-compatible-canisters/serving-static-assets-over-http) guide where this approach is covered in more detail.
+**WARNING** This means that a malicious replica can return whatever data it wants in response to requests directed towards the canister. Think carefully about whether or not this is the right fit for the canister. If certification should only be skipped for certain paths, then check out the ["Serving static assets over HTTP"](https://internetcomputer.org/docs/current/developer-docs/web-apps/http-compatible-canisters/serving-static-assets-over-http) guide where this approach is covered in more detail.
This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to HTTP Certification will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/skip-certification).
@@ -54,19 +52,14 @@ The call to `data_certificate` returns a certificate that proves the canister's
## Testing the canister
-Start DFX:
+To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
+dfx deploy
```
-Deploy the canister:
-
-```shell
-dfx deploy http_certification_skip_certification_backend
-```
-
-Make a request to the canister using cURL:
+Make a request to the canister using curl:
```shell
curl -s http://localhost:$(dfx info webserver-port)?canisterId=$(dfx canister id http_certification_skip_certification_backend) | jq
From 6abc4388cf96c83f586ff42eaf2607d3067578ac Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 10:03:23 -0600
Subject: [PATCH 07/31] Update README.md
---
.../upgrade-to-update-call/README.md | 20 ++++++-------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/examples/http-certification/upgrade-to-update-call/README.md b/examples/http-certification/upgrade-to-update-call/README.md
index fb4302e..e6d9fd1 100644
--- a/examples/http-certification/upgrade-to-update-call/README.md
+++ b/examples/http-certification/upgrade-to-update-call/README.md
@@ -1,10 +1,8 @@
# Upgrading HTTP calls to update calls
-## Overview
-
This guide walks through an example project that demonstrates how to use the ["Upgrade to Update call"](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#upgrade-to-update-calls) feature of the HTTP Gateway.
-Since browsers are unable to directly interact with the IC network, the HTTP Gateway acts as a bridge between the two. The HTTP Gateway forwards requests from clients to canisters and forwards responses from canisters back to clients. Before returning responses from canister back to clients, the HTTP Gateway verifies the certification of the response to ensure that they have not been tampered with.
+Since browsers are unable to directly interact with the ICP network, the HTTP Gateway acts as a bridge between the two. The HTTP Gateway forwards requests from clients to canisters and forwards responses from canisters back to clients. Before returning responses from canister back to clients, the HTTP Gateway verifies the certification of the response to ensure that they have not been tampered with.
Upgrading query calls to upgrade calls allows for the certification of any kind of dynamic response by leveraging ICP's consensus protocol without having to statically certify the response ahead of time. This is the simplest way to add _secure_ HTTP support to a canister.
@@ -22,7 +20,7 @@ Upon receiving a response from the canister with the `upgrade` field set to `opt
This example project features both Rust and Motoko code. If you rather follow the Motoko version, you can skip this section and go straight to the [section covering Motoko](#motoko).
-The Rust code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entry point for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)` (via the `build_update` method on the `HttpResponse::builder` struct). The `http_request_update` function is the entry point for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
+The Rust code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entrypoint for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)` (via the `build_update` method on the `HttpResponse::builder` struct). The `http_request_update` function is the entrypoint for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
```rust
use ic_cdk::*;
@@ -45,7 +43,7 @@ fn http_request_update() -> HttpUpdateResponse<'static> {
## Motoko
-The Motoko code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entry point for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)`. The `http_request_update` function is the entry point for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
+The Motoko code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entrypoint for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)`. The `http_request_update` function is the entrypoint for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
```motoko
import Text "mo:base/Text";
@@ -102,21 +100,15 @@ actor Http {
## Testing the canister
-Start DFX:
+To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-```
-Deploy the Rust canister:
-
-```shell
+## Deploy the Rust canister
dfx deploy http_certification_upgrade_to_update_call_rust_backend
-```
-
-Or deploy the Motoko canister:
-```shell
+## Deploy the Motoko canister
dfx deploy http_certification_upgrade_to_update_call_motoko_backend
```
From 85fbd33f6bbe0e2c2f53584ba81a0e7a0fbdabda Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 10:30:12 -0600
Subject: [PATCH 08/31] Update README.md
---
packages/ic-asset-certification/README.md | 53 ++++++++++-------------
1 file changed, 24 insertions(+), 29 deletions(-)
diff --git a/packages/ic-asset-certification/README.md b/packages/ic-asset-certification/README.md
index de6ed51..18d247d 100644
--- a/packages/ic-asset-certification/README.md
+++ b/packages/ic-asset-certification/README.md
@@ -1,35 +1,32 @@
-# Asset Certification
-
-## Overview
+# Asset certification
Asset certification is a specialized form of
-[HTTP Certification](https://internetcomputer.org/docs/current/developer-docs/http-compatible-canisters/custom-http-canisters)
-purpose-built for certifying static assets in [ICP](https://internetcomputer.org/) canisters.
+[HTTP certification](https://internetcomputer.org/docs/current/developer-docs/http-compatible-canisters/custom-http-canisters)
+built for certifying static assets in [ICP](https://internetcomputer.org/) canisters.
The `ic-asset-certification` crate provides the necessary functionality to
certify and serve static assets from Rust canisters.
This is implemented in the following steps:
-1. [Preparing assets](#preparing-assets)
-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)
+1. [Preparing assets](#preparing-assets).
+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).
## Preparing assets
-This library is unopinionated about where assets come from, so that is not
-covered in detail here, but there are three main options:
+This library is unopinionated about where assets come from. However, there are three main options:
- Embedding assets in the canister at compile time:
- [include_bytes!](https://doc.rust-lang.org/std/macro.include_bytes.html)
- [include_dir!](https://docs.rs/include_dir/latest/include_dir/index.html)
-- Uploading assets via canister endpoints at runtime.
- - The [DFX asset canister](https://github.com/dfinity/sdk/blob/master/docs/design/asset-canister-interface.md) is a good example of this approach.
+- Uploading assets via canister endpoints at runtime:
+ - The [`dfx` asset canister](https://github.com/dfinity/sdk/blob/master/docs/design/asset-canister-interface.md) is a good example of this approach.
- Generating assets dynamically in code, at runtime.
With the assets in memory, they can be converted into the `Asset` type:
@@ -43,8 +40,8 @@ let asset = Asset::new(
);
```
-It is recommended to use references when including assets directly into the
-canister, to avoid duplicating the content. This is particularly important for
+It is recommended to use references when including assets directly in the
+canister to avoid duplicating the content. This is particularly important for
larger assets.
```rust
@@ -73,7 +70,7 @@ let asset = Asset::new(
## Configuring asset certification
`AssetConfig` defines the configuration for any files that will be certified.
-The configuration can either be matched to an individual file by path, or to
+The configuration can either be matched to an individual file by path or to
many files by a glob.
In both cases, the following options can be configured for each asset:
@@ -133,7 +130,7 @@ match the path passed into the `Asset` constructor in the previous step.
In addition to the common configuration options, individual assets also have
the option of registering the asset as a fallback response for a particular
scope. This can be used to configure 404 pages or single-page application
-entry points, for example.
+entrypoints, for example.
When serving assets, if a requested path does not exactly match any assets then
a search is conducted for an asset configured with the fallback scope that most
@@ -191,7 +188,7 @@ let config = AssetConfig::File {
```
It's also possible to configure multiple fallbacks for a single asset. The
-following example configures an individual HTML file to be served by the on the
+following example configures an individual HTML file to be served on the
`/404.html` path, in addition to serving as the fallback for the `/js` and `/css`
scopes.
@@ -529,20 +526,20 @@ There are three ways to delete assets from the asset router:
### Deleting assets by configuration
-Deleting assets by configuration is similar to (certifying them)[#inserting-assets-into-the-asset-router].
+Deleting assets by configuration is similar to [certifying them](#inserting-assets-into-the-asset-router).
Depending on the configuration provided to the `certify_assets` function,
multiple responses may be generated for the same asset. To ensure that all generated responses are deleted,
the `delete_assets` function accepts the same configuration.
If a configuration different to the one used to certify assets in the first place is provided,
-one of two things can happen.
+one of two things can happen:
-If the configuration inclues a file that was not certified in the first place, it will be silently ignored.
+1. If the configuration inclues a file that was not certified in the first place, it will be silently ignored.
For example, if the configuration provided to `certify_assets` includes the Brotli and Gzip encodings, but the
configuration provided to `delete_assets` includes Brotli, Gzip and Deflate, the Brotli and Gzip encoded files will be deleted, while the Deflate file is ignored, since it doesn't exist.
-If the configuration excludes a file that was certified, it will not be deleted. For example, if the configuration,
+2. If the configuration excludes a file that was certified, it will not be deleted. For example, if the configuration,
provided to `certify_assets` includes the Brotli and Gzip encodings, but the configuration provided to `delete_assets`
only includes Brotli, then the Gzip file will not be deleted.
@@ -774,18 +771,16 @@ set_certified_data(&asset_router.root_hash());
### Deleting assets by path
-To delete assets by path, use the
-[delete_assets_by_path](AssetRouter::delete_assets_by_path) function.
+To delete assets by path, use the `delete_assets_by_path` function.
-Depending on the configuration provided to the [certify_assets](AssetRouter::certify_assets) function,
+Depending on the configuration provided to the `certify_assets` function,
multiple responses may be generated for the same asset. These assets may exist on different paths,
for example if the `alias` configuration is used. If `alias` paths are not passed to this function,
they will not be deleted.
If multiple encodings exist for a path, all encodings will be deleted.
-Fallbacks are also not deleted, to delete them, use the
-[delete_fallback_assets_by_path](AssetRouter::delete_fallback_assets_by_path) function.
+Fallbacks are also not deleted, to delete them, use the `delete_fallback_assets_by_path` function.
Assuming the same base example used above to demonstrate certifying assets:
From 5c9d436bd8903328df16dc28b023164c48c75545 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 10:50:23 -0600
Subject: [PATCH 09/31] Update README.md
---
packages/ic-http-certification/README.md | 428 +++++++++++------------
1 file changed, 213 insertions(+), 215 deletions(-)
diff --git a/packages/ic-http-certification/README.md b/packages/ic-http-certification/README.md
index 2fbec3c..0c83b11 100644
--- a/packages/ic-http-certification/README.md
+++ b/packages/ic-http-certification/README.md
@@ -1,14 +1,12 @@
# HTTP certification
-## Overview
-
HTTP certification is a sub-protocol of the [ICP](https://internetcomputer.org/) [HTTP gateway protocol](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec). It is used to verify HTTP responses received by an HTTP gateway from a [canister](https://internetcomputer.org/how-it-works/canister-lifecycle/), with respect to the corresponding HTTP request. This allows HTTP gateways to verify that the responses they receive from canisters are authentic and have not been tampered with.
The `ic-http-certification` crate provides the foundation for implementing the HTTP certification protocol in Rust canisters. Certification is implemented in a number of steps:
-1. [Defining CEL expressions](#defining-cel-expressions)
-2. [Creating certifications](#creating-certifications)
-3. [Creating an HTTP certification tree](#creating-an-http-certification-tree)
+1. [Defining CEL expressions](#defining-cel-expressions).
+2. [Creating certifications](#creating-certifications).
+3. [Creating an HTTP certification tree](#creating-an-http-certification-tree).
## Defining CEL expressions
@@ -57,20 +55,20 @@ Note that if the request is certified, the response must also be certified. It i
When a request is certified:
- The request body and method are always certified.
-- The request headers and query parameters are optionally certified using the `with_request_headers` and `with_request_query_parameters` associated functions respectively. Both associated functions take a `str` slice as an argument.
+- The request headers and query parameters are optionally certified using the `with_request_headers` and `with_request_query_parameters` associated functions, respectively. Both associated functions take a `str` slice as an argument.
When a response is certified:
- The response body and status code are always certified.
- The response headers are optionally certified using the `with_response_certification` associated function. This function takes the `DefaultResponseCertification` enum as an argument.
- - To specify header inclusions, use the `certified_response_headers` associated function of the `DefaultResponseCertification` enum.
- - To certify all response headers (with some optional exclusions) use the `response_header_exclusions` associated function of the `DefaultResponseCertification` enum. Both functions take a `str` slice as an argument.
+ - To specify header inclusions, use the `certified_response_headers` associated function of the `DefaultResponseCertification` enum.
+ - To certify all response headers (with some optional exclusions), use the `response_header_exclusions` associated function of the `DefaultResponseCertification` enum. Both functions take a `str` slice as an argument.
-Regardless of what is included in certification, the request path is always used to determine if that certification should be used. It's also possible to set a certification for a "scope" or "directory" of paths, see [Defining tree paths](#defining-tree-paths) for more information on this.
+Regardless of what is included in certification, the request path is always used to determine if that certification should be used. It's also possible to set a certification for a "scope" or "directory" of paths; see [Defining tree paths](#defining-tree-paths) for more information on this.
-When defining CEL expressions it's important to determine what should be certified and what can be safely excluded from certification. For example, if a response header is not certified, it will not be included in the certification and will not be verified by the HTTP gateway, meaning that the value of this header cannot be trusted by clients. As a general rule of thumb, starting with a fully certified request and response pair is a good idea, and then removing parts of the certification as needed.
+When defining CEL expressions, it's important to determine what should be certified and what can be safely excluded from certification. For example, if a response header is not certified, it will not be included in the certification and will not be verified by the HTTP gateway, meaning that the value of this header cannot be trusted by clients. As a general rule of thumb, starting with a fully certified request and response pair is a good idea and then removing parts of the certification as needed.
-It should be considered unsafe to exclude anything from request certification that can change the expected response. The request method for example can drastically affect what action is taken by the canister, and so excluding it from certification would allow a malicious replica to respond with the expected responses for `'GET'` request, even though a `'POST'` request was made.
+It should be considered unsafe to exclude anything from request certification that can change the expected response. The request method, for example, can drastically affect what action is taken by the canister, and so excluding it from certification would allow a malicious replica to respond with the expected responses for a `'GET'` request, even though a `'POST'` request was made.
For responses, it should be considered unsafe to exclude anything from response certification that will be used by clients in a meaningful way. For example, excluding the `Content-Type` header from certification would allow a malicious replica to respond with a different content type than expected, which could cause clients to misinterpret the response.
@@ -84,18 +82,18 @@ For example:
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::full_certification()
- .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
- .with_request_query_parameters(vec!["foo", "bar", "baz"])
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
- "Cache-Control",
- "ETag",
- ]))
- .build();
+ .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
+ .with_request_query_parameters(vec!["foo", "bar", "baz"])
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
+ "Cache-Control",
+ "ETag",
+ ]))
+ .build();
```
#### Partially certified request
-Any number of request headers or request query parameters can be certified via `with_request_headers` and `with_request_query_parameters` respectively. Both methods will accept empty arrays, which is the same as not calling them at all. Likewise for `with_request_query_parameters`, if it is called with an empty array, or not called at all, then no request query parameters will be certified. If both are called with an empty array, or neither are called, then only the request body and method will be certified, in addition to the response. As a reminder here, the response is always at least partially certified if the request is certified.
+Any number of request headers or request query parameters can be certified via `with_request_headers` and `with_request_query_parameters` respectively. Both methods will accept empty arrays, which is the same as not calling them at all. Likewise for `with_request_query_parameters`, if it is called with an empty array or not called at all, then no request query parameters will be certified. If both are called with an empty array, or neither is called, then only the request body and method will be certified, in addition to the response. As a reminder here, the response is always at least partially certified if the request is certified.
For example, to certify only the request body and method, in addition to the response:
@@ -103,11 +101,11 @@ For example, to certify only the request body and method, in addition to the res
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::full_certification()
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
- "Cache-Control",
- "ETag",
- ]))
- .build();
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
+ "Cache-Control",
+ "ETag",
+ ]))
+ .build();
```
Alternatively, this can be done more explicitly:
@@ -116,13 +114,13 @@ Alternatively, this can be done more explicitly:
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::full_certification()
- .with_request_headers(vec![])
- .with_request_query_parameters(vec![])
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
- "Cache-Control",
- "ETag",
- ]))
- .build();
+ .with_request_headers(vec![])
+ .with_request_query_parameters(vec![])
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
+ "Cache-Control",
+ "ETag",
+ ]))
+ .build();
```
#### Skipping request certification
@@ -135,12 +133,12 @@ For example:
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::response_only_certification()
- .with_response_certification(DefaultResponseCertification::response_header_exclusions(vec![
- "Date",
- "Cookie",
- "Set-Cookie",
- ]))
- .build();
+ .with_response_certification(DefaultResponseCertification::response_header_exclusions(vec![
+ "Date",
+ "Cookie",
+ "Set-Cookie",
+ ]))
+ .build();
```
#### Partially certified response
@@ -161,19 +159,19 @@ This can also be done more explicitly:
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::response_only_certification()
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![]))
- .build();
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![]))
+ .build();
```
-The same applies when both when using `DefaultCelBuilder::response_only_certification` and `DefaultCelBuilder::full_certification`:
+The same applies when using `DefaultCelBuilder::response_only_certification` and `DefaultCelBuilder::full_certification`:
```rust
use ic_http_certification::DefaultCelBuilder;
let cel_expr = DefaultCelBuilder::full_certification()
- .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
- .with_request_query_parameters(vec!["foo", "bar", "baz"])
- .build();
+ .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
+ .with_request_query_parameters(vec!["foo", "bar", "baz"])
+ .build();
```
To skip response certification completely, certification overall must be skipped completely. It wouldn't be useful to certify a request without certifying a response.
@@ -188,7 +186,7 @@ use ic_http_certification::DefaultCelBuilder;
let cel_expr = DefaultCelBuilder::skip_certification();
```
-Skipping certification may seem counter-intuitive at first, but it is not always possible to certify a request and response pair. For example, a canister method that will return different data for every user cannot be easily certified.
+Skipping certification may seem counterintuitive at first, but it is not always possible to certify a request and response pair. For example, a canister method that will return different data for every user cannot be easily certified.
Typically, these requests have been routed through `raw` ICP URLs in the past, but this is dangerous because `raw` URLs allow any responding replica to decide whether or not certification is required. In contrast, by skipping certification using the above method with a non-`raw` URL, a replica will no longer be able to decide whether or not certification is required and instead this decision will be made by the canister itself and the result will go through consensus.
@@ -204,7 +202,7 @@ Once a CEL expression has been defined, it can be used in conjunction with an `H
### Full certification
-To perform a full certification, a CEL expression created from `DefaultCelBuilder::full_certification` is required, along with an `HttpRequest` and `HttpResponse` and optionally, a pre-calculated response body hash.
+To perform a full certification, a CEL expression created from `DefaultCelBuilder::full_certification` is required, along with an `HttpRequest` and `HttpResponse`, and optionally, a pre-calculated response body hash.
For example:
@@ -212,34 +210,34 @@ For example:
use ic_http_certification::{HttpCertification, HttpRequest, HttpResponse, DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::full_certification()
- .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
- .with_request_query_parameters(vec!["foo", "bar", "baz"])
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
- "Cache-Control",
- "ETag",
- ]))
- .build();
+ .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
+ .with_request_query_parameters(vec!["foo", "bar", "baz"])
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
+ "Cache-Control",
+ "ETag",
+ ]))
+ .build();
let request = HttpRequest {
- method: "GET".to_string(),
- url: "/index.html?foo=a&bar=b&baz=c".to_string(),
- headers: vec![
- ("Accept".to_string(), "application/json".to_string()),
- ("Accept-Encoding".to_string(), "gzip".to_string()),
- ("If-None-Match".to_string(), "987654321".to_string()),
- ],
- body: vec![],
+ method: "GET".to_string(),
+ url: "/index.html?foo=a&bar=b&baz=c".to_string(),
+ headers: vec![
+ ("Accept".to_string(), "application/json".to_string()),
+ ("Accept-Encoding".to_string(), "gzip".to_string()),
+ ("If-None-Match".to_string(), "987654321".to_string()),
+ ],
+ body: vec![],
};
let response = HttpResponse {
- status_code: 200,
- headers: vec![
- ("Cache-Control".to_string(), "no-cache".to_string()),
- ("ETag".to_string(), "123456789".to_string()),
- ("IC-CertificateExpression".to_string(), cel_expr.to_string()),
- ],
- body: vec![1, 2, 3, 4, 5, 6],
- upgrade: None,
+ status_code: 200,
+ headers: vec![
+ ("Cache-Control".to_string(), "no-cache".to_string()),
+ ("ETag".to_string(), "123456789".to_string()),
+ ("IC-CertificateExpression".to_string(), cel_expr.to_string()),
+ ],
+ body: vec![1, 2, 3, 4, 5, 6],
+ upgrade: None,
};
let certification = HttpCertification::full(&cel_expr, &request, &response, None);
@@ -247,7 +245,7 @@ let certification = HttpCertification::full(&cel_expr, &request, &response, None
### Response-only certification
-To perform a response-only certification, a CEL expression created from `DefaultCelBuilder::response_only_certification` is required, along with an `HttpResponse` and optionally, a pre-calculated response body hash.
+To perform a response-only certification, a CEL expression created from `DefaultCelBuilder::response_only_certification` is required, along with an `HttpResponse` and, optionally, a pre-calculated response body hash.
For example:
@@ -255,21 +253,21 @@ For example:
use ic_http_certification::{HttpCertification, HttpResponse, DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::response_only_certification()
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
- "Cache-Control",
- "ETag",
- ]))
- .build();
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
+ "Cache-Control",
+ "ETag",
+ ]))
+ .build();
let response = HttpResponse {
- status_code: 200,
- headers: vec![
- ("Cache-Control".to_string(), "no-cache".to_string()),
- ("ETag".to_string(), "123456789".to_string()),
- ("IC-CertificateExpression".to_string(), cel_expr.to_string()),
- ],
- body: vec![1, 2, 3, 4, 5, 6],
- upgrade: None,
+ status_code: 200,
+ headers: vec![
+ ("Cache-Control".to_string(), "no-cache".to_string()),
+ ("ETag".to_string(), "123456789".to_string()),
+ ("IC-CertificateExpression".to_string(), cel_expr.to_string()),
+ ],
+ body: vec![1, 2, 3, 4, 5, 6],
+ upgrade: None,
};
let certification = HttpCertification::response_only(&cel_expr, &response, None).unwrap();
@@ -293,9 +291,9 @@ let certification = HttpCertification::skip();
Paths for the tree can be defined using the `HttpCertificationPath` struct and come in two types: `wildcard()` and `exact()`. Both types of paths may end with or without a trailing slash, but note that a path ending in a trailing slash is a distinct path from one that does not end with a trailing slash, and they will be treated as such by the tree.
-Wildcard paths can be used to match a sub-path of a request URL. This can be useful for 404 responses, fallbacks or rewrites. They are defined using the `wildcard()` associated function.
+Wildcard paths can be used to match a sub-path of a request URL. This can be useful for 404 responses, fallbacks, or rewrites. They are defined using the `wildcard()` associated function.
-In this example, the certification entered into the tree with this path will be valid for any request URL that begins with `/js`, unless there is a more specific path in the tree (ex. `/js/example.js`).
+In this example, the certification entered into the tree with this path will be valid for any request URL that begins with `/js`, unless there is a more specific path in the tree (e.g., `/js/example.js`).
```rust
use ic_http_certification::HttpCertificationPath;
@@ -303,7 +301,7 @@ use ic_http_certification::HttpCertificationPath;
let path = HttpCertificationPath::wildcard("/js");
```
-Exact paths are used to match an entire request URL. An exact path ending with a trailing slash refers to a file system directory, where as one without a trailing slash refers to an individual file. Both are separate paths within the certification tree and will be treated completely independently.
+Exact paths are used to match an entire request URL. An exact path ending with a trailing slash refers to a file system directory, whereas one without a trailing slash refers to an individual file. Both are separate paths within the certification tree and will be treated completely independently.
In this example, the certification entered into the tree with this path will only be valid for a request URL that is exactly `/js/example.js`.
@@ -315,7 +313,7 @@ let path = HttpCertificationPath::exact("/js/example.js");
### Using the HTTP certification tree
-The `HttpCertificationTree` can be easily initialized with the `Default` trait and entries can be added to, removed from, or have witnesses generated by the tree using the `HttpCertificationTreeEntry` struct. The `HttpCertificationTreeEntry` requires a `HttpCertification` and an `HttpCertificationPath`.
+The `HttpCertificationTree` can be easily initialized with the `Default` trait, and entries can be added to, removed from, or have witnesses generated by the tree using the `HttpCertificationTreeEntry` struct. The `HttpCertificationTreeEntry` requires a `HttpCertification` and an `HttpCertificationPath`.
For example:
@@ -323,34 +321,34 @@ For example:
use ic_http_certification::{HttpCertification, HttpRequest, HttpResponse, DefaultCelBuilder, DefaultResponseCertification, HttpCertificationTree, HttpCertificationTreeEntry, HttpCertificationPath};
let cel_expr = DefaultCelBuilder::full_certification()
- .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
- .with_request_query_parameters(vec!["foo", "bar", "baz"])
- .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
- "Cache-Control",
- "ETag",
- ]))
- .build();
+ .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
+ .with_request_query_parameters(vec!["foo", "bar", "baz"])
+ .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
+ "Cache-Control",
+ "ETag",
+ ]))
+ .build();
let request = HttpRequest {
- method: "GET".to_string(),
- url: "/index.html?foo=a&bar=b&baz=c".to_string(),
- headers: vec![
- ("Accept".to_string(), "application/json".to_string()),
- ("Accept-Encoding".to_string(), "gzip".to_string()),
- ("If-None-Match".to_string(), "987654321".to_string()),
- ],
- body: vec![],
+ method: "GET".to_string(),
+ url: "/index.html?foo=a&bar=b&baz=c".to_string(),
+ headers: vec![
+ ("Accept".to_string(), "application/json".to_string()),
+ ("Accept-Encoding".to_string(), "gzip".to_string()),
+ ("If-None-Match".to_string(), "987654321".to_string()),
+ ],
+ body: vec![],
};
let response = HttpResponse {
- status_code: 200,
- headers: vec![
- ("Cache-Control".to_string(), "no-cache".to_string()),
- ("ETag".to_string(), "123456789".to_string()),
- ("IC-CertificateExpression".to_string(), cel_expr.to_string()),
- ],
- body: vec![1, 2, 3, 4, 5, 6],
- upgrade: None,
+ status_code: 200,
+ headers: vec![
+ ("Cache-Control".to_string(), "no-cache".to_string()),
+ ("ETag".to_string(), "123456789".to_string()),
+ ("IC-CertificateExpression".to_string(), cel_expr.to_string()),
+ ],
+ body: vec![1, 2, 3, 4, 5, 6],
+ upgrade: None,
};
let request_url = "/example.json";
@@ -373,11 +371,11 @@ http_certification_tree.delete(&entry);
### Handling upgrades
-CEL expressions, certifications, the certification tree, and the corresponding requests and responses are not persisted across upgrades, by default. This means that if a canister is upgraded, all of this information will be lost. To handle upgrades effectively, all initialization logic run in the canister's `init` hook should also be run in the `post_upgrade` hook. This will ensure that the certification tree is correctly re-initialized after an upgrade. Most data structures, aside from the certification tree, can be persisted using stable memory, and the certification tree can be re-initialized using this persisted data. Care should be taken to not exceed the canister's instruction limit when re-initializing the certification tree, which can easily occur if the number of responses being certified grows very large. This case could potentially be addressed in the future by developing a stable memory compatible certification tree.
+CEL expressions, certifications, the certification tree, and the corresponding requests and responses are not persisted across upgrades, by default. This means that if a canister is upgraded, all of this information will be lost. To handle upgrades effectively, all initialization logic run in the canister's `init` hook should also be run in the `post_upgrade` hook. This will ensure that the certification tree is correctly re-initialized after an upgrade. Most data structures, aside from the certification tree, can be persisted using stable memory, and the certification tree can be re-initialized using this persisted data. Care should be taken to not exceed the canister's instruction limit when re-initializing the certification tree, which can easily occur if the number of responses being certified grows very large. This case could potentially be addressed in the future by developing a stable memory-compatible certification tree.
### Changing data
-In addition to initializing certifications in the `init` and `post_upgrade` hooks, if a response is changed during the canister's lifetime in response to an `update` call, the certification tree should be updated to reflect this change. This can be done by deleting the old certification from the tree and inserting the new certification. This should be done in the same `update` call as the response is changed to ensure that the certification tree is always up-to-date, otherwise, `query` calls returning that response will fail verification.
+In addition to initializing certifications in the `init` and `post_upgrade` hooks, if a response is changed during the canister's lifetime in response to an `update` call, the certification tree should be updated to reflect this change. This can be done by deleting the old certification from the tree and inserting the new certification. This should be done in the same `update` call as the response is changed to ensure that the certification tree is always up-to-date; otherwise, `query` calls returning that response will fail verification.
## Directly creating a CEL expression
@@ -406,42 +404,42 @@ use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
- DefaultFullCelExpression {
- request: DefaultRequestCertification::new(
- vec!["Accept", "Accept-Encoding", "If-None-Match"],
- vec!["foo", "bar", "baz"],
- ),
- response: DefaultResponseCertification::certified_response_headers(vec![
- "ETag",
- "Cache-Control",
- ]),
- }));
+ DefaultFullCelExpression {
+ request: DefaultRequestCertification::new(
+ vec!["Accept", "Accept-Encoding", "If-None-Match"],
+ vec!["foo", "bar", "baz"],
+ ),
+ response: DefaultResponseCertification::certified_response_headers(vec![
+ "ETag",
+ "Cache-Control",
+ ]),
+ }));
```
This will produce the following CEL expression:
```protobuf
default_certification (
- ValidationArgs {
- request_certification: RequestCertification {
- certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
- certified_query_parameters: ["foo", "bar", "baz"]
- },
- response_certification: ResponseCertification {
- certified_response_headers: ResponseHeaderList {
- headers: [
- "ETag",
- "Cache-Control"
- ]
- }
- }
- }
+ ValidationArgs {
+ request_certification: RequestCertification {
+ certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
+ certified_query_parameters: ["foo", "bar", "baz"]
+ },
+ response_certification: ResponseCertification {
+ certified_response_headers: ResponseHeaderList {
+ headers: [
+ "ETag",
+ "Cache-Control"
+ ]
+ }
+ }
+ }
)
```
### Partially certified request
-Any number of request headers or query parameters can be provided via the `headers` and `query_parameters` fields of the `DefaultRequestCertification` struct, and both can be an empty array. If the `headers` field is empty, no request headers will be certified. Likewise for the `query_parameters` field, if it is empty then no query parameters will be certified. If both are empty, only the request body and method will be certified.
+Any number of request headers or query parameters can be provided via the `headers` and `query_parameters` fields of the `DefaultRequestCertification` struct, and both can be an empty array. If the `headers` field is empty, no request headers will be certified. Likewise for the `query_parameters` field, if it is empty, then no query parameters will be certified. If both are empty, only the request body and method will be certified.
For example, to certify only the request body and method:
@@ -450,36 +448,36 @@ use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
- DefaultFullCelExpression {
- request: DefaultRequestCertification::new(
- vec![],
- vec![],
- ),
- response: DefaultResponseCertification::certified_response_headers(vec![
- "ETag",
- "Cache-Control",
- ]),
- }));
+ DefaultFullCelExpression {
+ request: DefaultRequestCertification::new(
+ vec![],
+ vec![],
+ ),
+ response: DefaultResponseCertification::certified_response_headers(vec![
+ "ETag",
+ "Cache-Control",
+ ]),
+ }));
```
This will produce the following CEL expression:
```protobuf
default_certification (
- ValidationArgs {
- request_certification: RequestCertification {
- certified_request_headers: [],
- certified_query_parameters: []
- },
- response_certification: ResponseCertification {
- certified_response_headers: ResponseHeaderList {
- headers: [
- "ETag",
- "Cache-Control"
- ]
- }
- }
- }
+ ValidationArgs {
+ request_certification: RequestCertification {
+ certified_request_headers: [],
+ certified_query_parameters: []
+ },
+ response_certification: ResponseCertification {
+ certified_response_headers: ResponseHeaderList {
+ headers: [
+ "ETag",
+ "Cache-Control"
+ ]
+ }
+ }
+ }
)
```
@@ -494,35 +492,35 @@ use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultResponseOnlyCelExpression, DefaultResponseCertification};
let cel_expr = CelExpression::Default(DefaultCelExpression::ResponseOnly(
- DefaultResponseOnlyCelExpression {
- response: DefaultResponseCertification::certified_response_headers(vec![
- "ETag",
- "Cache-Control",
- ]),
- }));
+ DefaultResponseOnlyCelExpression {
+ response: DefaultResponseCertification::certified_response_headers(vec![
+ "ETag",
+ "Cache-Control",
+ ]),
+ }));
```
This will produce the following CEL expression:
```protobuf
default_certification (
- ValidationArgs {
- no_request_certification: Empty {},
- response_certification: ResponseCertification {
- certified_response_headers: ResponseHeaderList {
- headers: [
- "ETag",
- "Cache-Control"
- ]
- }
- }
- }
+ ValidationArgs {
+ no_request_certification: Empty {},
+ response_certification: ResponseCertification {
+ certified_response_headers: ResponseHeaderList {
+ headers: [
+ "ETag",
+ "Cache-Control"
+ ]
+ }
+ }
+ }
)
```
### Partially certified response
-Similiarly to request certification, any number of response headers can be provided via the `certified_response_headers` associated function of the `DefaultResponseCertification` enum, and it can also be an empty array. If the array is empty, no response headers will be certified.
+Similarly to request certification, any number of response headers can be provided via the `certified_response_headers` associated function of the `DefaultResponseCertification` enum, and it can also be an empty array. If the array is empty, no response headers will be certified.
For example:
@@ -531,11 +529,11 @@ use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCertification, DefaultRequestCertification, DefaultResponseCertification};
let cel_expr = CelExpression::DefaultCertification(Some(DefaultCertification {
- request: DefaultRequestCertification::new(
- vec!["Accept", "Accept-Encoding", "If-None-Match"],
- vec!["foo", "bar", "baz"],
- ),
- response_certification: DefaultResponseCertification::certified_response_headers(vec![]),
+ request: DefaultRequestCertification::new(
+ vec!["Accept", "Accept-Encoding", "If-None-Match"],
+ vec!["foo", "bar", "baz"],
+ ),
+ response_certification: DefaultResponseCertification::certified_response_headers(vec![]),
}));
```
@@ -543,17 +541,17 @@ This will produce the following CEL expression:
```protobuf
default_certification (
- ValidationArgs {
- request_certification: RequestCertification {
- certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
- certified_query_parameters: ["foo", "bar", "baz"]
- },
- response_certification: ResponseCertification {
- certified_response_headers: ResponseHeaderList {
- headers: []
- }
- }
- }
+ ValidationArgs {
+ request_certification: RequestCertification {
+ certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
+ certified_query_parameters: ["foo", "bar", "baz"]
+ },
+ response_certification: ResponseCertification {
+ certified_response_headers: ResponseHeaderList {
+ headers: []
+ }
+ }
+ }
)
```
@@ -564,30 +562,30 @@ use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
- DefaultFullCelExpression {
- request: DefaultRequestCertification::new(
- vec!["Accept", "Accept-Encoding", "If-None-Match"],
- vec!["foo", "bar", "baz"],
- ),
- response: DefaultResponseCertification::response_header_exclusions(vec![]),
- }));
+ DefaultFullCelExpression {
+ request: DefaultRequestCertification::new(
+ vec!["Accept", "Accept-Encoding", "If-None-Match"],
+ vec!["foo", "bar", "baz"],
+ ),
+ response: DefaultResponseCertification::response_header_exclusions(vec![]),
+ }));
```
This will produce the following CEL expression:
```protobuf
default_certification (
- ValidationArgs {
- request_certification: RequestCertification {
- certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
- certified_query_parameters: ["foo", "bar", "baz"]
- },
- response_certification: ResponseCertification {
- response_header_exclusions: ResponseHeaderList {
- headers: []
- }
- }
- }
+ ValidationArgs {
+ request_certification: RequestCertification {
+ certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
+ certified_query_parameters: ["foo", "bar", "baz"]
+ },
+ response_certification: ResponseCertification {
+ response_header_exclusions: ResponseHeaderList {
+ headers: []
+ }
+ }
+ }
)
```
@@ -607,8 +605,8 @@ This will produce the following CEL expression:
```protobuf
default_certification (
- ValidationArgs {
- no_certification: Empty {}
- }
+ ValidationArgs {
+ no_certification: Empty {}
+ }
)
```
From 6806372e4f3a9066dee56825eda26a9daa369d81 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:07:03 -0600
Subject: [PATCH 10/31] Update README.md
---
packages/ic-asset-certification/README.md | 1020 ++++++++++-----------
1 file changed, 510 insertions(+), 510 deletions(-)
diff --git a/packages/ic-asset-certification/README.md b/packages/ic-asset-certification/README.md
index 18d247d..985c317 100644
--- a/packages/ic-asset-certification/README.md
+++ b/packages/ic-asset-certification/README.md
@@ -23,10 +23,10 @@ For canisters that need it, it's also possible to [delete assets](#deleting-asse
This library is unopinionated about where assets come from. However, there are three main options:
- Embedding assets in the canister at compile time:
- - [include_bytes!](https://doc.rust-lang.org/std/macro.include_bytes.html)
- - [include_dir!](https://docs.rs/include_dir/latest/include_dir/index.html)
+ - [include_bytes!](https://doc.rust-lang.org/std/macro.include_bytes.html)
+ - [include_dir!](https://docs.rs/include_dir/latest/include_dir/index.html)
- Uploading assets via canister endpoints at runtime:
- - The [`dfx` asset canister](https://github.com/dfinity/sdk/blob/master/docs/design/asset-canister-interface.md) is a good example of this approach.
+ - The [`dfx` asset canister](https://github.com/dfinity/sdk/blob/master/docs/design/asset-canister-interface.md) is a good example of this approach.
- Generating assets dynamically in code, at runtime.
With the assets in memory, they can be converted into the `Asset` type:
@@ -35,8 +35,8 @@ With the assets in memory, they can be converted into the `Asset` type:
use ic_asset_certification::Asset;
let asset = Asset::new(
- "index.html",
- b"
Hello World!
".as_slice(),
+ "index.html",
+ b"Hello World!
".as_slice(),
);
```
@@ -49,8 +49,8 @@ use ic_asset_certification::Asset;
let pretty_big_asset = include_bytes!("lib.rs");
let asset = Asset::new(
- "assets/pretty-big-asset.gz",
- pretty_big_asset.as_slice(),
+ "assets/pretty-big-asset.gz",
+ pretty_big_asset.as_slice(),
);
```
@@ -62,8 +62,8 @@ use ic_asset_certification::Asset;
let name = "World";
let asset = Asset::new(
- "index.html",
- format!("Hello {name}!
").into_bytes(),
+ "index.html",
+ format!("Hello {name}!
").into_bytes(),
);
```
@@ -76,51 +76,51 @@ many files by a glob.
In both cases, the following options can be configured for each asset:
- `content_type`
- - Providing this option will certify and serve a `Content-Type` header with
- the provided value.
- - If this value is not provided, the `Content-Type` header will not be
- inserted.
- - If the `Content-Type` header is not sent to the browser, the browser will
- try to guess the content type based on the file extension, unless an
- `X-Content-Type-Options: nosniff` header is sent.
- - Not certifying the `Content-Type` header will also allow a malicious replica
- to insert its own `Content-Type` header, which could lead to a security
- vulnerability.
+ - Providing this option will certify and serve a `Content-Type` header with
+ the provided value.
+ - If this value is not provided, the `Content-Type` header will not be
+ inserted.
+ - If the `Content-Type` header is not sent to the browser, the browser will
+ try to guess the content type based on the file extension, unless an
+ `X-Content-Type-Options: nosniff` header is sent.
+ - Not certifying the `Content-Type` header will also allow a malicious replica
+ to insert its own `Content-Type` header, which could lead to a security
+ vulnerability.
- `headers`
- - Any additional headers provided will be certified and served with the
- asset.
- - It's important to include any headers that can affect browser behavior,
- particularly [security headers](https://owasp.org/www-project-secure-headers/index.html).
+ - Any additional headers provided will be certified and served with the
+ asset.
+ - It's important to include any headers that can affect browser behavior,
+ particularly [security headers](https://owasp.org/www-project-secure-headers/index.html).
- `encodings`
- - A list of alternative encodings that can be used to serve the asset.
- - Each entry is a tuple of the [encoding name](AssetEncoding) and the file
- extension used in the file path, that can be conveniently created with
- the `default` factory method. For example, to include Brotli and Gzip encodings:
- `vec![AssetEncoding::Brotli.default(), AssetEncoding::Gzip.default()]`.
- - The default file extensions for each encoding are:
- - Brotli: `br`
- - Gzip: `gz`
- - Deflate: `zz`
- - Zstd: `zst`
- - Alternatively, a custom file extension can be provided for each encoding
- by using the `custom` factory method. For example, to include a custom
- file extension for Brotli and Gzip encodings:
- `vec![AssetEncoding::Brotli.custom("brotli"), AssetEncoding::Gzip.custom("gzip")]`.
- - Each encoding referenced must be provided to the asset router as a
- separate file with the same filename as the original file, but with an
- additional file extension matching the configuration. For example, if the
- current matched file is named `file.html`, then the asset router will
- look for `file.html.br` and `file.html.gz`.
- - If the file is found, the asset will be certified and served with the
- provided encoding according to the `Accept-Encoding`.
- - Encodings are prioritized in the following order:
- - Brotli
- - Zstd
- - Gzip
- - Deflate
- - Identity
- - The asset router will return the highest priority encoding that has been
- certified and is supported by the client.
+ - A list of alternative encodings that can be used to serve the asset.
+ - Each entry is a tuple of the [encoding name](AssetEncoding) and the file
+ extension used in the file path, that can be conveniently created with
+ the `default` factory method. For example, to include Brotli and Gzip encodings:
+ `vec![AssetEncoding::Brotli.default(), AssetEncoding::Gzip.default()]`.
+ - The default file extensions for each encoding are:
+ - Brotli: `br`
+ - Gzip: `gz`
+ - Deflate: `zz`
+ - Zstd: `zst`
+ - Alternatively, a custom file extension can be provided for each encoding
+ by using the `custom` factory method. For example, to include a custom
+ file extension for Brotli and Gzip encodings:
+ `vec![AssetEncoding::Brotli.custom("brotli"), AssetEncoding::Gzip.custom("gzip")]`.
+ - Each encoding referenced must be provided to the asset router as a
+ separate file with the same filename as the original file, but with an
+ additional file extension matching the configuration. For example, if the
+ current matched file is named `file.html`, then the asset router will
+ look for `file.html.br` and `file.html.gz`.
+ - If the file is found, the asset will be certified and served with the
+ provided encoding according to the `Accept-Encoding`.
+ - Encodings are prioritized in the following order:
+ - Brotli
+ - Zstd
+ - Gzip
+ - Deflate
+ - Identity
+ - The asset router will return the highest priority encoding that has been
+ certified and is supported by the client.
### Configuring individual files
@@ -130,9 +130,9 @@ match the path passed into the `Asset` constructor in the previous step.
In addition to the common configuration options, individual assets also have
the option of registering the asset as a fallback response for a particular
scope. This can be used to configure 404 pages or single-page application
-entrypoints, for example.
+entry points, for example.
-When serving assets, if a requested path does not exactly match any assets then
+When serving assets, if a requested path does not exactly match any assets, then
a search is conducted for an asset configured with the fallback scope that most
closely matches the requested asset's path.
@@ -170,20 +170,20 @@ use ic_http_certification::StatusCode;
use ic_asset_certification::{AssetConfig, AssetFallbackConfig};
let config = AssetConfig::File {
- path: "index.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![
- ("Cache-Control".to_string(), "public, no-cache, no-store".to_string()),
- ],
- fallback_for: vec![AssetFallbackConfig {
- scope: "/".to_string(),
- status_code: Some(StatusCode::OK),
- }],
- aliased_by: vec!["/".to_string()],
- encodings: vec![
- AssetEncoding::Brotli.default(),
- AssetEncoding::Gzip.default()
- ],
+ path: "index.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![
+ ("Cache-Control".to_string(), "public, no-cache, no-store".to_string()),
+ ],
+ fallback_for: vec![AssetFallbackConfig {
+ scope: "/".to_string(),
+ status_code: Some(StatusCode::OK),
+ }],
+ aliased_by: vec!["/".to_string()],
+ encodings: vec![
+ AssetEncoding::Brotli.default(),
+ AssetEncoding::Gzip.default()
+ ],
};
```
@@ -211,33 +211,33 @@ use ic_http_certification::StatusCode;
use ic_asset_certification::{AssetConfig, AssetFallbackConfig};
let config = AssetConfig::File {
- path: "404.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![
- ("Cache-Control".to_string(), "public, no-cache, no-store".to_string()),
- ],
- 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![
- "/404".to_string(),
- "/404/".to_string(),
- "/404.html".to_string(),
- "/not-found".to_string(),
- "/not-found/".to_string(),
- "/not-found/index.html".to_string(),
- ],
- encodings: vec![
- AssetEncoding::Brotli.default(),
- AssetEncoding::Gzip.default(),
- ],
+ path: "404.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![
+ ("Cache-Control".to_string(), "public, no-cache, no-store".to_string()),
+ ],
+ 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![
+ "/404".to_string(),
+ "/404/".to_string(),
+ "/404.html".to_string(),
+ "/not-found".to_string(),
+ "/not-found/".to_string(),
+ "/not-found/index.html".to_string(),
+ ],
+ encodings: vec![
+ AssetEncoding::Brotli.default(),
+ AssetEncoding::Gzip.default(),
+ ],
};
```
@@ -251,23 +251,23 @@ Standard Unix-style glob syntax is supported:
- `?` matches any single character.
- `*` matches zero or more characters.
- `**` recursively matches directories but is only legal in three
- situations.
- - If the glob starts with `**/`, then it matches all directories.
- For example, `**/foo` matches `foo` and `bar/foo` but not
- `foo/bar`.
- - If the glob ends with `/**`, then it matches all sub-entries.
- For example, `foo/**` matches `foo/a` and `foo/a/b`, but not
- `foo`.
- - If the glob contains `/**/` anywhere within the pattern, then it
- matches zero or more directories.
- - Using `**` anywhere else is illegal.
- - The glob `**` is allowed and means "match everything".
+ situations.
+ - If the glob starts with `**/`, then it matches all directories.
+ For example, `**/foo` matches `foo` and `bar/foo` but not
+ `foo/bar`.
+ - If the glob ends with `/**`, then it matches all sub-entries.
+ For example, `foo/**` matches `foo/a` and `foo/a/b`, but not
+ `foo`.
+ - If the glob contains `/**/` anywhere within the pattern, then it
+ matches zero or more directories.
+ - Using `**` anywhere else is illegal.
+ - The glob `**` is allowed and means "match everything."
- `{a,b}` matches `a` or `b` where `a` and `b` are arbitrary glob
- patterns. (N.B. Nesting `{...}` is not currently allowed.)
+ patterns. (N.B. Nesting `{...}` is not currently allowed.)
- `[ab]` matches `a` or `b` where `a` and `b` are characters.
- `[!ab]` to match any character except for `a` and `b`.
- Metacharacters such as `*` and `?` can be escaped with character
- class notation. e.g., `[*]` matches `*`.
+ class notation, e.g., `[*]` matches `*`.
For example, the following pattern will match all `.js` files in the `js`
directory:
@@ -277,22 +277,22 @@ use ic_http_certification::StatusCode;
use ic_asset_certification::AssetConfig;
let config = AssetConfig::Pattern {
- pattern: "js/*.js".to_string(),
- content_type: Some("application/javascript".to_string()),
- headers: vec![
- ("Cache-Control".to_string(), "public, max-age=31536000, immutable".to_string()),
- ],
- encodings: vec![
- AssetEncoding::Brotli.default(),
- AssetEncoding::Gzip.default(),
- ],
+ pattern: "js/*.js".to_string(),
+ content_type: Some("application/javascript".to_string()),
+ headers: vec![
+ ("Cache-Control".to_string(), "public, max-age=31536000, immutable".to_string()),
+ ],
+ encodings: vec![
+ AssetEncoding::Brotli.default(),
+ AssetEncoding::Gzip.default(),
+ ],
};
```
### Configuring redirects
Redirects can be configured using the `AssetConfig::Redirect` variant. This
-variant takes a `from` and `to` paths, and a redirect `kind`.
+variant takes `from` and `to` paths, and a redirect `kind`.
When a request is made to the `from` path, the client will be redirected to the
`to` path. The `AssetConfig::Redirect` config is not matched against any `Asset`s.
@@ -322,13 +322,13 @@ The following example configures a permanent redirect from `/old` to `/new`:
use ic_asset_certification::{AssetConfig, AssetRedirectKind};
let config = AssetConfig::Redirect {
- from: "/old".to_string(),
- to: "/new".to_string(),
- kind: AssetRedirectKind::Permanent,
- headers: vec![(
- "content-type".to_string(),
- "text/plain; charset=utf-8".to_string(),
- )],
+ from: "/old".to_string(),
+ to: "/new".to_string(),
+ kind: AssetRedirectKind::Permanent,
+ headers: vec![(
+ "content-type".to_string(),
+ "text/plain; charset=utf-8".to_string(),
+ )],
};
```
@@ -346,95 +346,95 @@ use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRoute
let mut asset_router = AssetRouter::default();
let assets = vec![
- Asset::new(
- "index.html",
- b"Hello World!
".as_slice(),
- ),
- Asset::new(
- "index.html.gz",
- [0, 1, 2, 3, 4, 5]
- ),
- Asset::new(
- "index.html.br",
- [6, 7, 8, 9, 10, 11]
- ),
- Asset::new(
- "app.js",
- b"console.log('Hello World!');".as_slice(),
- ),
- Asset::new(
- "app.js.gz",
- [12, 13, 14, 15, 16, 17],
- ),
- Asset::new(
- "app.js.br",
- [18, 19, 20, 21, 22, 23],
- ),
- Asset::new(
- "css/app-ba74b708.css",
- b"html,body{min-height:100vh;}".as_slice(),
- ),
- Asset::new(
- "css/app-ba74b708.css.gz",
- [24, 25, 26, 27, 28, 29],
- ),
- Asset::new(
- "css/app-ba74b708.css.br",
- [30, 31, 32, 33, 34, 35],
- ),
+ Asset::new(
+ "index.html",
+ b"Hello World!
".as_slice(),
+ ),
+ Asset::new(
+ "index.html.gz",
+ [0, 1, 2, 3, 4, 5]
+ ),
+ Asset::new(
+ "index.html.br",
+ [6, 7, 8, 9, 10, 11]
+ ),
+ Asset::new(
+ "app.js",
+ b"console.log('Hello World!');".as_slice(),
+ ),
+ Asset::new(
+ "app.js.gz",
+ [12, 13, 14, 15, 16, 17],
+ ),
+ Asset::new(
+ "app.js.br",
+ [18, 19, 20, 21, 22, 23],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css",
+ b"html,body{min-height:100vh;}".as_slice(),
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.gz",
+ [24, 25, 26, 27, 28, 29],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.br",
+ [30, 31, 32, 33, 34, 35],
+ ),
];
let asset_configs = vec![
- AssetConfig::File {
- path: "index.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, no-cache, no-store".to_string(),
- )],
- fallback_for: vec![AssetFallbackConfig {
- scope: "/".to_string(),
- status_code: Some(StatusCode::OK),
- }],
- aliased_by: vec!["/".to_string()],
- encodings: vec![
- AssetEncoding::Brotli.default(),
- AssetEncoding::Gzip.default(),
- ],
- },
- AssetConfig::Pattern {
- pattern: "**/*.js".to_string(),
- content_type: Some("text/javascript".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default(),
- AssetEncoding::Gzip.default(),
- ],
- },
- AssetConfig::Pattern {
- pattern: "**/*.css".to_string(),
- content_type: Some("text/css".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default(),
- AssetEncoding::Gzip.default(),
- ],
- },
- AssetConfig::Redirect {
- from: "/old".to_string(),
- to: "/new".to_string(),
- kind: AssetRedirectKind::Permanent,
- headers: vec![(
- "content-type".to_string(),
- "text/plain; charset=utf-8".to_string(),
- )],
- },
+ AssetConfig::File {
+ path: "index.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, no-cache, no-store".to_string(),
+ )],
+ fallback_for: vec![AssetFallbackConfig {
+ scope: "/".to_string(),
+ status_code: Some(StatusCode::OK),
+ }],
+ aliased_by: vec!["/".to_string()],
+ encodings: vec![
+ AssetEncoding::Brotli.default(),
+ AssetEncoding::Gzip.default(),
+ ],
+ },
+ AssetConfig::Pattern {
+ pattern: "**/*.js".to_string(),
+ content_type: Some("text/javascript".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default(),
+ AssetEncoding::Gzip.default(),
+ ],
+ },
+ AssetConfig::Pattern {
+ pattern: "**/*.css".to_string(),
+ content_type: Some("text/css".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default(),
+ AssetEncoding::Gzip.default(),
+ ],
+ },
+ AssetConfig::Redirect {
+ from: "/old".to_string(),
+ to: "/new".to_string(),
+ kind: AssetRedirectKind::Permanent,
+ headers: vec![(
+ "content-type".to_string(),
+ "text/plain; charset=utf-8".to_string(),
+ )],
+ },
];
asset_router.certify_assets(assets, asset_configs).unwrap();
@@ -471,7 +471,7 @@ asset_router.init_with_tree(http_certification_tree.clone());
## Serving assets
Assets can be served by calling the `serve_asset` method on the `AssetRouter`.
-This method will return a response, a witness and an expression path, which can be used
+This method will return a response, a witness, and an expression path, which can be used
alongside the canister's data certificate to add the required certificate header to the response.
```rust
@@ -481,22 +481,22 @@ use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRoute
let mut asset_router = AssetRouter::default();
let asset = Asset::new(
- "index.html",
- b"Hello World!
".as_slice(),
+ "index.html",
+ b"Hello World!
".as_slice(),
);
let asset_config = AssetConfig::File {
- path: "index.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![
- ("Cache-Control".to_string(), "public, no-cache, no-store".to_string()),
- ],
- fallback_for: vec![AssetFallbackConfig {
- scope: "/".to_string(),
- status_code: Some(StatusCode::OK),
- }],
- aliased_by: vec!["/".to_string()],
- encodings: vec![],
+ path: "index.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![
+ ("Cache-Control".to_string(), "public, no-cache, no-store".to_string()),
+ ],
+ fallback_for: vec![AssetFallbackConfig {
+ scope: "/".to_string(),
+ status_code: Some(StatusCode::OK),
+ }],
+ aliased_by: vec!["/".to_string()],
+ encodings: vec![],
};
let http_request = HttpRequest::get("/").build();
@@ -505,14 +505,14 @@ asset_router.certify_assets(vec![asset], vec![asset_config]).unwrap();
let (mut response, witness, expr_path) = asset_router.serve_asset(&http_request).unwrap();
-// this should normally be retrieved using `ic_cdk::api::data_certificate()`.
+// This should normally be retrieved using `ic_cdk::api::data_certificate()`.
let data_certificate = vec![1, 2, 3];
add_v2_certificate_header(
- data_certificate,
- &mut response,
- &witness,
- &expr_path,
+ data_certificate,
+ &mut response,
+ &witness,
+ &expr_path,
);
```
@@ -532,12 +532,12 @@ Depending on the configuration provided to the `certify_assets` function,
multiple responses may be generated for the same asset. To ensure that all generated responses are deleted,
the `delete_assets` function accepts the same configuration.
-If a configuration different to the one used to certify assets in the first place is provided,
+If a configuration different from the one used to certify assets in the first place is provided,
one of two things can happen:
-1. If the configuration inclues a file that was not certified in the first place, it will be silently ignored.
+1. If the configuration includes a file that was not certified in the first place, it will be silently ignored.
For example, if the configuration provided to `certify_assets` includes the Brotli and Gzip encodings, but the
-configuration provided to `delete_assets` includes Brotli, Gzip and Deflate, the Brotli and Gzip encoded files will be deleted, while the Deflate file is ignored, since it doesn't exist.
+configuration provided to `delete_assets` includes Brotli, Gzip, and Deflate. The Brotli and Gzip encoded files will be deleted, while the Deflate file is ignored, since it doesn't exist.
2. If the configuration excludes a file that was certified, it will not be deleted. For example, if the configuration,
provided to `certify_assets` includes the Brotli and Gzip encodings, but the configuration provided to `delete_assets`
@@ -552,95 +552,95 @@ use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRoute
let mut asset_router = AssetRouter::default();
let assets = vec![
- Asset::new(
- "index.html",
- b"Hello World!
".as_slice(),
- ),
- Asset::new(
- "index.html.gz",
- &[0, 1, 2, 3, 4, 5]
- ),
- Asset::new(
- "index.html.br",
- &[6, 7, 8, 9, 10, 11]
- ),
- Asset::new(
- "app.js",
- b"console.log('Hello World!');".as_slice(),
- ),
- Asset::new(
- "app.js.gz",
- &[12, 13, 14, 15, 16, 17],
- ),
- Asset::new(
- "app.js.br",
- &[18, 19, 20, 21, 22, 23],
- ),
- Asset::new(
- "css/app-ba74b708.css",
- b"html,body{min-height:100vh;}".as_slice(),
- ),
- Asset::new(
- "css/app-ba74b708.css.gz",
- &[24, 25, 26, 27, 28, 29],
- ),
- Asset::new(
- "css/app-ba74b708.css.br",
- &[30, 31, 32, 33, 34, 35],
- ),
+ Asset::new(
+ "index.html",
+ b"Hello World!
".as_slice(),
+ ),
+ Asset::new(
+ "index.html.gz",
+ &[0, 1, 2, 3, 4, 5]
+ ),
+ Asset::new(
+ "index.html.br",
+ &[6, 7, 8, 9, 10, 11]
+ ),
+ Asset::new(
+ "app.js",
+ b"console.log('Hello World!');".as_slice(),
+ ),
+ Asset::new(
+ "app.js.gz",
+ &[12, 13, 14, 15, 16, 17],
+ ),
+ Asset::new(
+ "app.js.br",
+ &[18, 19, 20, 21, 22, 23],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css",
+ b"html,body{min-height:100vh;}".as_slice(),
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.gz",
+ &[24, 25, 26, 27, 28, 29],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.br",
+ &[30, 31, 32, 33, 34, 35],
+ ),
];
let asset_configs = vec![
- AssetConfig::File {
- path: "index.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, no-cache, no-store".to_string(),
- )],
- fallback_for: vec![AssetFallbackConfig {
- scope: "/".to_string(),
- status_code: Some(StatusCode::OK),
- }],
- aliased_by: vec!["/".to_string()],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- AssetConfig::Pattern {
- pattern: "**/*.js".to_string(),
- content_type: Some("text/javascript".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- AssetConfig::Pattern {
- pattern: "**/*.css".to_string(),
- content_type: Some("text/css".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- AssetConfig::Redirect {
- from: "/old".to_string(),
- to: "/new".to_string(),
- kind: AssetRedirectKind::Permanent,
- headers: vec![(
- "content-type".to_string(),
- "text/plain; charset=utf-8".to_string(),
- )],
- },
+ AssetConfig::File {
+ path: "index.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, no-cache, no-store".to_string(),
+ )],
+ fallback_for: vec![AssetFallbackConfig {
+ scope: "/".to_string(),
+ status_code: Some(StatusCode::OK),
+ }],
+ aliased_by: vec!["/".to_string()],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ AssetConfig::Pattern {
+ pattern: "**/*.js".to_string(),
+ content_type: Some("text/javascript".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ AssetConfig::Pattern {
+ pattern: "**/*.css".to_string(),
+ content_type: Some("text/css".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ AssetConfig::Redirect {
+ from: "/old".to_string(),
+ to: "/new".to_string(),
+ kind: AssetRedirectKind::Permanent,
+ headers: vec![(
+ "content-type".to_string(),
+ "text/plain; charset=utf-8".to_string(),
+ )],
+ },
];
asset_router.certify_assets(assets, asset_configs).unwrap();
@@ -650,94 +650,94 @@ To delete the `index.html` asset, along with the fallback configuration for the
```rust
asset_router
- .delete_assets(
- vec![
- Asset::new(
- "index.html",
- b"Hello World!
".as_slice(),
- ),
- Asset::new("index.html.gz", &[0, 1, 2, 3, 4, 5]),
- Asset::new("index.html.br", &[6, 7, 8, 9, 10, 11]),
- ],
- vec![AssetConfig::File {
- path: "index.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, no-cache, no-store".to_string(),
- )],
- fallback_for: vec![AssetFallbackConfig {
- scope: "/".to_string(),
- status_code: Some(StatusCode::OK),
- }],
- aliased_by: vec!["/".to_string()],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- }],
- )
- .unwrap();
+ .delete_assets(
+ vec![
+ Asset::new(
+ "index.html",
+ b"Hello World!
".as_slice(),
+ ),
+ Asset::new("index.html.gz", &[0, 1, 2, 3, 4, 5]),
+ Asset::new("index.html.br", &[6, 7, 8, 9, 10, 11]),
+ ],
+ vec![AssetConfig::File {
+ path: "index.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, no-cache, no-store".to_string(),
+ )],
+ fallback_for: vec![AssetFallbackConfig {
+ scope: "/".to_string(),
+ status_code: Some(StatusCode::OK),
+ }],
+ aliased_by: vec!["/".to_string()],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ }],
+ )
+ .unwrap();
```
To delete the `app.js` asset, along with the alternative encodings:
```rust
asset_router
- .delete_assets(
- vec![
- Asset::new("app.js", b"console.log('Hello World!');".as_slice()),
- Asset::new("app.js.gz", &[12, 13, 14, 15, 16, 17]),
- Asset::new("app.js.br", &[18, 19, 20, 21, 22, 23]),
- ],
- vec![AssetConfig::Pattern {
- pattern: "**/*.js".to_string(),
- content_type: Some("text/javascript".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- }],
- )
- .unwrap();
+ .delete_assets(
+ vec![
+ Asset::new("app.js", b"console.log('Hello World!');".as_slice()),
+ Asset::new("app.js.gz", &[12, 13, 14, 15, 16, 17]),
+ Asset::new("app.js.br", &[18, 19, 20, 21, 22, 23]),
+ ],
+ vec![AssetConfig::Pattern {
+ pattern: "**/*.js".to_string(),
+ content_type: Some("text/javascript".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ }],
+ )
+ .unwrap();
```
To delete the `css/app-ba74b708.css` asset, along with the alternative encodings:
```rust
asset_router.delete_assets(
- vec![
- Asset::new(
- "css/app-ba74b708.css",
- b"html,body{min-height:100vh;}".as_slice(),
- ),
- Asset::new(
- "css/app-ba74b708.css.gz",
- &[24, 25, 26, 27, 28, 29],
- ),
- Asset::new(
- "css/app-ba74b708.css.br",
- &[30, 31, 32, 33, 34, 35],
- ),
- ],
- vec![
- AssetConfig::Pattern {
- pattern: "**/*.css".to_string(),
- content_type: Some("text/css".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- ]
+ vec![
+ Asset::new(
+ "css/app-ba74b708.css",
+ b"html,body{min-height:100vh;}".as_slice(),
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.gz",
+ &[24, 25, 26, 27, 28, 29],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.br",
+ &[30, 31, 32, 33, 34, 35],
+ ),
+ ],
+ vec![
+ AssetConfig::Pattern {
+ pattern: "**/*.css".to_string(),
+ content_type: Some("text/css".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ ]
).unwrap();
```
@@ -745,19 +745,19 @@ And finally, to delete the `/old` redirect:
```rust
asset_router
- .delete_assets(
- vec![],
- vec![AssetConfig::Redirect {
- from: "/old".to_string(),
- to: "/new".to_string(),
- kind: AssetRedirectKind::Permanent,
- headers: vec![(
- "content-type".to_string(),
- "text/plain; charset=utf-8".to_string(),
- )],
- }],
- )
- .unwrap();
+ .delete_assets(
+ vec![],
+ vec![AssetConfig::Redirect {
+ from: "/old".to_string(),
+ to: "/new".to_string(),
+ kind: AssetRedirectKind::Permanent,
+ headers: vec![(
+ "content-type".to_string(),
+ "text/plain; charset=utf-8".to_string(),
+ )],
+ }],
+ )
+ .unwrap();
```
After deleting any assets, make sure to set the canister's
@@ -775,12 +775,12 @@ To delete assets by path, use the `delete_assets_by_path` function.
Depending on the configuration provided to the `certify_assets` function,
multiple responses may be generated for the same asset. These assets may exist on different paths,
-for example if the `alias` configuration is used. If `alias` paths are not passed to this function,
+for example, if the `alias` configuration is used. If `alias` paths are not passed to this function,
they will not be deleted.
If multiple encodings exist for a path, all encodings will be deleted.
-Fallbacks are also not deleted, to delete them, use the `delete_fallback_assets_by_path` function.
+Fallbacks are also not deleted; to delete them, use the `delete_fallback_assets_by_path` function.
Assuming the same base example used above to demonstrate certifying assets:
@@ -791,92 +791,92 @@ use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRoute
let mut asset_router = AssetRouter::default();
let assets = vec![
- Asset::new(
- "index.html",
- b"Hello World!
".as_slice(),
- ),
- Asset::new(
- "index.html.gz",
- &[0, 1, 2, 3, 4, 5]
- ),
- Asset::new(
- "index.html.br",
- &[6, 7, 8, 9, 10, 11]
- ),
- Asset::new(
- "app.js",
- b"console.log('Hello World!');".as_slice(),
- ),
- Asset::new(
- "app.js.gz",
- &[12, 13, 14, 15, 16, 17],
- ),
- Asset::new(
- "app.js.br",
- &[18, 19, 20, 21, 22, 23],
- ),
- Asset::new(
- "css/app-ba74b708.css",
- b"html,body{min-height:100vh;}".as_slice(),
- ),
- Asset::new(
- "css/app-ba74b708.css.gz",
- &[24, 25, 26, 27, 28, 29],
- ),
- Asset::new(
- "css/app-ba74b708.css.br",
- &[30, 31, 32, 33, 34, 35],
- ),
+ Asset::new(
+ "index.html",
+ b"Hello World!
".as_slice(),
+ ),
+ Asset::new(
+ "index.html.gz",
+ &[0, 1, 2, 3, 4, 5]
+ ),
+ Asset::new(
+ "index.html.br",
+ &[6, 7, 8, 9, 10, 11]
+ ),
+ Asset::new(
+ "app.js",
+ b"console.log('Hello World!');".as_slice(),
+ ),
+ Asset::new(
+ "app.js.gz",
+ &[12, 13, 14, 15, 16, 17],
+ ),
+ Asset::new(
+ "app.js.br",
+ &[18, 19, 20, 21, 22, 23],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css",
+ b"html,body{min-height:100vh;}".as_slice(),
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.gz",
+ &[24, 25, 26, 27, 28, 29],
+ ),
+ Asset::new(
+ "css/app-ba74b708.css.br",
+ &[30, 31, 32, 33, 34, 35],
+ ),
];
let asset_configs = vec![
- AssetConfig::File {
- path: "index.html".to_string(),
- content_type: Some("text/html".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, no-cache, no-store".to_string(),
- )],
- fallback_for: vec![AssetFallbackConfig {
- scope: "/".to_string(),
- status_code: Some(StatusCode::OK),
- }],
- aliased_by: vec!["/".to_string()],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- AssetConfig::Pattern {
- pattern: "**/*.js".to_string(),
- content_type: Some("text/javascript".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- AssetConfig::Pattern {
- pattern: "**/*.css".to_string(),
- content_type: Some("text/css".to_string()),
- headers: vec![(
- "cache-control".to_string(),
- "public, max-age=31536000, immutable".to_string(),
- )],
- encodings: vec![
- AssetEncoding::Brotli.default_config(),
- AssetEncoding::Gzip.default_config(),
- ],
- },
- AssetConfig::Redirect {
- from: "/old".to_string(),
- to: "/new".to_string(),
- kind: AssetRedirectKind::Permanent,
- headers: vec![("content-type".to_string(), "text/plain".to_string())],
- },
+ AssetConfig::File {
+ path: "index.html".to_string(),
+ content_type: Some("text/html".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, no-cache, no-store".to_string(),
+ )],
+ fallback_for: vec![AssetFallbackConfig {
+ scope: "/".to_string(),
+ status_code: Some(StatusCode::OK),
+ }],
+ aliased_by: vec!["/".to_string()],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ AssetConfig::Pattern {
+ pattern: "**/*.js".to_string(),
+ content_type: Some("text/javascript".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ AssetConfig::Pattern {
+ pattern: "**/*.css".to_string(),
+ content_type: Some("text/css".to_string()),
+ headers: vec![(
+ "cache-control".to_string(),
+ "public, max-age=31536000, immutable".to_string(),
+ )],
+ encodings: vec![
+ AssetEncoding::Brotli.default_config(),
+ AssetEncoding::Gzip.default_config(),
+ ],
+ },
+ AssetConfig::Redirect {
+ from: "/old".to_string(),
+ to: "/new".to_string(),
+ kind: AssetRedirectKind::Permanent,
+ headers: vec![("content-type".to_string(), "text/plain".to_string())],
+ },
];
asset_router.certify_assets(assets, asset_configs).unwrap();
@@ -886,21 +886,21 @@ To delete the `index.html` asset, along with the fallback configuration for the
```rust
asset_router
- .delete_assets_by_path(
- vec![
- "/index.html", // deletes the index.html asset, along with all encodings
- "/" // deletes the `/` alias for index.html, along with all encodings
- ],
- )
- .unwrap();
+ .delete_assets_by_path(
+ vec![
+ "/index.html", // deletes the index.html asset, along with all encodings
+ "/" // deletes the `/` alias for index.html, along with all encodings
+ ],
+ )
+ .unwrap();
asset_router
- .delete_fallback_assets_by_path(
- vec![
- "/" // deletes the fallback configuration for the `/` scope, along with all encodings
- ]
- )
- .unwrap();
+ .delete_fallback_assets_by_path(
+ vec![
+ "/" // deletes the fallback configuration for the `/` scope, along with all encodings
+ ]
+ )
+ .unwrap();
```
To delete the `app.js`asset, along with the alternative encodings:
@@ -955,13 +955,13 @@ The `get_assets()` function returns all standard assets, while the `get_fallback
The `AssetMap` can be used to query assets by `path`, `encoding`, and `starting_range`.
-For standard assets, the path refers to the asset's path, e.g. `/index.html`.
+For standard assets, the path refers to the asset's path, e.g., `/index.html`.
-For fallback assets, the path refers to the scope that the fallback is valid for, e.g. `/`. See the `fallback_for` config option for more information on fallback scopes.
+For fallback assets, the path refers to the scope that the fallback is valid for, e.g., `/`. See the `fallback_for` config option for more information on fallback scopes.
-For all types of assets, the encoding refers to the encoding of the asset, see `AssetEncoding`.
+For all types of assets, the encoding refers to the encoding of the asset; see `AssetEncoding`.
-Assets greater than 2mb are split into multiple ranges, the starting range allows retrieval of
+Assets greater than 2 MiB are split into multiple ranges; the starting range allows retrieval of
individual chunks of these large assets. The first range is `Some(0)`, the second range is
`Some(ASSET_CHUNK_SIZE)`, the third range is `Some(ASSET_CHUNK_SIZE * 2)`, and so on. The entire asset can
also be retrieved by passing `None` as the `starting_range`. Note that `ASSET_CHUNK_SIZE` is a constant defined in the `ic_asset_certification` crate.
From 021ab4ebeabd20c049fb0afb6d5a4b02aef1a762 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:09:21 -0600
Subject: [PATCH 11/31] Update README.md
---
examples/http-certification/skip-certification/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index 707d21c..c95ae51 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -4,7 +4,7 @@ This guide walks through an example project that demonstrates how to skip HTTP c
**WARNING** This means that a malicious replica can return whatever data it wants in response to requests directed towards the canister. Think carefully about whether or not this is the right fit for the canister. If certification should only be skipped for certain paths, then check out the ["Serving static assets over HTTP"](https://internetcomputer.org/docs/current/developer-docs/web-apps/http-compatible-canisters/serving-static-assets-over-http) guide where this approach is covered in more detail.
-This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to HTTP Certification will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/skip-certification).
+This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to HTTP certification will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/skip-certification).
## Prerequisites
From 42149c0c91fcadd613f5fa9702491973f9d3ae10 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:12:50 -0600
Subject: [PATCH 12/31] Update README.md
---
.../upgrade-to-update-call/README.md | 120 +++++++++---------
1 file changed, 59 insertions(+), 61 deletions(-)
diff --git a/examples/http-certification/upgrade-to-update-call/README.md b/examples/http-certification/upgrade-to-update-call/README.md
index e6d9fd1..07ac980 100644
--- a/examples/http-certification/upgrade-to-update-call/README.md
+++ b/examples/http-certification/upgrade-to-update-call/README.md
@@ -1,26 +1,24 @@
# Upgrading HTTP calls to update calls
-This guide walks through an example project that demonstrates how to use the ["Upgrade to Update call"](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#upgrade-to-update-calls) feature of the HTTP Gateway.
+This guide walks through an example project that demonstrates how to use the ["Upgrade to Update call"](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec#upgrade-to-update-calls) feature of the HTTP gateway.
-Since browsers are unable to directly interact with the ICP network, the HTTP Gateway acts as a bridge between the two. The HTTP Gateway forwards requests from clients to canisters and forwards responses from canisters back to clients. Before returning responses from canister back to clients, the HTTP Gateway verifies the certification of the response to ensure that they have not been tampered with.
+Since browsers are unable to directly interact with the ICP network, the HTTP gateway acts as a bridge between the two. The HTTP gateway forwards requests from clients to canisters and forwards responses from canisters back to clients. Before returning responses from the canister back to clients, the HTTP gateway verifies the certification of the response to ensure that they have not been tampered with.
Upgrading query calls to upgrade calls allows for the certification of any kind of dynamic response by leveraging ICP's consensus protocol without having to statically certify the response ahead of time. This is the simplest way to add _secure_ HTTP support to a canister.
-A similairly simple yet more performant, but _insecure_ approach is to skip certification entirely. This is not recommended unless you are absolutely sure that certification really does not make sense for your canister. Check the ["Skipping certification for HTTP responses"](https://internetcomputer.org/docs/current/developer-docs/web-apps/http-compatible-canisters/skipping-certification-for-http-responses) guide for more details on how to do that.
-
-This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to upgrading to an update call will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/upgrade-to-update-call).
+A similarly simple yet more performant, but _insecure_ approach is to skip certification entirely. This is not recommended unless you are absolutely sure that certification really does not make sense for your canister. Check the ["Skipping certification for HTTP responses"](https://internetcomputer.org/docs/current/developer-docs/web-apps/http-compatible-canisters/skipping-certification-for-http-responses) guide for more details on how to do that.
## How it works
-When the HTTP Gateway receives a request from a client, it will forward the request to the target canister's `http_request` method as a query call. To upgrade this query call to an update call, the canister returns a response that sets the optional `upgrade` field to `opt true`. Ommiting this field, or setting it to `opt false` will result in the HTTP Gateway treating the query call response as-is, without upgrading.
+When the HTTP gateway receives a request from a client, it will forward the request to the target canister's `http_request` method as a query call. To upgrade this query call to an update call, the canister returns a response that sets the optional `upgrade` field to `opt true`. Omitting this field, or setting it to `opt false` will result in the HTTP Gateway treating the query call response as-is, without upgrading.
-Upon receiving a response from the canister with the `upgrade` field set to `opt true`, the HTTP Gateway will repeat the original request as an update call to the `http_request_update` method of the canister. The canister can then respond to the update call with any dynamic response and leverage the ICP consensus protocol for security. The certification resulting from putting this response through consensus will be verified by the HTTP Gateway to ensure it has not been tampered with.
+Upon receiving a response from the canister with the `upgrade` field set to `opt true`, the HTTP gateway will repeat the original request as an update call to the `http_request_update` method of the canister. The canister can then respond to the update call with any dynamic response and leverage the ICP consensus protocol for security. The certification resulting from putting this response through consensus will be verified by the HTTP Gateway to ensure it has not been tampered with.
## Rust
-This example project features both Rust and Motoko code. If you rather follow the Motoko version, you can skip this section and go straight to the [section covering Motoko](#motoko).
+This example project features both Rust and Motoko code. If you would rather follow the Motoko version, you can skip this section and go straight to the [section covering Motoko](#motoko).
-The Rust code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entrypoint for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)` (via the `build_update` method on the `HttpResponse::builder` struct). The `http_request_update` function is the entrypoint for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
+The Rust code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entry point for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)` (via the `build_update` method on the `HttpResponse::builder` struct). The `http_request_update` function is the entry point for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
```rust
use ic_cdk::*;
@@ -28,73 +26,73 @@ use ic_http_certification::{HttpResponse, HttpUpdateResponse};
#[query]
fn http_request() -> HttpResponse<'static> {
- HttpResponse::builder().with_upgrade(true).build()
+ HttpResponse::builder().with_upgrade(true).build()
}
#[update]
fn http_request_update() -> HttpUpdateResponse<'static> {
- HttpResponse::builder()
- .with_status_code(StatusCode::IM_A_TEAPOT)
- .with_body(b"I'm a teapot")
- .build_update()
+ HttpResponse::builder()
+ .with_status_code(StatusCode::IM_A_TEAPOT)
+ .with_body(b"I'm a teapot")
+ .build_update()
}
```
## Motoko
-The Motoko code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entrypoint for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)`. The `http_request_update` function is the entrypoint for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
+The Motoko code is split into two functions: `http_request` and `http_request_update`. The `http_request` function is the entry point for the query call from the HTTP Gateway. It returns an `HttpResponse` with the `upgrade` field set to `Some(true)`. The `http_request_update` function is the entry point for the update call from the HTTP Gateway. It returns an `HttpUpdateResponse` with a custom status code and body.
```motoko
import Text "mo:base/Text";
actor Http {
- type HeaderField = (Text, Text);
-
- type HttpRequest = {
- method : Text;
- url : Text;
- headers : [HeaderField];
- body : Blob;
- certificate_version : ?Nat16;
- };
-
- type HttpUpdateRequest = {
- method : Text;
- url : Text;
- headers : [HeaderField];
- body : Blob;
- };
-
- type HttpResponse = {
- status_code : Nat16;
- headers : [HeaderField];
- body : Blob;
- upgrade : ?Bool;
- };
-
- type HttpUpdateResponse = {
- status_code : Nat16;
- headers : [HeaderField];
- body : Blob;
- };
-
- public query func http_request(_req: HttpRequest) : async HttpResponse {
- return {
- status_code = 200;
- headers = [];
- body = "";
- upgrade = ?true;
- };
- };
-
- public func http_request_update(_req: HttpUpdateRequest) : async HttpUpdateResponse {
- return {
- status_code = 418;
- headers = [];
- body = Text.encodeUtf8("I'm a teapot");
- };
- };
+ type HeaderField = (Text, Text);
+
+ type HttpRequest = {
+ method : Text;
+ url : Text;
+ headers : [HeaderField];
+ body : Blob;
+ certificate_version : ?Nat16;
+ };
+
+ type HttpUpdateRequest = {
+ method : Text;
+ url : Text;
+ headers : [HeaderField];
+ body : Blob;
+ };
+
+ type HttpResponse = {
+ status_code : Nat16;
+ headers : [HeaderField];
+ body : Blob;
+ upgrade : ?Bool;
+ };
+
+ type HttpUpdateResponse = {
+ status_code : Nat16;
+ headers : [HeaderField];
+ body : Blob;
+ };
+
+ public query func http_request(_req: HttpRequest) : async HttpResponse {
+ return {
+ status_code = 200;
+ headers = [];
+ body = "";
+ upgrade = ?true;
+ };
+ };
+
+ public func http_request_update(_req: HttpUpdateRequest) : async HttpUpdateResponse {
+ return {
+ status_code = 418;
+ headers = [];
+ body = Text.encodeUtf8("I'm a teapot");
+ };
+ };
};
```
From 7e288d325585bd43455a6483c8f54892cfbcd1e9 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:14:19 -0600
Subject: [PATCH 13/31] Update README.md
---
.../skip-certification/README.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index c95ae51..a92e3ab 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -8,7 +8,7 @@ This is not a beginner's canister development guide. Many fundamental concepts t
## Prerequisites
-This is a relatively simple guide so there's no prerequisites as such, but it's recommended to check out the full certification guides to make sure that certification is not a good fit for your project.
+This is a relatively simple guide, so there are no prerequisites as such, but it's recommended to check out the full certification guides to make sure that certification is not a good fit for your project.
- [x] Complete the ["Serving static assets over HTTP"](https://internetcomputer.org/docs/current/developer-docs/web-apps/http-compatible-canisters/serving-static-assets-over-http) guide.
- [x] Complete the ["Custom HTTP Canisters"](https://internetcomputer.org/docs/current/developer-docs/http-compatible-canisters/custom-http-canisters) guide.
@@ -26,7 +26,7 @@ use ic_http_certification::utils::skip_certification_certified_data;
#[init]
fn init() {
- set_certified_data(&skip_certification_certified_data());
+ set_certified_data(&skip_certification_certified_data());
}
```
@@ -40,11 +40,11 @@ use ic_http_certification::utils::add_skip_certification_header;
#[query]
fn http_request() -> HttpResponse<'static> {
- let mut response = create_response();
+ let mut response = create_response();
- add_skip_certification_header(data_certificate().unwrap(), &mut response);
+ add_skip_certification_header(data_certificate().unwrap(), &mut response);
- response
+ response
}
```
@@ -69,11 +69,11 @@ You should see output similar to the following:
```json
{
- "cycle_balance": 3092211597987
+ "cycle_balance": 3092211597987
}
```
-Alternatively, print the URL in the terminal and then open in a browser:
+Alternatively, print the URL in the terminal and then open it in a browser:
```shell
echo http://localhost:$(dfx info webserver-port)?canisterId=$(dfx canister id http_certification_skip_certification_backend)
From b2c6f97d05f0c230c2e23b87f4eff2995f6da854 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:17:22 -0600
Subject: [PATCH 14/31] Update README.md
---
examples/http-certification/skip-certification/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index a92e3ab..e483677 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -52,7 +52,7 @@ The call to `data_certificate` returns a certificate that proves the canister's
## Testing the canister
-To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From 7eb69977c4240928f2c5963f3a049c824fa1c2e0 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:17:35 -0600
Subject: [PATCH 15/31] Update README.md
---
examples/http-certification/upgrade-to-update-call/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/upgrade-to-update-call/README.md b/examples/http-certification/upgrade-to-update-call/README.md
index 07ac980..43cc0c4 100644
--- a/examples/http-certification/upgrade-to-update-call/README.md
+++ b/examples/http-certification/upgrade-to-update-call/README.md
@@ -98,7 +98,7 @@ actor Http {
## Testing the canister
-To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From 897a60361561c564664b1a8ef5b03c68a591180c Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:17:56 -0600
Subject: [PATCH 16/31] Update README.md
---
examples/http-certification/json-api/README.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/examples/http-certification/json-api/README.md b/examples/http-certification/json-api/README.md
index 59f1de2..039622c 100644
--- a/examples/http-certification/json-api/README.md
+++ b/examples/http-certification/json-api/README.md
@@ -1,7 +1,5 @@
# Serving JSON over HTTP
-## Overview
-
This guide walks through an example project that demonstrates how to create a canister that can serve certified JSON over HTTP. The example project presents a very simple REST API for creating and listing to-do items. There is no authentication or persistent storage.
This is not a beginner's canister development guide. Many fundamental concepts that a relatively experienced canister developer should already know will be omitted. Concepts specific to HTTP certification will be called out here and can help to understand the [full code example](https://github.com/dfinity/response-verification/tree/main/examples/http-certification/json-api).
@@ -524,7 +522,7 @@ fn insert_update_route(method: &str, path: &str, route_handler: RouteHandler) {
## Testing the canister
-To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From d144021b096d65472b2112ddc52cc01ec3f3735c Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:18:10 -0600
Subject: [PATCH 17/31] Update README.md
---
examples/http-certification/custom-assets/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md
index e5fbb26..662451e 100644
--- a/examples/http-certification/custom-assets/README.md
+++ b/examples/http-certification/custom-assets/README.md
@@ -548,7 +548,7 @@ fn http_request(req: HttpRequest) -> HttpResponse {
## Testing the canister
-To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From 4f624400cd29959219d0e537624fc2b79f708070 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:18:38 -0600
Subject: [PATCH 18/31] Update README.md
---
examples/http-certification/assets/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/assets/README.md b/examples/http-certification/assets/README.md
index edcb73e..41f6478 100644
--- a/examples/http-certification/assets/README.md
+++ b/examples/http-certification/assets/README.md
@@ -340,7 +340,7 @@ fn serve_metrics() -> HttpResponse<'static> {
## Testing the canister
-To test the canister, you can use [`dfx`]([/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From 93b4ffa4e72334a5d644deedea402fec18550b5c Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:18:55 -0600
Subject: [PATCH 19/31] Update README.md
---
examples/http-certification/custom-assets/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md
index 662451e..43fdb8a 100644
--- a/examples/http-certification/custom-assets/README.md
+++ b/examples/http-certification/custom-assets/README.md
@@ -548,7 +548,7 @@ fn http_request(req: HttpRequest) -> HttpResponse {
## Testing the canister
-To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From e0042d14a62cbdcda89d49cd0f0fef1c27414de9 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:19:13 -0600
Subject: [PATCH 20/31] Update README.md
---
examples/http-certification/json-api/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/json-api/README.md b/examples/http-certification/json-api/README.md
index 039622c..4d0d762 100644
--- a/examples/http-certification/json-api/README.md
+++ b/examples/http-certification/json-api/README.md
@@ -522,7 +522,7 @@ fn insert_update_route(method: &str, path: &str, route_handler: RouteHandler) {
## Testing the canister
-To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From debae396eb1296823e2d4c270c2fc8eefde7c021 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:19:28 -0600
Subject: [PATCH 21/31] Update README.md
---
examples/http-certification/skip-certification/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index e483677..318664e 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -52,7 +52,7 @@ The call to `data_certificate` returns a certificate that proves the canister's
## Testing the canister
-To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
From b94b5305d59314f53a0d5fe09bea1e8a767d7d23 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:19:48 -0600
Subject: [PATCH 22/31] Update README.md
---
examples/http-certification/upgrade-to-update-call/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/examples/http-certification/upgrade-to-update-call/README.md b/examples/http-certification/upgrade-to-update-call/README.md
index 43cc0c4..c273ad6 100644
--- a/examples/http-certification/upgrade-to-update-call/README.md
+++ b/examples/http-certification/upgrade-to-update-call/README.md
@@ -98,7 +98,7 @@ actor Http {
## Testing the canister
-To test the canister, you can use [`dfx`](/docs/current/developer-docs/getting-started/install](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
@@ -110,7 +110,7 @@ dfx deploy http_certification_upgrade_to_update_call_rust_backend
dfx deploy http_certification_upgrade_to_update_call_motoko_backend
```
-Make a request to the Rust canister using cURL:
+Make a request to the Rust canister using curl:
```shell
curl -v http://localhost:$(dfx info webserver-port)?canisterId=$(dfx canister id http_certification_upgrade_to_update_call_rust_backend)
From 534e0d1a1490bbb1ca61d22d8eeb369246e5ccdb Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 12:40:50 -0600
Subject: [PATCH 23/31] Update README.md
---
examples/http-certification/assets/README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/examples/http-certification/assets/README.md b/examples/http-certification/assets/README.md
index 41f6478..575dbdc 100644
--- a/examples/http-certification/assets/README.md
+++ b/examples/http-certification/assets/README.md
@@ -340,11 +340,13 @@ fn serve_metrics() -> HttpResponse<'static> {
## Testing the canister
+This example uses a canister called `http_certification_assets_backend`.
+
To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-dfx deploy
+dfx deploy http_certification_assets_backend
```
You can now access the canister's assets by navigating to the canister's URL in a web browser. The URL can also be found using the following command:
From 005710a89ffd5da2cb140375302a96d4bdc8d967 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 12:43:08 -0600
Subject: [PATCH 24/31] Update README.md
---
examples/http-certification/custom-assets/README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md
index 43fdb8a..3ddd6f7 100644
--- a/examples/http-certification/custom-assets/README.md
+++ b/examples/http-certification/custom-assets/README.md
@@ -548,11 +548,13 @@ fn http_request(req: HttpRequest) -> HttpResponse {
## Testing the canister
+This example uses a canister called `http_certification_custom_assets_backend`.
+
To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-dfx deploy
+dfx deploy http_certification_custom_assets_backend
```
You can now access the canister's assets by navigating to the canister's URL in a web browser. The URL can also be found using the following command:
From b8a1634556eb64e0680b677e124b1a736968bd0c Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 12:45:35 -0600
Subject: [PATCH 25/31] Update README.md
---
examples/http-certification/skip-certification/README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index 318664e..fc7f436 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -52,11 +52,13 @@ The call to `data_certificate` returns a certificate that proves the canister's
## Testing the canister
+This example uses a canister called `http_certification_skip_certification_backend`.
+
To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
-dfx deploy
+dfx deploy http_certification_skip_certification_backend
```
Make a request to the canister using curl:
From 33871c19addefd69adf761a4b41884a22d307b55 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Wed, 18 Dec 2024 12:54:50 -0600
Subject: [PATCH 26/31] Update README.md
---
.../upgrade-to-update-call/README.md | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/examples/http-certification/upgrade-to-update-call/README.md b/examples/http-certification/upgrade-to-update-call/README.md
index c273ad6..1163c02 100644
--- a/examples/http-certification/upgrade-to-update-call/README.md
+++ b/examples/http-certification/upgrade-to-update-call/README.md
@@ -98,15 +98,23 @@ actor Http {
## Testing the canister
+This example uses a Rust canister called `http_certification_upgrade_to_update_call_rust_backend` or a Motoko canister called `http_certification_upgrade_to_update_call_motoko_backend`.
+
To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
```shell
dfx start --background --clean
+```
-## Deploy the Rust canister
+#### Deploy the Rust canister
+
+```
dfx deploy http_certification_upgrade_to_update_call_rust_backend
+```
+
+#### Deploy the Motoko canister
-## Deploy the Motoko canister
+```
dfx deploy http_certification_upgrade_to_update_call_motoko_backend
```
From 4db97bdd5fd2eca9a7fa069eb2c4780fc2728f3f Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:45:40 -0600
Subject: [PATCH 27/31] Update README.md
---
examples/http-certification/assets/README.md | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/examples/http-certification/assets/README.md b/examples/http-certification/assets/README.md
index 575dbdc..69c221e 100644
--- a/examples/http-certification/assets/README.md
+++ b/examples/http-certification/assets/README.md
@@ -342,10 +342,15 @@ fn serve_metrics() -> HttpResponse<'static> {
This example uses a canister called `http_certification_assets_backend`.
-To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica:
```shell
dfx start --background --clean
+```
+
+Then, deploy the canister:
+
+```shell
dfx deploy http_certification_assets_backend
```
From 394f9c6ff6dee9693e8e438d14ecd93d68ed62e1 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:46:45 -0600
Subject: [PATCH 28/31] Update README.md
---
examples/http-certification/custom-assets/README.md | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md
index 3ddd6f7..770d98b 100644
--- a/examples/http-certification/custom-assets/README.md
+++ b/examples/http-certification/custom-assets/README.md
@@ -550,10 +550,15 @@ fn http_request(req: HttpRequest) -> HttpResponse {
This example uses a canister called `http_certification_custom_assets_backend`.
-To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica:
```shell
dfx start --background --clean
+```
+
+Then, deploy the canister:
+
+```shell
dfx deploy http_certification_custom_assets_backend
```
From 884df3da33a590c7c5e59789216f59216ae1c7f3 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:48:06 -0600
Subject: [PATCH 29/31] Update README.md
---
examples/http-certification/json-api/README.md | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/examples/http-certification/json-api/README.md b/examples/http-certification/json-api/README.md
index 4d0d762..4575a45 100644
--- a/examples/http-certification/json-api/README.md
+++ b/examples/http-certification/json-api/README.md
@@ -522,11 +522,17 @@ fn insert_update_route(method: &str, path: &str, route_handler: RouteHandler) {
## Testing the canister
-To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+This example uses a canister called `http_certification_json_api_backend`.
+
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica:
```shell
dfx start --background --clean
-dfx deploy
+```
+Then, deploy the canister:
+
+```shell
+dfx deploy http_certification_json_api_backend
```
To fetch to-do items:
From 33c52eb8e22ef54360f82e349d9813d46b8b6bd1 Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:49:00 -0600
Subject: [PATCH 30/31] Update README.md
---
examples/http-certification/skip-certification/README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md
index fc7f436..6e3c53d 100644
--- a/examples/http-certification/skip-certification/README.md
+++ b/examples/http-certification/skip-certification/README.md
@@ -54,10 +54,14 @@ The call to `data_certificate` returns a certificate that proves the canister's
This example uses a canister called `http_certification_skip_certification_backend`.
-To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica:
```shell
dfx start --background --clean
+```
+Then, deploy the canister:
+
+```shell
dfx deploy http_certification_skip_certification_backend
```
From 43859456b0c1230b7026f2894848b2e680af082f Mon Sep 17 00:00:00 2001
From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:49:31 -0600
Subject: [PATCH 31/31] Update README.md
---
examples/http-certification/upgrade-to-update-call/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/http-certification/upgrade-to-update-call/README.md b/examples/http-certification/upgrade-to-update-call/README.md
index 1163c02..09acee0 100644
--- a/examples/http-certification/upgrade-to-update-call/README.md
+++ b/examples/http-certification/upgrade-to-update-call/README.md
@@ -100,7 +100,7 @@ actor Http {
This example uses a Rust canister called `http_certification_upgrade_to_update_call_rust_backend` or a Motoko canister called `http_certification_upgrade_to_update_call_motoko_backend`.
-To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica and deploy the canister:
+To test the canister, you can use [`dfx`](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) to start a local instance of the replica:
```shell
dfx start --background --clean