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