Skip to content

Commit b80fa7a

Browse files
committed
.
1 parent d9698de commit b80fa7a

File tree

5 files changed

+119
-39
lines changed

5 files changed

+119
-39
lines changed

ic-agent/src/agent/http_transport/hyper_transport.rs

+46-11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use hyper::{header::CONTENT_TYPE, Method, Request, Response};
1313
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
1414
use hyper_util::client::legacy::{connect::HttpConnector, Client};
1515
use hyper_util::rt::TokioExecutor;
16+
use ic_transport_types::{RejectResponse, TransportCallResponse};
1617
use tower::Service;
1718

1819
use crate::{
@@ -141,7 +142,7 @@ where
141142
method: Method,
142143
url: String,
143144
body: Option<Vec<u8>>,
144-
) -> Result<Vec<u8>, AgentError> {
145+
) -> Result<(StatusCode, Vec<u8>), AgentError> {
145146
let body = body.unwrap_or_default();
146147
fn map_error<E: Error + Send + Sync + 'static>(err: E) -> AgentError {
147148
if any::TypeId::of::<E>() == any::TypeId::of::<AgentError>() {
@@ -207,7 +208,7 @@ where
207208
content: body,
208209
}))
209210
} else {
210-
Ok(body)
211+
Ok((status, body))
211212
}
212213
}
213214
}
@@ -217,13 +218,39 @@ where
217218
B1: HyperBody + From<Vec<u8>>,
218219
S: HyperService<B1>,
219220
{
220-
fn call(&self, effective_canister_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>> {
221+
fn call(
222+
&self,
223+
effective_canister_id: Principal,
224+
envelope: Vec<u8>,
225+
) -> AgentFuture<TransportCallResponse> {
221226
Box::pin(async move {
222-
let url = format!(
223-
"{}api/v3/canister/{effective_canister_id}/call",
224-
self.route_provider.route()?
227+
let api_version = if cfg!(feature = "sync_call") {
228+
"v2"
229+
} else {
230+
"v3"
231+
};
232+
233+
let endpoint = format!(
234+
"api/{}/canister/{}/call",
235+
api_version,
236+
effective_canister_id.to_text()
225237
);
226-
self.request(Method::POST, url, Some(envelope)).await
238+
let (status_code, response_body) =
239+
self.request(Method::POST, endpoint, Some(envelope)).await?;
240+
241+
if status_code == StatusCode::ACCEPTED {
242+
return Ok(TransportCallResponse::Accepted);
243+
}
244+
245+
// status_code == OK (200)
246+
if cfg!(feature = "sync_call") {
247+
serde_cbor::from_slice(&response_body).map_err(AgentError::InvalidCborData)
248+
} else {
249+
let reject_response = serde_cbor::from_slice::<RejectResponse>(&response_body)
250+
.map_err(AgentError::InvalidCborData)?;
251+
252+
Err(AgentError::UncertifiedReject(reject_response))
253+
}
227254
})
228255
}
229256

@@ -237,7 +264,9 @@ where
237264
"{}api/v2/canister/{effective_canister_id}/read_state",
238265
self.route_provider.route()?
239266
);
240-
self.request(Method::POST, url, Some(envelope)).await
267+
self.request(Method::POST, url, Some(envelope))
268+
.await
269+
.map(|(_, body)| body)
241270
})
242271
}
243272

@@ -247,7 +276,9 @@ where
247276
"{}api/v2/subnet/{subnet_id}/read_state",
248277
self.route_provider.route()?
249278
);
250-
self.request(Method::POST, url, Some(envelope)).await
279+
self.request(Method::POST, url, Some(envelope))
280+
.await
281+
.map(|(_, body)| body)
251282
})
252283
}
253284

@@ -257,14 +288,18 @@ where
257288
"{}api/v2/canister/{effective_canister_id}/query",
258289
self.route_provider.route()?
259290
);
260-
self.request(Method::POST, url, Some(envelope)).await
291+
self.request(Method::POST, url, Some(envelope))
292+
.await
293+
.map(|(_, body)| body)
261294
})
262295
}
263296

264297
fn status(&self) -> AgentFuture<Vec<u8>> {
265298
Box::pin(async move {
266299
let url = format!("{}api/v2/status", self.route_provider.route()?);
267-
self.request(Method::GET, url, None).await
300+
self.request(Method::GET, url, None)
301+
.await
302+
.map(|(_, body)| body)
268303
})
269304
}
270305
}

ic-agent/src/agent/http_transport/reqwest_transport.rs

+50-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! A [`Transport`] that connects using a [`reqwest`] client.
22
#![cfg(feature = "reqwest")]
33

4+
use ic_transport_types::{RejectResponse, TransportCallResponse};
45
pub use reqwest;
56
use std::{sync::Arc, time::Duration};
67

@@ -125,7 +126,7 @@ impl ReqwestTransport {
125126
method: Method,
126127
endpoint: &str,
127128
body: Option<Vec<u8>>,
128-
) -> Result<Vec<u8>, AgentError> {
129+
) -> Result<(StatusCode, Vec<u8>), AgentError> {
129130
let url = self.route_provider.route()?.join(endpoint)?;
130131
let mut http_request = Request::new(method, url);
131132
http_request
@@ -154,22 +155,52 @@ impl ReqwestTransport {
154155
.map(|x| x.to_string()),
155156
content: body,
156157
}))
157-
} else if status != StatusCode::OK {
158+
} else if !(status == StatusCode::OK || status == StatusCode::ACCEPTED) {
158159
Err(AgentError::InvalidHttpResponse(format!(
159-
"Expected `200`, `4xx`, or `5xx` HTTP status code. Got: {}",
160+
"Expected `200`, `202`, 4xx`, or `5xx` HTTP status code. Got: {}",
160161
status
161162
)))
162163
} else {
163-
Ok(body)
164+
Ok((status, body))
164165
}
165166
}
166167
}
167168

168169
impl Transport for ReqwestTransport {
169-
fn call(&self, effective_canister_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>> {
170+
fn call(
171+
&self,
172+
effective_canister_id: Principal,
173+
envelope: Vec<u8>,
174+
) -> AgentFuture<TransportCallResponse> {
170175
Box::pin(async move {
171-
let endpoint = format!("api/v3/canister/{}/call", effective_canister_id.to_text());
172-
self.execute(Method::POST, &endpoint, Some(envelope)).await
176+
let api_version = if cfg!(feature = "sync_call") {
177+
"v2"
178+
} else {
179+
"v3"
180+
};
181+
182+
let endpoint = format!(
183+
"api/{}/canister/{}/call",
184+
api_version,
185+
effective_canister_id.to_text()
186+
);
187+
let (status_code, response_body) = self
188+
.execute(Method::POST, &endpoint, Some(envelope))
189+
.await?;
190+
191+
if status_code == StatusCode::ACCEPTED {
192+
return Ok(TransportCallResponse::Accepted);
193+
}
194+
195+
// status_code == OK (200)
196+
if cfg!(feature = "sync_call") {
197+
serde_cbor::from_slice(&response_body).map_err(AgentError::InvalidCborData)
198+
} else {
199+
let reject_response = serde_cbor::from_slice::<RejectResponse>(&response_body)
200+
.map_err(AgentError::InvalidCborData)?;
201+
202+
Err(AgentError::UncertifiedReject(reject_response))
203+
}
173204
})
174205
}
175206

@@ -183,27 +214,35 @@ impl Transport for ReqwestTransport {
183214
effective_canister_id.to_text()
184215
);
185216

186-
Box::pin(async move { self.execute(Method::POST, &endpoint, Some(envelope)).await })
217+
Box::pin(async move {
218+
self.execute(Method::POST, &endpoint, Some(envelope))
219+
.await
220+
.map(|r| r.1)
221+
})
187222
}
188223

189224
fn read_subnet_state(&self, subnet_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>> {
190225
Box::pin(async move {
191226
let endpoint = format!("api/v2/subnet/{}/read_state", subnet_id.to_text());
192-
self.execute(Method::POST, &endpoint, Some(envelope)).await
227+
self.execute(Method::POST, &endpoint, Some(envelope))
228+
.await
229+
.map(|r| r.1)
193230
})
194231
}
195232

196233
fn query(&self, effective_canister_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>> {
197234
Box::pin(async move {
198235
let endpoint = format!("api/v2/canister/{}/query", effective_canister_id.to_text());
199-
self.execute(Method::POST, &endpoint, Some(envelope)).await
236+
self.execute(Method::POST, &endpoint, Some(envelope))
237+
.await
238+
.map(|r| r.1)
200239
})
201240
}
202241

203242
fn status(&self) -> AgentFuture<Vec<u8>> {
204243
Box::pin(async move {
205244
let endpoint = "api/v2/status";
206-
self.execute(Method::GET, endpoint, None).await
245+
self.execute(Method::GET, endpoint, None).await.map(|r| r.1)
207246
})
208247
}
209248
}

ic-agent/src/agent/http_transport/route_provider.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ mod tests {
9191
fn test_routes_rotation() {
9292
let provider = RoundRobinRouteProvider::new(vec!["https://url1.com", "https://url2.com"])
9393
.expect("failed to create a route provider");
94-
let url_strings = vec!["https://url1.com", "https://url2.com", "https://url1.com"];
94+
let url_strings = ["https://url1.com", "https://url2.com", "https://url1.com"];
9595
let expected_urls: Vec<Url> = url_strings
9696
.iter()
9797
.map(|url_str| Url::parse(url_str).expect("Invalid URL"))

ic-agent/src/agent/mod.rs

+20-14
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ pub trait Transport: Send + Sync {
8080
/// Sends a synchronous call request to a replica.
8181
///
8282
/// This normally corresponds to the `/api/v3/canister/<effective_canister_id>/call` endpoint.
83-
fn call(&self, effective_canister_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>>;
83+
fn call(
84+
&self,
85+
effective_canister_id: Principal,
86+
envelope: Vec<u8>,
87+
) -> AgentFuture<TransportCallResponse>;
8488

8589
/// Sends a synchronous request to a replica. This call includes the body of the request message
8690
/// itself (envelope).
@@ -111,7 +115,11 @@ pub trait Transport: Send + Sync {
111115
}
112116

113117
impl<I: Transport + ?Sized> Transport for Box<I> {
114-
fn call(&self, effective_canister_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>> {
118+
fn call(
119+
&self,
120+
effective_canister_id: Principal,
121+
envelope: Vec<u8>,
122+
) -> AgentFuture<TransportCallResponse> {
115123
(**self).call(effective_canister_id, envelope)
116124
}
117125

@@ -133,7 +141,11 @@ impl<I: Transport + ?Sized> Transport for Box<I> {
133141
}
134142
}
135143
impl<I: Transport + ?Sized> Transport for Arc<I> {
136-
fn call(&self, effective_canister_id: Principal, envelope: Vec<u8>) -> AgentFuture<Vec<u8>> {
144+
fn call(
145+
&self,
146+
effective_canister_id: Principal,
147+
envelope: Vec<u8>,
148+
) -> AgentFuture<TransportCallResponse> {
137149
(**self).call(effective_canister_id, envelope)
138150
}
139151
fn read_state(
@@ -390,7 +402,7 @@ impl Agent {
390402
&self,
391403
effective_canister_id: Principal,
392404
serialized_bytes: Vec<u8>,
393-
) -> Result<Vec<u8>, AgentError> {
405+
) -> Result<TransportCallResponse, AgentError> {
394406
let _permit = self.concurrent_requests_semaphore.acquire().await;
395407
self.transport
396408
.call(effective_canister_id, serialized_bytes)
@@ -577,10 +589,7 @@ impl Agent {
577589
.call_endpoint(effective_canister_id, serialized_bytes)
578590
.await?;
579591

580-
let parsed_response: TransportCallResponse =
581-
serde_cbor::from_slice(&response_body).map_err(AgentError::InvalidCborData)?;
582-
583-
match parsed_response {
592+
match response_body {
584593
TransportCallResponse::CertifiedState(certificate) => {
585594
self.verify(&certificate, effective_canister_id)?;
586595
let status = lookup_request_status(certificate, &request_id)?;
@@ -616,14 +625,11 @@ impl Agent {
616625
.call_endpoint(effective_canister_id, signed_update)
617626
.await?;
618627

619-
let parsed_response: TransportCallResponse =
620-
serde_cbor::from_slice(&response_body).map_err(AgentError::InvalidCborData)?;
621-
622-
if let TransportCallResponse::CertifiedState(certificate) = &parsed_response {
628+
if let TransportCallResponse::CertifiedState(certificate) = &response_body {
623629
self.verify(certificate, effective_canister_id)?;
624630
}
625631

626-
match parsed_response {
632+
match response_body {
627633
TransportCallResponse::CertifiedState(certificate) => {
628634
self.verify(&certificate, effective_canister_id)?;
629635
let status = lookup_request_status(certificate, &request_id)?;
@@ -1817,7 +1823,7 @@ mod offline_tests {
18171823
&self,
18181824
_effective_canister_id: Principal,
18191825
_envelope: Vec<u8>,
1820-
) -> AgentFuture<Vec<u8>> {
1826+
) -> AgentFuture<TransportCallResponse> {
18211827
*self.0.lock().unwrap() += 1;
18221828
Box::pin(pending())
18231829
}

ref-tests/tests/ic-ref.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ mod management_canister {
302302
.iter()
303303
.cloned()
304304
.collect::<HashSet<_>>();
305-
let expected = vec![agent_principal, other_agent_principal]
305+
let expected = [agent_principal, other_agent_principal]
306306
.iter()
307307
.cloned()
308308
.collect::<HashSet<_>>();
@@ -320,7 +320,7 @@ mod management_canister {
320320
.iter()
321321
.cloned()
322322
.collect::<HashSet<_>>();
323-
let expected = vec![agent_principal, other_agent_principal]
323+
let expected = [agent_principal, other_agent_principal]
324324
.iter()
325325
.cloned()
326326
.collect::<HashSet<_>>();

0 commit comments

Comments
 (0)