libcoap_rs/
resource.rs

1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright © The libcoap-rs Contributors, all rights reserved.
4 * This file is part of the libcoap-rs project, see the README file for
5 * general information on this project and the NOTICE.md and LICENSE files
6 * for information regarding copyright ownership and terms of use.
7 *
8 * resource.rs - Types relating to CoAP resource management.
9 */
10
11//! Resource and resource handler descriptions
12
13use core::ffi::c_int;
14use std::{
15    any::Any,
16    cell::{Ref, RefMut},
17    fmt::{Debug, Formatter},
18    marker::PhantomData,
19};
20
21use libcoap_sys::{
22    coap_delete_resource, coap_new_str_const, coap_pdu_t, coap_register_request_handler, coap_resource_get_uri_path,
23    coap_resource_get_userdata, coap_resource_init, coap_resource_notify_observers, coap_resource_set_get_observable,
24    coap_resource_set_mode, coap_resource_set_userdata, coap_resource_t, coap_send_rst, coap_session_t, coap_string_t,
25    COAP_RESOURCE_FLAGS_NOTIFY_CON, COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_RELEASE_URI,
26};
27
28use crate::{
29    context::ensure_coap_started,
30    error::MessageConversionError,
31    mem::{CoapFfiRcCell, DropInnerExclusively},
32    message::{request::CoapRequest, response::CoapResponse, CoapMessage, CoapMessageCommon},
33    protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode},
34    session::{CoapServerSession, CoapSessionCommon},
35};
36
37// Trait aliases are experimental
38//trait CoapMethodHandlerFn<D> = FnMut(&D, &mut CoapSession, &CoapRequestMessage, &mut CoapResponseMessage);
39
40// Some macro wizardry to statically wrap request handlers.
41/// Create a CoapRequestHandler using the provided function.
42///
43/// This macro cannot be used if the intended handler function does not have a 'static lifetime,
44/// i.e. if the handler function is a closure.
45/// In these cases, use [CoapRequestHandler::new()] instead.
46#[macro_export]
47macro_rules! resource_handler {
48    ($f:ident, $t:path) => {{
49        #[allow(clippy::unnecessary_mut_passed)] // We don't know whether the function needs a mutable reference or not.
50        unsafe extern "C" fn _coap_method_handler_wrapper<D: Any + ?Sized + Debug>(
51            resource: *mut coap_resource_t,
52            session: *mut coap_session_t,
53            incoming_pdu: *const coap_pdu_t,
54            query: *const coap_string_t,
55            response_pdu: *mut coap_pdu_t,
56        ) {
57            let handler_data =
58                prepare_resource_handler_data::<$t>(resource, session, incoming_pdu, query, response_pdu);
59            if let Ok((mut resource, mut session, incoming_pdu, outgoing_pdu)) = handler_data {
60                ($f::<D>)(&mut resource, &mut session, &incoming_pdu, outgoing_pdu)
61            }
62        }
63        unsafe { CoapRequestHandler::<$t>::from_raw_handler(_coap_method_handler_wrapper::<$t>) }
64    }};
65}
66
67/// Converts the raw parameters provided to a request handler into the appropriate wrapped types.
68///
69/// If an error occurs while parsing the resource data, this function will send an RST message to the
70/// client and return a [MessageConversionError].
71///
72/// This function is not intended for public use, the only reason it is public is that the
73/// [resource_handler!] macro requires this function.
74///
75/// # Safety
76/// The provided pointers must all be valid and point to the appropriate data structures.
77#[inline]
78#[doc(hidden)]
79pub unsafe fn prepare_resource_handler_data<'a, D: Any + ?Sized + Debug>(
80    raw_resource: *mut coap_resource_t,
81    raw_session: *mut coap_session_t,
82    raw_incoming_pdu: *const coap_pdu_t,
83    _raw_query: *const coap_string_t,
84    raw_response_pdu: *mut coap_pdu_t,
85) -> Result<(CoapResource<D>, CoapServerSession<'a>, CoapRequest, CoapResponse), MessageConversionError> {
86    let resource_tmp = CoapFfiRcCell::clone_raw_weak(coap_resource_get_userdata(raw_resource));
87    let resource = CoapResource::from(resource_tmp);
88    let session = CoapServerSession::from_raw(raw_session);
89    let request = CoapMessage::from_raw_pdu(raw_incoming_pdu).and_then(|v| CoapRequest::from_message(v, &session));
90    let response = CoapMessage::from_raw_pdu(raw_response_pdu).and_then(CoapResponse::from_message);
91    match (request, response) {
92        (Ok(request), Ok(response)) => Ok((resource, session, request, response)),
93        (v1, v2) => {
94            coap_send_rst(raw_session, raw_incoming_pdu);
95            Err(v1.and(v2).err().unwrap())
96        },
97    }
98}
99
100/// Trait with functions relating to [CoapResource]s with an unknown data type.
101pub trait UntypedCoapResource: Any + Debug {
102    /// Returns the uri_path this resource responds to.
103    fn uri_path(&self) -> &str;
104    /// Provides a reference to this resource as an [Any] trait object.
105    ///
106    /// You can use the resulting [Any] reference to downcast the resource to its appropriate
107    /// concrete type (if you wish to e.g. change the application data).
108    ///
109    /// If you use unstable Rust, you can use trait upcasting instead (`[value] as Any`).
110    fn as_any(&self) -> &dyn Any;
111    /// Attempts to regain exclusive ownership of the inner resource in order to drop it.
112    ///
113    /// This function is used by the [CoapContext](crate::context::CoapContext) on cleanup to
114    /// reclaim resources before dropping the context itself. *You should not use this function*.
115    ///
116    /// # Panics
117    /// Panics if the inner resource instance associated with this resource cannot be exclusively
118    /// dropped, i.e. because the underlying [Rc] is used elsewhere.
119    #[doc(hidden)]
120    fn drop_inner_exclusive(self: Box<Self>);
121    /// Returns the raw resource associated with this CoapResource.
122    ///
123    /// # Safety
124    /// You must not do anything with this resource that could interfere with this instance.
125    /// Most notably, you must not...
126    /// - ...free the returned value using [coap_delete_resource](libcoap_sys::coap_delete_resource)
127    /// - ...associate the raw resource with a CoAP context, because if the context is dropped, so
128    ///   will the resource.
129    /// - ...modify the application-specific data.
130    unsafe fn raw_resource(&mut self) -> *mut coap_resource_t;
131}
132
133/// Representation of a CoapResource that can be requested from a server.
134#[derive(Debug)]
135pub struct CoapResource<D: Any + ?Sized + Debug> {
136    inner: CoapFfiRcCell<CoapResourceInner<D>>,
137}
138
139/// Container for resource handlers for various CoAP methods.
140#[derive(Debug)]
141struct CoapResourceHandlers<D: Any + ?Sized + Debug> {
142    get: Option<CoapRequestHandler<D>>,
143    put: Option<CoapRequestHandler<D>>,
144    delete: Option<CoapRequestHandler<D>>,
145    post: Option<CoapRequestHandler<D>>,
146    fetch: Option<CoapRequestHandler<D>>,
147    ipatch: Option<CoapRequestHandler<D>>,
148    patch: Option<CoapRequestHandler<D>>,
149}
150
151impl<D: Any + ?Sized + Debug> Default for CoapResourceHandlers<D> {
152    fn default() -> Self {
153        CoapResourceHandlers {
154            get: None,
155            put: None,
156            delete: None,
157            post: None,
158            fetch: None,
159            ipatch: None,
160            patch: None,
161        }
162    }
163}
164
165impl<D: Any + ?Sized + Debug> CoapResourceHandlers<D> {
166    #[inline]
167    fn handler(&self, code: CoapRequestCode) -> Option<&CoapRequestHandler<D>> {
168        match code {
169            CoapRequestCode::Get => self.get.as_ref(),
170            CoapRequestCode::Put => self.put.as_ref(),
171            CoapRequestCode::Delete => self.delete.as_ref(),
172            CoapRequestCode::Post => self.post.as_ref(),
173            CoapRequestCode::Fetch => self.fetch.as_ref(),
174            CoapRequestCode::IPatch => self.ipatch.as_ref(),
175            CoapRequestCode::Patch => self.patch.as_ref(),
176        }
177    }
178
179    #[inline]
180    // Clippy complains about this being unused, but I'd like to keep it for consistency.
181    #[allow(unused)]
182    fn handler_mut(&mut self, code: CoapRequestCode) -> Option<&mut CoapRequestHandler<D>> {
183        match code {
184            CoapRequestCode::Get => self.get.as_mut(),
185            CoapRequestCode::Put => self.put.as_mut(),
186            CoapRequestCode::Delete => self.delete.as_mut(),
187            CoapRequestCode::Post => self.post.as_mut(),
188            CoapRequestCode::Fetch => self.fetch.as_mut(),
189            CoapRequestCode::IPatch => self.ipatch.as_mut(),
190            CoapRequestCode::Patch => self.patch.as_mut(),
191        }
192    }
193
194    // Kept for consistency
195    #[allow(unused)]
196    #[inline]
197    fn handler_ref(&self, code: CoapRequestCode) -> &Option<CoapRequestHandler<D>> {
198        match code {
199            CoapRequestCode::Get => &self.get,
200            CoapRequestCode::Put => &self.put,
201            CoapRequestCode::Delete => &self.delete,
202            CoapRequestCode::Post => &self.post,
203            CoapRequestCode::Fetch => &self.fetch,
204            CoapRequestCode::IPatch => &self.ipatch,
205            CoapRequestCode::Patch => &self.patch,
206        }
207    }
208
209    #[inline]
210    fn handler_ref_mut(&mut self, code: CoapRequestCode) -> &mut Option<CoapRequestHandler<D>> {
211        match code {
212            CoapRequestCode::Get => &mut self.get,
213            CoapRequestCode::Put => &mut self.put,
214            CoapRequestCode::Delete => &mut self.delete,
215            CoapRequestCode::Post => &mut self.post,
216            CoapRequestCode::Fetch => &mut self.fetch,
217            CoapRequestCode::IPatch => &mut self.ipatch,
218            CoapRequestCode::Patch => &mut self.patch,
219        }
220    }
221}
222
223/// Inner part of a [CoapResource], which is referenced inside the raw resource and might be
224/// referenced multiple times, e.g. outside and inside of a resource handler.
225#[derive(Debug)]
226pub(crate) struct CoapResourceInner<D: Any + ?Sized + Debug> {
227    raw_resource: *mut coap_resource_t,
228    user_data: Box<D>,
229    handlers: CoapResourceHandlers<D>,
230}
231
232impl<D: Any + ?Sized + Debug> CoapResource<D> {
233    /// Creates a new CoapResource for the given `uri_path`.
234    ///
235    /// Handlers that are associated with this resource have to be able to take a reference to the
236    /// provided `user_data` value as their first value.
237    ///
238    /// The `notify_con` parameter specifies whether observe notifications originating from this
239    /// resource are sent as confirmable or non-confirmable.
240    pub fn new<C: Into<Box<D>>>(uri_path: &str, user_data: C, notify_con: bool) -> CoapResource<D> {
241        ensure_coap_started();
242        let inner = unsafe {
243            let uri_path = coap_new_str_const(uri_path.as_ptr(), uri_path.len());
244            let raw_resource = coap_resource_init(
245                uri_path,
246                (COAP_RESOURCE_FLAGS_RELEASE_URI
247                    | if notify_con {
248                        COAP_RESOURCE_FLAGS_NOTIFY_CON
249                    } else {
250                        COAP_RESOURCE_FLAGS_NOTIFY_NON
251                    }) as i32,
252            );
253            let inner = CoapFfiRcCell::new(CoapResourceInner {
254                raw_resource,
255                user_data: user_data.into(),
256                handlers: CoapResourceHandlers::default(),
257            });
258            coap_resource_set_userdata(raw_resource, inner.create_raw_weak());
259            inner
260        };
261        Self::from(inner)
262    }
263
264    /// Notify any observers about changes to this resource.
265    pub fn notify_observers(&self) -> bool {
266        // SAFETY: Resource is valid as long as CoapResourceInner exists, query is currently unused.
267        unsafe { coap_resource_notify_observers(self.inner.borrow_mut().raw_resource, std::ptr::null_mut()) != 0 }
268    }
269
270    /// Sets whether this resource can be observed by clients according to
271    /// [RFC 7641](https://datatracker.ietf.org/doc/html/rfc7641).
272    pub fn set_get_observable(&self, observable: bool) {
273        // SAFETY: Resource is valid as long as CoapResourceInner exists, query is currently unused.
274        unsafe { coap_resource_set_get_observable(self.inner.borrow_mut().raw_resource, observable as c_int) }
275    }
276
277    /// Sets whether observe notifications for this resource should be sent as confirmable or
278    /// non-confirmable CoAP messages.
279    pub fn set_observe_notify_confirmable(&self, confirmable: bool) {
280        // SAFETY: Resource is valid as long as CoapResourceInner exists, query is currently unused.
281        unsafe { coap_resource_set_mode(self.inner.borrow_mut().raw_resource, confirmable as c_int) }
282    }
283
284    /// Returns the user data associated with this resource.
285    pub fn user_data(&self) -> Ref<D> {
286        Ref::map(self.inner.borrow(), |v| v.user_data.as_ref())
287    }
288
289    /// Mutably returns the user data associated with this resource.
290    pub fn user_data_mut(&self) -> RefMut<D> {
291        RefMut::map(self.inner.borrow_mut(), |v| v.user_data.as_mut())
292    }
293
294    /// Restores a resource from its raw [coap_resource_t](libcoap_sys::coap_resource_t).
295    ///
296    /// # Safety
297    /// The supplied pointer must point to a valid [coap_resource_t](libcoap_sys::coap_resource_t)
298    /// instance that has a `Rc<RefCell<CoapResourceInner<D>>>` as its user data.
299    pub unsafe fn restore_from_raw(raw_resource: *mut coap_resource_t) -> CoapResource<D> {
300        let resource_tmp = CoapFfiRcCell::clone_raw_weak(coap_resource_get_userdata(raw_resource));
301        CoapResource::from(resource_tmp)
302    }
303
304    /// Sets the handler function for a given method code.
305    pub fn set_method_handler<H: Into<CoapRequestHandler<D>>>(&self, code: CoapRequestCode, handler: Option<H>) {
306        let mut inner = self.inner.borrow_mut();
307        *inner.handlers.handler_ref_mut(code) = handler.map(|v| v.into());
308        unsafe {
309            coap_register_request_handler(
310                inner.raw_resource,
311                code.to_raw_request(),
312                inner.handlers.handler(code).map(|h| h.raw_handler),
313            );
314        }
315    }
316
317    fn call_dynamic_handler(
318        &self,
319        session: &mut CoapServerSession,
320        req_message: &CoapRequest,
321        mut rsp_message: CoapResponse,
322    ) {
323        let mut inner = self.inner.borrow_mut();
324        let req_code = match req_message.code() {
325            CoapMessageCode::Request(req_code) => req_code,
326            _ => {
327                rsp_message.set_type_(CoapMessageType::Rst);
328                // TODO some better error handling
329                session.send(rsp_message).expect("error while sending RST packet");
330                return;
331            },
332        };
333
334        // Take handler function out of resource handler so that we no longer need the inner borrow
335        // (otherwise, we couldn't call any resource functions in the handler).
336        let mut handler_fn = inner
337            .handlers
338            .handler_ref_mut(req_code)
339            .take()
340            .expect("attempted to call dynamic handler for method that has no handler set");
341        std::mem::drop(inner);
342
343        (handler_fn
344            .dynamic_handler_function
345            .as_mut()
346            .expect("attempted to call dynamic handler for method that has no dynamic handler set"))(
347            self,
348            session,
349            req_message,
350            rsp_message,
351        );
352
353        // Put the handler function back into the resource, unless the handler was replaced.
354        self.inner
355            .borrow_mut()
356            .handlers
357            .handler_ref_mut(req_code)
358            .get_or_insert(handler_fn);
359    }
360}
361
362impl<D: Any + ?Sized + Debug> UntypedCoapResource for CoapResource<D> {
363    fn uri_path(&self) -> &str {
364        unsafe {
365            let raw_path = coap_resource_get_uri_path(self.inner.borrow().raw_resource);
366            return std::str::from_utf8_unchecked(std::slice::from_raw_parts((*raw_path).s, (*raw_path).length));
367        }
368    }
369
370    fn as_any(&self) -> &dyn Any {
371        self as &(dyn Any)
372    }
373
374    fn drop_inner_exclusive(self: Box<Self>) {
375        self.inner.drop_exclusively();
376    }
377
378    unsafe fn raw_resource(&mut self) -> *mut coap_resource_t {
379        self.inner.borrow_mut().raw_resource
380    }
381}
382
383#[doc(hidden)]
384impl<D: Any + ?Sized + Debug> From<CoapFfiRcCell<CoapResourceInner<D>>> for CoapResource<D> {
385    fn from(raw_cell: CoapFfiRcCell<CoapResourceInner<D>>) -> Self {
386        CoapResource { inner: raw_cell }
387    }
388}
389
390impl<D: Any + ?Sized + Debug> Drop for CoapResourceInner<D> {
391    fn drop(&mut self) {
392        // SAFETY: We set the user data on creation of the inner resource, so it cannot be invalid.
393        std::mem::drop(unsafe {
394            CoapFfiRcCell::<CoapResourceInner<D>>::raw_ptr_to_weak(coap_resource_get_userdata(self.raw_resource))
395        });
396        // SAFETY: First argument is ignored, second argument is guaranteed to exist while the inner
397        // resource exists.
398        unsafe { coap_delete_resource(std::ptr::null_mut(), self.raw_resource) };
399    }
400}
401
402/// A handler for CoAP requests on a resource.
403///
404/// This handler can be associated with a [CoapResource] in order to be called when a request for
405/// the associated resource and the provided method arrives. The handler is then able to generate
406/// and send a response to the request accordingly.
407///
408/// # Creating a CoapRequestHandler
409/// There are multiple ways to create a [CoapRequestHandler]:
410/// - Using the [resource_handler!] macro: Preferred for handlers with a static lifetime (i.e.,
411///   function pointers, not closures).
412/// - Using [CoapRequestHandler::new]: Preferred for closures if you don't need access to the
413///   [CoapResource] itself (but can be used for function pointers as well).
414/// - Using [CoapRequestHandler::new_resource_ref]: Preferred for closures if you need access to
415///   the [CoapResource] itself (but can be used for function pointers as well).
416///
417/// For method 2, the provided handler has to be a `FnMut(&mut D, &mut CoapServerSession, &CoapRequest, CoapResponse)`,
418/// while for the other two methods, the handler has to be a `FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)`,
419/// with the following arguments:
420/// - Either the associated [CoapResource] or the user data depending on the type of handler.
421///   Getting the user data directly without the associated resource has the advantage that it is
422///   easy to pass a method as a handler, while getting the [CoapResource] gives you the option to
423///   manipulate the resource (you can still get the user data from the resource using
424///   [CoapResource::user_data].
425/// - The server-side session with the peer this request was received from. You may want to store or
426///   retrieve additional information about the peer using [CoapSessionCommon::set_app_data()] and
427///   [CoapSessionCommon::app_data()].
428/// - The incoming [CoapRequest] received from the client.
429/// - A prepared [CoapResponse] instance that is already set to the correct token value to be
430///   treated as a response to the request by the client.
431// We'll allow the complex type as trait aliases are experimental and we'll probably want to use
432// those instead of aliasing the entire type including wrappers.
433#[allow(clippy::type_complexity)]
434pub struct CoapRequestHandler<D: Any + ?Sized + Debug> {
435    raw_handler: unsafe extern "C" fn(
436        resource: *mut coap_resource_t,
437        session: *mut coap_session_t,
438        incoming_pdu: *const coap_pdu_t,
439        query: *const coap_string_t,
440        response_pdu: *mut coap_pdu_t,
441    ),
442    dynamic_handler_function:
443        Option<Box<dyn FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)>>,
444    __handler_data_type: PhantomData<D>,
445}
446
447impl<D: 'static + ?Sized + Debug> CoapRequestHandler<D> {
448    /// Creates a new CoapResourceHandler with the given function as the handler function to call.
449    pub fn new<F: 'static + FnMut(&mut D, &mut CoapServerSession, &CoapRequest, CoapResponse)>(
450        mut handler: F,
451    ) -> CoapRequestHandler<D> {
452        CoapRequestHandler::new_resource_ref(move |resource, session, request, response| {
453            handler(&mut *resource.user_data_mut(), session, request, response)
454        })
455    }
456
457    /// Creates a new CoapResourceHandler with the given function as the handler function to call.
458    ///
459    /// In contrast to [CoapRequestHandler::new], the handler for this function is not provided with
460    /// a direct reference to the user data, but instead with a reference to the associated
461    /// `CoapResource`. This way, you can perform actions on the resource directly (e.g., notify
462    /// observers).
463    pub fn new_resource_ref<
464        F: 'static + FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse),
465    >(
466        handler: F,
467    ) -> CoapRequestHandler<D> {
468        let mut wrapped_handler = resource_handler!(coap_resource_handler_dynamic_wrapper, D);
469        wrapped_handler.dynamic_handler_function = Some(Box::new(handler));
470        wrapped_handler
471    }
472
473    /// Creates a new request handler using the given raw handler function.
474    ///
475    /// The handler function provided here is called directly by libcoap.
476    ///
477    /// # Safety
478    /// The handler function must not modify the user data value inside of the provided raw resource
479    /// in a way that would break normal handler functions. Also, neither the resource nor the
480    /// session may be freed by calling `coap_delete_resource` or `coap_session_release`.
481    // We'll allow the complex type as trait aliases are experimental and we'll probably want to use
482    // those instead of aliasing the entire type including wrappers.
483    #[allow(clippy::type_complexity)]
484    pub unsafe fn from_raw_handler(
485        raw_handler: unsafe extern "C" fn(
486            resource: *mut coap_resource_t,
487            session: *mut coap_session_t,
488            incoming_pdu: *const coap_pdu_t,
489            query: *const coap_string_t,
490            response_pdu: *mut coap_pdu_t,
491        ),
492    ) -> CoapRequestHandler<D> {
493        ensure_coap_started();
494        let handler_fn: Option<Box<dyn FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)>> =
495            None;
496        CoapRequestHandler {
497            raw_handler,
498            dynamic_handler_function: handler_fn,
499            __handler_data_type: PhantomData,
500        }
501    }
502}
503
504impl<D: 'static + ?Sized + Debug> Debug for CoapRequestHandler<D> {
505    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
506        f.debug_struct("CoapRequestHandler").finish()
507    }
508}
509
510fn coap_resource_handler_dynamic_wrapper<D: Any + ?Sized + Debug>(
511    resource: &CoapResource<D>,
512    session: &mut CoapServerSession,
513    req_message: &CoapRequest,
514    rsp_message: CoapResponse,
515) {
516    resource.call_dynamic_handler(session, req_message, rsp_message);
517}