COAP_RESOURCE(3)

πŸ“¦ version 4.2.1, in ubuntu jammy

🌸 coap_resource β€” it's giving "let's talk about CoAP resources, bestie" πŸ’–

okay so this is COAP_RESOURCE(3) from the libcoap Manual (section 3, iykyk), and we're about to become unreasonably knowledgeable about CoAP resources. grab a snack. πŸ“


✨ NAME ✨

coap_resource, coap_resource_init, coap_resource_unknown_init, coap_add_resource, coap_delete_resource, coap_delete_all_resources, coap_resource_set_mode β€” literally just "Work with CoAP resources". that's the whole vibe. πŸ¦‹


πŸŽ€ SYNOPSIS πŸŽ€

#include <coap2/coap.h>

coap_resource_t *coap_resource_init(coap_str_const_t *uri_path, int
flags);

coap_resource_t *coap_resource_unknown_init(coap_method_handler_t
put_handler);

void coap_add_resource(coap_context_t *context, coap_resource_t
*resource);

int coap_delete_resource(coap_context_t context, coap_resource_t
resource);

void coap_delete_all_resources(coap_context_t *context);

void coap_resource_set_mode(coap_resource_t *resource, int mode);

and bestie, you gotta link this thing up β€” pick your fighter: -lcoap-2, -lcoap-2-gnutls, -lcoap-2-openssl or -lcoap-2-tinydtls, depending on your (D)TLS library type. ✨


πŸ’… DESCRIPTION πŸ’…

so. CoAP Resources on a CoAP Server need to be created, updated, all of that. the URI in the request packet is literally what defines which resource you're working with β€” and possibly the Query, which refers to a sub-resource. πŸ‘‘

when resources are configured on the CoAP server, you specify the URI to match against in the request packet. then Callback Handlers get added to the resource to handle the different request methods. that's the whole loop. πŸ”

adding Attributes is how you slap textual information onto a resource, which can then get reported back to any client doing a GET .well-known/core request. very "here's my whole resume" energy. πŸ“‹

if an incoming packet request matches a resource's URI and Method, then the appropriate callback resource handler gets invoked to process the packet β€” and that handler should then update a suitable response packet for sending back to the requester. no cap. πŸ’«

there's also support for handling incoming packets where the URI is unknown. when does that happen? for example when a PUT request is trying to create a new resource. and it is the responsibility of the unknown resource callback handler to either create a new resource for the URI, or just manage things separately. your call, your problem, your moment. 🫢

big one: CoAP Observe (RFC 7641) is not supported for unknown resources. so a new resource with a GET handler must be created by the unknown resource callback handle matching the URI β€” that one can then be Observable. 😭

🌈 the functions, one by one 🌈

The coap_resource_init() function returns a newly created resource of type coap_resource_t *. uri_path specifies the uri string path to match against. flags is used to define whether the resource is of type Confirmable Message or Non-Confirmable Message for any "observe" responses. see coap_observe(3). flags can be one of the following definitions, or'ed together:

  • COAP_RESOURCE_FLAGS_NOTIFY_NON β€” set the notification message type to non-confirmable for any triggered "observe" responses.
  • COAP_RESOURCE_FLAGS_NOTIFY_CON β€” set the notification message type to confirmable for any triggered "observe" responses.
  • COAP_RESOURCE_FLAGS_RELEASE_URI β€” free off the coap_str_const_t for uri_path when the resource is deleted.
NOTE: uri_path, if not 7 bit readable ASCII, binary bytes must be hex encoded according to the rules defined in RFC3968 Section 2.1. πŸ“

The coap_resource_unknown_init() returns a newly created resource of type coap_resource_t *. put_handler is automatically added to the resource to handle PUT requests to resources that are unknown. additional handlers can be added to this resource if required. πŸ’–

The coap_add_resource() function registers the given resource with the context. the resource must have been created by coap_resource_init(), or coap_resource_unknown_init(). the storage allocated for the resource will be released by coap_delete_resource(). ✨

and listen β€” since the uri_path of the resource has to be unique across all of the resources associated with a context, coap_add_resource() (or coap_add_resource_release()) will delete any previous resource with the same uri_path before adding in the new resource. it's giving "out with the old, in with the new". πŸ’€

The coap_delete_resource() function deletes a resource identified by resource from context. the storage allocated for that resource is freed, along with any attributes associated with the resource. gone. poof. πŸ¦‹

The coap_delete_all_resources() function deletes all resources from context and frees their storage. total reset. main character spring cleaning. 🧹

The coap_resource_set_mode() changes the notification message type of resource to the given mode β€” which must be one of COAP_RESOURCE_FLAGS_NOTIFY_NON or COAP_RESOURCE_FLAGS_NOTIFY_CON. those are the only two options, bestie. don't try anything else. πŸ™ˆ


⭐ RETURN VALUES ⭐

The coap_resource_init() and coap_resource_unknown_init() functions return a newly created resource, or NULL if there is a malloc failure. 😭

The coap_delete_resource() function return 0 on failure (resource not found), 1 on success. slay. πŸ‘‘

✨ EXAMPLES ✨

ok bestie this is the part where it all clicks, because nothing hits like seeing the actual code πŸ‘‘

🌸 Fixed Resources Set Up 🌸

so here's the whole vibe: you got a server with two resources, one for general info and one for the clock, and every handler is just chillin' with the same signature. no cap, look:

#include <coap2/coap.h>

#define INDEX "This is an example server using libcoap\n"

void
hnd_get_index(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {
  unsigned char buf[3];

  response->code = COAP_RESPONSE_CODE(205);

  coap_add_option(response,
                  COAP_OPTION_CONTENT_TYPE,
                  coap_encode_var_safe(buf, sizeof(buf),
                                       COAP_MEDIATYPE_TEXT_PLAIN),
                  buf);

  coap_add_option(response,
                  COAP_OPTION_MAXAGE,
                  coap_encode_var_safe(buf, sizeof(buf), 0x2ffff), buf);

  coap_add_data(response, strlen(INDEX), (const uint8_t *)INDEX);

}

void
hnd_delete_time(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  /* .. code .. */

}

void
hnd_get_time(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  /* .. code .. */

}

void
hnd_put_time(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  /* .. code .. */

}

static void
init_resources(coap_context_t *ctx) {

  coap_resource_t *r;

  /* Create a resource to return general information */
  r = coap_resource_init(NULL, 0);
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);

  /* Document resource for .well-known/core request */
  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
  coap_add_attr(r, coap_make_str_const("title"),
                coap_make_str_const("\"General Info\""), 0);

  coap_add_resource(ctx, r);

  /* Create a resource to return return or update time */
  r = coap_resource_init(coap_make_str_const("time"),
                         COAP_RESOURCE_FLAGS_NOTIFY_CON);
  coap_resource_set_get_observable(r, 1);
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
  coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
  coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);

  /* Document resource for .well-known/core request */
  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
  coap_add_attr(r, coap_make_str_const("title"),
                coap_make_str_const("\"Internal Clock\""), 0);
  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"secs\""),
                0);
  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""),
                0);

  coap_add_resource(ctx, r);

}

like, the hnd_get_index one is giving main character energy β€” it slaps a COAP_RESPONSE_CODE(205) on there, adds the COAP_OPTION_CONTENT_TYPE as COAP_MEDIATYPE_TEXT_PLAIN, sets COAP_OPTION_MAXAGE to 0x2ffff, and then dumps the INDEX string in with coap_add_data() πŸ’…

and the time resource? she's the popular one fr β€” COAP_RESOURCE_FLAGS_NOTIFY_CON in the init, coap_resource_set_get_observable(r, 1) so besties can subscribe, and THREE handlers registered: COAP_REQUEST_GET, COAP_REQUEST_PUT, COAP_REQUEST_DELETE. queen behavior πŸ‘‘


πŸ¦‹ Dynamic Resources Set Up πŸ¦‹

now THIS is the plot twist: resources that don't even exist until somebody PUTs them into existence. it's giving ✨manifestation✨

#include <coap2/coap.h>

/* Regular DELETE handler - used by resources created by the
 * Unknown Resource PUT handler */

void
hnd_delete(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  /* .. code .. */

  /* Dynamic resource no longer required - delete it */
  coap_delete_resource(ctx, resource);

}

/* Regular GET handler - used by resources created by the
 * Unknown Resource PUT handler */

void
hnd_get(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  coap_str_const_t *get_uri_path;
  coap_string_t *get_query;
  coap_opt_iterator_t opt_iter;
  coap_opt_filter_t filter;
  coap_opt_t *option;
  int len;

  /*
   * request will be NULL if an Observe triggered request, so the uri_path,
   * if needed, must be abstracted from the resource.
   * The uri_path string is a const pointer
   */

  get_uri_path = coap_resource_get_uri_path(resource);
  get_query = query;

  /* .. code .. */

}

/* Regular PUT handler - used by resources created by the
 * Unknown Resource PUT handler */

void
hnd_put(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  coap_string_t *put_uri_path;

  /* get the uri_path */
  put_uri_path = coap_get_uri_path(request);
  if (!put_uri_path) {
    response->code = COAP_RESPONSE_CODE(404);
    return;
  }

  /* .. code .. */

  /* Need to do this as coap_get_uri_path() created it */
  coap_delete_string(put_uri_path);

}

/* Unknown Resource PUT handler */

void
hnd_unknown_put(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  coap_resource_t *r;
  int len;
  coap_string_t *uri_path;

  /* get the uri_path - which will get used by coap_resource_init() */
  uri_path = coap_get_uri_path(request);
  if (!uri_path) {
    response->code = COAP_RESPONSE_CODE(404);
    return;
  }

  /* Check if new URI Path is valid */
  if (!check_url_fn (uri_path, request->code)) {
    response->code = COAP_RESPONSE_CODE(404);
    coap_delete_string(uri_path);
    return;
  }

  /*
   * Create a resource to handle the new URI
   * uri_path will get deleted when the resource is removed
   */
  r = coap_resource_init((coap_str_const_t*)uri_path,
        COAP_RESOURCE_FLAGS_RELEASE_URI | COAP_RESOURCE_FLAGS_NOTIFY_NON);
  coap_register_handler(r, COAP_REQUEST_PUT, hnd_put);
  coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete);
  /* We possibly want to Observe the GETs */
  coap_resource_set_get_observable(r, 1);
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get);
  coap_add_resource(ctx, r);

  /* Do the PUT for this first call */
  hnd_put(ctx, r, session, request, token, query, response);

  return;

}

/* Initialize single Unknown Resource PUT handler */

static void
init_resources(coap_context_t *ctx) {

  coap_resource_t *r;

  /* Create a resource to handle PUTs to unknown URIs */
  r = coap_resource_unknown_init(hnd_unknown_put);
  /*
   * Additional handlers can be added - for example
   *  coap_register_handler(r, COAP_REQUEST_POST, hnd_post_unknown);
   *  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_unknown);
   *  coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_unknown);
   */
  coap_add_resource(ctx, r);

}

okay let me break down the drama for you bestie πŸ“

  • hnd_delete β€” the closer. when the resource isn't wanted anymore it literally calls coap_delete_resource(ctx, resource) and poof, gone πŸ’€
  • hnd_get β€” the multitasker. it's got a whole stack of locals (get_uri_path, get_query, opt_iter, filter, option, len) and it pulls the path off the resource with coap_resource_get_uri_path(resource). and the comment is SO important: request will be NULL if an Observe triggered request, so if you need the uri_path you gotta abstract it from the resource instead. the uri_path string is a const pointer, iykyk 🫢
  • hnd_put β€” grabs the path via coap_get_uri_path(request), and if it comes back falsy it sets COAP_RESPONSE_CODE(404) and bails. then at the end it coap_delete_string(put_uri_path) because coap_get_uri_path() created it β€” you must do this, it's not optional, don't leak that memory bestie 😭
  • hnd_unknown_put β€” the plot device. gets the uri_path, 404s if it's missing, runs check_url_fn (uri_path, request->code) to validate and 404s (+ coap_delete_string(uri_path)) if it's invalid, then builds a brand new resource with coap_resource_init((coap_str_const_t*)uri_path, COAP_RESOURCE_FLAGS_RELEASE_URI | COAP_RESOURCE_FLAGS_NOTIFY_NON). note that uri_path gets deleted when the resource is removed. registers hnd_put for COAP_REQUEST_PUT and hnd_delete for COAP_REQUEST_DELETE, flips on coap_resource_set_get_observable(r, 1) because we possibly want to Observe the GETs, registers hnd_get for COAP_REQUEST_GET, adds it, and then β€” iconic move β€” calls hnd_put(ctx, r, session, request, token, query, response) so the very first call gets handled too πŸ”₯
  • init_resources β€” creates ONE resource with coap_resource_unknown_init(hnd_unknown_put) to catch PUTs to unknown URIs. and the comment whispers that you can stack more handlers on it, like coap_register_handler(r, COAP_REQUEST_POST, hnd_post_unknown), coap_register_handler(r, COAP_REQUEST_GET, hnd_get_unknown), coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_unknown). slay 🌈

πŸ‘€ SEE ALSO πŸ‘€

go read those next, bestie, they're all part of the same friend group 🫢


🌟 FURTHER INFORMATION 🌟

See "RFC7252: The Constrained Application Protocol (CoAP)" for further information. it's giving required reading, ngl πŸ“š


πŸ’€ BUGS πŸ’€

Please report bugs on the mailing list for libcoap:

libcoap-developers@lists.sourceforge.net

do NOT suffer in silence, we want the tea β˜•


πŸ‘‘ AUTHORS πŸ‘‘

The libcoap project <libcoap-developers@lists.sourceforge.net>


and that's a wrap on COAP_RESOURCE(3) bestie!! this whole page is from the libcoap project, coap_resource 4.2.1, dated 11/23/2021 β€” literally vintage, and she ate πŸ’–βœ¨ no cap, you're gonna build something gorgeous with this. go forth and register those handlers, you absolute icon πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from