1use std::collections::HashMap;
2
3use candid::CandidType;
4use serde::{Deserialize, Serialize};
5
6pub use ic_management_canister_types::{
7 HttpHeader, HttpMethod, HttpRequestArgs, HttpRequestResult, TransformArgs, TransformContext,
8};
9
10use crate::{canister::types::CanisterCallError, identity::CanisterId};
11
12pub const MAX_RESPONSE_LENGTH: usize = 1024 * 1024 * 3 - 1024 * 64;
16
17#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
19pub struct CustomHttpRequest {
20 pub url: String,
22
23 pub method: String,
25
26 pub headers: HashMap<String, String>,
28
29 pub body: Vec<u8>,
31}
32
33#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
35pub struct StreamingCallbackToken {
36 pub path: String,
38
39 pub token: HashMap<String, String>,
41}
42
43#[derive(CandidType, Serialize, Deserialize, Debug, Clone, Default)]
45pub struct StreamingCallbackHttpResponse {
46 pub body: Vec<u8>,
48
49 pub token: Option<StreamingCallbackToken>,
51}
52
53impl StreamingCallbackHttpResponse {
54 pub fn empty() -> Self {
56 Self::default()
57 }
58}
59
60#[allow(missing_docs)]
62mod callback {
63 use super::*;
64
65 candid::define_function!(pub HttpRequestStreamingCallback : (StreamingCallbackToken) -> (StreamingCallbackHttpResponse) query);
66}
67pub use callback::HttpRequestStreamingCallback;
68
69#[derive(CandidType, Deserialize, Debug, Clone)]
71pub enum StreamingStrategy {
72 Callback {
74 callback: HttpRequestStreamingCallback, token: StreamingCallbackToken,
79 },
80}
81
82#[derive(CandidType, Debug, Clone)]
84pub struct CustomHttpResponse {
85 pub status_code: u16,
87
88 pub headers: HashMap<String, String>,
90
91 pub body: Vec<u8>,
93
94 pub streaming_strategy: Option<StreamingStrategy>, pub upgrade: Option<bool>,
99}
100
101pub fn http_transform(response: TransformArgs) -> HttpRequestResult {
105 let mut t = response.response;
106 t.headers = vec![];
107 t
108}
109
110pub async fn do_http_request(
114 arg: HttpRequestArgs,
115 cycles: u128,
116) -> super::types::CanisterCallResult<HttpRequestResult> {
117 let cost = ic_cdk_management_canister::cost_http_request(&arg);
118 if cycles < cost {
119 return Err(CanisterCallError {
120 canister_id: CanisterId::management_canister(),
121 method: "ic#http_request".to_string(),
122 message: format!("Insufficient cycles. cost: {}, provided: {}", cost, cycles),
123 });
124 }
125 ic_cdk_management_canister::http_request(&arg)
126 .await
127 .map_err(|err| CanisterCallError {
128 canister_id: CanisterId::management_canister(),
129 method: "ic#http_request".to_string(),
130 message: err.to_string(),
131 })
132}
133
134#[allow(clippy::future_not_send)]
136pub async fn do_http_request_with_closure(
137 arg: HttpRequestArgs,
138 cycles: u128,
139 transform_func: impl FnOnce(HttpRequestResult) -> HttpRequestResult + 'static,
140) -> super::types::CanisterCallResult<HttpRequestResult> {
141 let cost = ic_cdk_management_canister::cost_http_request(&arg);
142 if cycles < cost {
143 return Err(CanisterCallError {
144 canister_id: CanisterId::management_canister(),
145 method: "ic#http_request".to_string(),
146 message: format!("Insufficient cycles. cost: {}, provided: {}", cost, cycles),
147 });
148 }
149 ic_cdk_management_canister::http_request_with_closure(&arg, transform_func)
150 .await
151 .map_err(|err| CanisterCallError {
152 canister_id: CanisterId::management_canister(),
153 method: "ic#http_request".to_string(),
154 message: err.to_string(),
155 })
156}