libcoap_rs/
resource.rs

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