coap_handler_implementations/
typed_resource.rs

1//! Module containing the [TypeHandler] handler and the [TypeRenderable]
2//! trait, along with the [TypeSerializer] helper trait and its corresponding implementations
3//! for various serde(ish) (de)serializers.
4
5use crate::helpers::block2_write_with_cf;
6use crate::Error;
7use coap_handler::Handler;
8use coap_message::{
9    Code as _, MessageOption, MinimalWritableMessage, MutableWritableMessage, ReadableMessage,
10};
11use coap_message_utils::{option_value::Block2RequestData, OptionsExt};
12use coap_numbers::{code, option};
13use core::marker::PhantomData;
14
15use serde::Serialize;
16
17/// A type that (for minicbor typed handlers) represents an empty payload.
18///
19/// This is particularly practical for resources that have a GET and PUT handler that act on
20/// serialized data, but where the POST handler just takes an empty resource.
21pub struct Empty;
22
23/// A simple Handler trait that supports GET, POST and/or PUT on a data structure that supports
24/// serde.
25///
26/// A TypeRenderable implementation can be turned into a [Handler] by wrapping it in
27/// [TypeHandler::new].
28pub trait TypeRenderable {
29    /// Output type of the [get()][Self::get()] method, serialized into the response payload.
30    type Get;
31    /// Input type of the [post()][Self::post()] method, deserialized from the request payload.
32    ///
33    /// Note that with TypedRenderable in this version, the response is always empty.
34    type Post;
35    /// Input type of the [put()][Self::put()] method, deserialized from the request payload.
36    type Put;
37
38    fn get(&mut self) -> Result<Self::Get, u8> {
39        Err(code::METHOD_NOT_ALLOWED)
40    }
41    fn post(&mut self, _representation: &Self::Post) -> u8 {
42        code::METHOD_NOT_ALLOWED
43    }
44    fn put(&mut self, _representation: &Self::Put) -> u8 {
45        code::METHOD_NOT_ALLOWED
46    }
47    fn delete(&mut self) -> u8 {
48        code::METHOD_NOT_ALLOWED
49    }
50}
51
52/// Keeping them hidden to stay flexible; they don't need to be named for their'e either default or
53/// their users have aliases.
54mod sealed {
55    pub trait TypeSerializer {
56        const CF: Option<u16>;
57    }
58
59    pub struct SerdeCBORSerialization;
60    pub struct MiniCBORSerialization0_19;
61    pub struct MiniCBORSerialization0_24;
62    pub struct MiniCBORSerialization0_26;
63}
64use sealed::*;
65
66impl TypeSerializer for SerdeCBORSerialization {
67    const CF: Option<u16> = coap_numbers::content_format::from_str("application/cbor");
68}
69
70/// Wrapper for resource handlers that are implemented in terms of GETting, POSTing or PUTting
71/// objects in CBOR format.
72///
73/// The wrapper handles all encoding and decoding, options processing (ignoring the critical
74/// Uri-Path option under the assumption that that has been already processed by an underlying
75/// request router), the corresponding errors and block-wise GETs.
76///
77/// More complex handlers (eg. implementing additional representations, or processing query
78/// aprameters into additional data available to the [TypeRenderable]) can be built by
79/// forwarding to this (where any critical but already processed options would need to be masked
80/// from the message's option) or taking inspiration from it.
81pub struct TypeHandler<H, S: TypeSerializer = SerdeCBORSerialization>
82where
83    H: TypeRenderable,
84{
85    handler: H,
86    _phantom: PhantomData<S>,
87}
88
89impl<H, S> TypeHandler<H, S>
90where
91    H: TypeRenderable,
92    S: TypeSerializer,
93{
94    fn check_get_options(request: &impl ReadableMessage) -> Result<Block2RequestData, Error> {
95        let mut block2 = None;
96
97        request
98            .options()
99            .take_block2(&mut block2)
100            .filter(|o| {
101                if o.number() == option::ACCEPT {
102                    if let Some(cf) = S::CF {
103                        // If they differ, we'll keep the option for later failing
104                        o.value_uint() != Some(cf)
105                    } else {
106                        // We don't know any content format, so we keep the option in the iterator
107                        // to fail later
108                        true
109                    }
110                } else {
111                    true
112                }
113            })
114            .ignore_elective_others()?;
115
116        Ok(block2.unwrap_or_default())
117    }
118
119    fn check_delete_options(request: &impl ReadableMessage) -> Result<(), Error> {
120        request.options().ignore_elective_others()
121    }
122
123    fn check_postput_options(request: &impl ReadableMessage) -> Result<(), Error> {
124        let mut cf = Ok(());
125
126        request
127            .options()
128            .filter(|o| {
129                if o.number() == option::CONTENT_FORMAT
130                    && (S::CF.is_none() || o.value_uint() != S::CF)
131                {
132                    cf = Err(Error::bad_option(option::CONTENT_FORMAT));
133                }
134                // It's not a critical option so we don't really have to filter it out
135                true
136            })
137            .ignore_elective_others()?;
138
139        cf
140    }
141}
142
143impl<H> TypeHandler<H, SerdeCBORSerialization>
144where
145    H: TypeRenderable,
146    H::Get: for<'de> serde::Serialize,
147    H::Post: for<'de> serde::Deserialize<'de>,
148    H::Put: for<'de> serde::Deserialize<'de>,
149{
150    /// Wrap a handler that uses [serde::Serialize] / [serde::Deserialize] through [serde_cbor]
151    pub fn new(handler: H) -> Self {
152        TypeHandler {
153            handler,
154            _phantom: PhantomData,
155        }
156    }
157}
158
159/// Data carried around between a request and its response for [TypeHandler]s
160pub struct TypeRequestData(TypeRequestDataE);
161
162enum TypeRequestDataE {
163    Get(Block2RequestData), // GET to be processed later, but all request opions were in order
164    Done(u8), // All done, just a response to emit -- if POST/PUT has been processed, or GET had a bad accept/option
165}
166use self::TypeRequestDataE::{Done, Get};
167
168// FIXME for all the below: deduplicate (but not sure how, without HKTs) -- and some alterations
169// have accumulated
170
171impl<H> Handler for TypeHandler<H, SerdeCBORSerialization>
172where
173    H: TypeRenderable,
174    H::Get: for<'de> serde::Serialize,
175    H::Post: for<'de> serde::Deserialize<'de>,
176    H::Put: for<'de> serde::Deserialize<'de>,
177{
178    type RequestData = TypeRequestData;
179    type ExtractRequestError = Error;
180    type BuildResponseError<M: MinimalWritableMessage> = M::UnionError;
181
182    fn extract_request_data<M: ReadableMessage>(
183        &mut self,
184        request: &M,
185    ) -> Result<Self::RequestData, Error> {
186        Ok(TypeRequestData(match request.code().into() {
187            code::DELETE => {
188                Self::check_delete_options(request)?;
189                Done(self.handler.delete())
190            }
191            code::GET => Get(Self::check_get_options(request)?),
192            code::POST => {
193                Self::check_postput_options(request)?;
194
195                // FIXME: allow getting a mutable payload here, which may be hard for general
196                // Message but usually cheap for GNRC-based.
197                let parsed: H::Post =
198                    serde_cbor::de::from_slice_with_scratch(request.payload(), &mut [])
199                        .map_err(|_| Error::bad_request())?;
200                Done(self.handler.post(&parsed))
201            }
202            code::PUT => {
203                Self::check_postput_options(request)?;
204
205                let parsed: H::Put =
206                    serde_cbor::de::from_slice_with_scratch(request.payload(), &mut [])
207                        .map_err(|_| Error::bad_request())?;
208                Done(self.handler.put(&parsed))
209            }
210            _ => Done(code::METHOD_NOT_ALLOWED),
211        }))
212    }
213
214    fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
215        match &request.0 {
216            Done(_) => 4,
217            Get(block) => (block.size() + 25).into(), // FIXME: hard-coded copied over from block2_write_with_cf's estimated overhead
218        }
219    }
220
221    fn build_response<M: MutableWritableMessage>(
222        &mut self,
223        response: &mut M,
224        request: Self::RequestData,
225    ) -> Result<(), Self::BuildResponseError<M>> {
226        match request.0 {
227            Done(r) => response.set_code(M::Code::new(r)?),
228            Get(block2) => {
229                let repr = self.handler.get();
230                match repr {
231                    Err(e) => response.set_code(M::Code::new(e)?),
232                    Ok(repr) => {
233                        response.set_code(M::Code::new(code::CONTENT)?);
234                        match block2_write_with_cf(
235                            block2,
236                            response,
237                            |win| repr.serialize(&mut serde_cbor::ser::Serializer::new(win)),
238                            SerdeCBORSerialization::CF,
239                        ) {
240                            Ok(()) => (),
241                            Err(_) => {
242                                // FIXME: Rewind all the written options
243                                response.set_code(M::Code::new(code::INTERNAL_SERVER_ERROR)?);
244                            }
245                        }
246                    }
247                }
248            }
249        };
250        Ok(())
251    }
252}
253
254trait ToTypedResourceError {
255    fn into_general_error(self, total_len: usize) -> Error;
256}
257
258macro_rules! for_each_minicbor {
259    ($minicbor:ident, $mcstype:ident) => {
260        impl<'b, C> $minicbor::decode::Decode<'b, C> for Empty {
261            fn decode(
262                _d: &mut $minicbor::decode::Decoder<'b>,
263                _ctx: &mut C,
264            ) -> Result<Self, $minicbor::decode::Error> {
265                Err($minicbor::decode::Error::message("No element expected").at(0))
266            }
267
268            fn nil() -> Option<Self> {
269                Some(Empty)
270            }
271        }
272
273        impl<H> Handler for TypeHandler<H, $mcstype>
274        where
275            H: TypeRenderable,
276            H::Get: for<'de> $minicbor::Encode<()>,
277            H::Post: for<'de> $minicbor::Decode<'de, ()>,
278            H::Put: for<'de> $minicbor::Decode<'de, ()>,
279        {
280            type RequestData = TypeRequestData;
281            type ExtractRequestError = Error;
282            type BuildResponseError<M: MinimalWritableMessage> = M::UnionError;
283
284            fn extract_request_data<M: ReadableMessage>(
285                &mut self,
286                request: &M,
287            ) -> Result<Self::RequestData, Error> {
288                Ok(TypeRequestData(match request.code().into() {
289                    code::DELETE => {
290                        Self::check_delete_options(request)?;
291                        Done(self.handler.delete())
292                    }
293                    code::GET => Get(Self::check_get_options(request)?),
294                    code::POST => {
295                        use $minicbor::decode::Decode;
296                        Self::check_postput_options(request)?;
297
298                        let payload = request.payload();
299                        match (payload, H::Post::nil()) {
300                            (b"", Some(nil)) => Done(self.handler.post(&nil)),
301                            (payload, _) => {
302                                let parsed: H::Post =
303                                    $minicbor::decode(payload).map_err(|deserialize_error| {
304                                        deserialize_error.into_general_error(payload.len())
305                                    })?;
306                                Done(self.handler.post(&parsed))
307                            }
308                        }
309                    }
310                    code::PUT => {
311                        Self::check_postput_options(request)?;
312
313                        let payload = request.payload();
314
315                        let parsed: H::Put =
316                            $minicbor::decode(payload).map_err(|deserialize_error| {
317                                deserialize_error.into_general_error(payload.len())
318                            })?;
319                        Done(self.handler.put(&parsed))
320                    }
321                    _ => Done(code::METHOD_NOT_ALLOWED),
322                }))
323            }
324
325            fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
326                match &request.0 {
327                    Done(_) => 4,
328                    Get(block) => (block.size() + 25).into(), // FIXME: hard-coded copied over from block2_write_with_cf's estimated overhead
329                }
330            }
331
332            fn build_response<M: MutableWritableMessage>(
333                &mut self,
334                response: &mut M,
335                request: Self::RequestData,
336            ) -> Result<(), Self::BuildResponseError<M>> {
337                match request.0 {
338                    Done(r) => response.set_code(M::Code::new(r)?),
339                    Get(block2) => {
340                        let repr = self.handler.get();
341                        match repr {
342                            Err(e) => response.set_code(M::Code::new(e)?),
343                            Ok(repr) => {
344                                response.set_code(M::Code::new(code::CONTENT)?);
345                                match block2_write_with_cf(
346                                    block2,
347                                    response,
348                                    |win| $minicbor::encode(&repr, win),
349                                    $mcstype::CF,
350                                ) {
351                                    Ok(()) => (),
352                                    Err(_) => {
353                                        // FIXME: Rewind all the written options
354                                        response
355                                            .set_code(M::Code::new(code::INTERNAL_SERVER_ERROR)?);
356                                    }
357                                }
358                            }
359                        }
360                    }
361                };
362                Ok(())
363            }
364        }
365
366        impl TypeSerializer for $mcstype {
367            const CF: Option<u16> = coap_numbers::content_format::from_str("application/cbor");
368        }
369    };
370}
371
372for_each_minicbor!(minicbor_0_19, MiniCBORSerialization0_19);
373for_each_minicbor!(minicbor_0_24, MiniCBORSerialization0_24);
374for_each_minicbor!(minicbor_0_26, MiniCBORSerialization0_26);
375
376impl<H> TypeHandler<H, MiniCBORSerialization0_19>
377where
378    H: TypeRenderable,
379    H::Get: for<'de> minicbor_0_19::Encode<()>,
380    H::Post: for<'de> minicbor_0_19::Decode<'de, ()>,
381    H::Put: for<'de> minicbor_0_19::Decode<'de, ()>,
382{
383    /// Wrap a handler through minicbor 0.19
384    pub fn new_minicbor(handler: H) -> Self {
385        TypeHandler {
386            handler,
387            _phantom: PhantomData,
388        }
389    }
390}
391
392impl<H> TypeHandler<H, MiniCBORSerialization0_24>
393where
394    H: TypeRenderable,
395    H::Get: for<'de> minicbor_0_24::Encode<()>,
396    H::Post: for<'de> minicbor_0_24::Decode<'de, ()>,
397    H::Put: for<'de> minicbor_0_24::Decode<'de, ()>,
398{
399    /// Wrap a handler through minicbor 0.24
400    pub fn new_minicbor_0_24(handler: H) -> Self {
401        TypeHandler {
402            handler,
403            _phantom: PhantomData,
404        }
405    }
406}
407
408impl<H> TypeHandler<H, MiniCBORSerialization0_26>
409where
410    H: TypeRenderable,
411    H::Get: for<'de> minicbor_0_24::Encode<()>,
412    H::Post: for<'de> minicbor_0_24::Decode<'de, ()>,
413    H::Put: for<'de> minicbor_0_24::Decode<'de, ()>,
414{
415    /// Wrap a handler through minicbor 0.26
416    pub fn new_minicbor_0_26(handler: H) -> Self {
417        TypeHandler {
418            handler,
419            _phantom: PhantomData,
420        }
421    }
422}
423
424impl ToTypedResourceError for minicbor_0_19::decode::Error {
425    fn into_general_error(self, total_len: usize) -> Error {
426        let mut error = Error::bad_request();
427        // This version predates https://gitlab.com/twittner/minicbor/-/merge_requests/47
428        if self.is_end_of_input() {
429            error = Error::bad_request_with_rbep(total_len);
430        };
431        if self.is_type_mismatch() {
432            error = error.with_title("Type mismatch")
433        }
434        error
435    }
436}
437
438macro_rules! minicbor_24plus_error {
439    ($minicbor:ident) => {
440        impl ToTypedResourceError for $minicbor::decode::Error {
441            fn into_general_error(self, total_len: usize) -> Error {
442                let mut error = Error::bad_request();
443                if let Some(position) = self.position() {
444                    error = Error::bad_request_with_rbep(position);
445                }
446                if self.is_end_of_input() {
447                    // Data is not set, but it's convenient to point that out in the error response just in
448                    // case something goes wrong with block-wise transfers
449                    error = Error::bad_request_with_rbep(total_len);
450                };
451                if self.is_type_mismatch() {
452                    error = error.with_title("Type mismatch")
453                }
454                error
455            }
456        }
457    };
458}
459
460minicbor_24plus_error!(minicbor_0_24);
461minicbor_24plus_error!(minicbor_0_26);