Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
ec16626
feat: use ic-ref master for response authentication support (#67)
Oct 22, 2020
d7a4a63
Use Certification ic-ref feature branch
Oct 23, 2020
657770f
Only build ic-ref
nomeata Oct 23, 2020
cf80f77
Build less from ic-ref
nomeata Oct 23, 2020
b89bf15
Try to decode agent HttpError plan text values (display as text, not …
Oct 23, 2020
d83bde2
wip
Oct 22, 2020
9e267a0
checkpoint: signature verification failed
Oct 23, 2020
3c238d2
trying to pass signature check
Oct 23, 2020
795850f
DER encoding
Oct 23, 2020
5f3bc16
Revert "DER encoding"
Oct 24, 2020
d77a646
no extra space in hex dump
Oct 26, 2020
ea1a535
checkpoint: paths use BytesBuf
Oct 26, 2020
f1c2704
StateTreePath
Oct 27, 2020
be33120
panic if read_endpoint even succeeds
Oct 27, 2020
45b4c2c
format
Oct 27, 2020
9082075
ignore two unit tests that need to be updated with new responses
Oct 27, 2020
e738856
clippeh
Oct 27, 2020
0f0a852
Add a test case for request id calculation with arrays
nomeata Oct 27, 2020
939a324
Update ic-ref instructions for generating request id
Oct 28, 2020
2dd6aa9
2 more tests
Oct 30, 2020
7b1e530
checkpoint (compiles)
Oct 30, 2020
de10fae
checkpoint: not panicking
Oct 30, 2020
61e0b76
checkpoint: empty array test passes
Oct 30, 2020
e8a33f0
checkpoint: request id tests pass
Oct 30, 2020
57e320f
fix 1234 = 0x4d2 not 0x3d2
Oct 30, 2020
c296537
enum-based rather than trait-based
Nov 2, 2020
3264abc
disable clippy warning because the proposed fix causes a different ki…
Nov 2, 2020
63c14c8
wip
Nov 2, 2020
4ce7eab
checkpoint all request unit tests pass
Nov 3, 2020
6b3914c
remove old comments
Nov 3, 2020
f6ce53f
ValueEncoder
Nov 3, 2020
799902a
streamline ElementEncoder creation
Nov 3, 2020
497c8ba
output certificate bytes
Nov 3, 2020
29c240d
wip fixing up tests
Nov 5, 2020
16ea239
Merge remote-tracking branch 'origin/next' into es-1139-resp-auth-use…
Nov 5, 2020
c5dc478
(temporarily merge in hash tree stuff)
Nov 5, 2020
45ceefb
TreeHash deserialization
Nov 5, 2020
da4fbad
deserialize certificate;
Nov 5, 2020
4f9e345
extract request status from read_state certificate
Nov 6, 2020
d2079bd
refactor: convert_read_state_to_request_status
Nov 6, 2020
9c19da4
ignore tests because they need a different mocked response: agent_tes…
Nov 6, 2020
5b0099c
format - hash_tree
Nov 6, 2020
95aff34
format
Nov 6, 2020
053163f
ElementEncoder -> Hasher
Nov 6, 2020
fa89f4f
Remove call and call_rejected agent tests, because they are nontrivia…
Nov 6, 2020
25047ce
Remove support for maps as requests. This didn't work anyway, and it…
Nov 6, 2020
7f3105e
fix warnings
Nov 6, 2020
c02b7e7
format
Nov 6, 2020
d0c82cb
Hasher::Struct direct
Nov 6, 2020
2366e78
&mut not ref mut
Nov 6, 2020
0913b7f
fix warning;
Nov 6, 2020
5f6169f
fmt
Nov 6, 2020
75f1c9c
Debug decoding for HttpError (plain text)
Nov 6, 2020
dd4d8f7
why is it not using the Debug formatter
Nov 6, 2020
8881f60
test against release-0.13
Nov 6, 2020
2d4b73a
clippy
Nov 6, 2020
dc9de26
force CI build
Nov 7, 2020
d34d3f1
format
Nov 7, 2020
ababe5a
do not print certificate
Nov 9, 2020
e0d1932
path not found in certificate -> status unknown
Nov 10, 2020
2e21560
Revert "format - hash_tree"
Nov 10, 2020
874dcdd
Revert "TreeHash deserialization"
Nov 10, 2020
95b7e46
Revert "(temporarily merge in hash tree stuff)"
Nov 10, 2020
6b63ebb
Merge remote-tracking branch 'origin/next' into es-1139-resp-auth-use…
Nov 10, 2020
d51bcdd
Use new HashTree
Nov 10, 2020
70a3460
refactor into methods;
Nov 10, 2020
564a7ec
Disable dead code warning for the digest method
Nov 10, 2020
4c58350
better errors
Nov 11, 2020
bfd4609
format
Nov 11, 2020
b0a4a9c
cleanup
Nov 12, 2020
e709010
cleanup: read_state returns Certificate
Nov 12, 2020
f7c74f7
use Label rather than StateTreePath
Nov 12, 2020
18c82db
format
Nov 12, 2020
975ba90
cleanup
Nov 12, 2020
abe5f6a
update expected ic ref version
Nov 12, 2020
5fc8278
Remove Some() around content type in error message
Nov 12, 2020
67aa707
grammar
Nov 12, 2020
6f82235
remove unnecessary clippy directive
Nov 12, 2020
88868c7
simplify vec -> label
Nov 12, 2020
59f179f
fmt
Nov 12, 2020
803b2f8
Merge branch 'next' into es-1139-resp-auth-use-read-state
Nov 16, 2020
6720277
From code review: remove SyncContent::RequestStatusRequest
Nov 18, 2020
fe4efbd
From code review: DRY for LookupResult handling
Nov 18, 2020
c00ce02
lookup_value -> lookup_path
Nov 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ic-ref.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
include:
- build: linux-stable
ghc: '8.8.4'
spec: 'release-0.11'
spec: 'release-0.13'
os: ubuntu-latest
rust: stable

Expand Down Expand Up @@ -75,7 +75,7 @@ jobs:
mkdir -p $HOME/bin
cd ic-ref/impl
cabal update
cabal install -w ghc-${{ matrix.ghc }} --overwrite-policy=always --installdir=$HOME/bin
cabal install exe:ic-ref -w ghc-${{ matrix.ghc }} --overwrite-policy=always --installdir=$HOME/bin

- name: Build universal-canister
run: |
Expand Down
1 change: 1 addition & 0 deletions ic-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ delay = "0.3.0"
hex = "0.4.0"
ic-types = { path = "../ic-types", version = "0.1", features = [ "serde" ] }
leb128 = "0.2.4"
mime = "0.3.16"
openssl = "0.10.24"
rand = "0.7.2"
reqwest = { version = "0.10.4", features = [ "blocking", "json", "rustls-tls" ] }
Expand Down
92 changes: 86 additions & 6 deletions ic-agent/src/agent/agent_error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::hash_tree::Label;
use crate::RequestIdError;
use leb128::read;
use std::fmt::{Debug, Display, Formatter};
use std::str::Utf8Error;
use thiserror::Error;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -36,12 +40,8 @@ pub enum AgentError {
reject_message: String,
},

#[error(r#"The replica returned an HTTP Error: status code {status}"#)]
HttpError {
status: u16,
content_type: Option<String>,
content: Vec<u8>,
},
#[error("The replica returned an HTTP Error: {0}")]
HttpError(HttpErrorPayload),

#[error("HTTP Authentication cannot be used in a non-secure URL (either HTTPS or localhost)")]
CannotUseAuthenticationOnNonSecureUrl(),
Expand All @@ -60,6 +60,24 @@ pub enum AgentError {

#[error("A tool returned a custom error: {0}")]
CustomError(#[from] Box<dyn Send + Sync + std::error::Error>),

#[error("Error reading LEB128 value: {0}")]
Leb128ReadError(#[from] read::Error),

#[error("Error in UTF-8 string: {0}")]
Utf8ReadError(#[from] Utf8Error),

#[error("The lookup path ({0:?}) is absent in the certificate.")]
LookupPathAbsent(Vec<Label>),

#[error("The lookup path ({0:?}) is unknown in the certificate.")]
LookupPathUnknown(Vec<Label>),

#[error("The lookup path ({0:?}) does not make sense for the certificate.")]
LookupPathError(Vec<Label>),

#[error("The request status ({1}) at path {0:?} is invalid.")]
InvalidRequestStatus(Vec<Label>, String),
}

impl PartialEq for AgentError {
Expand All @@ -69,3 +87,65 @@ impl PartialEq for AgentError {
format!("{:?}", self) == format!("{:?}", other)
}
}

pub struct HttpErrorPayload {
pub status: u16,
pub content_type: Option<String>,
pub content: Vec<u8>,
}

impl HttpErrorPayload {
fn fmt_human_readable(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
HttpErrorPayload {
status,
content_type,
content,
} if is_plain_text_utf8(content_type) => {
f.write_fmt(format_args!(
"Http Error: status {}, content type {:?}, content: {}",
status,
content_type.as_ref().unwrap(),
String::from_utf8(content.to_vec()).unwrap_or_else(|from_utf8_err| format!(
"(unable to decode content: {:#?})",
from_utf8_err
))
))?;
}
HttpErrorPayload {
status,
content_type,
content,
} => {
f.write_fmt(format_args!(
"Http Error: status {}, content type {:?}, content: {:?}",
status,
content_type.as_ref().unwrap(),
content
))?;
}
}
Ok(())
}
}

impl Debug for HttpErrorPayload {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.fmt_human_readable(f)
}
}

impl Display for HttpErrorPayload {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.fmt_human_readable(f)
}
}

fn is_plain_text_utf8(content_type: &Option<String>) -> bool {
// text/plain is also sometimes returned by the replica (or ic-ref),
// depending on where in the stack the error happens.
matches!(
content_type.as_ref().and_then(|s|s.parse::<mime::Mime>().ok()),
Some(mt) if mt == mime::TEXT_PLAIN || mt == mime::TEXT_PLAIN_UTF_8
)
}
86 changes: 0 additions & 86 deletions ic-agent/src/agent/agent_test.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use crate::agent::replica_api::{CallReply, QueryResponse};
use crate::agent::response::{Replied, RequestStatusResponse};
use crate::agent::{AgentConfig, Status};
use crate::export::Principal;
use crate::{Agent, AgentError};
use delay::Delay;
use mockito::mock;
use std::collections::BTreeMap;
use std::time::Duration;

#[test]
fn query() -> Result<(), AgentError> {
Expand Down Expand Up @@ -93,45 +90,6 @@ fn query_rejected() -> Result<(), AgentError> {
Ok(())
}

#[test]
fn call() -> Result<(), AgentError> {
let blob = Vec::from("Hello World");
let response = QueryResponse::Replied {
reply: CallReply { arg: blob.clone() },
};

let submit_mock = mock("POST", "/api/v1/submit").with_status(200).create();
let status_mock = mock("POST", "/api/v1/read")
.with_status(200)
.with_header("content-type", "application/cbor")
.with_body(serde_cbor::to_vec(&response)?)
.create();

let agent = Agent::builder().with_url(&mockito::server_url()).build()?;

let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
let result = runtime.block_on(async {
let request_id = agent
.update(&Principal::management_canister(), "greet")
.with_arg(&[])
.call()
.await?;
agent.request_status_raw(&request_id, None).await
});

submit_mock.assert();
status_mock.assert();

assert_eq!(
result?,
RequestStatusResponse::Replied {
reply: Replied::CallReplied(blob)
}
);

Ok(())
}

#[test]
fn call_error() -> Result<(), AgentError> {
let submit_mock = mock("POST", "/api/v1/submit").with_status(500).create();
Expand All @@ -154,50 +112,6 @@ fn call_error() -> Result<(), AgentError> {
Ok(())
}

#[test]
fn call_rejected() -> Result<(), AgentError> {
let response: QueryResponse = QueryResponse::Rejected {
reject_code: 1234,
reject_message: "Rejected Message".to_string(),
};

let submit_mock = mock("POST", "/api/v1/submit").with_status(200).create();
let status_mock = mock("POST", "/api/v1/read")
.with_status(200)
.with_header("content-type", "application/cbor")
.with_body(serde_cbor::to_vec(&response)?)
.create();

let agent = Agent::new(AgentConfig {
url: mockito::server_url(),
..Default::default()
})?;

let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
let result = runtime.block_on(async {
agent
.update(&Principal::management_canister(), "greet")
.call_and_wait(Delay::timeout(Duration::from_millis(100)))
.await
});

match result {
Err(AgentError::ReplicaError {
reject_code: code,
reject_message: msg,
}) => {
assert_eq!(code, 1234);
assert_eq!(msg, "Rejected Message");
}
result => unreachable!("{:?}", result),
}

submit_mock.assert();
status_mock.assert();

Ok(())
}

#[test]
fn status() -> Result<(), AgentError> {
let ic_api_version = "1.2.3".to_string();
Expand Down
Loading