ic_host_tools/response/
mod.rs1#[cfg(test)]
9mod tests;
10
11use serde::de::{Deserialize, Deserializer, IgnoredAny, MapAccess, Visitor};
12use serde_json::error::Category;
13use std::{collections::TryReserveError, fmt};
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub enum ResponseFormat {
18 Json,
22 Hex,
25}
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub struct ResponseLimits {
30 pub input_bytes: usize,
32 pub decoded_bytes: usize,
34}
35
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
38pub enum JsonErrorKind {
39 Syntax,
41 Data,
43 EndOfInput,
45 Io,
47}
48
49#[derive(Debug)]
51pub enum ResponseError {
52 InputLimit {
54 limit: usize,
56 },
57 InvalidUtf8 {
59 offset: usize,
61 },
62 Json {
64 kind: JsonErrorKind,
66 line: usize,
68 column: usize,
70 },
71 MissingResponseBytes,
73 EmptyHex,
75 InvalidHex {
77 offset: usize,
80 },
81 OddHexLength,
83 DecodedLimit {
85 limit: usize,
87 },
88 Allocation(TryReserveError),
90}
91
92impl fmt::Display for ResponseError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 Self::InputLimit { limit } => write!(f, "response input exceeds {limit} bytes"),
96 Self::InvalidUtf8 { offset } => {
97 write!(f, "response JSON is not UTF-8 at byte {offset}")
98 }
99 Self::Json { kind, line, column } => {
100 write!(f, "response JSON failed ({kind:?}) at {line}:{column}")
101 }
102 Self::MissingResponseBytes => f.write_str("response JSON lacks response_bytes"),
103 Self::EmptyHex => f.write_str("response hex is empty"),
104 Self::InvalidHex { offset } => write!(f, "invalid response hex at byte {offset}"),
105 Self::OddHexLength => f.write_str("response hex has an odd digit count"),
106 Self::DecodedLimit { limit } => write!(f, "decoded response exceeds {limit} bytes"),
107 Self::Allocation(source) => write!(f, "response allocation failed: {source}"),
108 }
109 }
110}
111
112impl std::error::Error for ResponseError {
113 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
114 match self {
115 Self::Allocation(source) => Some(source),
116 _ => None,
117 }
118 }
119}
120
121pub fn decode(
134 input: &[u8],
135 format: ResponseFormat,
136 limits: ResponseLimits,
137) -> Result<Vec<u8>, ResponseError> {
138 if input.len() > limits.input_bytes {
139 return Err(ResponseError::InputLimit {
140 limit: limits.input_bytes,
141 });
142 }
143 match format {
144 ResponseFormat::Json => {
145 std::str::from_utf8(input).map_err(|error| ResponseError::InvalidUtf8 {
146 offset: error.valid_up_to(),
147 })?;
148 let envelope: Envelope =
149 serde_json::from_slice(input).map_err(|error| json_error(&error))?;
150 let hex = envelope.0.ok_or(ResponseError::MissingResponseBytes)?;
151 decode_hex(hex.as_bytes(), false, limits.decoded_bytes)
152 }
153 ResponseFormat::Hex => decode_hex(input, true, limits.decoded_bytes),
154 }
155}
156
157fn decode_hex(hex: &[u8], whitespace: bool, limit: usize) -> Result<Vec<u8>, ResponseError> {
158 let mut digits = 0;
159 for (offset, &byte) in hex.iter().enumerate() {
160 if byte.is_ascii_hexdigit() {
161 digits += 1;
162 } else if !(whitespace && byte.is_ascii_whitespace()) {
163 return Err(ResponseError::InvalidHex { offset });
164 }
165 }
166 if whitespace && digits == 0 {
168 return Err(ResponseError::EmptyHex);
169 }
170 if digits % 2 != 0 {
171 return Err(ResponseError::OddHexLength);
172 }
173 if digits / 2 > limit {
174 return Err(ResponseError::DecodedLimit { limit });
175 }
176 let mut bytes = Vec::new();
177 bytes
178 .try_reserve_exact(digits / 2)
179 .map_err(ResponseError::Allocation)?;
180 let mut high = None;
181 for byte in hex.iter().copied().filter(u8::is_ascii_hexdigit) {
182 let nibble = if byte <= b'9' {
184 byte - b'0'
185 } else {
186 byte.to_ascii_lowercase() - b'a' + 10
187 };
188 if let Some(previous) = high.take() {
189 bytes.push((previous << 4) | nibble);
190 } else {
191 high = Some(nibble);
192 }
193 }
194 Ok(bytes)
195}
196
197fn json_error(error: &serde_json::Error) -> ResponseError {
198 let kind = match error.classify() {
199 Category::Io => JsonErrorKind::Io,
200 Category::Syntax => JsonErrorKind::Syntax,
201 Category::Data => JsonErrorKind::Data,
202 Category::Eof => JsonErrorKind::EndOfInput,
203 };
204 ResponseError::Json {
205 kind,
206 line: error.line(),
207 column: error.column(),
208 }
209}
210
211struct Envelope(Option<String>);
212
213impl<'de> Deserialize<'de> for Envelope {
214 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
215 struct EnvelopeVisitor;
216 impl<'de> Visitor<'de> for EnvelopeVisitor {
217 type Value = Envelope;
218
219 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 f.write_str("an ICP response object")
221 }
222
223 fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<Envelope, M::Error> {
224 let mut response = None;
225 let mut seen = false;
226 while let Some(key) = map.next_key::<String>()? {
227 if key == "response_bytes" {
228 if seen {
229 return Err(serde::de::Error::duplicate_field("response_bytes"));
230 }
231 seen = true;
232 response = map.next_value::<Option<String>>()?;
233 } else {
234 map.next_value::<IgnoredAny>()?;
235 }
236 }
237 Ok(Envelope(response))
238 }
239 }
240 deserializer.deserialize_map(EnvelopeVisitor)
241 }
242}