diff --git a/Cargo.lock b/Cargo.lock index 40ce29a8..51176623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1990,6 +1990,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ic-http-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207166ed1477b107f56c3d98f6c9ea771ed84be407567bc7099db907d98490a3" +dependencies = [ + "candid", + "serde", + "serde_bytes", +] + [[package]] name = "ic-metrics-encoder" version = "1.1.1" @@ -4091,6 +4102,7 @@ dependencies = [ "hex", "http 1.3.1", "ic-cdk", + "ic-http-types", "ic-metrics-encoder", "ic-stable-structures", "maplit", @@ -4148,6 +4160,7 @@ dependencies = [ "const_format", "futures", "ic-cdk", + "ic-http-types", "ic-test-utilities-load-wasm", "num-traits", "pocket-ic", diff --git a/Cargo.toml b/Cargo.toml index 18d93cf8..aa4eaa17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ hex = "0.4.3" http = "1.2.0" ic-canister-log = "0.2.0" ic-cdk = "0.17.1" +ic-http-types = "0.1.0" ic-ed25519 = "0.1.0" ic-metrics-encoder = "1.1" ic-stable-structures = "0.6.7" diff --git a/canister/Cargo.toml b/canister/Cargo.toml index b50ff7c4..c598b07d 100644 --- a/canister/Cargo.toml +++ b/canister/Cargo.toml @@ -23,6 +23,7 @@ derive_more = { workspace = true } hex = { workspace = true } http = { workspace = true } ic-cdk = { workspace = true } +ic-http-types = { workspace = true } ic-metrics-encoder = { workspace = true } ic-stable-structures = { workspace = true } maplit = { workspace = true } diff --git a/canister/src/http_types/mod.rs b/canister/src/http_types/mod.rs deleted file mode 100644 index 0b4247e3..00000000 --- a/canister/src/http_types/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Copy of the types from the unpublished [`ic-canisters-http-types`](https://github.com/dfinity/ic/blob/f4242cbcf83f0725663f3cd1a6b3a83eb2dace01/rs/rust_canisters/http_types/src/lib.rs) crate. - -#[cfg(test)] -mod tests; - -use candid::{CandidType, Deserialize}; -use serde_bytes::ByteBuf; - -#[derive(Clone, Debug, CandidType, Deserialize)] -pub struct HttpRequest { - pub method: String, - pub url: String, - pub headers: Vec<(String, String)>, - pub body: ByteBuf, -} - -impl HttpRequest { - pub fn path(&self) -> &str { - match self.url.find('?') { - None => &self.url[..], - Some(index) => &self.url[..index], - } - } - - /// Searches for the first appearance of a parameter in the request URL. - /// Returns `None` if the given parameter does not appear in the query. - pub fn raw_query_param(&self, param: &str) -> Option<&str> { - const QUERY_SEPARATOR: &str = "?"; - let query_string = self.url.split(QUERY_SEPARATOR).nth(1)?; - if query_string.is_empty() { - return None; - } - const PARAMETER_SEPARATOR: &str = "&"; - for chunk in query_string.split(PARAMETER_SEPARATOR) { - const KEY_VALUE_SEPARATOR: &str = "="; - let mut split = chunk.splitn(2, KEY_VALUE_SEPARATOR); - let name = split.next()?; - if name == param { - return Some(split.next().unwrap_or_default()); - } - } - None - } -} - -#[derive(Clone, Debug, CandidType, Deserialize)] -pub struct HttpResponse { - pub status_code: u16, - pub headers: Vec<(String, String)>, - pub body: ByteBuf, -} - -pub struct HttpResponseBuilder(HttpResponse); - -impl HttpResponseBuilder { - pub fn ok() -> Self { - Self(HttpResponse { - status_code: 200, - headers: vec![], - body: ByteBuf::default(), - }) - } - - pub fn bad_request() -> Self { - Self(HttpResponse { - status_code: 400, - headers: vec![], - body: ByteBuf::from("bad request"), - }) - } - - pub fn not_found() -> Self { - Self(HttpResponse { - status_code: 404, - headers: vec![], - body: ByteBuf::from("not found"), - }) - } - - pub fn server_error(reason: impl ToString) -> Self { - Self(HttpResponse { - status_code: 500, - headers: vec![], - body: ByteBuf::from(reason.to_string()), - }) - } - - pub fn header(mut self, name: impl ToString, value: impl ToString) -> Self { - self.0.headers.push((name.to_string(), value.to_string())); - self - } - - pub fn body(mut self, bytes: impl Into>) -> Self { - self.0.body = ByteBuf::from(bytes.into()); - self - } - - pub fn with_body_and_content_length(self, bytes: impl Into>) -> Self { - let bytes = bytes.into(); - self.header("Content-Length", bytes.len()).body(bytes) - } - - pub fn build(self) -> HttpResponse { - self.0 - } -} diff --git a/canister/src/http_types/tests.rs b/canister/src/http_types/tests.rs deleted file mode 100644 index eaac5677..00000000 --- a/canister/src/http_types/tests.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::http_types::HttpRequest; - -#[test] -fn test_raw_query_param() { - fn request_with_url(url: String) -> HttpRequest { - HttpRequest { - method: "".to_string(), - url, - headers: vec![], - body: Default::default(), - } - } - let http_request = request_with_url("/endpoint?time=1000".to_string()); - assert_eq!(http_request.raw_query_param("time"), Some("1000")); - let http_request = request_with_url("/endpoint".to_string()); - assert_eq!(http_request.raw_query_param("time"), None); - let http_request = - request_with_url("/endpoint?time=1000&time=1001&other=abcde&time=1002".to_string()); - assert_eq!(http_request.raw_query_param("time"), Some("1000")); -} diff --git a/canister/src/lib.rs b/canister/src/lib.rs index 9e7d6ade..d3b070ad 100644 --- a/canister/src/lib.rs +++ b/canister/src/lib.rs @@ -1,7 +1,6 @@ pub mod candid_rpc; pub mod constants; pub mod http; -pub mod http_types; pub mod lifecycle; pub mod logs; pub mod memory; diff --git a/canister/src/main.rs b/canister/src/main.rs index 3b99391e..fc4e06b2 100644 --- a/canister/src/main.rs +++ b/canister/src/main.rs @@ -1,10 +1,11 @@ use candid::candid_method; use canlog::{log, Log, Sort}; use ic_cdk::{api::is_controller, query, update}; +use ic_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder}; use ic_metrics_encoder::MetricsEncoder; use sol_rpc_canister::{ candid_rpc::send_multi, - http_types, lifecycle, + lifecycle, logs::Priority, memory::{mutate_state, read_state, State}, metrics::encode_metrics, @@ -241,21 +242,20 @@ async fn json_request_cycles_cost( } #[query(hidden = true)] -fn http_request(request: http_types::HttpRequest) -> http_types::HttpResponse { +fn http_request(request: HttpRequest) -> HttpResponse { match request.path() { "/metrics" => { let mut writer = MetricsEncoder::new(vec![], ic_cdk::api::time() as i64 / 1_000_000); match encode_metrics(&mut writer) { - Ok(()) => http_types::HttpResponseBuilder::ok() + Ok(()) => HttpResponseBuilder::ok() .header("Content-Type", "text/plain; version=0.0.4") .with_body_and_content_length(writer.into_inner()) .build(), - Err(err) => http_types::HttpResponseBuilder::server_error(format!( - "Failed to encode metrics: {}", - err - )) - .build(), + Err(err) => { + HttpResponseBuilder::server_error(format!("Failed to encode metrics: {}", err)) + .build() + } } } "/logs" => { @@ -263,7 +263,7 @@ fn http_request(request: http_types::HttpRequest) -> http_types::HttpResponse { Some(arg) => match u64::from_str(arg) { Ok(value) => value, Err(_) => { - return http_types::HttpResponseBuilder::bad_request() + return HttpResponseBuilder::bad_request() .with_body_and_content_length("failed to parse the 'time' parameter") .build() } @@ -307,12 +307,12 @@ fn http_request(request: http_types::HttpRequest) -> http_types::HttpResponse { )); const MAX_BODY_SIZE: usize = 2_000_000; - http_types::HttpResponseBuilder::ok() + HttpResponseBuilder::ok() .header("Content-Type", "application/json; charset=utf-8") .with_body_and_content_length(log.serialize_logs(MAX_BODY_SIZE)) .build() } - _ => http_types::HttpResponseBuilder::not_found().build(), + _ => HttpResponseBuilder::not_found().build(), } } diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 1788641a..ad2560c7 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -14,6 +14,7 @@ canhttp = { workspace = true } canlog = { path = "../canlog" } const_format = { workspace = true } ic-cdk = { workspace = true } +ic-http-types = { workspace = true } ic-test-utilities-load-wasm = { workspace = true } num-traits = { workspace = true } pocket-ic = { workspace = true } diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index cc72f970..e2a09227 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -3,6 +3,7 @@ use candid::{decode_args, encode_args, utils::ArgumentEncoder, CandidType, Encod use canhttp::http::json::ConstantSizeId; use canlog::{Log, LogEntry}; use ic_cdk::api::call::RejectionCode; +use ic_http_types::{HttpRequest, HttpResponse}; use num_traits::ToPrimitive; use pocket_ic::{ common::rest::{ @@ -14,10 +15,7 @@ use pocket_ic::{ }; use regex::Regex; use serde::{de::DeserializeOwned, Deserialize}; -use sol_rpc_canister::{ - http_types::{HttpRequest, HttpResponse}, - logs::Priority, -}; +use sol_rpc_canister::logs::Priority; use sol_rpc_client::{ClientBuilder, Runtime, SolRpcClient}; use sol_rpc_types::{InstallArgs, RpcAccess, SupportedRpcProviderId}; use std::{