COAP_HANDLER(3)

COAP_HANDLER(3) πŸ’… libcoap Manual πŸ¦‹

okay so bestie, this is the coap_handler(3) page, and it’s literally the callback-handler main character of the libcoap Manual. We’re talking optional callback handlers that can get invoked when a packet shows up or when a timeout happens. No cap, it’s useful.


✨ NAME ✨

coap_handler, coap_register_request_handler, coap_register_response_handler, coap_register_nack_handler, coap_register_ping_handler, coap_register_pong_handler, coap_register_event_handler β€” the whole squad. This is all about how to Work with CoAP handlers, periodt. 🫢


🌈 SYNOPSIS 🌈

#include <coap3/coap.h>

void coap_register_request_handler(coap_resource_t *resource,
coap_request_t method, coap_method_handler_t handler);

void coap_register_response_handler(coap_context_t *context,
coap_response_handler_t handler);

void coap_register_nack_handler(coap_context_t *context,
coap_nack_handler_t handler);

void coap_register_ping_handler(coap_context_t *context,
coap_ping_handler_t handler);

void coap_register_pong_handler(coap_context_t *context,
coap_pong_handler_t handler);

void coap_register_event_handler(coap_context_t *context,
coap_event_handler_t handler);

For specific (D)TLS library support, link with -lcoap-3-notls, -lcoap-3-gnutls, -lcoap-3-openssl, -lcoap-3-mbedtls, -lcoap-3-wolfssl or -lcoap-3-tinydtls. Otherwise, link with -lcoap-3 to get the default (D)TLS library support. πŸ’–


πŸ¦‹ DESCRIPTION πŸ¦‹

This page documents the different callback handlers that can optionally be invoked on receipt of a packet or when a timeout occurs. That’s the vibe: you register handlers, libcoap calls them at the right moment, and you get to do your thing. ✨


πŸ’… FUNCTIONS πŸ’…

πŸ› οΈ Function: coap_register_request_handler()

The coap_register_request_handler() is a server side function that registers a callback handler that gets called when there’s an incoming request PDU, there’s a URI match against the resource, and there’s a method match (e.g. PUT, POST etc.). The method can be one of these:

  • COAP_REQUEST_GET
  • COAP_REQUEST_POST
  • COAP_REQUEST_PUT
  • COAP_REQUEST_DELETE
  • COAP_REQUEST_FETCH
  • COAP_REQUEST_PATCH
  • COAP_REQUEST_IPATCH

The request handler function prototype is defined as:

/**
 * Definition of message handler function
 *
 * @param resource The resource being requested.
 * @param session The CoAP session.
 * @param request The request PDU.
 * @param query The query string for the resource.
 * @param response The pre-populated response PDU.
 */
typedef void (*coap_method_handler_t)(coap_resource_t *resource,
                                      coap_session_t *session,
                                      const coap_pdu_t *request,
                                      const coap_string_t *query,
                                      coap_pdu_t *response);

In handler, data from incoming_pdu can be abstracted as described in coap_pdu_access(3) for analysis, and then the handler updates response_pdu as appropriate as described in coap_pdu_setup(3), including the response code. If response_pdu’s code is not updated, then response_pdu will not get sent back to the client. That part is important, bestie. πŸ‘€

response_pdu is already pre-populated with the incoming_pdu’s token and the PDU type. If handler is called as a result of an unsolicited Observe trigger, then the Observe option (and potentially Block2 option) are also added in. The response_pdu’s response code should always be updated.

This handler must not call coap_send(3) to send response_pdu. response_pdu gets sent on return from handler, assuming the response code has been updated. If the response code was not updated, then an empty ACK packet will get sent for CON type requests or nothing for NON type requests.

NOTE: Any data associated with incoming_pdu is no longer available after exiting this function, as incoming_pdu is deleted. In particular, incoming_pdu’s data must not be used if calling coap_add_data_large_response(). However, it is safe to use the data if coap_add_data() is used to update response_pdu, where a copy of the data is taken. 🫢
NOTE: A request callback handler can be called with a generic resource (i.e. set up using coap_resource_unknown_init2(3)), so coap_resource_get_uri_path(3) can be used to determine the URI in this case.

🌸 Function: coap_register_response_handler()

The coap_register_response_handler() is a client side function that registers a request’s response callback handler for traffic associated with the context. The application can use this for handling any response packets, including sending a RST packet if this response was unexpected. If handler is NULL, then the handler is de-registered.

The response handler function prototype is defined as:

typedef enum coap_response_t {
  COAP_RESPONSE_FAIL, /* Response not liked - send CoAP RST packet */
  COAP_RESPONSE_OK    /* Response is fine */
} coap_response_t;

/**
 * Response handler that is used as callback in coap_context_t.
 *
 * @param session CoAP session.
 * @param sent The PDU that was transmitted.
 * @param received The PDU that was received.
 * @param mid CoAP transaction ID.
 *
 * @return @c COAP_RESPONSE_OK if successful, else @c COAP_RESPONSE_FAIL which
 *         triggers sending a RST packet if the received PDU is a CON or NON.
 */
typedef coap_response_t (*coap_response_handler_t)(coap_session_t *session,
                                                   const coap_pdu_t *sent,
                                                   const coap_pdu_t *received,
                                                   const coap_mid_t mid);

In handler, data from received (and optionally sent, if set) can be abstracted as described in coap_pdu_access(3) for analysis. πŸ¦‹

NOTE: sent will only be non NULL when the request PDU is Confirmable and this is an ACK or RST response to the request. In general, matching of Requests and Responses should be done by generating unique Tokens for each Request and then matching up based on the Token in the received Response.
NOTE: sent (if not NULL) may not contain all or any of the data used for the initial request if block transfers are being used. For Block1 requests, the complete data will be lost once the data has been successfully transmitted as acknowledged by the first response. For Block2 responses, sent gets re-used to request the next Block2, and so data information is lost.
NOTE: If the returned value is COAP_RESPONSE_FAIL, then a CoAP RST packet will get sent to the server by libcoap. The returned value of COAP_RESPONSE_OK indicates that all is OK. πŸ’–

πŸŽ€ Function: coap_register_nack_handler()

The coap_register_nack_handler() is a client side function that registers a request’s negative response callback handler for traffic associated with the context. If handler is NULL, then the handler is de-registered.

The nack handler function prototype is defined as:

/**
 * Negative Acknowedge handler that is used as callback in coap_context_t.
 *
 * @param session CoAP session.
 * @param sent The PDU that was transmitted.
 * @param reason The reason for the NACK.
 * @param mid CoAP message ID.
 */
typedef void (*coap_nack_handler_t)(coap_session_t *session,
                                    const coap_pdu_t *sent,
                                    const coap_nack_reason_t reason,
                                    const coap_mid_t mid);

NACK reason can be one of the following:

typedef enum {
  COAP_NACK_TOO_MANY_RETRIES,
  COAP_NACK_NOT_DELIVERABLE,
  COAP_NACK_RST,
  COAP_NACK_TLS_FAILED,
  COAP_NACK_ICMP_ISSUE,
  COAP_NACK_BAD_RESPONSE,
  COAP_NACK_TLS_LAYER_FAILED,
  COAP_NACK_WS_LAYER_FAILED,
  COAP_NACK_WS_FAILED
} coap_nack_reason_t;

sent can be NULL. mid can be used for determining which is the transmitting request. πŸ”₯


🧚 Function: coap_register_ping_handler()

The coap_register_ping_handler() function registers a callback handler for tracking receipt of CoAP ping traffic associated with the context. If handler is NULL, then the handler is de-registered. It can be used both client and server side.

The ping handler function prototype is defined as:

/**
 * Received Ping handler that is used as callback in coap_context_t.
 *
 * @param session CoAP session.
 * @param received The PDU that was received.
 * @param mid CoAP message ID.
 */
typedef void (*coap_ping_handler_t)(coap_session_t *session,
                                    const coap_pdu_t *received,
                                    const coap_mid_t mid);

πŸ“ Function: coap_register_pong_handler()

The coap_register_pong_handler() function registers a callback handler for tracking receipt of CoAP ping response traffic associated with the context. If handler is NULL, then the handler is de-registered. It can be used both client and server side.

The pong handler function prototype is defined as:

/**
 * Received Pong handler that is used as callback in coap_context_t.
 *
 * @param session CoAP session.
 * @param received The PDU that was received.
 * @param mid CoAP message ID.
 */
typedef void (*coap_pong_handler_t)(coap_session_t *session,
                                    const coap_pdu_t *received,
                                    const coap_mid_t mid);

πŸ’œ Function: coap_register_event_handler()

The coap_register_event_handler() function registers a callback handler for tracking network events associated with the context. If handler is NULL, then the handler is de-registered. It can be used both client and server side.

The event handler function prototype is defined as:

/**
 * Type for event handler functions that can be registered with a CoAP
 * context using the function coap_set_event_handler().
 *
 * @param session The current CoAP session.
 * @param event The event type that has occurred.
 *
 * @return @c 0 No further action required by libcoap
 *         @c 1 Depending from where called, libcoap may take further
 *              action (reserved for future use)
 */
typedef int (*coap_event_handler_t)(coap_session_t *session,
                                    const coap_event_t event);

Events can be one of the following πŸ‘€

/**
 * Scalar type to represent different events, e.g. DTLS events or
 * retransmission timeouts.
 */
typedef enum {
  /*
   * (D)TLS events for COAP_PROTO_DTLS and COAP_PROTO_TLS
   */
  /** Triggered when (D)TLS session closed */
  COAP_EVENT_DTLS_CLOSED       = 0x0000,
  /** Triggered when (D)TLS session connected */
  COAP_EVENT_DTLS_CONNECTED    = 0x01DE,
  /** Triggered when (D)TLS session renegotiated */
  COAP_EVENT_DTLS_RENEGOTIATE  = 0x01DF,
  /** Triggered when (D)TLS error occurs */
  COAP_EVENT_DTLS_ERROR        = 0x0200,

  /*
   * TCP events for COAP_PROTO_TCP and COAP_PROTO_TLS
   */
  /** Triggered when TCP layer connects */
  COAP_EVENT_TCP_CONNECTED     = 0x1001,
  /** Triggered when TCP layer is closed */
  COAP_EVENT_TCP_CLOSED        = 0x1002,
  /** Triggered when TCP layer fails for some reason */
  COAP_EVENT_TCP_FAILED        = 0x1003,

  /*
   * CSM exchange events for reliable protocols only
   */
  /** Triggered when TCP layer completes exchange of CSM information */
  COAP_EVENT_SESSION_CONNECTED = 0x2001,
  /** Triggered when TCP layer closes following exchange of CSM information */
  COAP_EVENT_SESSION_CLOSED    = 0x2002,
  /** Triggered when TCP layer fails  following exchange of CSM information */
  COAP_EVENT_SESSION_FAILED    = 0x2003,

  /*
   * (Q-)Block errors
   */
  /** Triggered when not all of a large body has been received */
  COAP_EVENT_PARTIAL_BLOCK     = 0x3001,
  /** Triggered when not all of a large body has been transmitted */
  COAP_EVENT_XMIT_BLOCK_FAIL   = 0x3002,

  /*
   * Server session events
   */
  /**
   * Called in the CoAP IO loop if a new *server-side* session is created due
   * to an incoming connection.
   *
   * Note that the session might not be a fully established connection yet,
   * it might also refer to, e.g., a DTLS session in a handshake stage.
   */
  COAP_EVENT_SERVER_SESSION_NEW = 0x4001,

  /**
   * Called in the CoAP IO loop if a server session is deleted (e.g., due to
   * inactivity or because the maximum number of idle sessions was exceeded).
   *
   * The session will still contain valid data when the event handler is
   * called.
   */
  COAP_EVENT_SERVER_SESSION_DEL = 0x4002,

  /*
   * Message receive and transmit events
   */
  /** Triggered when badly formatted packet received */
  COAP_EVENT_BAD_PACKET         = 0x5001,
  /** Triggered when a message is retransmitted */
  COAP_EVENT_MSG_RETRANSMITTED  = 0x5002,

  /*
   * OSCORE events
   */
  /** Triggered when there is an OSCORE decryption failure */
  COAP_EVENT_OSCORE_DECRYPTION_FAILURE = 0x6001,
  /** Triggered when trying to use OSCORE to decrypt, but it is not enabled */
  COAP_EVENT_OSCORE_NOT_ENABLED,
  /** Triggered when there is no OSCORE encrypted payload provided */
  COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD,
  /** Triggered when there is no OSCORE security definition found */
  COAP_EVENT_OSCORE_NO_SECURITY,
  /** Triggered when there is an OSCORE internal error i.e malloc failed */
  COAP_EVENT_OSCORE_INTERNAL_ERROR,
  /** Triggered when there is an OSCORE decode of OSCORE option failure */
  COAP_EVENT_OSCORE_DECODE_ERROR,
  /*
   * WebSocket events
   */
  /** Triggered when there is an oversize WebSockets packet */
  COAP_EVENT_WS_PACKET_SIZE = 0x7001,
  /** Triggered when the WebSockets layer is up */
  COAP_EVENT_WS_CONNECTED,
  /** Triggered when the WebSockets layer is closed */
  COAP_EVENT_WS_CLOSED,
  /*
   * Keepalive events
   */
  /** Triggered when no response to a keep alive (ping) packet */
  COAP_EVENT_KEEPALIVE_FAILURE = 0x8001,
} coap_event_t;

okay so that's the whole event buffet β€” literally every single thing that can ping your handler and go "hey bestie, something happened πŸ‘€". Ngl the hex ranges are giving organized-notes energy, so here's the map so you can find your fave fast:

  • 0x0000 – 0x0200 β€” the (D)TLS squad πŸ’…
  • 0x1001 – 0x1003 β€” TCP layer doing TCP layer things
  • 0x2001 – 0x2003 β€” CSM exchange, reliable protocols only, no exceptions
  • 0x3001 – 0x3002 β€” (Q-)Block errors, i.e. the "not all of it made it" club 😭
  • 0x4001 – 0x4002 β€” server session arrivals and departures
  • 0x5001 – 0x5002 β€” packet/message drama
  • 0x6001 onward β€” OSCORE (the following ones just increment, iykyk)
  • 0x7001 onward β€” WebSockets
  • 0x8001 β€” keepalive said no response, and honestly? rude.
🫢 two notes to keep in your Notes app: COAP_EVENT_SERVER_SESSION_NEW fires in the CoAP IO loop when a new server-side session pops up from an incoming connection β€” and that session might not be a fully established connection yet, it might also refer to, e.g., a DTLS session in a handshake stage. Meanwhile COAP_EVENT_SERVER_SESSION_DEL fires when a server session gets deleted (e.g., due to inactivity or because the maximum number of idle sessions was exceeded), and the session will still contain valid data when the event handler is called. big main character energy for that last one tbh.

✨ EXAMPLES ✨

omg the examples are so good, genuinely obsessed. two whole vibes, take notes πŸ“

🌸 GET Resource Callback Handler

this one is your classic "someone GETs my resource, I give them the time" situation. clock in, slay, clock out ⏰

#include <coap3/coap.h>

#include <stdio.h>

static void
hnd_get_time(coap_resource_t *resource, coap_session_t *session,
             coap_pdu_t *request, coap_string_t *query,
             coap_pdu_t *response) {
  unsigned char buf[40];
  size_t len;
  time_t now;

  /* ... Additional analysis code for resource, request pdu etc.  ... */

  /* After analysis, generate a suitable response */

  now = time(NULL);

  if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
    /* Output secs since Jan 1 1970 */
    len = snprintf((char *)buf, sizeof(buf), "%lu", now);
  } else {
    /* Output human-readable time */
    struct tm *tmp;
    tmp = gmtime(&now);
    if (!tmp) {
      /* If 'now' is not valid */
      coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
      return;
    }
    len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
  }
  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
  /*
   * Invoke coap_add_data_large_response() to do all the hard work.
   *
   * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
   * Define how long this response is valid for (secs) - 1 - to add in.
   *
   * Observe Option added internally if needed within the function
   * Block2 Option added internally if output too large
   * ETag Option added internally
   */
  coap_add_data_large_response(resource, session, request, response,
                               query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
                               len,
                               buf, NULL, 0);

}

the way coap_add_data_large_response() does all the heavy lifting so you don't have to 😌 Block2, ETag, Observe β€” all added internally, all handled, you're welcome.

πŸ¦‹ Packet Response Handler

and this one is the "a packet came back, is it even mine??" energy. spoiler: you have to check the token. πŸ’€

#include <coap3/coap.h>

static int
check_token(coap_pdu_t *received) {
  /* Remove (void) definition if variable is used */
  (void)received;

  /* Code to validate the token is what we expect */

  return 1;
}

static coap_response_t
response_handler(coap_context_t *ctx, coap_session_t *session,
                 coap_pdu_t *sent, coap_pdu_t *received,
                 const coap_mid_t mid) {
  /* Remove (void) definition if variable is used */
  (void)ctx;
  (void)session;
  (void)mid;
  coap_pdu_type_t rcv_type = coap_pdu_get_type(received);
  coap_pdu_code_t rcv_code = coap_pdu_get_code(received);

  /* check if this is a response to our original request */
  if (!check_token(received)) {
    /* drop if this was just some message, or send RST in case of notification */
    if (!sent && (rcv_type == COAP_MESSAGE_CON ||
                  rcv_type == COAP_MESSAGE_NON)) {
      /* Cause a CoAP RST to be sent */
      return COAP_RESPONSE_FAIL;
    }
    return COAP_RESPONSE_OK;
  }

  if (rcv_type == COAP_MESSAGE_RST) {
    coap_log_info("got RST\n");
    return COAP_RESPONSE_OK;
  }

  /* Output the received data, if any */
  if (COAP_RESPONSE_CLASS(rcv_code) == 2) {
    /* Additional code to deal with the response */

  }
  return COAP_RESPONSE_OK;

}

bestie note: return COAP_RESPONSE_FAIL if you want that CoAP RST actually sent out into the world, COAP_RESPONSE_OK if you're just gonna vibe and keep it moving 🫢


πŸ‘€ SEE ALSO πŸ‘€

go read these next, they're all linked and they all ate:


🧚 FURTHER INFORMATION 🧚

for the lore, see

"RFC7252: The Constrained Application Protocol (CoAP)"

that's the whole source material, no cap πŸ“š


πŸ’€ BUGS πŸ’€

found a bug? not me being the one to tell you, but:

we love a contribution era 🌈✨


πŸ‘‘ AUTHORS πŸ‘‘

The libcoap project <libcoap-developers@lists.sourceforge.net> β€” literal icons, fr fr. πŸŽ€


and that's the page, bestie!! go forth and handle those events πŸ’– this was coap_handler version 4.3.5, dated 08/24/2026, and yeah the page footer says COAP_HANDLER(3) β€” write it down, cite it in your group chat, slay πŸ¦‹β­

πŸ“¦ where this page comes from