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, thiserror::Error)]
51pub enum ResponseError {
52 #[error("response input exceeds {limit} bytes")]
54 InputLimit {
55 limit: usize,
57 },
58 #[error("response JSON is not UTF-8 at byte {offset}")]
60 InvalidUtf8 {
61 offset: usize,
63 },
64 #[error("response JSON failed ({kind:?}) at {line}:{column}")]
66 Json {
67 kind: JsonErrorKind,
69 line: usize,
71 column: usize,
73 },
74 #[error("response JSON lacks response_bytes")]
76 MissingResponseBytes,
77 #[error("response hex is empty")]
79 EmptyHex,
80 #[error("invalid response hex at byte {offset}")]
82 InvalidHex {
83 offset: usize,
86 },
87 #[error("response hex has an odd digit count")]
89 OddHexLength,
90 #[error("decoded response exceeds {limit} bytes")]
92 DecodedLimit {
93 limit: usize,
95 },
96 #[error("response allocation failed: {0}")]
98 Allocation(#[source] TryReserveError),
99}
100
101pub fn decode(
114 input: &[u8],
115 format: ResponseFormat,
116 limits: ResponseLimits,
117) -> Result<Vec<u8>, ResponseError> {
118 if input.len() > limits.input_bytes {
119 return Err(ResponseError::InputLimit {
120 limit: limits.input_bytes,
121 });
122 }
123 match format {
124 ResponseFormat::Json => {
125 std::str::from_utf8(input).map_err(|error| ResponseError::InvalidUtf8 {
126 offset: error.valid_up_to(),
127 })?;
128 let envelope: Envelope =
129 serde_json::from_slice(input).map_err(|error| json_error(&error))?;
130 let hex = envelope.0.ok_or(ResponseError::MissingResponseBytes)?;
131 decode_hex(hex.as_bytes(), false, limits.decoded_bytes)
132 }
133 ResponseFormat::Hex => decode_hex(input, true, limits.decoded_bytes),
134 }
135}
136
137fn decode_hex(hex: &[u8], whitespace: bool, limit: usize) -> Result<Vec<u8>, ResponseError> {
138 let mut digits = 0;
139 for (offset, &byte) in hex.iter().enumerate() {
140 if byte.is_ascii_hexdigit() {
141 digits += 1;
142 } else if !(whitespace && byte.is_ascii_whitespace()) {
143 return Err(ResponseError::InvalidHex { offset });
144 }
145 }
146 if whitespace && digits == 0 {
148 return Err(ResponseError::EmptyHex);
149 }
150 if digits % 2 != 0 {
151 return Err(ResponseError::OddHexLength);
152 }
153 if digits / 2 > limit {
154 return Err(ResponseError::DecodedLimit { limit });
155 }
156 let mut bytes = Vec::new();
157 bytes
158 .try_reserve_exact(digits / 2)
159 .map_err(ResponseError::Allocation)?;
160 let mut high = None;
161 for byte in hex.iter().copied().filter(u8::is_ascii_hexdigit) {
162 let nibble = if byte <= b'9' {
164 byte - b'0'
165 } else {
166 byte.to_ascii_lowercase() - b'a' + 10
167 };
168 if let Some(previous) = high.take() {
169 bytes.push((previous << 4) | nibble);
170 } else {
171 high = Some(nibble);
172 }
173 }
174 Ok(bytes)
175}
176
177fn json_error(error: &serde_json::Error) -> ResponseError {
178 let kind = match error.classify() {
179 Category::Io => JsonErrorKind::Io,
180 Category::Syntax => JsonErrorKind::Syntax,
181 Category::Data => JsonErrorKind::Data,
182 Category::Eof => JsonErrorKind::EndOfInput,
183 };
184 ResponseError::Json {
185 kind,
186 line: error.line(),
187 column: error.column(),
188 }
189}
190
191struct Envelope(Option<String>);
192
193impl<'de> Deserialize<'de> for Envelope {
194 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
195 struct EnvelopeVisitor;
196 impl<'de> Visitor<'de> for EnvelopeVisitor {
197 type Value = Envelope;
198
199 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 f.write_str("an ICP response object")
201 }
202
203 fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<Envelope, M::Error> {
204 let mut response = None;
205 let mut seen = false;
206 while let Some(key) = map.next_key::<String>()? {
207 if key == "response_bytes" {
208 if seen {
209 return Err(serde::de::Error::duplicate_field("response_bytes"));
210 }
211 seen = true;
212 response = map.next_value::<Option<String>>()?;
213 } else {
214 map.next_value::<IgnoredAny>()?;
215 }
216 }
217 Ok(Envelope(response))
218 }
219 }
220 deserializer.deserialize_map(EnvelopeVisitor)
221 }
222}