1use 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#[macro_export]
51macro_rules! resource_handler {
52 ($f:ident, $t:path) => {{
53 #[allow(clippy::unnecessary_mut_passed)] 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#[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
104pub trait UntypedCoapResource: Any + Debug {
106 fn uri_path(&self) -> &str;
108 fn as_any(&self) -> &dyn Any;
115 #[doc(hidden)]
124 fn drop_inner_exclusive(self: Box<Self>);
125 unsafe fn raw_resource(&mut self) -> *mut coap_resource_t;
135}
136
137#[derive(Debug)]
139pub struct CoapResource<D: Any + ?Sized + Debug> {
140 inner: CoapFfiRcCell<CoapResourceInner<D>>,
141}
142
143#[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 #[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 #[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#[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 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 pub fn notify_observers(&self) -> bool {
269 unsafe { coap_resource_notify_observers(self.inner.borrow_mut().raw_resource, std::ptr::null_mut()) != 0 }
271 }
272
273 pub fn set_get_observable(&self, observable: bool) {
276 unsafe { coap_resource_set_get_observable(self.inner.borrow_mut().raw_resource, observable as c_int) }
278 }
279
280 pub fn set_observe_notify_confirmable(&self, confirmable: bool) {
283 unsafe { coap_resource_set_mode(self.inner.borrow_mut().raw_resource, confirmable as c_int) }
285 }
286
287 pub fn user_data(&self) -> Ref<D> {
289 Ref::map(self.inner.borrow(), |v| v.user_data.as_ref())
290 }
291
292 pub fn user_data_mut(&self) -> RefMut<D> {
294 RefMut::map(self.inner.borrow_mut(), |v| v.user_data.as_mut())
295 }
296
297 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 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 session.send(rsp_message).expect("error while sending RST packet");
333 return;
334 },
335 };
336
337 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 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 std::mem::drop(unsafe {
397 CoapFfiRcCell::<CoapResourceInner<D>>::raw_ptr_to_weak(coap_resource_get_userdata(self.raw_resource))
398 });
399 unsafe { coap_delete_resource(std::ptr::null_mut(), self.raw_resource) };
402 }
403}
404
405#[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 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 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 #[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}