mirror of
https://github.com/obgm/libcoap.git
synced 2026-11-08 10:32:53 +03:00
This then makes sure that all things libcoap are properly initialized before use, and coders realize they need to do this. Note: coap_new_context() continues to make sure that coap_startup() is invoked if not done previously, but this may be too late.
428 lines
14 KiB
Plaintext
428 lines
14 KiB
Plaintext
// -*- mode:doc; -*-
|
|
// vim: set syntax=asciidoc tw=0
|
|
|
|
coap_cache(3)
|
|
=============
|
|
:doctype: manpage
|
|
:man source: coap_cache
|
|
:man version: @PACKAGE_VERSION@
|
|
:man manual: libcoap Manual
|
|
|
|
NAME
|
|
----
|
|
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,
|
|
coap_cache_get_app_data
|
|
- Work with CoAP cache functions
|
|
|
|
SYNOPSIS
|
|
--------
|
|
*#include <coap@LIBCOAP_API_VERSION@/coap.h>*
|
|
|
|
*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_);*
|
|
|
|
For specific (D)TLS library support, link with
|
|
*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*,
|
|
*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*
|
|
or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with
|
|
*-lcoap-@LIBCOAP_API_VERSION@* 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.
|
|
|
|
The first is the ability to derive a Cache Key from the cacheable parts of a
|
|
CoAP PDU as defined in
|
|
"https://rfc-editor.org/rfc/rfc7252#section-5.6[RFC7252 5.6. Caching]"
|
|
updated by
|
|
"https://rfc-editor.org/rfc/rfc7641#section-2[RFC7641 2. The Observe Option]"
|
|
and
|
|
"https://rfc-editor.org/rfc/rfc8132#section-2[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.
|
|
|
|
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.
|
|
|
|
The following enums are defined.
|
|
|
|
[source, c]
|
|
----
|
|
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.
|
|
|
|
*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.
|
|
|
|
*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:
|
|
[source, c]
|
|
----
|
|
typedef void (*coap_cache_app_data_free_callback_t)(void *data);
|
|
----
|
|
where _data_ is passed into the callback function whenever the Cache Entry is
|
|
deleted.
|
|
|
|
*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
|
|
-------------
|
|
*coap_cache_derive_key*() and *coap_cache_derive_key_w_ignore*()
|
|
returns a newly created Cache Key or NULL if there is a creation failure.
|
|
|
|
*coap_cache_ignore_options*() returns 1 if success, 0 on failure.
|
|
|
|
*coap_new_cache_entry*(), *coap_cache_get_by_key*() and
|
|
*coap_cache_get_by_pdu*() return the Cache Entry or NULL if there
|
|
is a failure.
|
|
|
|
*coap_cache_get_pdu*() returns the PDU that is held within the Cache Entry or
|
|
NULL if there is no PDU available.
|
|
|
|
*coap_cache_get_app_data*() returns the application data value
|
|
previously set by the *coap_cache_set_app_data*() function or NULL.
|
|
|
|
EXAMPLES
|
|
--------
|
|
*PUT Handler supporting BLOCK1*
|
|
|
|
[source, c]
|
|
----
|
|
#include <coap@LIBCOAP_API_VERSION@/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();
|
|
|
|
}
|
|
----
|
|
|
|
SEE ALSO
|
|
--------
|
|
*coap_block*(3), *coap_init*(3), *coap_pdu_setup*(3), *coap_resource*(3)
|
|
and *coap_string*(3)
|
|
|
|
FURTHER INFORMATION
|
|
-------------------
|
|
See
|
|
|
|
"https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]"
|
|
|
|
"https://rfc-editor.org/rfc/rfc7959[RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)]"
|
|
|
|
for further information.
|
|
|
|
BUGS
|
|
----
|
|
Please report bugs on the mailing list for libcoap:
|
|
libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
|
|
https://github.com/obgm/libcoap/issues
|
|
|
|
AUTHORS
|
|
-------
|
|
The libcoap project <libcoap-developers@lists.sourceforge.net>
|