ndn_config/
control_response.rs1use bytes::Bytes;
10use ndn_tlv::{TlvReader, TlvWriter};
11
12use crate::control_parameters::ControlParameters;
13
14pub mod tlv {
17 pub const CONTROL_RESPONSE: u64 = 0x65;
18 pub const STATUS_CODE: u64 = 0x66;
19 pub const STATUS_TEXT: u64 = 0x67;
20}
21
22pub mod status {
25 pub const OK: u64 = 200;
26 pub const BAD_PARAMS: u64 = 400;
27 pub const UNAUTHORIZED: u64 = 403;
28 pub const NOT_FOUND: u64 = 404;
29 pub const CONFLICT: u64 = 409;
30 pub const SERVER_ERROR: u64 = 500;
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ControlResponse {
38 pub status_code: u64,
39 pub status_text: String,
40 pub body: Option<ControlParameters>,
42}
43
44impl ControlResponse {
45 pub fn ok(text: impl Into<String>, body: ControlParameters) -> Self {
47 Self {
48 status_code: status::OK,
49 status_text: text.into(),
50 body: Some(body),
51 }
52 }
53
54 pub fn ok_empty(text: impl Into<String>) -> Self {
56 Self {
57 status_code: status::OK,
58 status_text: text.into(),
59 body: None,
60 }
61 }
62
63 pub fn error(code: u64, text: impl Into<String>) -> Self {
65 Self {
66 status_code: code,
67 status_text: text.into(),
68 body: None,
69 }
70 }
71
72 pub fn is_ok(&self) -> bool {
74 (200..300).contains(&self.status_code)
75 }
76
77 pub fn encode(&self) -> Bytes {
79 let mut w = TlvWriter::new();
80 w.write_nested(tlv::CONTROL_RESPONSE, |w| {
81 write_non_neg_int(w, tlv::STATUS_CODE, self.status_code);
82 w.write_tlv(tlv::STATUS_TEXT, self.status_text.as_bytes());
83 if let Some(ref body) = self.body {
84 let body_bytes = body.encode();
86 w.write_raw(&body_bytes);
87 }
88 });
89 w.finish()
90 }
91
92 pub fn decode(wire: Bytes) -> Result<Self, ControlResponseError> {
94 let mut r = TlvReader::new(wire);
95 let (typ, value) = r
96 .read_tlv()
97 .map_err(|_| ControlResponseError::MalformedTlv)?;
98 if typ != tlv::CONTROL_RESPONSE {
99 return Err(ControlResponseError::WrongType(typ));
100 }
101 Self::decode_value(value)
102 }
103
104 pub fn decode_value(value: Bytes) -> Result<Self, ControlResponseError> {
106 let mut r = TlvReader::new(value);
107 let mut status_code = None;
108 let mut status_text = None;
109 let mut body = None;
110
111 while !r.is_empty() {
112 let (typ, val) = r
113 .read_tlv()
114 .map_err(|_| ControlResponseError::MalformedTlv)?;
115 match typ {
116 tlv::STATUS_CODE => {
117 status_code = Some(read_non_neg_int(&val)?);
118 }
119 tlv::STATUS_TEXT => {
120 status_text = Some(
121 std::str::from_utf8(&val)
122 .map_err(|_| ControlResponseError::InvalidUtf8)?
123 .to_owned(),
124 );
125 }
126 crate::control_parameters::tlv::CONTROL_PARAMETERS => {
127 body = Some(
128 ControlParameters::decode_value(val)
129 .map_err(|_| ControlResponseError::MalformedTlv)?,
130 );
131 }
132 _ => {} }
134 }
135
136 Ok(ControlResponse {
137 status_code: status_code.ok_or(ControlResponseError::MissingField("StatusCode"))?,
138 status_text: status_text.ok_or(ControlResponseError::MissingField("StatusText"))?,
139 body,
140 })
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
147pub enum ControlResponseError {
148 #[error("malformed TLV")]
149 MalformedTlv,
150 #[error("unexpected TLV type {0:#x}")]
151 WrongType(u64),
152 #[error("invalid NonNegativeInteger length")]
153 InvalidNonNegInt,
154 #[error("invalid UTF-8 in string field")]
155 InvalidUtf8,
156 #[error("missing required field: {0}")]
157 MissingField(&'static str),
158}
159
160fn encode_non_neg_int(value: u64) -> Vec<u8> {
163 if value <= 0xFF {
164 vec![value as u8]
165 } else if value <= 0xFFFF {
166 (value as u16).to_be_bytes().to_vec()
167 } else if value <= 0xFFFF_FFFF {
168 (value as u32).to_be_bytes().to_vec()
169 } else {
170 value.to_be_bytes().to_vec()
171 }
172}
173
174fn write_non_neg_int(w: &mut TlvWriter, typ: u64, value: u64) {
175 w.write_tlv(typ, &encode_non_neg_int(value));
176}
177
178fn read_non_neg_int(buf: &[u8]) -> Result<u64, ControlResponseError> {
179 match buf.len() {
180 1 => Ok(buf[0] as u64),
181 2 => Ok(u16::from_be_bytes([buf[0], buf[1]]) as u64),
182 4 => Ok(u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) as u64),
183 8 => Ok(u64::from_be_bytes([
184 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
185 ])),
186 _ => Err(ControlResponseError::InvalidNonNegInt),
187 }
188}
189
190#[cfg(test)]
193mod tests {
194 use super::*;
195 use crate::control_parameters::ControlParameters;
196 use ndn_packet::{Name, NameComponent};
197
198 fn name(components: &[&[u8]]) -> Name {
199 Name::from_components(
200 components
201 .iter()
202 .map(|c| NameComponent::generic(Bytes::copy_from_slice(c))),
203 )
204 }
205
206 #[test]
207 fn encode_decode_ok_empty() {
208 let resp = ControlResponse::ok_empty("OK");
209 let wire = resp.encode();
210 let decoded = ControlResponse::decode(wire).unwrap();
211 assert_eq!(decoded.status_code, 200);
212 assert_eq!(decoded.status_text, "OK");
213 assert!(decoded.body.is_none());
214 assert!(decoded.is_ok());
215 }
216
217 #[test]
218 fn encode_decode_ok_with_body() {
219 let params = ControlParameters {
220 name: Some(name(&[b"ndn", b"test"])),
221 face_id: Some(5),
222 cost: Some(10),
223 ..Default::default()
224 };
225 let resp = ControlResponse::ok("OK", params.clone());
226 let wire = resp.encode();
227 let decoded = ControlResponse::decode(wire).unwrap();
228 assert_eq!(decoded.status_code, 200);
229 assert_eq!(decoded.body, Some(params));
230 }
231
232 #[test]
233 fn encode_decode_error() {
234 let resp = ControlResponse::error(status::NOT_FOUND, "face not found");
235 let wire = resp.encode();
236 let decoded = ControlResponse::decode(wire).unwrap();
237 assert_eq!(decoded.status_code, 404);
238 assert_eq!(decoded.status_text, "face not found");
239 assert!(decoded.body.is_none());
240 assert!(!decoded.is_ok());
241 }
242
243 #[test]
244 fn decode_wrong_type_errors() {
245 let mut w = TlvWriter::new();
246 w.write_nested(0x05, |_| {});
247 let result = ControlResponse::decode(w.finish());
248 assert!(matches!(result, Err(ControlResponseError::WrongType(0x05))));
249 }
250
251 #[test]
252 fn decode_missing_status_code_errors() {
253 let mut w = TlvWriter::new();
254 w.write_nested(tlv::CONTROL_RESPONSE, |w| {
255 w.write_tlv(tlv::STATUS_TEXT, b"oops");
256 });
257 let result = ControlResponse::decode(w.finish());
258 assert!(matches!(
259 result,
260 Err(ControlResponseError::MissingField("StatusCode"))
261 ));
262 }
263
264 #[test]
265 fn status_code_ranges() {
266 assert!(ControlResponse::ok_empty("OK").is_ok());
267 assert!(!ControlResponse::error(400, "bad").is_ok());
268 assert!(!ControlResponse::error(500, "err").is_ok());
269 }
270}