1use 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#[macro_export]
48macro_rules! resource_handler {
49 ($f:ident, $t:path) => {{
50 #[allow(clippy::unnecessary_mut_passed)] 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#[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
101pub trait UntypedCoapResource: Any + Debug {
103 fn uri_path(&self) -> &str;
105 fn as_any(&self) -> &dyn Any;
112 #[doc(hidden)]
121 fn drop_inner_exclusive(self: Box<Self>);
122 unsafe fn raw_resource(&mut self) -> *mut coap_resource_t;
132}
133
134#[derive(Debug)]
136pub struct CoapResource<D: Any + ?Sized + Debug> {
137 inner: CoapFfiRcCell<CoapResourceInner<D>>,
138}
139
140#[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 #[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 #[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#[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 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 pub fn notify_observers(&self) -> bool {
267 unsafe { coap_resource_notify_observers(self.inner.borrow_mut().raw_resource, std::ptr::null_mut()) != 0 }
269 }
270
271 pub fn set_get_observable(&self, observable: bool) {
274 unsafe { coap_resource_set_get_observable(self.inner.borrow_mut().raw_resource, observable as c_int) }
276 }
277
278 pub fn set_observe_notify_confirmable(&self, confirmable: bool) {
281 unsafe { coap_resource_set_mode(self.inner.borrow_mut().raw_resource, confirmable as c_int) }
283 }
284
285 pub fn user_data(&self) -> Ref<D> {
287 Ref::map(self.inner.borrow(), |v| v.user_data.as_ref())
288 }
289
290 pub fn user_data_mut(&self) -> RefMut<D> {
292 RefMut::map(self.inner.borrow_mut(), |v| v.user_data.as_mut())
293 }
294
295 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 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 pub fn add_attr(&self, name: &str, val: Option<&str>) {
320 let mut inner = self.inner.borrow_mut();
321 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_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 session.send(rsp_message).expect("error while sending RST packet");
354 return;
355 },
356 };
357
358 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 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 std::mem::drop(unsafe {
418 CoapFfiRcCell::<CoapResourceInner<D>>::raw_ptr_to_weak(coap_resource_get_userdata(self.raw_resource))
419 });
420 unsafe { coap_delete_resource(std::ptr::null_mut(), self.raw_resource) };
423 }
424}
425
426#[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 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 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 #[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}