From 78e7b4a3ed32b619cead5d1c44f358528a96828f Mon Sep 17 00:00:00 2001 From: NathanosDev Date: Mon, 4 Dec 2023 10:42:15 +0100 Subject: [PATCH] feat: migrate http request and response types from ic-response-verification to ic-http-certification BREAKING CHANGE: --- Cargo.lock | 9 + Cargo.toml | 1 + examples/nodejs/src/index.ts | 4 - examples/rust/Cargo.toml | 1 + examples/rust/src/main.rs | 23 +- examples/service-worker/src/sw.ts | 4 - examples/web/src/index.ts | 4 - packages/ic-http-certification/Cargo.toml | 7 + packages/ic-http-certification/src/cel/mod.rs | 4 +- packages/ic-http-certification/src/error.rs | 17 ++ .../src/http/header_field.rs | 2 + .../src/http/http_request.rs | 113 +++++++++ .../src/http/http_response.rs | 14 ++ .../ic-http-certification/src/http/mod.rs | 11 + packages/ic-http-certification/src/lib.rs | 4 + .../ic-response-verification-tests/Cargo.toml | 1 + .../src/main.rs | 9 +- .../ic-response-verification-wasm/Cargo.toml | 1 + .../ic-response-verification-wasm/src/lib.rs | 14 +- .../src/request.rs | 104 +++++++++ .../src/response.rs | 90 ++++++++ packages/ic-response-verification/Cargo.toml | 3 +- .../ic-response-verification/src/error.rs | 47 ++-- .../src/hash/request_hash.rs | 9 +- .../src/hash/response_hash.rs | 22 +- .../ic-response-verification/src/types/mod.rs | 8 - .../src/types/request.rs | 218 ------------------ .../src/types/response.rs | 105 --------- .../verify_request_response_pair.rs | 19 +- .../tests/v1_response_verification.rs | 47 ++-- ...se_verification_certification_scenarios.rs | 65 +++--- .../v2_response_verification_happy_path.rs | 19 +- .../v2_response_verification_sad_path.rs | 22 +- 33 files changed, 524 insertions(+), 497 deletions(-) create mode 100644 packages/ic-http-certification/src/error.rs create mode 100644 packages/ic-http-certification/src/http/header_field.rs create mode 100644 packages/ic-http-certification/src/http/http_request.rs create mode 100644 packages/ic-http-certification/src/http/http_response.rs create mode 100644 packages/ic-http-certification/src/http/mod.rs create mode 100644 packages/ic-response-verification-wasm/src/request.rs create mode 100644 packages/ic-response-verification-wasm/src/response.rs delete mode 100644 packages/ic-response-verification/src/types/request.rs delete mode 100644 packages/ic-response-verification/src/types/response.rs diff --git a/Cargo.lock b/Cargo.lock index a2c2d928..ee674a92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1491,7 +1491,12 @@ dependencies = [ name = "ic-http-certification" version = "1.3.0" dependencies = [ + "candid 0.9.8", + "http", "rstest", + "serde", + "thiserror", + "urlencoding", ] [[package]] @@ -1552,6 +1557,7 @@ dependencies = [ "ic-certification 1.3.0", "ic-certification-testing", "ic-crypto-tree-hash", + "ic-http-certification", "ic-representation-independent-hash", "ic-response-verification-test-utils", "ic-types", @@ -1593,6 +1599,7 @@ dependencies = [ "hex", "http", "ic-agent", + "ic-http-certification", "ic-response-verification", "ic-utils 0.29.0", "tokio", @@ -1604,6 +1611,7 @@ version = "1.3.0" dependencies = [ "base64 0.21.4", "console_error_panic_hook", + "ic-http-certification", "ic-response-verification", "ic-response-verification-test-utils", "js-sys", @@ -2607,6 +2615,7 @@ version = "0.1.0" dependencies = [ "candid 0.9.8", "hex", + "ic-http-certification", "ic-response-verification", "serde", "serde_bytes", diff --git a/Cargo.toml b/Cargo.toml index b6981119..c0e061e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ getrandom = { version = "0.2", features = ["js"] } ic-certification = { path = "./packages/ic-certification", default-features = false, version = "1.3.0" } +ic-http-certification = { path = "./packages/ic-http-certification", version = "1.3.0" } ic-certification-testing = { path = "./packages/ic-certification-testing" } ic-representation-independent-hash = { path = "./packages/ic-representation-independent-hash", version = "1.3.0" } ic-certificate-verification = { path = "./packages/ic-certificate-verification", version = "1.3.0" } diff --git a/examples/nodejs/src/index.ts b/examples/nodejs/src/index.ts index 24fe3d15..956f88d5 100644 --- a/examples/nodejs/src/index.ts +++ b/examples/nodejs/src/index.ts @@ -70,10 +70,6 @@ try { console.log(`Error parsing cbor: ${error.message}`); break; - case ResponseVerificationErrorCode.MalformedUrl: - console.log(`Invalid URL provided: ${error.message}`); - break; - case ResponseVerificationErrorCode.CertificateVerificationFailed: console.log(`Certificate verification failed: ${error.message}`); break; diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index 91c510d4..57e11515 100644 --- a/examples/rust/Cargo.toml +++ b/examples/rust/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] ic-response-verification.workspace = true +ic-http-certification.workspace = true candid.workspace = true hex.workspace = true serde.workspace = true diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index 1372af69..f8fdd536 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -1,28 +1,13 @@ -use candid::{CandidType, Decode, Deserialize, Principal}; -use ic_response_verification::types::{Request, Response}; +use candid::{Decode, Principal}; +use ic_http_certification::{HttpRequest, HttpResponse}; use ic_response_verification::{verify_request_response_pair, MIN_VERIFICATION_VERSION}; -#[derive(Debug, Clone, CandidType, Deserialize)] -struct HttpRequest { - pub url: String, - pub headers: Vec<(String, String)>, - #[serde(with = "serde_bytes")] - pub body: Vec, -} - -#[derive(Debug, Clone, CandidType, Deserialize)] -pub struct HttpResponse { - pub headers: Vec<(String, String)>, - #[serde(with = "serde_bytes")] - pub body: Vec, -} - fn main() { let request_hex = "4449444C046D7B6C02007101716D016C04EFD6E40271E1EDEB4A71A2F5ED880400C6A4A19806020103012F03474554000704486F73742372646D78362D6A616161612D61616161612D61616164712D6361692E6963302E617070066163636570748701746578742F68746D6C2C6170706C69636174696F6E2F7868746D6C2B786D6C2C6170706C69636174696F6E2F786D6C3B713D302E392C696D6167652F617669662C696D6167652F776562702C696D6167652F61706E672C2A2F2A3B713D302E382C6170706C69636174696F6E2F7369676E65642D65786368616E67653B763D62333B713D302E39097365632D63682D756128224368726F6D69756D223B763D22313037222C20224E6F743D413F4272616E64223B763D22323422107365632D63682D75612D6D6F62696C65023F30127365632D63682D75612D706C6174666F726D092257696E646F77732219757067726164652D696E7365637572652D726571756573747301310A757365722D6167656E74744D6F7A696C6C612F352E30202857696E646F7773204E542031302E303B2057696E36343B2078363429204170706C655765624B69742F3533372E333620284B48544D4C2C206C696B65204765636B6F29204368726F6D652F3130372E302E353330342E313037205361666172692F3533372E3336"; let request_candid = hex::decode(request_hex).expect("Could not decode request from hex"); let http_request = Decode!(&request_candid, HttpRequest).expect("Could not decode request from candid"); - let request = Request { + let request = HttpRequest { method: "GET".into(), url: http_request.url, headers: http_request.headers, @@ -39,7 +24,7 @@ fn main() { let http_response = Decode!(&response_candid, HttpResponse).expect("Could not decode response from candid"); - let response = Response { + let response = HttpResponse { status_code: 200, headers: http_response.headers, body: http_response.body, diff --git a/examples/service-worker/src/sw.ts b/examples/service-worker/src/sw.ts index 84da6a97..b160ce4e 100644 --- a/examples/service-worker/src/sw.ts +++ b/examples/service-worker/src/sw.ts @@ -73,10 +73,6 @@ self.addEventListener('activate', async () => { console.log(`Error parsing cbor: ${error.message}`); break; - case ResponseVerificationErrorCode.MalformedUrl: - console.log(`Invalid URL provided: ${error.message}`); - break; - case ResponseVerificationErrorCode.CertificateVerificationFailed: console.log(`Certificate verification failed: ${error.message}`); break; diff --git a/examples/web/src/index.ts b/examples/web/src/index.ts index 08fdc0e2..e5983175 100644 --- a/examples/web/src/index.ts +++ b/examples/web/src/index.ts @@ -73,10 +73,6 @@ window.addEventListener('load', async () => { console.log(`Error parsing cbor: ${error.message}`); break; - case ResponseVerificationErrorCode.MalformedUrl: - console.log(`Invalid URL provided: ${error.message}`); - break; - case ResponseVerificationErrorCode.CertificateVerificationFailed: console.log(`Certificate verification failed: ${error.message}`); break; diff --git a/packages/ic-http-certification/Cargo.toml b/packages/ic-http-certification/Cargo.toml index cca19fa9..7b7f12c0 100644 --- a/packages/ic-http-certification/Cargo.toml +++ b/packages/ic-http-certification/Cargo.toml @@ -14,5 +14,12 @@ repository.workspace = true license.workspace = true homepage.workspace = true +[dependencies] +candid.workspace = true +serde.workspace = true +http.workspace = true +urlencoding.workspace = true +thiserror.workspace = true + [dev-dependencies] rstest.workspace = true diff --git a/packages/ic-http-certification/src/cel/mod.rs b/packages/ic-http-certification/src/cel/mod.rs index e1e28d1f..3c4ac4d0 100644 --- a/packages/ic-http-certification/src/cel/mod.rs +++ b/packages/ic-http-certification/src/cel/mod.rs @@ -1,5 +1,5 @@ -//! The CEL modules contains functions and builders for creating CEL expression -//! definitions and conveting them into their `String` representation. +//! The CEL module contains functions and builders for creating CEL expression +//! definitions and converting them into their `String` representation. mod cel_builder; pub use cel_builder::*; diff --git a/packages/ic-http-certification/src/error.rs b/packages/ic-http-certification/src/error.rs new file mode 100644 index 00000000..b0e0d7e9 --- /dev/null +++ b/packages/ic-http-certification/src/error.rs @@ -0,0 +1,17 @@ +//! The error module contains types for common errors that may be thrown +//! by other modules in this crate. + +/// HTTP certification result type. +pub type HttpCertificationResult = Result; + +/// HTTP certification error type. +#[derive(thiserror::Error, Debug)] +pub enum HttpCertificationError { + /// The URL was malformed and could not be parsed correctly. + #[error(r#"Failed to parse url: "{0}""#)] + MalformedUrl(String), + + /// Error converting UTF-8 string. + #[error(r#"Error converting UTF8 string bytes: "{0}""#)] + Utf8ConversionError(#[from] std::string::FromUtf8Error), +} diff --git a/packages/ic-http-certification/src/http/header_field.rs b/packages/ic-http-certification/src/http/header_field.rs new file mode 100644 index 00000000..0b1eb0cf --- /dev/null +++ b/packages/ic-http-certification/src/http/header_field.rs @@ -0,0 +1,2 @@ +/// An HTTP header field, represented as a tuple of (name, value). +pub type HeaderField = (String, String); diff --git a/packages/ic-http-certification/src/http/http_request.rs b/packages/ic-http-certification/src/http/http_request.rs new file mode 100644 index 00000000..85b3909b --- /dev/null +++ b/packages/ic-http-certification/src/http/http_request.rs @@ -0,0 +1,113 @@ +use crate::{HeaderField, HttpCertificationError, HttpCertificationResult}; +use candid::{CandidType, Deserialize}; +use http::Uri; + +/// A Candid-encodable representation of an HTTP request. +/// This struct is used by canisters that implement the HTTP interface required by the HTTP Gateway Protocol. +#[derive(Clone, Debug, CandidType, Deserialize, PartialEq, Eq)] +pub struct HttpRequest { + /// HTTP request method. + pub method: String, + /// Request URL. + pub url: String, + /// HTTP request headers. + pub headers: Vec, + /// Request body as an array of bytes. + pub body: Vec, +} + +impl HttpRequest { + /// Returns the path of the request URL, without domain, query parameters or fragments. + pub fn get_path<'a>(&'a self) -> HttpCertificationResult { + let uri = self + .url + .parse::() + .map_err(|_| HttpCertificationError::MalformedUrl(self.url.clone()))?; + + let decoded_path = urlencoding::decode(uri.path()).map(|path| path.into_owned())?; + Ok(decoded_path) + } + + /// Returns the query parameters of the request URL, if any, as a string. + pub fn get_query<'a>(&'a self) -> HttpCertificationResult> { + self.url + .parse::() + .map(|uri| uri.query().map(|uri| uri.to_owned())) + .map_err(|_| HttpCertificationError::MalformedUrl(self.url.clone())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn request_get_uri() { + let req = HttpRequest { + method: "GET".to_string(), + url: "https://canister.com/sample-asset.txt".to_string(), + headers: vec![], + body: vec![], + }; + + let path = req.get_path().unwrap(); + let query = req.get_query().unwrap(); + + assert_eq!(path, "/sample-asset.txt"); + assert!(query.is_none()); + } + + #[test] + fn request_get_encoded_uri() { + let test_requests = [ + ( + HttpRequest { + method: "GET".to_string(), + url: "https://canister.com/%73ample-asset.txt".to_string(), + headers: vec![], + body: vec![], + }, + "/sample-asset.txt", + "", + ), + ( + HttpRequest { + method: "GET".to_string(), + url: "https://canister.com/path/123?foo=test%20component&bar=1".to_string(), + headers: vec![], + body: vec![], + }, + "/path/123", + "foo=test%20component&bar=1", + ), + ( + HttpRequest { + method: "GET".to_string(), + url: "https://canister.com/a%20file.txt".to_string(), + headers: vec![], + body: vec![], + }, + "/a file.txt", + "", + ), + ( + HttpRequest { + method: "GET".to_string(), + url: "https://canister.com/mujin0722/3888-zjfrd-tqaaa-aaaaf-qakia-cai/%E6%97%A0%E8%AE%BA%E7%BE%8E%E8%81%94%E5%82%A8%E6%98%AF%E5%90%A6%E5%8A%A0%E6%81%AFbtc%E4%BB%8D%E5%B0%86%E5%9B%9E%E5%88%B07%E4%B8%87%E5%88%80".to_string(), + headers: vec![], + body: vec![], + }, + "/mujin0722/3888-zjfrd-tqaaa-aaaaf-qakia-cai/无论美联储是否加息btc仍将回到7万刀", + "", + ), + ]; + + for (req, expected_path, expected_query) in test_requests.iter() { + let path = req.get_path().unwrap(); + let query = req.get_query().unwrap(); + + assert_eq!(path, *expected_path); + assert_eq!(query.unwrap_or_default(), *expected_query); + } + } +} diff --git a/packages/ic-http-certification/src/http/http_response.rs b/packages/ic-http-certification/src/http/http_response.rs new file mode 100644 index 00000000..dd578606 --- /dev/null +++ b/packages/ic-http-certification/src/http/http_response.rs @@ -0,0 +1,14 @@ +use crate::HeaderField; +use candid::{CandidType, Deserialize}; + +/// A Candid-encodable representation of an HTTP response. +/// This struct is used by canisters that implement the HTTP interface required by the HTTP Gateway Protocol. +#[derive(Clone, Debug, CandidType, Deserialize, PartialEq, Eq)] +pub struct HttpResponse { + /// HTTP response status code. + pub status_code: u16, + /// HTTP response headers. + pub headers: Vec, + /// Response body as an array of bytes. + pub body: Vec, +} diff --git a/packages/ic-http-certification/src/http/mod.rs b/packages/ic-http-certification/src/http/mod.rs new file mode 100644 index 00000000..50c3164b --- /dev/null +++ b/packages/ic-http-certification/src/http/mod.rs @@ -0,0 +1,11 @@ +//! The HTTP module contains types for representing HTTP requests and responses in Rust. +//! These types are Candid-encodable and are used by canisters that implement the +//! HTTP interface required by the HTTP Gateway Protocol. + +mod header_field; +mod http_request; +mod http_response; + +pub use header_field::*; +pub use http_request::*; +pub use http_response::*; diff --git a/packages/ic-http-certification/src/lib.rs b/packages/ic-http-certification/src/lib.rs index f6803a6d..aa4da61a 100644 --- a/packages/ic-http-certification/src/lib.rs +++ b/packages/ic-http-certification/src/lib.rs @@ -384,3 +384,7 @@ Typically these requests have been routed through `raw` Internet Computer URLs i pub mod cel; pub use cel::{DefaultCelBuilder, DefaultResponseCertification}; +pub mod error; +pub use error::*; +pub mod http; +pub use crate::http::*; diff --git a/packages/ic-response-verification-tests/Cargo.toml b/packages/ic-response-verification-tests/Cargo.toml index 485594d4..4cbc78b7 100644 --- a/packages/ic-response-verification-tests/Cargo.toml +++ b/packages/ic-response-verification-tests/Cargo.toml @@ -15,4 +15,5 @@ ic-agent.workspace = true ic-utils.workspace = true tokio.workspace = true ic-response-verification.workspace = true +ic-http-certification.workspace = true anyhow.workspace = true diff --git a/packages/ic-response-verification-tests/src/main.rs b/packages/ic-response-verification-tests/src/main.rs index 68481e3d..724bf5fd 100644 --- a/packages/ic-response-verification-tests/src/main.rs +++ b/packages/ic-response-verification-tests/src/main.rs @@ -2,7 +2,8 @@ use crate::agent::create_agent; use anyhow::{anyhow, Result}; use ic_agent::export::Principal; use ic_agent::Agent; -use ic_response_verification::types::{Request, Response, VerificationInfo}; +use ic_http_certification::{HttpRequest, HttpResponse}; +use ic_response_verification::types::VerificationInfo; use ic_utils::call::SyncCall; use ic_utils::interfaces::http_request::HeaderField; use ic_utils::interfaces::HttpRequestCanister; @@ -150,7 +151,7 @@ async fn perform_test( path: &str, certificate_version: Option<&u16>, agent: &Agent, -) -> Result<(VerificationInfo, Response)> { +) -> Result<(VerificationInfo, HttpResponse)> { let canister_id = Principal::from_text(canister_id)?; let canister_interface = HttpRequestCanister::create(agent, canister_id); @@ -159,13 +160,13 @@ async fn perform_test( .call() .await?; - let request = Request { + let request = HttpRequest { method: "GET".into(), headers: vec![], url: path.into(), body: vec![], }; - let response = Response { + let response = HttpResponse { headers: response .headers .iter() diff --git a/packages/ic-response-verification-wasm/Cargo.toml b/packages/ic-response-verification-wasm/Cargo.toml index aec2f2f5..8ba27ce3 100644 --- a/packages/ic-response-verification-wasm/Cargo.toml +++ b/packages/ic-response-verification-wasm/Cargo.toml @@ -22,6 +22,7 @@ wasm-opt = ["-Oz", "--enable-mutable-globals"] [dependencies] ic-response-verification = { workspace = true, features = ["js"] } +ic-http-certification.workspace = true console_error_panic_hook.workspace = true js-sys.workspace = true wasm-bindgen.workspace = true diff --git a/packages/ic-response-verification-wasm/src/lib.rs b/packages/ic-response-verification-wasm/src/lib.rs index 2583e570..57725a74 100644 --- a/packages/ic-response-verification-wasm/src/lib.rs +++ b/packages/ic-response-verification-wasm/src/lib.rs @@ -1,10 +1,14 @@ +use crate::request::request_from_js; +use crate::response::response_from_js; use ic_response_verification::{ - types::{Request, Response, VerificationInfo}, - verify_request_response_pair as verify_request_response_pair_impl, ResponseVerificationJsError, - MAX_VERIFICATION_VERSION, MIN_VERIFICATION_VERSION, + types::VerificationInfo, verify_request_response_pair as verify_request_response_pair_impl, + ResponseVerificationJsError, MAX_VERIFICATION_VERSION, MIN_VERIFICATION_VERSION, }; use wasm_bindgen::{prelude::*, JsCast}; +mod request; +mod response; + #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "VerificationInfo")] @@ -46,8 +50,8 @@ pub fn verify_request_response_pair( ic_public_key: &[u8], min_requested_verification_version: u8, ) -> Result { - let request = Request::from(JsValue::from(request)); - let response = Response::from(JsValue::from(response)); + let request = request_from_js(JsValue::from(request)); + let response = response_from_js(JsValue::from(response)); verify_request_response_pair_impl( request.into(), diff --git a/packages/ic-response-verification-wasm/src/request.rs b/packages/ic-response-verification-wasm/src/request.rs new file mode 100644 index 00000000..26fee38c --- /dev/null +++ b/packages/ic-response-verification-wasm/src/request.rs @@ -0,0 +1,104 @@ +use ic_http_certification::HttpRequest; +use wasm_bindgen::{prelude::*, JsCast}; + +#[wasm_bindgen(typescript_custom_section)] +const REQUEST: &'static str = r#" +interface Request { + method: String; + url: String; + headers: [string, string][]; + body: Uint8Array; +} +"#; + +pub fn request_from_js(req: JsValue) -> HttpRequest { + use js_sys::{Array, JsString, Object, Uint8Array}; + + let method_str = JsString::from("method"); + let url_str = JsString::from("url"); + let headers_str = JsString::from("headers"); + let body_str = JsString::from("body"); + + let mut method = String::from(""); + let mut url = String::from(""); + let mut headers = Vec::new(); + let mut body = Vec::new(); + + let req = Object::unchecked_from_js(req); + for entry in Object::entries(&req).iter() { + let entry = Array::unchecked_from_js(entry); + let k = JsString::unchecked_from_js(entry.get(0)); + + if k == method_str { + method = JsString::unchecked_from_js(entry.get(1)) + .as_string() + .unwrap(); + } + + if k == url_str { + url = JsString::unchecked_from_js(entry.get(1)) + .as_string() + .unwrap(); + } + + if k == headers_str { + let headers_v = Array::unchecked_from_js(entry.get(1)); + let headers_v = headers_v.iter(); + headers = Vec::with_capacity(headers_v.len()); + for header in headers_v { + let header = Array::unchecked_from_js(header); + let header_name = header.get(0).as_string().unwrap(); + let header_val = header.get(1).as_string().unwrap(); + headers.push((header_name, header_val)) + } + } + + if k == body_str { + body = Uint8Array::unchecked_from_js(entry.get(1)).to_vec(); + } + } + + HttpRequest { + method, + url, + headers, + body, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use js_sys::JSON; + use wasm_bindgen_test::wasm_bindgen_test; + + #[wasm_bindgen_test] + fn request_from() { + let v = JSON::parse( + r#"{ + "method": "GET", + "url": "http://url.com", + "headers": [ + ["header1", "header1val"], + ["header2", "header2val"] + ], + "body": [0, 1, 2, 3, 4, 5, 6] + }"#, + ) + .expect("failed to parse JSON"); + let r = request_from_js(v); + + assert_eq!( + r, + HttpRequest { + method: "GET".into(), + url: "http://url.com".into(), + headers: vec![ + ("header1".into(), "header1val".into()), + ("header2".into(), "header2val".into()), + ], + body: vec![0, 1, 2, 3, 4, 5, 6], + } + ); + } +} diff --git a/packages/ic-response-verification-wasm/src/response.rs b/packages/ic-response-verification-wasm/src/response.rs new file mode 100644 index 00000000..4834ffff --- /dev/null +++ b/packages/ic-response-verification-wasm/src/response.rs @@ -0,0 +1,90 @@ +use ic_http_certification::HttpResponse; +use wasm_bindgen::{prelude::*, JsCast}; + +#[wasm_bindgen(typescript_custom_section)] +const RESPONSE: &'static str = r#" +interface Response { + statusCode: number; + headers: [string, string][]; + body: Uint8Array; +} +"#; + +pub fn response_from_js(resp: JsValue) -> HttpResponse { + use js_sys::{Array, JsString, Number, Object, Uint8Array}; + + let status_code_str = JsString::from("statusCode"); + let headers_str = JsString::from("headers"); + let body_str = JsString::from("body"); + + let mut status_code: u16 = 0; + let mut headers = Vec::new(); + let mut body = Vec::new(); + + let resp = Object::unchecked_from_js(resp); + for entry in Object::entries(&resp).iter() { + let entry = Array::unchecked_from_js(entry); + let k = JsString::unchecked_from_js(entry.get(0)); + + if k == status_code_str { + status_code = Number::unchecked_from_js(entry.get(1)).as_f64().unwrap() as u16; + } + + if k == headers_str { + let headers_v = Array::unchecked_from_js(entry.get(1)); + let headers_v = headers_v.iter(); + headers = Vec::with_capacity(headers_v.len()); + for header in headers_v { + let header = Array::unchecked_from_js(header); + let header_name = header.get(0).as_string().unwrap(); + let header_val = header.get(1).as_string().unwrap(); + headers.push((header_name, header_val)) + } + } + + if k == body_str { + body = Uint8Array::unchecked_from_js(entry.get(1)).to_vec(); + } + } + + HttpResponse { + status_code, + headers, + body, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use js_sys::JSON; + use wasm_bindgen_test::wasm_bindgen_test; + + #[wasm_bindgen_test] + fn request_from() { + let v = JSON::parse( + r#"{ + "statusCode": 200, + "body": [0, 1, 2, 3, 4, 5, 6], + "headers": [ + ["header1", "header1val"], + ["header2", "header2val"] + ] + }"#, + ) + .expect("failed to parse JSON"); + let r = response_from_js(v); + + assert_eq!( + r, + HttpResponse { + status_code: 200, + body: vec![0, 1, 2, 3, 4, 5, 6], + headers: vec![ + ("header1".into(), "header1val".into()), + ("header2".into(), "header2val".into()), + ], + } + ); + } +} diff --git a/packages/ic-response-verification/Cargo.toml b/packages/ic-response-verification/Cargo.toml index 2cf2c23b..df0dfc63 100644 --- a/packages/ic-response-verification/Cargo.toml +++ b/packages/ic-response-verification/Cargo.toml @@ -29,7 +29,8 @@ wasm-bindgen = { version = "0.2", optional = true } thiserror.workspace = true sha2.workspace = true http.workspace = true -ic-certification = { workspace = true } +ic-certification.workspace = true +ic-http-certification.workspace = true ic-representation-independent-hash.workspace = true ic-cbor.workspace = true ic-certificate-verification.workspace = true diff --git a/packages/ic-response-verification/src/error.rs b/packages/ic-response-verification/src/error.rs index 40665478..95ea4c90 100644 --- a/packages/ic-response-verification/src/error.rs +++ b/packages/ic-response-verification/src/error.rs @@ -13,18 +13,10 @@ pub type ResponseVerificationResult = Result for ResponseVerificationJsError { fn from(error: ResponseVerificationError) -> ResponseVerificationJsError { let code = match error { - ResponseVerificationError::MalformedUrl(_) => { - ResponseVerificationJsErrorCode::MalformedUrl - } ResponseVerificationError::IoError(_) => ResponseVerificationJsErrorCode::IoError, - ResponseVerificationError::Utf8ConversionError { .. } => { - ResponseVerificationJsErrorCode::Utf8ConversionError - } ResponseVerificationError::UnsupportedVerificationVersion { .. } => { ResponseVerificationJsErrorCode::UnsupportedVerificationVersion } @@ -214,6 +202,9 @@ impl From for ResponseVerificationJsError { ResponseVerificationError::CertificateVerificationFailed(_) => { ResponseVerificationJsErrorCode::CertificateVerificationFailed } + ResponseVerificationError::HttpCertificationError(_) => { + ResponseVerificationJsErrorCode::HttpCertificationError + } }; let message = error.to_string(); @@ -229,20 +220,22 @@ mod tests { use super::*; use crate::cel::CelParserError; use base64::{engine::general_purpose, Engine as _}; + use ic_http_certification::HttpCertificationError; use ic_response_verification_test_utils::hex_decode; use wasm_bindgen_test::wasm_bindgen_test; #[wasm_bindgen_test] - fn error_into_invalid_url() { - let error = ResponseVerificationError::MalformedUrl("https://internetcomputer.org".into()); - + fn error_into_http_certification_error() { + let error = ResponseVerificationError::HttpCertificationError( + HttpCertificationError::MalformedUrl("https://internetcomputer.org".into()), + ); let result = ResponseVerificationJsError::from(error); assert_eq!( result, ResponseVerificationJsError { - code: ResponseVerificationJsErrorCode::MalformedUrl, - message: r#"Failed to parse url: "https://internetcomputer.org""#.into(), + code: ResponseVerificationJsErrorCode::HttpCertificationError, + message: r#"HTTP Certification error: "Failed to parse url: "https://internetcomputer.org"""#.into(), } ) } @@ -270,16 +263,18 @@ mod tests { let invalid_utf_bytes = hex_decode("fca1a1a1a1a1"); let inner_error = String::from_utf8(invalid_utf_bytes).expect_err("Expected error"); - let error = ResponseVerificationError::Utf8ConversionError(inner_error.clone()); + let error = ResponseVerificationError::HttpCertificationError( + HttpCertificationError::Utf8ConversionError(inner_error.clone()), + ); let result = ResponseVerificationJsError::from(error); assert_eq!( result, ResponseVerificationJsError { - code: ResponseVerificationJsErrorCode::Utf8ConversionError, + code: ResponseVerificationJsErrorCode::HttpCertificationError, message: format!( - r#"Error converting UTF8 string bytes: "{0}""#, + r#"HTTP Certification error: "Error converting UTF8 string bytes: "{0}"""#, inner_error.to_string() ), } diff --git a/packages/ic-response-verification/src/hash/request_hash.rs b/packages/ic-response-verification/src/hash/request_hash.rs index bdbc8e48..b71e74cc 100644 --- a/packages/ic-response-verification/src/hash/request_hash.rs +++ b/packages/ic-response-verification/src/hash/request_hash.rs @@ -1,6 +1,7 @@ use crate::error::ResponseVerificationResult; -use crate::types::{Request, RequestCertification}; +use crate::types::RequestCertification; use ic_certification::hash_tree::Hash; +use ic_http_certification::HttpRequest; use ic_representation_independent_hash::{hash, representation_independent_hash, Value}; /// Calculates the @@ -8,7 +9,7 @@ use ic_representation_independent_hash::{hash, representation_independent_hash, /// of [crate::types::Request] according to [crate::types::RequestCertification] returned from /// [crate::cel::cel_to_certification]. pub fn request_hash( - request: &Request, + request: &HttpRequest, request_certification: &RequestCertification, ) -> ResponseVerificationResult { let mut filtered_headers = get_filtered_headers(&request.headers, request_certification); @@ -157,8 +158,8 @@ mod tests { assert_eq!(result, result_with_fragment); } - fn create_request(uri: &str) -> Request { - Request { + fn create_request(uri: &str) -> HttpRequest { + HttpRequest { url: uri.into(), method: "POST".into(), headers: vec![ diff --git a/packages/ic-response-verification/src/hash/response_hash.rs b/packages/ic-response-verification/src/hash/response_hash.rs index e8324d20..a15faadd 100644 --- a/packages/ic-response-verification/src/hash/response_hash.rs +++ b/packages/ic-response-verification/src/hash/response_hash.rs @@ -1,5 +1,6 @@ -use crate::types::{Response, ResponseCertification}; +use crate::types::ResponseCertification; use ic_certification::hash_tree::Hash; +use ic_http_certification::HttpResponse; use ic_representation_independent_hash::{hash, representation_independent_hash, Value}; const CERTIFICATE_HEADER_NAME: &str = "IC-Certificate"; @@ -20,7 +21,7 @@ pub struct ResponseHeaders { /// Filters headers of [crate::types::Response] according to [crate::types::ResponseCertification] /// returned from [crate::cel::cel_to_certification]. pub fn filter_response_headers( - response: &Response, + response: &HttpResponse, response_certification: &ResponseCertification, ) -> ResponseHeaders { let headers_filter: Box _> = match response_certification { @@ -113,7 +114,10 @@ pub fn response_headers_hash(status_code: &u64, response_headers: &ResponseHeade /// [Representation Independent Hash](https://internetcomputer.org/docs/current/references/ic-interface-spec/#hash-of-map) /// of a [crate::types::Response] according to [crate::types::ResponseCertification] returned from /// [crate::cel::cel_to_certification]. -pub fn response_hash(response: &Response, response_certification: &ResponseCertification) -> Hash { +pub fn response_hash( + response: &HttpResponse, + response_certification: &ResponseCertification, +) -> Hash { let filtered_headers = filter_response_headers(response, response_certification); let concatenated_hashes = [ response_headers_hash(&response.status_code.into(), &filtered_headers), @@ -213,7 +217,7 @@ mod tests { let response_certification = ResponseCertification::CertifiedHeaders(vec!["Accept-Encoding".into()]); let response = create_response(CERTIFIED_HEADERS_CEL_EXPRESSION); - let response_without_excluded_headers = Response { + let response_without_excluded_headers = HttpResponse { status_code: 200, headers: vec![ ( @@ -253,7 +257,7 @@ mod tests { let response_certification = ResponseCertification::HeaderExclusions(vec!["Content-Security-Policy".into()]); let response = create_response(HEADER_EXCLUSIONS_CEL_EXPRESSION); - let response_without_excluded_headers = Response { + let response_without_excluded_headers = HttpResponse { status_code: 200, headers: vec![ ( @@ -296,7 +300,7 @@ mod tests { let response_certification = ResponseCertification::CertifiedHeaders(vec!["Accept-Encoding".into()]); let response = create_response(CERTIFIED_HEADERS_CEL_EXPRESSION); - let response_without_excluded_headers = Response { + let response_without_excluded_headers = HttpResponse { status_code: 200, headers: vec![ ("IC-Certificate".into(), CERTIFICATE.into()), @@ -343,7 +347,7 @@ mod tests { let response_certification = ResponseCertification::HeaderExclusions(vec!["Content-Security-Policy".into()]); let response = create_response(HEADER_EXCLUSIONS_CEL_EXPRESSION); - let response_without_excluded_headers = Response { + let response_without_excluded_headers = HttpResponse { status_code: 200, headers: vec![ ("IC-Certificate".into(), CERTIFICATE.into()), @@ -375,8 +379,8 @@ mod tests { /// of the expected hashes. Generating the hash for a string with so much whitespace manually /// may be prone to error in copy/pasting the string into a website and missing a leading/trailing /// newline or a tab character somewhere. - fn create_response(cel_expression: &str) -> Response { - Response { + fn create_response(cel_expression: &str) -> HttpResponse { + HttpResponse { status_code: 200, headers: vec![ ("IC-Certificate".into(), CERTIFICATE.into()), diff --git a/packages/ic-response-verification/src/types/mod.rs b/packages/ic-response-verification/src/types/mod.rs index 7a11886f..a764b982 100644 --- a/packages/ic-response-verification/src/types/mod.rs +++ b/packages/ic-response-verification/src/types/mod.rs @@ -4,14 +4,6 @@ mod certification; pub use certification::*; -/// Types to represent response objects used for certification. -mod request; -pub use request::*; - -/// Types to represent request objects used for certification. -mod response; -pub use response::*; - /// Types to represent the result of verifying a request/response pair's certification. mod verification_result; pub use verification_result::*; diff --git a/packages/ic-response-verification/src/types/request.rs b/packages/ic-response-verification/src/types/request.rs deleted file mode 100644 index a11030d2..00000000 --- a/packages/ic-response-verification/src/types/request.rs +++ /dev/null @@ -1,218 +0,0 @@ -use crate::error::{ResponseVerificationError, ResponseVerificationResult}; -use http::Uri; - -#[cfg(all(target_arch = "wasm32", feature = "js"))] -use wasm_bindgen::{prelude::*, JsCast}; - -#[cfg(all(target_arch = "wasm32", feature = "js"))] -#[wasm_bindgen(typescript_custom_section)] -const REQUEST: &'static str = r#" -interface Request { - method: String; - url: String; - headers: [string, string][]; - body: Uint8Array; -} -"#; - -/// Represents a Request from the [Internet Computer](https://dfinity.org). -#[derive(Debug, PartialEq, Eq)] -pub struct Request { - /// The HTTP method of the request, i.e. "GET". - pub method: String, - /// The URL of the request, i.e. "/". - pub url: String, - /// The HTTP headers of the request, i.e. \[\["Host", "rdmx6-jaaaa-aaaaa-aaadq-cai.ic0.app"\]\] - pub headers: Vec<(String, String)>, - /// The body of the request as an array of bytes, i.e. \[60, 33, 100, 111, 99\] - pub body: Vec, -} - -impl Request { - pub(crate) fn get_path(&self) -> ResponseVerificationResult { - let uri = self - .url - .parse::() - .map_err(|_| ResponseVerificationError::MalformedUrl(self.url.clone()))?; - - let decoded_path = urlencoding::decode(uri.path()).map(|path| path.into_owned())?; - Ok(decoded_path) - } - - pub(crate) fn get_query(&self) -> ResponseVerificationResult> { - self.url - .parse::() - .map(|uri| uri.query().map(|uri| uri.to_owned())) - .map_err(|_| ResponseVerificationError::MalformedUrl(self.url.clone())) - } -} - -#[cfg(all(target_arch = "wasm32", feature = "js"))] -impl From for Request { - fn from(req: JsValue) -> Self { - use js_sys::{Array, JsString, Object, Uint8Array}; - - let method_str = JsString::from("method"); - let url_str = JsString::from("url"); - let headers_str = JsString::from("headers"); - let body_str = JsString::from("body"); - - let mut method = String::from(""); - let mut url = String::from(""); - let mut headers = Vec::new(); - let mut body = Vec::new(); - - let req = Object::unchecked_from_js(req); - for entry in Object::entries(&req).iter() { - let entry = Array::unchecked_from_js(entry); - let k = JsString::unchecked_from_js(entry.get(0)); - - if k == method_str { - method = JsString::unchecked_from_js(entry.get(1)) - .as_string() - .unwrap(); - } - - if k == url_str { - url = JsString::unchecked_from_js(entry.get(1)) - .as_string() - .unwrap(); - } - - if k == headers_str { - let headers_v = Array::unchecked_from_js(entry.get(1)); - let headers_v = headers_v.iter(); - headers = Vec::with_capacity(headers_v.len()); - for header in headers_v { - let header = Array::unchecked_from_js(header); - let header_name = header.get(0).as_string().unwrap(); - let header_val = header.get(1).as_string().unwrap(); - headers.push((header_name, header_val)) - } - } - - if k == body_str { - body = Uint8Array::unchecked_from_js(entry.get(1)).to_vec(); - } - } - - Self { - method, - url, - headers, - body, - } - } -} - -#[cfg(all(not(target_arch = "wasm32"), test))] -mod tests { - use super::*; - - #[test] - fn request_get_uri() { - let req = Request { - method: "GET".to_string(), - url: "https://canister.com/sample-asset.txt".to_string(), - headers: vec![], - body: vec![], - }; - - let path = req.get_path().unwrap(); - let query = req.get_query().unwrap(); - - assert_eq!(path, "/sample-asset.txt"); - assert!(query.is_none()); - } - - #[test] - fn request_get_encoded_uri() { - let test_requests = [ - ( - Request { - method: "GET".to_string(), - url: "https://canister.com/%73ample-asset.txt".to_string(), - headers: vec![], - body: vec![], - }, - "/sample-asset.txt", - "", - ), - ( - Request { - method: "GET".to_string(), - url: "https://canister.com/path/123?foo=test%20component&bar=1".to_string(), - headers: vec![], - body: vec![], - }, - "/path/123", - "foo=test%20component&bar=1", - ), - ( - Request { - method: "GET".to_string(), - url: "https://canister.com/a%20file.txt".to_string(), - headers: vec![], - body: vec![], - }, - "/a file.txt", - "", - ), - ( - Request { - method: "GET".to_string(), - url: "https://canister.com/mujin0722/3888-zjfrd-tqaaa-aaaaf-qakia-cai/%E6%97%A0%E8%AE%BA%E7%BE%8E%E8%81%94%E5%82%A8%E6%98%AF%E5%90%A6%E5%8A%A0%E6%81%AFbtc%E4%BB%8D%E5%B0%86%E5%9B%9E%E5%88%B07%E4%B8%87%E5%88%80".to_string(), - headers: vec![], - body: vec![], - }, - "/mujin0722/3888-zjfrd-tqaaa-aaaaf-qakia-cai/无论美联储是否加息btc仍将回到7万刀", - "", - ), - ]; - - for (req, expected_path, expected_query) in test_requests.iter() { - let path = req.get_path().unwrap(); - let query = req.get_query().unwrap(); - - assert_eq!(path, *expected_path); - assert_eq!(query.unwrap_or_default(), *expected_query); - } - } -} - -#[cfg(all(target_arch = "wasm32", feature = "js", test))] -mod tests { - use super::*; - use js_sys::JSON; - use wasm_bindgen_test::wasm_bindgen_test; - - #[wasm_bindgen_test] - fn request_from() { - let v = JSON::parse( - r#"{ - "method": "GET", - "url": "http://url.com", - "headers": [ - ["header1", "header1val"], - ["header2", "header2val"] - ], - "body": [0, 1, 2, 3, 4, 5, 6] - }"#, - ) - .expect("failed to parse JSON"); - let r = Request::from(v); - - assert_eq!( - r, - Request { - method: "GET".into(), - url: "http://url.com".into(), - headers: vec![ - ("header1".into(), "header1val".into()), - ("header2".into(), "header2val".into()), - ], - body: vec![0, 1, 2, 3, 4, 5, 6], - } - ); - } -} diff --git a/packages/ic-response-verification/src/types/response.rs b/packages/ic-response-verification/src/types/response.rs deleted file mode 100644 index 729abc86..00000000 --- a/packages/ic-response-verification/src/types/response.rs +++ /dev/null @@ -1,105 +0,0 @@ -#[cfg(all(target_arch = "wasm32", feature = "js"))] -use wasm_bindgen::{prelude::*, JsCast}; - -#[cfg(all(target_arch = "wasm32", feature = "js"))] -#[wasm_bindgen(typescript_custom_section)] -const RESPONSE: &'static str = r#" -interface Response { - statusCode: number; - headers: [string, string][]; - body: Uint8Array; -} -"#; - -/// Represents a Response from the [Internet Computer](https://internetcomputer.org). -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Response { - /// The HTTP status code of the response, i.e. 200. - pub status_code: u16, - /// The HTTP headers of the request, i.e. \[\["Ic-Certificate", "certificate=:2dn3o2R0cmVlgw=:, tree=:2dn3gwGDA:"\]\] - pub headers: Vec<(String, String)>, - /// The body of the request as an array of bytes, i.e. \[60, 33, 100, 111, 99\] - pub body: Vec, -} - -#[cfg(all(target_arch = "wasm32", feature = "js"))] -impl From for Response { - fn from(resp: JsValue) -> Self { - use js_sys::{Array, JsString, Number, Object, Uint8Array}; - - let status_code_str = JsString::from("statusCode"); - let headers_str = JsString::from("headers"); - let body_str = JsString::from("body"); - - let mut status_code: u16 = 0; - let mut headers = Vec::new(); - let mut body = Vec::new(); - - let resp = Object::unchecked_from_js(resp); - for entry in Object::entries(&resp).iter() { - let entry = Array::unchecked_from_js(entry); - let k = JsString::unchecked_from_js(entry.get(0)); - - if k == status_code_str { - status_code = Number::unchecked_from_js(entry.get(1)).as_f64().unwrap() as u16; - } - - if k == headers_str { - let headers_v = Array::unchecked_from_js(entry.get(1)); - let headers_v = headers_v.iter(); - headers = Vec::with_capacity(headers_v.len()); - for header in headers_v { - let header = Array::unchecked_from_js(header); - let header_name = header.get(0).as_string().unwrap(); - let header_val = header.get(1).as_string().unwrap(); - headers.push((header_name, header_val)) - } - } - - if k == body_str { - body = Uint8Array::unchecked_from_js(entry.get(1)).to_vec(); - } - } - - Self { - status_code, - headers, - body, - } - } -} - -#[cfg(all(target_arch = "wasm32", feature = "js", test))] -mod tests { - use super::*; - use js_sys::JSON; - use wasm_bindgen_test::wasm_bindgen_test; - - #[wasm_bindgen_test] - fn request_from() { - let v = JSON::parse( - r#"{ - "statusCode": 200, - "body": [0, 1, 2, 3, 4, 5, 6], - "headers": [ - ["header1", "header1val"], - ["header2", "header2val"] - ] - }"#, - ) - .expect("failed to parse JSON"); - let r = Response::from(v); - - assert_eq!( - r, - Response { - status_code: 200, - body: vec![0, 1, 2, 3, 4, 5, 6], - headers: vec![ - ("header1".into(), "header1val".into()), - ("header2".into(), "header2val".into()), - ], - } - ); - } -} diff --git a/packages/ic-response-verification/src/verification/verify_request_response_pair.rs b/packages/ic-response-verification/src/verification/verify_request_response_pair.rs index 93d5d69c..3b6d645f 100644 --- a/packages/ic-response-verification/src/verification/verify_request_response_pair.rs +++ b/packages/ic-response-verification/src/verification/verify_request_response_pair.rs @@ -4,7 +4,7 @@ use crate::{ error::{ResponseVerificationError, ResponseVerificationResult}, hash, hash::filter_response_headers, - types::{Certification, Request, Response, VerificationInfo, VerifiedResponse}, + types::{Certification, VerificationInfo, VerifiedResponse}, validation::{ validate_body, validate_expr_hash, validate_expr_path, validate_hashes, validate_tree, }, @@ -12,6 +12,7 @@ use crate::{ use ic_cbor::{parse_cbor_string_array, CertificateToCbor, HashTreeToCbor}; use ic_certificate_verification::{validate_certificate_time, VerifyCertificate}; use ic_certification::{hash_tree::Hash, Certificate, HashTree}; +use ic_http_certification::{HttpRequest, HttpResponse}; use ic_representation_independent_hash::hash; /// The minimum verification version supported by this package. @@ -22,8 +23,8 @@ pub const MAX_VERIFICATION_VERSION: u8 = 2; /// The primary entry point for verifying a request and response pair. This will verify the response /// with respect to the request, according the [Response Verification Spec](). pub fn verify_request_response_pair( - request: Request, - response: Response, + request: HttpRequest, + response: HttpResponse, canister_id: &[u8], current_time_ns: u128, max_cert_time_offset_ns: u128, @@ -100,8 +101,8 @@ pub fn verify_request_response_pair( fn verification( version: u8, - request: Request, - response: Response, + request: HttpRequest, + response: HttpResponse, canister_id: &[u8], current_time_ns: u128, max_cert_time_offset_ns: u128, @@ -147,8 +148,8 @@ fn verification( } fn v1_verification( - request: Request, - response: Response, + request: HttpRequest, + response: HttpResponse, canister_id: &[u8], current_time_ns: u128, max_cert_time_offset_ns: u128, @@ -196,8 +197,8 @@ fn v1_verification( } fn v2_verification( - request: Request, - response: Response, + request: HttpRequest, + response: HttpResponse, canister_id: &[u8], current_time_ns: u128, max_cert_time_offset_ns: u128, diff --git a/packages/ic-response-verification/tests/v1_response_verification.rs b/packages/ic-response-verification/tests/v1_response_verification.rs index f8a57037..b5ab3b81 100644 --- a/packages/ic-response-verification/tests/v1_response_verification.rs +++ b/packages/ic-response-verification/tests/v1_response_verification.rs @@ -2,7 +2,8 @@ mod tests { use ic_certificate_verification::CertificateVerificationError; use ic_certification_testing::{CertificateBuilder, CertificateData}; - use ic_response_verification::types::{Request, Response, VerificationInfo, VerifiedResponse}; + use ic_http_certification::{HttpRequest, HttpResponse}; + use ic_response_verification::types::{VerificationInfo, VerifiedResponse}; use ic_response_verification::verify_request_response_pair; use ic_response_verification::ResponseVerificationError; use ic_response_verification_test_utils::{ @@ -39,14 +40,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -102,14 +103,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: encoded_path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -164,14 +165,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: "/".into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -226,14 +227,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: b"Hello IC!".to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -280,14 +281,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -338,14 +339,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -398,14 +399,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -456,14 +457,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -511,14 +512,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -564,14 +565,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], @@ -617,14 +618,14 @@ mod tests { let certificate_header = create_certificate_header(&cbor_encoded_certificate, &tree_cbor); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let response = Response { + let response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![("IC-Certificate".into(), certificate_header)], diff --git a/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs b/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs index 80557b38..9bc4aef7 100644 --- a/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs +++ b/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs @@ -10,8 +10,9 @@ mod tests { etag_caching_match_response, etag_caching_mismatch_request, etag_caching_mismatch_response, etag_certificate_tree, index_js_response, not_found_response, redirect_response, }; + use ic_http_certification::{HttpRequest, HttpResponse}; use ic_response_verification::{ - types::{Request, Response, VerificationInfo, VerifiedResponse}, + types::{VerificationInfo, VerifiedResponse}, verify_request_response_pair, ResponseVerificationError, }; use ic_response_verification_test_utils::{ @@ -44,9 +45,9 @@ mod tests { #[from(certificate_tree)] expr_tree: ExprTree, #[case] req_path: &str, #[case] expr_path: &[&str], - #[case] mut expected_response: Response, + #[case] mut expected_response: HttpResponse, ) { - let request = Request { + let request = HttpRequest { url: req_path.into(), method: "GET".into(), headers: vec![ @@ -142,9 +143,9 @@ mod tests { #[from(certificate_tree)] expr_tree: ExprTree, #[case] req_path: &str, #[case] expr_path: &[&str], - #[case] mut expected_response: Response, + #[case] mut expected_response: HttpResponse, ) { - let request = Request { + let request = HttpRequest { url: req_path.into(), method: "GET".into(), headers: vec![ @@ -199,8 +200,8 @@ mod tests { )] fn etag_scenarios_pass_verification( #[from(etag_certificate_tree)] expr_tree: ExprTree, - #[case] request: Request, - #[case] mut expected_response: Response, + #[case] request: HttpRequest, + #[case] mut expected_response: HttpResponse, ) { let expr_path = ["app", "<$>"]; let current_time = get_current_timestamp(); @@ -255,8 +256,8 @@ mod tests { #[rstest] fn etag_scenarios_fail_verification( #[from(etag_certificate_tree)] expr_tree: ExprTree, - #[from(etag_caching_mismatch_request)] request: Request, - #[from(etag_caching_match_response)] mut expected_response: Response, + #[from(etag_caching_mismatch_request)] request: HttpRequest, + #[from(etag_caching_match_response)] mut expected_response: HttpResponse, ) { let expr_path = ["app", "<$>"]; let current_time = get_current_timestamp(); @@ -295,10 +296,10 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] mod fixtures { + use ic_http_certification::{HttpRequest, HttpResponse}; use ic_response_verification::{ cel::cel_to_certification, hash::{request_hash, response_hash}, - types::{Request, Response}, }; use ic_response_verification_test_utils::{ create_expr_tree_path, deflate_encode, gzip_encode, hash, remove_whitespace, ExprTree, @@ -314,10 +315,10 @@ mod fixtures { } #[fixture] - pub fn index_html_response() -> Response { + pub fn index_html_response() -> HttpResponse { let cel = asset_cel(); - Response { + HttpResponse { status_code: 200, body: gzip_encode(&html_body()), headers: vec![ @@ -329,11 +330,11 @@ mod fixtures { } #[fixture] - pub fn index_js_response() -> Response { + pub fn index_js_response() -> HttpResponse { let cel = asset_cel(); let body = br#"window.onload=function(){console.log("Hello World")};"#; - Response { + HttpResponse { status_code: 200, body: gzip_encode(body), headers: vec![ @@ -345,11 +346,11 @@ mod fixtures { } #[fixture] - pub fn not_found_response() -> Response { + pub fn not_found_response() -> HttpResponse { let cel = asset_cel(); let body = br#"Not Found"#; - Response { + HttpResponse { status_code: 404, body: body.to_vec(), headers: vec![ @@ -361,11 +362,11 @@ mod fixtures { } #[fixture] - pub fn redirect_response() -> Response { + pub fn redirect_response() -> HttpResponse { let cel = redirect_cel(); let body = br#"Moved Permanently"#; - Response { + HttpResponse { status_code: 301, body: body.to_vec(), headers: vec![ @@ -376,10 +377,10 @@ mod fixtures { } #[fixture] - pub fn content_encoding_identity_response() -> Response { + pub fn content_encoding_identity_response() -> HttpResponse { let cel = asset_cel(); - Response { + HttpResponse { status_code: 200, body: html_body(), headers: vec![ @@ -391,10 +392,10 @@ mod fixtures { } #[fixture] - pub fn content_encoding_gzip_response() -> Response { + pub fn content_encoding_gzip_response() -> HttpResponse { let cel = asset_cel(); - Response { + HttpResponse { status_code: 200, body: gzip_encode(&html_body()), headers: vec![ @@ -406,10 +407,10 @@ mod fixtures { } #[fixture] - pub fn content_encoding_deflate_response() -> Response { + pub fn content_encoding_deflate_response() -> HttpResponse { let cel = asset_cel(); - Response { + HttpResponse { status_code: 200, body: deflate_encode(&html_body()), headers: vec![ @@ -421,10 +422,10 @@ mod fixtures { } #[fixture] - pub fn etag_caching_match_request() -> Request { + pub fn etag_caching_match_request() -> HttpRequest { let etag = hex::encode(hash(html_body().as_slice())); - Request { + HttpRequest { url: "/app".into(), method: "GET".into(), headers: vec![ @@ -437,11 +438,11 @@ mod fixtures { } #[fixture] - pub fn etag_caching_match_response() -> Response { + pub fn etag_caching_match_response() -> HttpResponse { let cel = etag_caching_match_cel(); let body = br#"Not Modified"#; - Response { + HttpResponse { status_code: 304, body: body.to_vec(), headers: vec![ @@ -453,8 +454,8 @@ mod fixtures { } #[fixture] - pub fn etag_caching_mismatch_request() -> Request { - Request { + pub fn etag_caching_mismatch_request() -> HttpRequest { + HttpRequest { url: "/app".into(), method: "GET".into(), headers: vec![ @@ -470,11 +471,11 @@ mod fixtures { } #[fixture] - pub fn etag_caching_mismatch_response() -> Response { + pub fn etag_caching_mismatch_response() -> HttpResponse { let cel = etag_caching_mismatch_cel(); let etag = hex::encode(hash(html_body().as_slice())); - Response { + HttpResponse { status_code: 200, body: deflate_encode(&html_body()), headers: vec![ diff --git a/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs b/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs index 59981db9..aceb0e64 100644 --- a/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs +++ b/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs @@ -1,8 +1,9 @@ #[cfg(not(target_arch = "wasm32"))] mod tests { + use ic_http_certification::{HttpRequest, HttpResponse}; use ic_response_verification::cel::cel_to_certification; use ic_response_verification::hash::{request_hash, response_hash}; - use ic_response_verification::types::{Request, Response, VerificationInfo, VerifiedResponse}; + use ic_response_verification::types::{VerificationInfo, VerifiedResponse}; use ic_response_verification::verify_request_response_pair; use ic_response_verification_test_utils::{ create_v2_fixture, get_current_timestamp, remove_whitespace, V2Fixture, @@ -27,13 +28,13 @@ mod tests { "#, ); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ @@ -97,13 +98,13 @@ mod tests { let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); let response_certification = certification.response_certification; - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ @@ -185,7 +186,7 @@ mod tests { let request_certification = certification.request_certification.unwrap(); let response_certification = certification.response_certification; - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![ @@ -194,7 +195,7 @@ mod tests { ], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ @@ -273,13 +274,13 @@ mod tests { let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); let response_certification = certification.response_certification; - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ diff --git a/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs b/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs index 7ef326ff..09fac33c 100644 --- a/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs +++ b/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs @@ -7,10 +7,10 @@ mod tests { }; use candid::Principal; use ic_certificate_verification::CertificateVerificationError; + use ic_http_certification::{HttpRequest, HttpResponse}; use ic_response_verification::{ cel::cel_to_certification, hash::{request_hash, response_hash}, - types::{Request, Response}, verify_request_response_pair, ResponseVerificationError, }; use ic_response_verification_test_utils::{ @@ -29,7 +29,7 @@ mod tests { let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); let response_certification = certification.response_certification; - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![ @@ -38,7 +38,7 @@ mod tests { ], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ @@ -94,7 +94,7 @@ mod tests { let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); let request_certification = certification.request_certification.unwrap(); - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![ @@ -103,7 +103,7 @@ mod tests { ], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ @@ -161,7 +161,7 @@ mod tests { let request_certification = certification.request_certification.unwrap(); let response_certification = certification.response_certification; - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![ @@ -170,7 +170,7 @@ mod tests { ], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ @@ -228,13 +228,13 @@ mod tests { ) { let current_time = get_current_timestamp(); - let request = Request { + let request = HttpRequest { url: "/assets/js/app.js".to_string(), method: "GET".to_string(), headers: vec![], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: b"Hello World!".to_vec(), headers: vec![("IC-CertificateExpression".to_string(), cel_expr.clone())], @@ -329,13 +329,13 @@ mod tests { let path = "/"; let body = "Hello World!"; - let request = Request { + let request = HttpRequest { url: path.into(), method: "GET".into(), headers: vec![], body: vec![], }; - let mut response = Response { + let mut response = HttpResponse { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![