coap_handler_implementations/
wkc_implementation.rs

1//! Implementation of a /.well-known/core resource
2//!
3//! This is a private module to retain flexibilty around the implementation (even though all types
4//! need to be public as they are associated types).
5
6use crate::wkc::write_link_format;
7use coap_handler::{Handler, Reporting};
8use coap_message::{
9    Code as _, MessageOption, MinimalWritableMessage, MutableWritableMessage, ReadableMessage,
10    error::RenderableOnMinimal,
11};
12use coap_message_utils::{OptionsExt, option_value::Block2RequestData};
13use core::fmt::Debug;
14
15const LINK_FORMAT: u16 = 40;
16
17/// Wrapper around a reporting handler that makes all reported resources discoverable at the path
18/// `/.well-known/core`.
19///
20/// This can be constructed via [crate::ReportingHandlerBuilder::with_wkc()], typically after
21/// having gathered all resource in one handler.
22pub struct WellKnownCore<H: Reporting + Handler>(H);
23
24impl<H: Reporting + Handler> WellKnownCore<H> {
25    pub(crate) fn new(handler: H) -> Self {
26        WellKnownCore(handler)
27    }
28}
29
30pub enum WkcData<T> {
31    Wkc {
32        // easiest error indication...
33        code: u8,
34        // to be extended later to queries, content format and more
35        block2: Block2RequestData,
36    },
37    Other(T),
38}
39
40/// Error indicating that something went wrong rendering the response to a [WellKnownCore] handler
41/// -- either in then handler itself, or in the tree it was set on top of.
42#[derive(Debug)]
43pub enum WkcBuildResponseError<IE: RenderableOnMinimal + Debug, HBRE: RenderableOnMinimal> {
44    Wkc(IE),
45    Other(HBRE),
46}
47
48impl<IE: RenderableOnMinimal + Debug, HBRE: RenderableOnMinimal + Debug> RenderableOnMinimal
49    for WkcBuildResponseError<IE, HBRE>
50{
51    type Error<IE2: RenderableOnMinimal + Debug> = WkcBuildResponseError<
52        <IE as RenderableOnMinimal>::Error<IE2>,
53        <HBRE as RenderableOnMinimal>::Error<IE2>,
54    >;
55    fn render<M: MinimalWritableMessage>(
56        self,
57        message: &mut M,
58    ) -> Result<(), Self::Error<M::UnionError>> {
59        match self {
60            WkcBuildResponseError::Wkc(e) => e.render(message).map_err(WkcBuildResponseError::Wkc),
61            WkcBuildResponseError::Other(e) => {
62                e.render(message).map_err(WkcBuildResponseError::Other)
63            }
64        }
65    }
66}
67
68impl<H: Reporting + Handler> Handler for WellKnownCore<H> {
69    type RequestData = WkcData<H::RequestData>;
70    type ExtractRequestError = H::ExtractRequestError;
71    type BuildResponseError<M: MinimalWritableMessage> =
72        WkcBuildResponseError<M::UnionError, H::BuildResponseError<M>>;
73
74    fn extract_request_data<M: ReadableMessage>(
75        &mut self,
76        req: &M,
77    ) -> Result<Self::RequestData, Self::ExtractRequestError> {
78        let mut block2 = None;
79
80        let mut pathmatch = 0;
81
82        let mut accept: Option<u16> = None;
83
84        let opts = req
85            .options()
86            .ignore_uri_host()
87            .filter(|o| match o.number() {
88                coap_numbers::option::ACCEPT if accept.is_none() => {
89                    accept = o.value_uint();
90                    // leave option around to fail later if conversion failed
91                    accept.is_none()
92                }
93                _ => true,
94            })
95            // Because the resource definition of RFC6690 explicitly allows ignoring these arguments
96            .ignore_uri_query()
97            .take_block2(&mut block2)
98            // Don't take this as template, this is quick & dirty
99            .take_uri_path(|p| {
100                pathmatch = match (pathmatch, p) {
101                    (0, ".well-known") => 1,
102                    (1, "core") => 2,
103                    _ => -1,
104                }
105            });
106
107        let remaining = opts.ignore_elective_others();
108
109        let block2 = block2.unwrap_or_default();
110
111        if pathmatch == 2 {
112            let code;
113            if remaining.is_err() {
114                code = coap_numbers::code::BAD_OPTION;
115            } else if accept.unwrap_or(LINK_FORMAT) != LINK_FORMAT {
116                code = coap_numbers::code::NOT_ACCEPTABLE;
117            } else {
118                code = coap_numbers::code::CONTENT;
119            }
120            Ok(WkcData::Wkc { code, block2 })
121        } else {
122            Ok(WkcData::Other(self.0.extract_request_data(req)?))
123        }
124    }
125    fn estimate_length(&mut self, req: &Self::RequestData) -> usize {
126        match req {
127            // FIXME precision, consider requester block size
128            WkcData::Wkc { .. } => 1024,
129            WkcData::Other(req) => self.0.estimate_length(req),
130        }
131    }
132    fn build_response<M: MutableWritableMessage>(
133        &mut self,
134        m: &mut M,
135        req: Self::RequestData,
136    ) -> Result<(), Self::BuildResponseError<M>> {
137        match req {
138            // FIXME extend own BuildResponseError type to also allow raising from here
139            WkcData::Wkc { code, block2 } => {
140                m.set_code(M::Code::new(code).map_err(|e| WkcBuildResponseError::Wkc(e.into()))?);
141                if code == coap_numbers::code::CONTENT {
142                    crate::helpers::block2_write_with_cf(
143                        block2,
144                        m,
145                        |w| {
146                            write_link_format(w, &self.0, &[]).expect("Block writers do not err.");
147                        },
148                        Some(40),
149                    );
150                } else {
151                    m.truncate(0)
152                        .map_err(|e| WkcBuildResponseError::Wkc(e.into()))?;
153                }
154                Ok(())
155            }
156            WkcData::Other(req) => self
157                .0
158                .build_response(m, req)
159                .map_err(WkcBuildResponseError::Other),
160        }
161    }
162}
163
164/// For reporting, a [WellKnownCore] handler simply passes on its inner description; the exposed
165/// .well-known/core resource is not listed.
166///
167/// Utilizing this (when in having a WellKnownCore handler around the resources, then adding more
168/// resources using [`HandlerBuilder::at()`](crate::HandlerBuilder::at()), and wrapping the result in a WellKnownCore again) is
169/// wasteful in terms of computation (as the inner one will never be hit), but can be convenient
170/// for composition in examples.
171impl<H: Reporting + Handler> Reporting for WellKnownCore<H> {
172    type Record<'res>
173        = H::Record<'res>
174    where
175        H: 'res;
176    type Reporter<'res>
177        = H::Reporter<'res>
178    where
179        H: 'res;
180
181    fn report(&self) -> Self::Reporter<'_> {
182        self.0.report()
183    }
184}