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_delete_resource, coap_new_str_const, coap_pdu_t, coap_register_request_handler, coap_resource_get_uri_path,
23 coap_resource_get_userdata, coap_resource_init, coap_resource_notify_observers, coap_resource_set_get_observable,
24 coap_resource_set_mode, coap_resource_set_userdata, coap_resource_t, coap_send_rst, coap_session_t, coap_string_t,
25 COAP_RESOURCE_FLAGS_NOTIFY_CON, COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_RELEASE_URI,
26};
27
28use crate::{
29 context::ensure_coap_started,
30 error::MessageConversionError,
31 mem::{CoapFfiRcCell, DropInnerExclusively},
32 message::{request::CoapRequest, response::CoapResponse, CoapMessage, CoapMessageCommon},
33 protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode},
34 session::{CoapServerSession, CoapSessionCommon},
35};
36
37#[macro_export]
47macro_rules! resource_handler {
48 ($f:ident, $t:path) => {{
49 #[allow(clippy::unnecessary_mut_passed)] unsafe extern "C" fn _coap_method_handler_wrapper<D: Any + ?Sized + Debug>(
51 resource: *mut coap_resource_t,
52 session: *mut coap_session_t,
53 incoming_pdu: *const coap_pdu_t,
54 query: *const coap_string_t,
55 response_pdu: *mut coap_pdu_t,
56 ) {
57 let handler_data =
58 prepare_resource_handler_data::<$t>(resource, session, incoming_pdu, query, response_pdu);
59 if let Ok((mut resource, mut session, incoming_pdu, outgoing_pdu)) = handler_data {
60 ($f::<D>)(&mut resource, &mut session, &incoming_pdu, outgoing_pdu)
61 }
62 }
63 unsafe { CoapRequestHandler::<$t>::from_raw_handler(_coap_method_handler_wrapper::<$t>) }
64 }};
65}
66
67#[inline]
78#[doc(hidden)]
79pub unsafe fn prepare_resource_handler_data<'a, D: Any + ?Sized + Debug>(
80 raw_resource: *mut coap_resource_t,
81 raw_session: *mut coap_session_t,
82 raw_incoming_pdu: *const coap_pdu_t,
83 _raw_query: *const coap_string_t,
84 raw_response_pdu: *mut coap_pdu_t,
85) -> Result<(CoapResource<D>, CoapServerSession<'a>, CoapRequest, CoapResponse), MessageConversionError> {
86 let resource_tmp = CoapFfiRcCell::clone_raw_weak(coap_resource_get_userdata(raw_resource));
87 let resource = CoapResource::from(resource_tmp);
88 let session = CoapServerSession::from_raw(raw_session);
89 let request = CoapMessage::from_raw_pdu(raw_incoming_pdu).and_then(|v| CoapRequest::from_message(v, &session));
90 let response = CoapMessage::from_raw_pdu(raw_response_pdu).and_then(CoapResponse::from_message);
91 match (request, response) {
92 (Ok(request), Ok(response)) => Ok((resource, session, request, response)),
93 (v1, v2) => {
94 coap_send_rst(raw_session, raw_incoming_pdu);
95 Err(v1.and(v2).err().unwrap())
96 },
97 }
98}
99
100pub trait UntypedCoapResource: Any + Debug {
102 fn uri_path(&self) -> &str;
104 fn as_any(&self) -> &dyn Any;
111 #[doc(hidden)]
120 fn drop_inner_exclusive(self: Box<Self>);
121 unsafe fn raw_resource(&mut self) -> *mut coap_resource_t;
131}
132
133#[derive(Debug)]
135pub struct CoapResource<D: Any + ?Sized + Debug> {
136 inner: CoapFfiRcCell<CoapResourceInner<D>>,
137}
138
139#[derive(Debug)]
141struct CoapResourceHandlers<D: Any + ?Sized + Debug> {
142 get: Option<CoapRequestHandler<D>>,
143 put: Option<CoapRequestHandler<D>>,
144 delete: Option<CoapRequestHandler<D>>,
145 post: Option<CoapRequestHandler<D>>,
146 fetch: Option<CoapRequestHandler<D>>,
147 ipatch: Option<CoapRequestHandler<D>>,
148 patch: Option<CoapRequestHandler<D>>,
149}
150
151impl<D: Any + ?Sized + Debug> Default for CoapResourceHandlers<D> {
152 fn default() -> Self {
153 CoapResourceHandlers {
154 get: None,
155 put: None,
156 delete: None,
157 post: None,
158 fetch: None,
159 ipatch: None,
160 patch: None,
161 }
162 }
163}
164
165impl<D: Any + ?Sized + Debug> CoapResourceHandlers<D> {
166 #[inline]
167 fn handler(&self, code: CoapRequestCode) -> Option<&CoapRequestHandler<D>> {
168 match code {
169 CoapRequestCode::Get => self.get.as_ref(),
170 CoapRequestCode::Put => self.put.as_ref(),
171 CoapRequestCode::Delete => self.delete.as_ref(),
172 CoapRequestCode::Post => self.post.as_ref(),
173 CoapRequestCode::Fetch => self.fetch.as_ref(),
174 CoapRequestCode::IPatch => self.ipatch.as_ref(),
175 CoapRequestCode::Patch => self.patch.as_ref(),
176 }
177 }
178
179 #[inline]
180 #[allow(unused)]
182 fn handler_mut(&mut self, code: CoapRequestCode) -> Option<&mut CoapRequestHandler<D>> {
183 match code {
184 CoapRequestCode::Get => self.get.as_mut(),
185 CoapRequestCode::Put => self.put.as_mut(),
186 CoapRequestCode::Delete => self.delete.as_mut(),
187 CoapRequestCode::Post => self.post.as_mut(),
188 CoapRequestCode::Fetch => self.fetch.as_mut(),
189 CoapRequestCode::IPatch => self.ipatch.as_mut(),
190 CoapRequestCode::Patch => self.patch.as_mut(),
191 }
192 }
193
194 #[allow(unused)]
196 #[inline]
197 fn handler_ref(&self, code: CoapRequestCode) -> &Option<CoapRequestHandler<D>> {
198 match code {
199 CoapRequestCode::Get => &self.get,
200 CoapRequestCode::Put => &self.put,
201 CoapRequestCode::Delete => &self.delete,
202 CoapRequestCode::Post => &self.post,
203 CoapRequestCode::Fetch => &self.fetch,
204 CoapRequestCode::IPatch => &self.ipatch,
205 CoapRequestCode::Patch => &self.patch,
206 }
207 }
208
209 #[inline]
210 fn handler_ref_mut(&mut self, code: CoapRequestCode) -> &mut Option<CoapRequestHandler<D>> {
211 match code {
212 CoapRequestCode::Get => &mut self.get,
213 CoapRequestCode::Put => &mut self.put,
214 CoapRequestCode::Delete => &mut self.delete,
215 CoapRequestCode::Post => &mut self.post,
216 CoapRequestCode::Fetch => &mut self.fetch,
217 CoapRequestCode::IPatch => &mut self.ipatch,
218 CoapRequestCode::Patch => &mut self.patch,
219 }
220 }
221}
222
223#[derive(Debug)]
226pub(crate) struct CoapResourceInner<D: Any + ?Sized + Debug> {
227 raw_resource: *mut coap_resource_t,
228 user_data: Box<D>,
229 handlers: CoapResourceHandlers<D>,
230}
231
232impl<D: Any + ?Sized + Debug> CoapResource<D> {
233 pub fn new<C: Into<Box<D>>>(uri_path: &str, user_data: C, notify_con: bool) -> CoapResource<D> {
241 ensure_coap_started();
242 let inner = unsafe {
243 let uri_path = coap_new_str_const(uri_path.as_ptr(), uri_path.len());
244 let raw_resource = coap_resource_init(
245 uri_path,
246 (COAP_RESOURCE_FLAGS_RELEASE_URI
247 | if notify_con {
248 COAP_RESOURCE_FLAGS_NOTIFY_CON
249 } else {
250 COAP_RESOURCE_FLAGS_NOTIFY_NON
251 }) as i32,
252 );
253 let inner = CoapFfiRcCell::new(CoapResourceInner {
254 raw_resource,
255 user_data: user_data.into(),
256 handlers: CoapResourceHandlers::default(),
257 });
258 coap_resource_set_userdata(raw_resource, inner.create_raw_weak());
259 inner
260 };
261 Self::from(inner)
262 }
263
264 pub fn notify_observers(&self) -> bool {
266 unsafe { coap_resource_notify_observers(self.inner.borrow_mut().raw_resource, std::ptr::null_mut()) != 0 }
268 }
269
270 pub fn set_get_observable(&self, observable: bool) {
273 unsafe { coap_resource_set_get_observable(self.inner.borrow_mut().raw_resource, observable as c_int) }
275 }
276
277 pub fn set_observe_notify_confirmable(&self, confirmable: bool) {
280 unsafe { coap_resource_set_mode(self.inner.borrow_mut().raw_resource, confirmable as c_int) }
282 }
283
284 pub fn user_data(&self) -> Ref<D> {
286 Ref::map(self.inner.borrow(), |v| v.user_data.as_ref())
287 }
288
289 pub fn user_data_mut(&self) -> RefMut<D> {
291 RefMut::map(self.inner.borrow_mut(), |v| v.user_data.as_mut())
292 }
293
294 pub unsafe fn restore_from_raw(raw_resource: *mut coap_resource_t) -> CoapResource<D> {
300 let resource_tmp = CoapFfiRcCell::clone_raw_weak(coap_resource_get_userdata(raw_resource));
301 CoapResource::from(resource_tmp)
302 }
303
304 pub fn set_method_handler<H: Into<CoapRequestHandler<D>>>(&self, code: CoapRequestCode, handler: Option<H>) {
306 let mut inner = self.inner.borrow_mut();
307 *inner.handlers.handler_ref_mut(code) = handler.map(|v| v.into());
308 unsafe {
309 coap_register_request_handler(
310 inner.raw_resource,
311 code.to_raw_request(),
312 inner.handlers.handler(code).map(|h| h.raw_handler),
313 );
314 }
315 }
316
317 fn call_dynamic_handler(
318 &self,
319 session: &mut CoapServerSession,
320 req_message: &CoapRequest,
321 mut rsp_message: CoapResponse,
322 ) {
323 let mut inner = self.inner.borrow_mut();
324 let req_code = match req_message.code() {
325 CoapMessageCode::Request(req_code) => req_code,
326 _ => {
327 rsp_message.set_type_(CoapMessageType::Rst);
328 session.send(rsp_message).expect("error while sending RST packet");
330 return;
331 },
332 };
333
334 let mut handler_fn = inner
337 .handlers
338 .handler_ref_mut(req_code)
339 .take()
340 .expect("attempted to call dynamic handler for method that has no handler set");
341 std::mem::drop(inner);
342
343 (handler_fn
344 .dynamic_handler_function
345 .as_mut()
346 .expect("attempted to call dynamic handler for method that has no dynamic handler set"))(
347 self,
348 session,
349 req_message,
350 rsp_message,
351 );
352
353 self.inner
355 .borrow_mut()
356 .handlers
357 .handler_ref_mut(req_code)
358 .get_or_insert(handler_fn);
359 }
360}
361
362impl<D: Any + ?Sized + Debug> UntypedCoapResource for CoapResource<D> {
363 fn uri_path(&self) -> &str {
364 unsafe {
365 let raw_path = coap_resource_get_uri_path(self.inner.borrow().raw_resource);
366 return std::str::from_utf8_unchecked(std::slice::from_raw_parts((*raw_path).s, (*raw_path).length));
367 }
368 }
369
370 fn as_any(&self) -> &dyn Any {
371 self as &(dyn Any)
372 }
373
374 fn drop_inner_exclusive(self: Box<Self>) {
375 self.inner.drop_exclusively();
376 }
377
378 unsafe fn raw_resource(&mut self) -> *mut coap_resource_t {
379 self.inner.borrow_mut().raw_resource
380 }
381}
382
383#[doc(hidden)]
384impl<D: Any + ?Sized + Debug> From<CoapFfiRcCell<CoapResourceInner<D>>> for CoapResource<D> {
385 fn from(raw_cell: CoapFfiRcCell<CoapResourceInner<D>>) -> Self {
386 CoapResource { inner: raw_cell }
387 }
388}
389
390impl<D: Any + ?Sized + Debug> Drop for CoapResourceInner<D> {
391 fn drop(&mut self) {
392 std::mem::drop(unsafe {
394 CoapFfiRcCell::<CoapResourceInner<D>>::raw_ptr_to_weak(coap_resource_get_userdata(self.raw_resource))
395 });
396 unsafe { coap_delete_resource(std::ptr::null_mut(), self.raw_resource) };
399 }
400}
401
402#[allow(clippy::type_complexity)]
434pub struct CoapRequestHandler<D: Any + ?Sized + Debug> {
435 raw_handler: unsafe extern "C" fn(
436 resource: *mut coap_resource_t,
437 session: *mut coap_session_t,
438 incoming_pdu: *const coap_pdu_t,
439 query: *const coap_string_t,
440 response_pdu: *mut coap_pdu_t,
441 ),
442 dynamic_handler_function:
443 Option<Box<dyn FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)>>,
444 __handler_data_type: PhantomData<D>,
445}
446
447impl<D: 'static + ?Sized + Debug> CoapRequestHandler<D> {
448 pub fn new<F: 'static + FnMut(&mut D, &mut CoapServerSession, &CoapRequest, CoapResponse)>(
450 mut handler: F,
451 ) -> CoapRequestHandler<D> {
452 CoapRequestHandler::new_resource_ref(move |resource, session, request, response| {
453 handler(&mut *resource.user_data_mut(), session, request, response)
454 })
455 }
456
457 pub fn new_resource_ref<
464 F: 'static + FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse),
465 >(
466 handler: F,
467 ) -> CoapRequestHandler<D> {
468 let mut wrapped_handler = resource_handler!(coap_resource_handler_dynamic_wrapper, D);
469 wrapped_handler.dynamic_handler_function = Some(Box::new(handler));
470 wrapped_handler
471 }
472
473 #[allow(clippy::type_complexity)]
484 pub unsafe fn from_raw_handler(
485 raw_handler: unsafe extern "C" fn(
486 resource: *mut coap_resource_t,
487 session: *mut coap_session_t,
488 incoming_pdu: *const coap_pdu_t,
489 query: *const coap_string_t,
490 response_pdu: *mut coap_pdu_t,
491 ),
492 ) -> CoapRequestHandler<D> {
493 ensure_coap_started();
494 let handler_fn: Option<Box<dyn FnMut(&CoapResource<D>, &mut CoapServerSession, &CoapRequest, CoapResponse)>> =
495 None;
496 CoapRequestHandler {
497 raw_handler,
498 dynamic_handler_function: handler_fn,
499 __handler_data_type: PhantomData,
500 }
501 }
502}
503
504impl<D: 'static + ?Sized + Debug> Debug for CoapRequestHandler<D> {
505 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
506 f.debug_struct("CoapRequestHandler").finish()
507 }
508}
509
510fn coap_resource_handler_dynamic_wrapper<D: Any + ?Sized + Debug>(
511 resource: &CoapResource<D>,
512 session: &mut CoapServerSession,
513 req_message: &CoapRequest,
514 rsp_message: CoapResponse,
515) {
516 resource.call_dynamic_handler(session, req_message, rsp_message);
517}