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