COAP_CACHE(3)
π COAP_CACHE(3) β libcoap Manual π
πΈ NAME πΈ
Okay bestie, this is the whole libcoap cache squad: coap_cache, coap_cache_derive_key, coap_cache_derive_key_w_ignore, coap_delete_cache_key, coap_cache_ignore_options, coap_new_cache_entry, coap_delete_cache_entry, coap_cache_get_by_key, coap_cache_get_by_pdu, coap_cache_get_pdu, coap_cache_set_app_data, and coap_cache_get_app_data β all here to Work with CoAP cache functions. Periodt. π
β¨ SYNOPSIS β¨
Grab the header first:
#include <coap3/coap.h>
Then these are the exact function signatures, no cap:
coap_cache_key_t *coap_cache_derive_key(const coap_session_t *session,
const coap_pdu_t *pdu, coap_cache_session_based_t session_based);
coap_cache_key_t *coap_cache_derive_key_w_ignore( const coap_session_t
*session, const coap_pdu_t *pdu, coap_cache_session_based_t
session_based, const uint16_t *ignore_options, size_t ignore_count);
void coap_delete_cache_key(coap_cache_key_t *cache_key);
int coap_cache_ignore_options(coap_context_t *context, const uint16_t
*options, size_t count);
coap_cache_entry_t *coap_new_cache_entry(coap_session_t *session, const
coap_pdu_t *pdu, coap_cache_record_pdu_t record_pdu,
coap_cache_session_based_t session_based, unsigned int idle_timeout);
void coap_delete_cache_entry(coap_context_t *context, coap_cache_entry_t
*cache_entry);
coap_cache_entry_t *coap_cache_get_by_key(coap_context_t *context, const
coap_cache_key_t *cache_key);
coap_cache_entry_t *coap_cache_get_by_pdu(coap_session_t *session, const
coap_pdu_t *pdu, coap_cache_session_based_t session_based);
const coap_pdu_t *coap_cache_get_pdu(const coap_cache_entry_t
*cache_entry);
void coap_cache_set_app_data(coap_cache_entry_t *cache_entry, void
*data, coap_cache_app_data_free_callback_t callback);
void *coap_cache_get_app_data(const coap_cache_entry_t *cache_entry);
Link-time tea β: 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 π¦
The CoAP Cache provides support for two opaque objects that can be used for tracking requests and responses. Itβs giving βbackstage pass for your PDUs.β π
The first is the ability to derive a Cache Key from the cacheable parts of a CoAP PDU as defined in "RFC7252 5.6. Caching" updated by "RFC7641 2. The Observe Option" and "RFC8132 2. Fetch Method".
The Cache Key is a SHA256 digest if libcoap was built with TLS support, otherwise it uses the internal coap_hash() function, using the information abstracted from the PDU and (optionally) the CoAP session. π₯
This Cache Key can then be used to match against incoming PDUs and then appropriate action logic can take place. Main character energy for lookup tables, honestly.
There is support for excluding specific CoAP options from the Cache Key. Examples could be to exclude CoAP BLOCK1 and BLOCK2 Options for the client or server for ease of tracking a large PUT or GET response, but to not exclude these CoAP options in a proxy where it makes sense to cache the individual blocks. π¦
The second is providing Cache Entries (which can be looked up by PDU and hence by Cache Key) which hold additional information to make information tracking simpler. These Cache Entries are automatically deleted when a session closes or a context is deleted. These Cache Entries are maintained on a hashed list for speed of lookup. Slay, efficient queen. π
The following enums are defined.
typedef enum coap_cache_session_based_t {
COAP_CACHE_NOT_SESSION_BASED,
COAP_CACHE_IS_SESSION_BASED
} coap_cache_session_based_t;
typedef enum coap_cache_record_pdu_t {
COAP_CACHE_NOT_RECORD_PDU,
COAP_CACHE_RECORD_PDU
} coap_cache_record_pdu_t;
π FUNCTIONS π
π Function: coap_cache_derive_key() π
The coap_cache_derive_key() function abstracts all the non NoCacheKey CoAP options, ignores the CoAP Observe option and includes a FETCH body from pdu. If session_based is COAP_CACHE_IS_SESSION_BASED, then session pointer is also included. CoAP options can be specifically ignored by the use of coap_cache_ignore_options(). A digest is then built from all of the information and returned. NULL is returned on error. π
π Function: coap_cache_derive_key_w_ignore() π
The coap_cache_derive_key_w_ignore() function abstracts all the non NoCacheKey CoAP options, ignores the CoAP Observe option and includes a FETCH body from pdu. Further options to ignore are specified by the ignore_count of ignore_options. If session_based is COAP_CACHE_IS_SESSION_BASED, then session pointer is also included. A digest is then built from all of the information and returned. NULL is returned on error. β¨
π¦ Function: coap_delete_cache_key() π¦
The coap_delete_cache_key() function deletes the cache_key that was returned from a coap_cache_derive_key() or coap_cache_derive_key_w_ignore() call. Cleanup is self-care, bestie. π§
π Function: coap_cache_ignore_options() π
The coap_cache_ignore_options() function is used to store in context a list of count options held in options. The specified options will not be included in the data used for the coap_cache_derive_key() function. π
π Function: coap_new_cache_entry() π
The coap_new_cache_entry() function will create a new Cache Entry based on the Cache Key derived from the pdu, session_based and session. If record_pdu is COAP_CACHE_RECORD_PDU, then a copy of the pdu is stored in the Cache Entry for subsequent retrieval. The Cache Entry can also store application specific data (coap_cache_set_app_data() and coap_cache_get_app_data()). idle_timeout in seconds defines the length of time not being used before it gets deleted. If idle_timeout is set to 0, then the Cache Entry will not get idle expired. The created Cache Entry is returned, or NULL on error. π
π Function: coap_delete_cache_entry() π
The coap_delete_cache_entry() function can be used to delete the Cache Entry cache_entry held within context. This will remove the Cache Entry from the hash lookup list and free off any internally held data. If the Cache Entry is session based, then it will automatically get deleted when the session is freed off or when the idle timeout expires. π«Ά
π Function: coap_cache_get_by_key() π
The coap_cache_get_by_key() function will locate the Cache Entry held in the context environment that has Cache Key cache_key. Returns NULL if the Cache Key was not found. No cap, if itβs not there itβs not there. π
π₯ Function: coap_cache_get_by_pdu() π₯
The coap_cache_get_by_pdu() function will locate the Cache Entry held in the session environment that has a Cache Key derived from the pdu and whether session_based or not. This function calls coap_cache_derive_key() internally, and so normally coap_cache_ignore_options() would have previously been called with COAP_OPTION_BLOCK1 or COAP_OPTION_BLOCK2 to ignore the values held within these options. π
β Function: coap_cache_get_pdu() β
The coap_cache_get_pdu() function returns the PDU that was stored with the Cache Entry when it was created with coap_new_cache_entry() and record_pdu was set to COAP_CACHE_RECORD_PDU. If a PDU was not initially stored, NULL is returned.
NOTE: A copy of the returned PDU must be taken for use in sending a CoAP packet using coap_pdu_duplicate().
π Function: coap_cache_set_app_data() π
The coap_cache_set_app_data() function is used to associate data with the cache_entry. If callback is not NULL, it points to a function to free off data when the cache_entry is deleted. If any data has been previously stored in the cache_entry, the pointer to the old data will get overwritten, but the old data will not get freed off. β οΈ
The callback handler function prototype is defined as:
/**
* Callback to free off the app data when the cache-entry is
* being deleted / freed off.
*
* @param data The app data to be freed off.
*/
typedef void (*coap_cache_app_data_free_callback_t)(void *data);
πΈ Function: coap_cache_get_app_data() πΈ
The coap_cache_get_app_data() function is used to get the previously stored data in the cache_entry. π«Ά
π RETURN VALUES π
Hereβs the return-value tea, bestie:
coap_cache_derive_key()andcoap_cache_derive_key_w_ignore()returns a newly created Cache Key orNULLif there is a creation failure.coap_cache_ignore_options()returns1if success,0on failure.coap_new_cache_entry(),coap_cache_get_by_key()andcoap_cache_get_by_pdu()return the Cache Entry orNULLif there is a failure.coap_cache_get_pdu()returns the PDU that is held within the Cache Entry orNULLif there is no PDU available.coap_cache_get_app_data()returns the application data value previously set by thecoap_cache_set_app_data()function orNULL.
β¨ EXAMPLES β¨
Okay bestie, this is the part where it all comes together. Like, the whole page was the tutorial and now we get the actual real-world slay π
π«Ά PUT Handler supporting BLOCK1
This is the big one β a full example showing a PUT handler that deals with block-wise transfers and uses the cache to stitch everything together.
#include <coap3/coap.h>
static coap_binary_t *example_data_ptr = NULL;
static int example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN;
static void
cache_free_app_data(void *data) {
coap_binary_t *bdata = (coap_binary_t *)data;
coap_delete_binary(bdata);
}
/*
* Large Data PUT handler
*/
static void
hnd_put_example_data(coap_context_t *ctx,
coap_resource_t *resource,
coap_session_t *session,
coap_pdu_t *request,
coap_binary_t *token,
coap_string_t *query,
coap_pdu_t *response) {
size_t size;
const uint8_t *data;
coap_opt_iterator_t opt_iter;
coap_opt_t *option;
size_t offset;
size_t total;
coap_binary_t *data_so_far;
/* Remove (void) definition if variable is used */
(void)ctx;
(void)token;
(void)query;
if (coap_get_data_large(request, &size, &data, &offset, &total) &&
size != total) {
/*
* A part of the data has been received (COAP_BLOCK_SINGLE_BODY not set).
* However, total unfortunately is only an indication, so it is not safe to
* allocate a block based on total. As per
* https://rfc-editor.org/rfc/rfc7959#section-4
* o In a request carrying a Block1 Option, to indicate the current
* estimate the client has of the total size of the resource
* representation, measured in bytes ("size indication").
*
* coap_cache_ignore_options() must have previously been called with at
* least COAP_OPTION_BLOCK1 set as the option value will change per block.
*/
coap_cache_entry_t *cache_entry = coap_cache_get_by_pdu(session,
request,
COAP_CACHE_IS_SESSION_BASED);
if (offset == 0) {
if (!cache_entry) {
/*
* Set idle_timeout parameter to COAP_MAX_TRANSMIT_WAIT if you want
* early removal on transmission failure. 0 means only delete when
* the session is deleted as session_based is set here.
*/
cache_entry = coap_new_cache_entry(session, request,
COAP_CACHE_NOT_RECORD_PDU,
COAP_CACHE_IS_SESSION_BASED, 0);
} else {
data_so_far = coap_cache_get_app_data(cache_entry);
if (data_so_far) {
coap_delete_binary(data_so_far);
data_so_far = NULL;
}
coap_cache_set_app_data(cache_entry, NULL, NULL);
}
}
if (!cache_entry) {
if (offset == 0) {
coap_log_warn("Unable to create a new cache entry\n");
} else {
coap_log_warn(
"No cache entry available for the non-first BLOCK\n");
}
coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
return;
}
if (size) {
/* Add in the new data to cache entry */
data_so_far = coap_cache_get_app_data(cache_entry);
data_so_far = coap_block_build_body(data_so_far, size, data,
offset, total);
/* Yes, data_so_far can be NULL if error */
coap_cache_set_app_data(cache_entry, data_so_far, cache_free_app_data);
}
if (offset + size == total) {
/* All the data is now in */
data_so_far = coap_cache_get_app_data(cache_entry);
coap_cache_set_app_data(cache_entry, NULL, NULL);
} else {
/* Give us the next block response */
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTINUE);
return;
}
} else {
/* single body of data received */
data_so_far = coap_new_binary(size);
if (data_so_far) {
memcpy(data_so_far->s, data, size);
}
}
if (example_data_ptr) {
/* pre-existed response */
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
coap_delete_binary(example_data_ptr);
} else {
/* just generated response */
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
}
example_data_ptr = data_so_far;
if ((option = coap_check_option(request, COAP_OPTION_CONTENT_FORMAT,
&opt_iter)) != NULL) {
example_data_media_type =
coap_decode_var_bytes(coap_opt_value(option),
coap_opt_length(option));
} else {
example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN;
}
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
coap_resource_notify_observers(resource, NULL);
}
int
main(int argc, char *argv[]) {
coap_context_t *ctx = NULL; /* Set up as normal */
/* ... */
uint16_t cache_ignore_options[] = { COAP_OPTION_BLOCK1,
COAP_OPTION_BLOCK2
};
/* Initialize libcoap library */
coap_startup();
/* Remove (void) definition if variable is used */
(void)argc;
(void)argv;
/* ... */
/* Define the options to ignore when setting up cache-keys */
coap_cache_ignore_options(ctx, cache_ignore_options,
sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
/* ... */
coap_cleanup();
}
Let me narrate the vibe real quick because this example is doing so much π
The handler calls coap_get_data_large() and checks size != total β that means we got part of the data, not the whole thing (so COAP_BLOCK_SINGLE_BODY isn't set). But heads up, bestie: total is only an indication, so it is not safe to allocate a block based on total. That's straight from [RFC 7959 section 4](https://rfc-editor.org/rfc/rfc7959#section-4), where a Block1 request option is just the client's current estimate of the total size of the resource representation in bytes β the "size indication". So don't trust it with your malloc, fr fr.
Also key: coap_cache_ignore_options() must have previously been called with at least COAP_OPTION_BLOCK1 set, because that option's value changes per block and would wreck your cache key. iykyk.
Then it grabs the cache entry with coap_cache_get_by_pdu(session, request, COAP_CACHE_IS_SESSION_BASED). If offset == 0 and there's no entry, it makes one with coap_new_cache_entry(). About that idle_timeout parameter: set it to COAP_MAX_TRANSMIT_WAIT if you want early removal on transmission failure, and 0 means it's only deleted when the session is deleted (since session_based is set here).
If there's no cache entry at all, it warns and bails with COAP_RESPONSE_CODE_INTERNAL_ERROR β the message is either Unable to create a new cache entry (when offset == 0) or No cache entry available for the non-first BLOCK.
When size is nonzero it appends via coap_block_build_body() and stores it back with cache_free_app_data as the free callback. And yes β the comment says it: data_so_far can be NULL if there's an error. π
Then if offset + size == total all data is in, so it pulls it out and clears the cache app data. Otherwise it sets COAP_RESPONSE_CODE_CONTINUE and returns β basically "give us the next block."
In the else branch, a single body of data came in, so it just does coap_new_binary(size) and memcpys.
Finally: if example_data_ptr already existed you get COAP_RESPONSE_CODE_CHANGED and the old one gets deleted; otherwise it's COAP_RESPONSE_CODE_CREATED. Content format comes from coap_check_option() with COAP_OPTION_CONTENT_FORMAT and coap_decode_var_bytes(), falling back to COAP_MEDIATYPE_TEXT_PLAIN. Then COAP_RESPONSE_CODE_CHANGED again and coap_resource_notify_observers(resource, NULL) to tell the observers something's new β¨
In main(), the cache_ignore_options[] array holds COAP_OPTION_BLOCK1 and COAP_OPTION_BLOCK2, and it's handed to coap_cache_ignore_options() with that classic sizeof(...)/sizeof(...[0]) count. Don't forget coap_startup() at the top and coap_cleanup() at the bottom, bestie π
π SEE ALSO π
Go read these next, they're all besties of this page:
π¦ FURTHER INFORMATION π¦
If you want the actual lore, go read:
"RFC7252: The Constrained Application Protocol (CoAP)"
"RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)"
That's where the further information lives π
π BUGS π
Found a bug? Not on your watch, bestie. Please raise an issue on GitHub at https://github.com/obgm/libcoap/issues to report any bugs.
Got a fix instead? Please raise a Pull Request at https://github.com/obgm/libcoap/pulls for any fixes. Slay π₯
π AUTHORS π
The libcoap project <libcoap-developers@lists.sourceforge.net>
And that's the whole page, bestie! π«Ά This was coap_cache version 4.3.5, dated 08/24/2026, and it's the COAP_CACHE(3) manual page. Go build something iconic with it β¨π
π¦ where this page comes from
- ubuntu stonking: libcoap3t64 4.3.5-4ubuntu1 Β· BSD-2-Clause
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms