use std::{
any::Any,
cell::Ref,
cell::RefMut,
fmt::{Debug, Formatter},
marker::PhantomData,
};
use libc::c_int;
use libcoap_sys::{
coap_delete_resource, coap_new_str_const, coap_pdu_t, coap_pdu_type_t::COAP_MESSAGE_RST,
coap_register_request_handler, coap_resource_get_uri_path, coap_resource_get_userdata, coap_resource_init,
coap_resource_notify_observers, coap_resource_set_get_observable, coap_resource_set_mode,
coap_resource_set_userdata, coap_resource_t, coap_send_rst, coap_session_t, coap_string_t,
COAP_RESOURCE_FLAGS_NOTIFY_CON, COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_RELEASE_URI,
};
use crate::mem::{CoapFfiRcCell, DropInnerExclusively};
use crate::message::request::CoapRequest;
use crate::message::response::CoapResponse;
use crate::message::CoapMessageCommon;
use crate::protocol::CoapMessageCode;
use crate::protocol::CoapMessageType;
use crate::session::CoapServerSession;
use crate::session::CoapSessionCommon;
use crate::{error::MessageConversionError, message::CoapMessage, protocol::CoapRequestCode};
#[macro_export]
macro_rules! resource_handler {
($f:ident, $t:path) => {{
#[allow(clippy::unnecessary_mut_passed)] unsafe extern "C" fn _coap_method_handler_wrapper<D: Any + ?Sized + Debug>(
resource: *mut coap_resource_t,
session: *mut coap_session_t,
incoming_pdu: *const coap_pdu_t,
query: *const coap_string_t,
response_pdu: *mut coap_pdu_t,
) {
let handler_data =
prepare_resource_handler_data::<$t>(resource, session, incoming_pdu, query, response_pdu);
if let Ok((mut resource, mut session, incoming_pdu, outgoing_pdu)) = handler_data {
($f::<D>)(&mut resource, &mut session, &incoming_pdu, outgoing_pdu)
}
}
unsafe { CoapRequestHandler::<$t>::from_raw_handler(_coap_method_handler_wrapper::<$t>) }
}};
}
#[inline]
#[doc(hidden)]
pub unsafe fn prepare_resource_handler_data<'a, D: Any + ?Sized + Debug>(
raw_resource: *mut coap_resource_t,
raw_session: *mut coap_session_t,
raw_incoming_pdu: *const coap_pdu_t,
_raw_query: *const coap_string_t,
raw_response_pdu: *mut coap_pdu_t,
) -> Result<(CoapResource<D>, CoapServerSession<'a>, CoapRequest, CoapResponse), MessageConversionError> {
let resource_tmp = CoapFfiRcCell::clone_raw_weak(coap_resource_get_userdata(raw_resource));
let resource = CoapResource::from(resource_tmp);
let session = CoapServerSession::from_raw(raw_session);
let request = CoapMessage::from_raw_pdu(raw_incoming_pdu).and_then(CoapRequest::from_message);
let response = CoapMessage::from_raw_pdu(raw_response_pdu).and_then(CoapResponse::from_message);
match (request, response) {
(Ok(request), Ok(response)) => Ok((resource, session, request, response)),
(v1, v2) => {
coap_send_rst(raw_session, raw_incoming_pdu, COAP_MESSAGE_RST);
Err(v1.and(v2).err().unwrap())
},
}
}
pub trait UntypedCoapResource: Any + Debug {
fn uri_path(&self) -> &str;
fn as_any(&self) -> &dyn Any;
#[doc(hidden)]
fn drop_inner_exclusive(self: Box<Self>);
unsafe fn raw_resource(&mut self) -> *mut coap_resource_t;
}
#[derive(Debug)]
pub struct CoapResource<D: Any + ?Sized + Debug> {
inner: CoapFfiRcCell<CoapResourceInner<D>>,
}
#[derive(Debug)]
struct CoapResourceHandlers<D: Any + ?Sized + Debug> {
get: Option<CoapRequestHandler<D>>,
put: Option<CoapRequestHandler<D>>,
delete: Option<CoapRequestHandler<D>>,
post: Option<CoapRequestHandler<D>>,
fetch: Option<CoapRequestHandler<D>>,
ipatch: Option<CoapRequestHandler<D>>,
patch: Option<CoapRequestHandler<D>>,
}
impl<D: Any + ?Sized + Debug> Default for CoapResourceHandlers<D> {
fn default() -> Self {
CoapResourceHandlers {
get: None,
put: None,
delete: None,
post: None,
fetch: None,
ipatch: None,
patch: None,
}
}
}
impl<D: Any + ?Sized + Debug> CoapResourceHandlers<D> {
#[inline]
fn handler(&self, code: CoapRequestCode) -> Option<&CoapRequestHandler<D>> {
match code {
CoapRequestCode::Get => self.get.as_ref(),
CoapRequestCode::Put => self.put.as_ref(),
CoapRequestCode::Delete => self.delete.as_ref(),
CoapRequestCode::Post => self.post.as_ref(),
CoapRequestCode::Fetch => self.fetch.as_ref(),
CoapRequestCode::IPatch => self.ipatch.as_ref(),
CoapRequestCode::Patch => self.patch.as_ref(),
}
}
#[inline]
#[allow(unused)]
fn handler_mut(&mut self, code: CoapRequestCode) -> Option<&mut CoapRequestHandler<D>> {
match code {
CoapRequestCode::Get => self.get.as_mut(),
CoapRequestCode::Put => self.put.as_mut(),
CoapRequestCode::Delete => self.delete.as_mut(),
CoapRequestCode::Post => self.post.as_mut(),
CoapRequestCode::Fetch => self.fetch.as_mut(),
CoapRequestCode::IPatch => self.ipatch.as_mut(),
CoapRequestCode::Patch => self.patch.as_mut(),
}
}
#[allow(unused)]
#[inline]
fn handler_ref(&self, code: CoapRequestCode) -> &Option<CoapRequestHandler<D>> {
match code {
CoapRequestCode::Get => &self.get,
CoapRequestCode::Put => &self.put,
CoapRequestCode::Delete => &self.delete,
CoapRequestCode::Post => &self.post,
CoapRequestCode::Fetch => &self.fetch,
CoapRequestCode::IPatch => &self.ipatch,
CoapRequestCode::Patch => &self.patch,
}
}
#[inline]
fn handler_ref_mut(&mut self, code: CoapRequestCode) -> &mut Option<CoapRequestHandler<D>> {
match code {
CoapRequestCode::Get => &mut self.get,
CoapRequestCode::Put => &mut self.put,
CoapRequestCode::Delete => &mut self.delete,
CoapRequestCode::Post => &mut self.post,
CoapRequestCode::Fetch => &mut self.fetch,
CoapRequestCode::IPatch => &mut self.ipatch,
CoapRequestCode::Patch => &mut self.patch,
}
}
}
#[derive(Debug)]
pub(crate) struct CoapResourceInner<D: Any + ?Sized + Debug> {
raw_resource: *mut coap_resource_t,
user_data: Box<D>,
handlers: CoapResourceHandlers<D>,
}
impl<D: Any + ?Sized + Debug> CoapResource<D> {
pub fn new<C: Into<Box<D>>>(uri_path: &str, user_data: C, notify_con: bool) -> CoapResource<D> {
let inner = unsafe {
let uri_path = coap_new_str_const(uri_path.as_ptr(), uri_path.len());
let raw_resource = coap_resource_init(
uri_path,
(COAP_RESOURCE_FLAGS_RELEASE_URI
| (notify_con
.then(|| COAP_RESOURCE_FLAGS_NOTIFY_CON)
.unwrap_or(COAP_RESOURCE_FLAGS_NOTIFY_NON))) as i32,
);
let inner = CoapFfiRcCell::new(CoapResourceInner {
raw_resource,
user_data: user_data.into(),
handlers: CoapResourceHandlers::default(),
});
coap_resource_set_userdata(raw_resource, inner.create_raw_weak());
inner
};
Self::from(inner)
}
pub fn notify_observers(&self) -> bool {
unsafe { coap_resource_notify_observers(self.inner.borrow_mut().raw_resource, std::ptr::null_mut()) != 0 }
}
pub fn set_get_observable(&self, observable: bool) {
unsafe { coap_resource_set_get_observable(self.inner.borrow_mut().raw_resource, observable as c_int) }
}
pub fn set_observe_notify_confirmable(&self, confirmable: bool) {
unsafe { coap_resource_set_mode(self.inner.borrow_mut().raw_resource, confirmable as c_int) }
}
pub fn user_data(&self) -> Ref<D> {
Ref::map(self.inner.borrow(), |v| v.user_data.as_ref())
}
pub fn user_data_mut(&self) -> RefMut<D> {
RefMut::map(self.inner.borrow_mut(), |v| v.user_data.as_mut())
}
pub unsafe fn restore_from_raw(raw_resource: *mut coap_resource_t) -> CoapResource<D> {
let resource_tmp = CoapFfiRcCell::clone_raw_weak(coap_resource_get_userdata(raw_resource));
CoapResource::from(resource_tmp)
}
pub fn set_method_handler<H: Into<CoapRequestHandler<D>>>(&self, code: CoapRequestCode, handler: Option<H>) {
let mut inner = self.inner.borrow_mut();
*inner.handlers.handler_ref_mut(code) = handler.map(|v| v.into());
unsafe {
coap_register_request_handler(
inner.raw_resource,
code.to_raw_request(),
inner.handlers.handler(code).map(|h| h.raw_handler),
);
}
}
fn call_dynamic_handler(
&self,
session: &mut CoapServerSession,
req_message: &CoapRequest,
mut rsp_message: CoapResponse,
) {
let mut inner = self.inner.borrow_mut();
let req_code = match req_message.code() {
CoapMessageCode::Request(req_code) => req_code,
_ => {
rsp_message.set_type_(CoapMessageType::Rst);
session.send(rsp_message).expect("error while sending RST packet");
return;
},
};
let mut handler_fn = inner
.handlers
.handler_ref_mut(req_code)
.take()
.expect("attempted to call dynamic handler for method that has no handler set");
std::mem::drop(inner);
(handler_fn
.dynamic_handler_function
.as_mut()
.expect("attempted to call dynamic handler for method that has no dynamic handler set"))(
self,
session,
req_message,
rsp_message,
);
self.inner
.borrow_mut()
.handlers
.handler_ref_mut(req_code)
.get_or_insert(handler_fn);
}
}
impl<D: Any + ?Sized + Debug> UntypedCoapResource for CoapResource<D> {
fn uri_path(&self) -> &str {
unsafe {
let raw_path = coap_resource_get_uri_path(self.inner.borrow().raw_resource);
return std::str::from_utf8_unchecked(std::slice::from_raw_parts((*raw_path).s, (*raw_path).length));
}
}
fn as_any(&self) -> &dyn Any {
self as &(dyn Any)
}
fn drop_inner_exclusive(self: Box<Self>) {
self.inner.drop_exclusively();
}
unsafe fn raw_resource(&mut self) -> *mut coap_resource_t {
self.inner.borrow_mut().raw_resource
}
}
#[doc(hidden)]
impl<D: Any + ?Sized + Debug> From<CoapFfiRcCell<CoapResourceInner<D>>> for CoapResource<D> {
fn from(raw_cell: CoapFfiRcCell<CoapResourceInner<D>>) -> Self {
CoapResource { inner: raw_cell }
}
}
impl<D: Any + ?Sized + Debug> Drop for CoapResourceInner<D> {
fn drop(&mut self) {
std::mem::drop(unsafe {
CoapFfiRcCell::<CoapResourceInner<D>>::raw_ptr_to_weak(coap_resource_get_userdata(self.raw_resource))
});
unsafe { coap_delete_resource(std::ptr::null_mut(), self.raw_resource) };
}
}
#[allow(clippy::type_complexity)]
pub struct CoapRequestHandler<D: Any + ?Sized + Debug> {
raw_handler: unsafe extern "C" fn(
resource: *mut coap_resource_t,
session: *mut coap_session_t,
incoming_pdu: *const coap_pdu_t,
query: *const coap_string_t,
response_pdu: *mut coap_pdu_t,
),
dynamic_handler_function:
Option<Box<dyn FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)>>,
__handler_data_type: PhantomData<D>,
}
impl<D: 'static + ?Sized + Debug> CoapRequestHandler<D> {
pub fn new<F: 'static + FnMut(&mut D, &mut CoapServerSession, &CoapRequest, CoapResponse)>(
mut handler: F,
) -> CoapRequestHandler<D> {
CoapRequestHandler::new_resource_ref(move |resource, session, request, response| {
handler(&mut *resource.user_data_mut(), session, request, response)
})
}
pub fn new_resource_ref<
F: 'static + FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse),
>(
handler: F,
) -> CoapRequestHandler<D> {
let mut wrapped_handler = resource_handler!(coap_resource_handler_dynamic_wrapper, D);
wrapped_handler.dynamic_handler_function = Some(Box::new(handler));
wrapped_handler
}
#[allow(clippy::type_complexity)]
pub unsafe fn from_raw_handler(
raw_handler: unsafe extern "C" fn(
resource: *mut coap_resource_t,
session: *mut coap_session_t,
incoming_pdu: *const coap_pdu_t,
query: *const coap_string_t,
response_pdu: *mut coap_pdu_t,
),
) -> CoapRequestHandler<D> {
let handler_fn: Option<Box<dyn FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)>> =
None;
CoapRequestHandler {
raw_handler,
dynamic_handler_function: handler_fn,
__handler_data_type: PhantomData,
}
}
}
impl<D: 'static + ?Sized + Debug> Debug for CoapRequestHandler<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CoapRequestHandler").finish()
}
}
fn coap_resource_handler_dynamic_wrapper<D: Any + ?Sized + Debug>(
resource: &CoapResource<D>,
session: &mut CoapServerSession,
req_message: &CoapRequest,
rsp_message: CoapResponse,
) {
resource.call_dynamic_handler(session, req_message, rsp_message);
}