ic_web3/transports/
ic_http_client.rs

1//! IC http client
2
3use serde::{self, Deserialize, Serialize};
4use candid::CandidType;
5use jsonrpc_core::Request;
6use candid::{Principal, candid_method};
7use ic_cdk::api::management_canister::http_request::{
8    CanisterHttpRequestArgument, HttpHeader, HttpMethod, 
9    HttpResponse, http_request,
10    TransformFunc, TransformContext, 
11};
12
13// #[derive(CandidType, Deserialize, Debug)]
14// pub struct CanisterHttpRequestArgs {
15//     pub url: String,
16//     pub max_response_bytes: Option<u64>,
17//     pub headers: Vec<HttpHeader>,
18//     pub body: Option<Vec<u8>>,
19//     pub http_method: HttpMethod,
20//     pub transform_method_name: Option<String>,
21// }
22
23#[derive(Clone, Debug)]
24pub struct ICHttpClient {
25    pub max_response_bytes: u64,
26    pub cycles: u64,
27}
28
29impl ICHttpClient {
30    pub fn new(max_resp: Option<u64>, default_cycles: Option<u64>) -> Self {
31        ICHttpClient {
32            max_response_bytes: if let Some(v) = max_resp { v } else { 500_000 },
33            cycles: if let Some(v) = default_cycles { v } else { 1_000_000_000_000 },
34        }
35    }
36
37    pub fn set_max_response_bytes(&mut self, v: u64) {
38        self.max_response_bytes = v;
39    }
40
41    pub fn set_cycles_per_call(&mut self, v: u64) {
42        self.cycles = v;
43    }
44
45    async fn request(
46        &self, 
47        url: String,
48        req_type: HttpMethod, 
49        req_headers: Vec<HttpHeader>, 
50        payload: &Request,
51        max_resp: Option<u64>,
52        cycles: Option<u64>
53    ) -> Result<Vec<u8>, String> {
54        let request = CanisterHttpRequestArgument {
55            url: url.clone(),
56            max_response_bytes: if let Some(v) = max_resp { Some(v) } else { Some(self.max_response_bytes) },
57            method: req_type,
58            headers: req_headers,
59            body: Some(serde_json::to_vec(&payload).unwrap()),
60            // transform: Some(TransformType::Function(TransformFunc(candid::Func {
61            //     principal: ic_cdk::api::id(),
62            //     method: "transform".to_string(),
63            // }))),
64            transform: Some(TransformContext {
65                function: TransformFunc(candid::Func {
66                        principal: ic_cdk::api::id(),
67                        method: "transform".to_string(),
68                    }),
69                context: vec![],
70            }),
71        };
72
73        match http_request(request).await {
74            Ok((result, )) => {
75                Ok(result.body)
76            }
77            Err((r, m)) => {
78                let message =
79                    format!("The http_request resulted into error. RejectionCode: {r:?}, Error: {m}");
80                ic_cdk::api::print(message.clone());
81                Err(message)
82            }
83        }
84    }
85
86    pub async fn get(&self, url: String, payload: &Request, max_resp: Option<u64>, cycles: Option<u64>) -> Result<Vec<u8>, String> {
87        let request_headers = vec![
88            HttpHeader {
89                name: "Content-Type".to_string(),
90                value: "application/json".to_string(),
91            },
92        ];
93
94        self.request(url, HttpMethod::GET, request_headers, payload, max_resp, cycles).await
95    }
96
97    pub async fn post(&self, url: String, payload: &Request, max_resp: Option<u64>, cycles: Option<u64>) -> Result<Vec<u8>, String> {
98        let request_headers = vec![
99            HttpHeader {
100                name: "Content-Type".to_string(),
101                value: "application/json".to_string(),
102            },
103        ];
104
105        self.request(url, HttpMethod::POST, request_headers, payload, max_resp, cycles).await
106    }
107}
108