kwap_msg/
opt.rs

1use kwap_common::{Array, GetSize};
2use kwap_macros::rfc_7252_doc;
3
4use crate::from_bytes::*;
5
6#[doc = rfc_7252_doc!("5.4")]
7/// <details><summary><b>RFC7252 Section 3.1 Option binary format</b></summary>
8#[doc = concat!("\n#", rfc_7252_doc!("3.1"))]
9/// </details>
10///
11/// # `Opt` struct
12/// Low-level representation of a freshly parsed CoAP Option
13///
14/// ## Option Numbers
15/// This struct just stores data parsed directly from the message on the wire,
16/// and does not compute or store the Option Number.
17///
18/// To get [`OptNumber`]s, you can use the iterator extension [`EnumerateOptNumbers`] on a collection of [`Opt`]s.
19#[derive(Clone, PartialEq, PartialOrd, Debug, Default)]
20pub struct Opt<C: Array<Item = u8>> {
21  /// See [`OptDelta`]
22  pub delta: OptDelta,
23  /// See [`OptValue`]
24  pub value: OptValue<C>,
25}
26
27impl<C: Array<Item = u8>> GetSize for Opt<C> {
28  fn get_size(&self) -> usize {
29    let header_size = 1;
30    let delta_size = match self.delta.0 {
31      | n if n >= 269 => 2,
32      | n if n >= 13 => 1,
33      | _ => 0,
34    };
35
36    let value_len_size = match self.value.0.get_size() {
37      | n if n >= 269 => 2,
38      | n if n >= 13 => 1,
39      | _ => 0,
40    };
41
42    header_size + delta_size + value_len_size + self.value.0.get_size()
43  }
44
45  fn max_size(&self) -> Option<usize> {
46    None
47  }
48}
49
50impl<C: Array<Item = u8>> Opt<C> {
51  /// Given a collection to [`Extend`] and an Opt, add that Opt's bytes to the collection.
52  pub fn extend_bytes(self, bytes: &mut impl Extend<u8>) {
53    let (del, del_bytes) = crate::to_bytes::opt_len_or_delta(self.delta.0);
54    let (len, len_bytes) = crate::to_bytes::opt_len_or_delta(self.value.0.get_size() as u16);
55    let del = del << 4;
56
57    let header = del | len;
58
59    bytes.extend(Some(header));
60
61    if let Some(bs) = del_bytes {
62      bytes.extend(bs);
63    }
64
65    if let Some(bs) = len_bytes {
66      bytes.extend(bs);
67    }
68
69    bytes.extend(self.value.0);
70  }
71}
72
73/// The "Option Delta" is the difference between this Option's Number
74/// and the previous Option's number.
75///
76/// This is just used to compute the Option Number, identifying which
77/// Option is being set (e.g. Content-Format has a Number of 12)
78///
79/// To use this to get Option Numbers, see [`EnumerateOptNumbers`].
80///
81/// # Related
82/// - [RFC7252#section-3.1 Option Format](https://datatracker.ietf.org/doc/html/rfc7252#section-3.1)
83#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)]
84pub struct OptDelta(pub u16);
85
86#[doc = rfc_7252_doc!("5.4.6")]
87/// <details><summary><b>RFC7252 Section 12.2 Core CoAP Option Numbers</b></summary>
88#[doc = concat!("\n#", rfc_7252_doc!("12.2"))]
89/// </details>
90///
91/// # `OptNumber` struct
92/// Because Option Numbers are only able to be computed in the context of other options, in order to
93/// get Option Numbers you must have a collection of [`Opt`]s, and use the provided [`EnumerateOptNumbers`].
94#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Default)]
95pub struct OptNumber(pub u32);
96
97#[doc = rfc_7252_doc!("5.4.1")]
98#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
99pub enum OptionMustBeProcessed {
100  /// This option must be processed,
101  /// and a response that ignores it
102  /// will be rejected.
103  ///
104  /// Corresponds to the option being "critical"
105  /// in strict CoAP terms
106  Yes,
107  /// This option does not _need_ to
108  /// be processed,
109  /// and a response that ignores it
110  /// will be processed anyway.
111  ///
112  /// Corresponds to the option being "elective"
113  /// in strict CoAP terms
114  No,
115}
116
117#[doc = rfc_7252_doc!("5.4.2")]
118#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
119pub enum WhenOptionUnsupportedByProxy {
120  /// This option /must be/ processed & understood by proxies
121  /// and may not be forwarded blindly to their destination.
122  ///
123  /// Corresponds to the option being "UnSafe" to forward
124  /// in strict CoAP terms
125  Error,
126  /// This option may not be processed & understood by proxies
127  /// and may be forwarded blindly to their destination.
128  ///
129  /// Corresponds to the option being "SafeToForward"
130  /// in strict CoAP terms
131  Forward,
132}
133
134#[doc = rfc_7252_doc!("5.4.2")]
135#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
136pub enum WhenOptionChanges {
137  /// If this option is [safe to forward](`ProxySafe::ForwardWhenUnknown`),
138  /// but unknown to a proxy, it should be included in the proxy's
139  /// cache key for this message.
140  ///
141  /// Corresponds to the option being not "NoCacheKey"
142  /// in strict CoAP terms
143  ResponseChanges,
144  /// If this option is [safe to forward](`ProxySafe::ForwardWhenUnknown`),
145  /// but unknown to a proxy, it should not be included in the proxy's
146  /// cache key for this message, and different values for this option
147  /// should yield the cached response.
148  ///
149  /// Corresponds to the option being "NoCacheKey"
150  /// in strict CoAP terms
151  ResponseDoesNotChange,
152}
153
154impl OptNumber {
155  /// Whether or not this option may be ignored by a server
156  pub fn must_be_processed(&self) -> OptionMustBeProcessed {
157    #[allow(clippy::wildcard_in_or_patterns)] // will only ever be 0 or 1
158    match self.0 & 0b1 {
159      | 1 => OptionMustBeProcessed::Yes,
160      | 0 | _ => OptionMustBeProcessed::No,
161    }
162  }
163
164  /// Whether or not this option may be forwarded blindly by
165  /// a proxy that does not support processing it
166  pub fn when_unsupported_by_proxy(&self) -> WhenOptionUnsupportedByProxy {
167    #[allow(clippy::wildcard_in_or_patterns)] // will only ever be 0 or 1
168    match (self.0 & 0b10) >> 1 {
169      | 1 => WhenOptionUnsupportedByProxy::Error,
170      | 0 | _ => WhenOptionUnsupportedByProxy::Forward,
171    }
172  }
173
174  /// Whether or not different values for this option should
175  /// yield proxies' cached response
176  ///
177  /// _(when the proxy does not support processing it and
178  /// the option is safe to forward)_
179  pub fn when_option_changes(&self) -> WhenOptionChanges {
180    match (self.0 & 0b11100) >> 2 {
181      | 0b111 => WhenOptionChanges::ResponseDoesNotChange,
182      | _ => WhenOptionChanges::ResponseChanges,
183    }
184  }
185}
186
187#[doc = rfc_7252_doc!("3.2")]
188#[derive(Default, Clone, PartialEq, PartialOrd, Debug)]
189pub struct OptValue<C: Array<Item = u8>>(pub C);
190
191/// Peek at the first byte of a byte iterable and interpret as an Option header.
192///
193/// This converts the iterator into a Peekable and looks at bytes0.
194/// Checks if byte 0 is a Payload marker, indicating all options have been read.
195pub(crate) fn opt_header<I: Iterator<Item = u8>>(mut bytes: I) -> Result<u8, OptParseError> {
196  let opt_header = bytes.next();
197
198  if let Some(0b11111111) | None = opt_header {
199    // This isn't an option, it's the payload!
200    return Err(OptParseError::OptionsExhausted);
201  }
202
203  Ok(opt_header.unwrap())
204}
205
206impl<C: Array<Item = u8>, OptArray: Array<Item = Opt<C>>, I: Iterator<Item = u8>> TryConsumeBytes<I>
207  for OptArray
208{
209  type Error = OptParseError;
210
211  fn try_consume_bytes(bytes: &mut I) -> Result<Self, Self::Error> {
212    let mut opts = OptArray::default();
213
214    loop {
215      match Opt::try_consume_bytes(bytes) {
216        | Ok(opt) => {
217          if !opts.is_full() {
218            opts.extend(Some(opt));
219          } else {
220            break Err(Self::Error::TooManyOptions(opts.get_size()));
221          }
222        },
223        | Err(OptParseError::OptionsExhausted) => break Ok(opts),
224        | Err(e) => break Err(e),
225      }
226    }
227  }
228}
229
230impl<I: Iterator<Item = u8>, C: Array<Item = u8>> TryConsumeBytes<I> for Opt<C> {
231  type Error = OptParseError;
232
233  fn try_consume_bytes(bytes: &mut I) -> Result<Self, Self::Error> {
234    let opt_header = opt_header(bytes.by_ref())?;
235
236    // NOTE: Delta **MUST** be consumed before Value. see comment on `opt_len_or_delta` for more info
237    let delta =
238      OptDelta::try_consume_bytes(&mut core::iter::once(opt_header).chain(bytes.by_ref()))?;
239    let len = opt_header & 0b00001111;
240    let len = parse_opt_len_or_delta(len,
241                                     bytes.by_ref(),
242                                     OptParseError::ValueLengthReservedValue(15))?;
243    let value = OptValue::try_consume_n_bytes(len as usize, bytes)?;
244    Ok(Opt { delta, value })
245  }
246}
247
248impl<I: Iterator<Item = u8>, C: Array<Item = u8>> TryConsumeNBytes<I> for OptValue<C> {
249  type Error = OptParseError;
250
251  fn try_consume_n_bytes(n: usize, bytes: &mut I) -> Result<Self, Self::Error> {
252    let mut data = C::reserve(n);
253    data.extend(&mut bytes.take(n));
254
255    if data.get_size() < n {
256      Err(Self::Error::UnexpectedEndOfStream)
257    } else {
258      Ok(OptValue(data))
259    }
260  }
261}
262
263/// Creates an iterator which gives the current opt's number as well as the option.
264///
265/// The iterator returned yields pairs `(i, val)`, where `i` is the [`OptNumber`] and `val` is the Opt returned by the iterator.
266pub trait EnumerateOptNumbers<T>
267  where Self: Sized + Iterator<Item = T>
268{
269  /// Creates an iterator which gives the current Opt along with its Number.
270  ///
271  /// ```
272  /// use kwap_msg::*;
273  ///
274  /// let opt_a = Opt { delta: OptDelta(12),
275  ///                   value: OptValue(Vec::new()) };
276  /// let opt_b = Opt { delta: OptDelta(2),
277  ///                   value: OptValue(Vec::new()) };
278  /// let opts = vec![opt_a.clone(), opt_b.clone()];
279  ///
280  /// let opt_ns = opts.into_iter()
281  ///                  .enumerate_option_numbers()
282  ///                  .collect::<Vec<_>>();
283  ///
284  /// assert_eq!(opt_ns, vec![(OptNumber(12), opt_a), (OptNumber(14), opt_b)])
285  /// ```
286  fn enumerate_option_numbers(self) -> EnumerateOptNumbersIter<T, Self>;
287}
288
289impl<C: Array<Item = u8>, I: Iterator<Item = Opt<C>>> EnumerateOptNumbers<Opt<C>> for I {
290  fn enumerate_option_numbers(self) -> EnumerateOptNumbersIter<Opt<C>, Self> {
291    EnumerateOptNumbersIter { number: 0,
292                              iter: self }
293  }
294}
295
296impl<'a, C: Array<Item = u8>, I: Iterator<Item = &'a Opt<C>>> EnumerateOptNumbers<&'a Opt<C>>
297  for I
298{
299  fn enumerate_option_numbers(self) -> EnumerateOptNumbersIter<&'a Opt<C>, Self> {
300    EnumerateOptNumbersIter { number: 0,
301                              iter: self }
302  }
303}
304
305/// Iterator yielded by [`EnumerateOptNumbers`], wrapping an Iterator
306/// over [`Opt`]s.
307///
308/// Invoking [`Iterator::next`] on this struct will advance the
309/// inner iterator, and add the delta of the new opt to its running sum of deltas.
310///
311/// This running sum is the Number of the newly iterated Opt.
312#[derive(Clone, Debug)]
313pub struct EnumerateOptNumbersIter<T, I: Iterator<Item = T>> {
314  number: u32,
315  iter: I,
316}
317
318impl<C: Array<Item = u8>, I: Iterator<Item = Opt<C>>> Iterator
319  for EnumerateOptNumbersIter<Opt<C>, I>
320{
321  type Item = (OptNumber, Opt<C>);
322
323  fn next(&mut self) -> Option<Self::Item> {
324    self.iter.next().map(|opt| {
325                      self.number += opt.delta.0 as u32;
326                      (OptNumber(self.number), opt)
327                    })
328  }
329}
330
331impl<'a, C: Array<Item = u8>, I: Iterator<Item = &'a Opt<C>>> Iterator
332  for EnumerateOptNumbersIter<&'a Opt<C>, I>
333{
334  type Item = (OptNumber, &'a Opt<C>);
335
336  fn next(&mut self) -> Option<Self::Item> {
337    self.iter.next().map(|opt| {
338                      self.number += opt.delta.0 as u32;
339                      (OptNumber(self.number), opt)
340                    })
341  }
342}
343
344#[cfg(test)]
345mod tests {
346  use core::iter::{once, repeat};
347
348  use super::*;
349
350  #[test]
351  fn parse_opt_delta() {
352    let mut del_4bit = [0b00010000u8].into_iter();
353    let del_4bit = OptDelta::try_consume_bytes(&mut del_4bit).unwrap();
354    assert_eq!(del_4bit, OptDelta(1));
355
356    let mut del_1byte = [0b11010000u8, 0b00000000].into_iter();
357    let del_1byte = OptDelta::try_consume_bytes(&mut del_1byte).unwrap();
358    assert_eq!(del_1byte, OptDelta(13));
359
360    let mut del_2bytes = [[0b11100000u8].as_ref(), u16::to_be_bytes(12076).as_ref()].concat()
361                                                                                    .into_iter();
362    let del_2bytes = OptDelta::try_consume_bytes(&mut del_2bytes).unwrap();
363    assert_eq!(del_2bytes, OptDelta(12345));
364
365    let errs = [[0b11010000u8].as_ref().iter(), // delta is 13 but no byte following
366                [0b11100000u8, 0b00000001].as_ref().iter(), // delta is 14 but only 1 byte following
367                [].as_ref().iter()];
368
369    errs.into_iter().for_each(|iter| {
370                      let del = OptDelta::try_consume_bytes(&mut iter.copied());
371                      assert_eq!(del, Err(OptParseError::UnexpectedEndOfStream))
372                    });
373  }
374
375  #[test]
376  fn parse_opt_value() {
377    let mut val_1byte = once(2);
378    let val_1byte = OptValue::try_consume_n_bytes(1, &mut val_1byte).unwrap();
379    assert_eq!(val_1byte, OptValue(vec![2]));
380
381    let data13bytes = repeat(1u8).take(13).collect::<Vec<_>>();
382    let mut val_13bytes = data13bytes.iter().copied();
383    let val_13bytes = OptValue::try_consume_n_bytes(13, &mut val_13bytes).unwrap();
384    assert_eq!(val_13bytes, OptValue(data13bytes));
385
386    let data270bytes = repeat(1u8).take(270).collect::<Vec<_>>();
387    let mut val_270bytes = data270bytes.iter().copied();
388    let val_270bytes = OptValue::try_consume_n_bytes(270, &mut val_270bytes).unwrap();
389    assert_eq!(val_270bytes, OptValue(data270bytes));
390
391    let errs = [(1, [].into_iter())];
392
393    errs.into_iter().for_each(|(n, mut bytes)| {
394                      let del = OptValue::<Vec<_>>::try_consume_n_bytes(n, &mut bytes);
395                      assert_eq!(del, Err(OptParseError::UnexpectedEndOfStream))
396                    });
397  }
398
399  #[test]
400  fn parse_opt() {
401    let opt_bytes: [u8; 2] = [0b00000001, 0b00000001];
402    let opt = Opt::try_consume_bytes(&mut opt_bytes.into_iter()).unwrap();
403    assert_eq!(opt,
404               Opt { delta: OptDelta(0),
405                     value: OptValue(vec![1]) });
406
407    let opt_bytes: [u8; 5] = [0b00000001, 0b00000001, 0b00010001, 0b00000011, 0b11111111];
408    let opt = Vec::<Opt<Vec<_>>>::try_consume_bytes(&mut opt_bytes.into_iter()).unwrap();
409    assert_eq!(opt,
410               vec![Opt { delta: OptDelta(0),
411                          value: OptValue(vec![1]) },
412                    Opt { delta: OptDelta(1),
413                          value: OptValue(vec![3]) },]);
414  }
415
416  #[test]
417  fn opt_number_qualities() {
418    // critical, safe-to-fwd, cache-key
419    let if_match = OptNumber(1);
420
421    // critical, unsafe-to-fwd, cache-key
422    let uri_host = OptNumber(3);
423
424    // elective, safe-to-fwd, cache-key
425    let etag = OptNumber(4);
426
427    // elective, safe-to-fwd, no-cache-key
428    let size1 = OptNumber(60);
429
430    [&if_match, &uri_host].into_iter()
431                          .for_each(|num| {
432                            assert_eq!(num.must_be_processed(), OptionMustBeProcessed::Yes);
433                          });
434
435    [&etag, &size1].into_iter().for_each(|num| {
436                                 assert_eq!(num.must_be_processed(), OptionMustBeProcessed::No);
437                               });
438
439    [&if_match, &etag, &size1].into_iter().for_each(|num| {
440                                            assert_eq!(num.when_unsupported_by_proxy(),
441                                                       WhenOptionUnsupportedByProxy::Forward);
442                                          });
443
444    [&uri_host].into_iter().for_each(|num| {
445                             assert_eq!(num.when_unsupported_by_proxy(),
446                                        WhenOptionUnsupportedByProxy::Error);
447                           });
448
449    [&if_match, &uri_host, &etag].into_iter().for_each(|num| {
450                                               assert_eq!(num.when_option_changes(),
451                                                          WhenOptionChanges::ResponseChanges);
452                                             });
453
454    [&size1].into_iter().for_each(|num| {
455                          assert_eq!(num.when_option_changes(),
456                                     WhenOptionChanges::ResponseDoesNotChange);
457                        });
458  }
459}