1// -*- mode:doc; -*- 2// vim: set syntax=asciidoc tw=0 3 4coap_cache(3) 5============= 6:doctype: manpage 7:man source: coap_cache 8:man version: @PACKAGE_VERSION@ 9:man manual: libcoap Manual 10 11NAME 12---- 13coap_cache, 14coap_cache_derive_key, 15coap_cache_derive_key_w_ignore, 16coap_delete_cache_key, 17coap_cache_ignore_options, 18coap_new_cache_entry, 19coap_delete_cache_entry, 20coap_cache_get_by_key, 21coap_cache_get_by_pdu, 22coap_cache_get_pdu, 23coap_cache_set_app_data, 24coap_cache_get_app_data 25- Work with CoAP cache functions 26 27SYNOPSIS 28-------- 29*#include <coap@LIBCOAP_API_VERSION@/coap.h>* 30 31*coap_cache_key_t *coap_cache_derive_key(const coap_session_t *_session_, 32const coap_pdu_t *_pdu_, coap_cache_session_based_t _session_based_);* 33 34*coap_cache_key_t *coap_cache_derive_key_w_ignore( 35const coap_session_t *_session_, const coap_pdu_t *_pdu_, 36coap_cache_session_based_t _session_based_, 37const uint16_t *_ignore_options_, size_t _ignore_count_);* 38 39*void coap_delete_cache_key(coap_cache_key_t *_cache_key_);* 40 41*int coap_cache_ignore_options(coap_context_t *_context_, 42const uint16_t *_options_, size_t _count_);* 43 44*coap_cache_entry_t *coap_new_cache_entry(coap_session_t *_session_, 45const coap_pdu_t *_pdu_, coap_cache_record_pdu_t _record_pdu_, 46coap_cache_session_based_t _session_based_, unsigned int _idle_timeout_);* 47 48*void coap_delete_cache_entry(coap_context_t *_context_, 49coap_cache_entry_t *_cache_entry_);* 50 51*coap_cache_entry_t *coap_cache_get_by_key(coap_context_t *_context_, 52const coap_cache_key_t *_cache_key_);* 53 54*coap_cache_entry_t *coap_cache_get_by_pdu(coap_session_t *_session_, 55const coap_pdu_t *_pdu_, coap_cache_session_based_t _session_based_);* 56 57*const coap_pdu_t *coap_cache_get_pdu(const coap_cache_entry_t *_cache_entry_);* 58 59*void coap_cache_set_app_data(coap_cache_entry_t *_cache_entry_, void *_data_, 60coap_cache_app_data_free_callback_t _callback_);* 61 62*void *coap_cache_get_app_data(const coap_cache_entry_t *_cache_entry_);* 63 64For specific (D)TLS library support, link with 65*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, 66*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls* 67or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with 68*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. 69 70DESCRIPTION 71----------- 72 73The CoAP Cache provides support for two opaque objects that can be used for 74tracking requests and responses. 75 76The first is the ability to derive a Cache Key from the cacheable parts of a 77CoAP PDU as defined in 78"https://rfc-editor.org/rfc/rfc7252#section-5.6[RFC7252 5.6. Caching]" 79updated by 80"https://rfc-editor.org/rfc/rfc7641#section-2[RFC7641 2. The Observe Option]" 81and 82"https://rfc-editor.org/rfc/rfc8132#section-2[RFC8132 2. Fetch Method]". 83 84The Cache Key is a SHA256 digest if libcoap was built with TLS support, 85otherwise it uses the internal coap_hash() function, using the information 86abstracted from the PDU and (optionally) the CoAP session. 87 88This Cache Key can then be used to match against incoming PDUs and then 89appropriate action logic can take place. 90 91There is support for excluding specific CoAP options from the Cache Key. 92Examples could be to exclude CoAP BLOCK1 and BLOCK2 Options for the client or 93server for ease of tracking a large PUT or GET response, but to not exclude 94these CoAP options in a proxy where it makes sense to cache the individual 95blocks. 96 97The second is providing Cache Entries (which can be looked up by PDU and hence 98by Cache Key) which hold additional information to make information tracking 99simpler. These Cache Entries are automatically deleted when a session closes 100or a context is deleted. These Cache Entries are maintained on a hashed list 101for speed of lookup. 102 103The following enums are defined. 104 105[source, c] 106---- 107typedef enum coap_cache_session_based_t { 108 COAP_CACHE_NOT_SESSION_BASED, 109 COAP_CACHE_IS_SESSION_BASED 110} coap_cache_session_based_t; 111 112typedef enum coap_cache_record_pdu_t { 113 COAP_CACHE_NOT_RECORD_PDU, 114 COAP_CACHE_RECORD_PDU 115} coap_cache_record_pdu_t; 116---- 117 118FUNCTIONS 119--------- 120 121*Function: coap_cache_derive_key()* 122 123The *coap_cache_derive_key*() function abstracts all the non NoCacheKey CoAP 124options, ignores the CoAP Observe option and includes a FETCH body from _pdu_. 125If _session_based_ is COAP_CACHE_IS_SESSION_BASED, then _session_ pointer is 126also included. CoAP options can be specifically ignored by the use of 127*coap_cache_ignore_options*(). A digest is then built from all of the 128information and returned. NULL is returned on error. 129 130*Function: coap_cache_derive_key_w_ignore()* 131 132The *coap_cache_derive_key_w_ignore*() function abstracts all the non 133NoCacheKey CoAP options, ignores the CoAP Observe option and includes a FETCH 134body from _pdu_. Further options to ignore are specified by the _ignore_count_ 135of _ignore_options_. If _session_based_ is COAP_CACHE_IS_SESSION_BASED, then 136_session_ pointer is also included. A digest is then built from all of the 137information and returned. NULL is returned on error. 138 139*Function: coap_delete_cache_key()* 140 141The *coap_delete_cache_key*() function deletes the _cache_key_ that was 142returned from a *coap_cache_derive_key*() or 143*coap_cache_derive_key_w_ignore*() call. 144 145*Function: coap_cache_ignore_options()* 146 147The *coap_cache_ignore_options*() function is used to store in _context_ a 148list of _count_ options held in _options_. The specified _options_ will not 149be included in the data used for the *coap_cache_derive_key*() function. 150 151*Function: coap_new_cache_entry()* 152 153The *coap_new_cache_entry*() function will create a new Cache Entry based on 154the Cache Key derived from the _pdu_, _session_based_ and _session_. If 155_record_pdu_ is COAP_CACHE_RECORD_PDU, then a copy of the _pdu_ is stored in 156the Cache Entry for subsequent retrieval. The Cache Entry can also store 157application specific data (*coap_cache_set_app_data*() and 158*coap_cache_get_app_data*()). _idle_timeout_ in seconds defines the length of 159time not being used before it gets deleted. If _idle_timeout_ is set to 1600, then the Cache Entry will not get idle expired. The created Cache 161Entry is returned, or NULL on error. 162 163*Function: coap_delete_cache_entry()* 164 165The *coap_delete_cache_entry*() function can be used to delete the Cache Entry 166_cache_entry_ held within _context_. This will remove the Cache Entry from 167the hash lookup list and 168free off any internally held data. If the Cache Entry is session based, then 169it will automatically get deleted when the session is freed off or when the 170idle timeout expires. 171 172*Function: coap_cache_get_by_key()* 173 174The *coap_cache_get_by_key*() function will locate the Cache Entry held in the 175_context_ environment that has Cache Key _cache_key_. Returns NULL if the 176Cache Key was not found. 177 178*Function: coap_cache_get_by_pdu()* 179 180The *coap_cache_get_by_pdu*() function will locate the Cache Entry held in the 181_session_ environment that has a Cache Key derived from the _pdu_ and 182whether _session_based_ or not. This function calls *coap_cache_derive_key*() 183internally, and so normally *coap_cache_ignore_options*() would have 184previously been called with COAP_OPTION_BLOCK1 or COAP_OPTION_BLOCK2 to 185ignore the values held within these options. 186 187*Function: coap_cache_get_pdu()* 188 189The *coap_cache_get_pdu*() function returns the PDU that was stored with the 190Cache Entry when it was created with *coap_new_cache_entry*() and _record_pdu_ 191was set to COAP_CACHE_RECORD_PDU. If a PDU was not initially stored, NULL is 192returned. + 193*NOTE:* A copy of the returned PDU must be taken for use in sending a CoAP 194packet using *coap_pdu_duplicate*(). 195 196*Function: coap_cache_set_app_data()* 197 198The *coap_cache_set_app_data*() function is used to associate _data_ with the 199_cache_entry_. If _callback_ is not NULL, it points to a function to free off 200_data_ when the _cache_entry_ is deleted. If any data has been previously 201stored in the _cache_entry_, the pointer to the old data will get overwritten, 202but the old data will not get freed off. 203 204The _callback_ handler function prototype is defined as: 205[source, c] 206---- 207typedef void (*coap_cache_app_data_free_callback_t)(void *data); 208---- 209where _data_ is passed into the callback function whenever the Cache Entry is 210deleted. 211 212*Function: coap_cache_get_app_data()* 213 214The *coap_cache_get_app_data*() function is used to get the previously stored 215_data_ in the _cache_entry_. 216 217RETURN VALUES 218------------- 219*coap_cache_derive_key*() and *coap_cache_derive_key_w_ignore*() 220returns a newly created Cache Key or NULL if there is a creation failure. 221 222*coap_cache_ignore_options*() returns 1 if success, 0 on failure. 223 224*coap_new_cache_entry*(), *coap_cache_get_by_key*() and 225*coap_cache_get_by_pdu*() return the Cache Entry or NULL if there 226is a failure. 227 228*coap_cache_get_pdu*() returns the PDU that is held within the Cache Entry or 229NULL if there is no PDU available. 230 231*coap_cache_get_app_data*() returns the application data value 232previously set by the *coap_cache_set_app_data*() function or NULL. 233 234EXAMPLES 235-------- 236*PUT Handler supporting BLOCK1* 237 238[source, c] 239---- 240#include <coap@LIBCOAP_API_VERSION@/coap.h> 241 242static coap_binary_t *example_data_ptr = NULL; 243static int example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN; 244 245static void 246cache_free_app_data(void *data) { 247 coap_binary_t *bdata = (coap_binary_t*)data; 248 coap_delete_binary(bdata); 249} 250 251/* 252 * Large Data PUT handler 253 */ 254 255static void 256hnd_put_example_data(coap_context_t *ctx, 257 coap_resource_t *resource, 258 coap_session_t *session, 259 coap_pdu_t *request, 260 coap_binary_t *token, 261 coap_string_t *query, 262 coap_pdu_t *response 263) { 264 size_t size; 265 const uint8_t *data; 266 coap_opt_iterator_t opt_iter; 267 coap_opt_t *option; 268 size_t offset; 269 size_t total; 270 coap_binary_t *data_so_far; 271 272 /* Remove (void) definition if variable is used */ 273 (void)ctx; 274 (void)token; 275 (void)query; 276 277 if (coap_get_data_large(request, &size, &data, &offset, &total) && 278 size != total) { 279 /* 280 * A part of the data has been received (COAP_BLOCK_SINGLE_BODY not set). 281 * However, total unfortunately is only an indication, so it is not safe to 282 * allocate a block based on total. As per 283 * https://rfc-editor.org/rfc/rfc7959#section-4 284 * o In a request carrying a Block1 Option, to indicate the current 285 * estimate the client has of the total size of the resource 286 * representation, measured in bytes ("size indication"). 287 * 288 * coap_cache_ignore_options() must have previously been called with at 289 * least COAP_OPTION_BLOCK1 set as the option value will change per block. 290 */ 291 coap_cache_entry_t *cache_entry = coap_cache_get_by_pdu(session, 292 request, 293 COAP_CACHE_IS_SESSION_BASED); 294 295 if (offset == 0) { 296 if (!cache_entry) { 297 /* 298 * Set idle_timeout parameter to COAP_MAX_TRANSMIT_WAIT if you want 299 * early removal on transmission failure. 0 means only delete when 300 * the session is deleted as session_based is set here. 301 */ 302 cache_entry = coap_new_cache_entry(session, request, 303 COAP_CACHE_NOT_RECORD_PDU, 304 COAP_CACHE_IS_SESSION_BASED, 0); 305 } 306 else { 307 data_so_far = coap_cache_get_app_data(cache_entry); 308 if (data_so_far) { 309 coap_delete_binary(data_so_far); 310 data_so_far = NULL; 311 } 312 coap_cache_set_app_data(cache_entry, NULL, NULL); 313 } 314 } 315 if (!cache_entry) { 316 if (offset == 0) { 317 coap_log_warn("Unable to create a new cache entry\n"); 318 } 319 else { 320 coap_log_warn( 321 "No cache entry available for the non-first BLOCK\n"); 322 } 323 coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); 324 return; 325 } 326 327 if (size) { 328 /* Add in the new data to cache entry */ 329 data_so_far = coap_cache_get_app_data(cache_entry); 330 data_so_far = coap_block_build_body(data_so_far, size, data, 331 offset, total); 332 /* Yes, data_so_far can be NULL if error */ 333 coap_cache_set_app_data(cache_entry, data_so_far, cache_free_app_data); 334 } 335 if (offset + size == total) { 336 /* All the data is now in */ 337 data_so_far = coap_cache_get_app_data(cache_entry); 338 coap_cache_set_app_data(cache_entry, NULL, NULL); 339 } 340 else { 341 /* Give us the next block response */ 342 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTINUE); 343 return; 344 } 345 } 346 else { 347 /* single body of data received */ 348 data_so_far = coap_new_binary(size); 349 if (data_so_far) { 350 memcpy(data_so_far->s, data, size); 351 } 352 } 353 354 if (example_data_ptr) { 355 /* pre-existed response */ 356 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); 357 coap_delete_binary(example_data_ptr); 358 } 359 else 360 /* just generated response */ 361 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED); 362 363 example_data_ptr = data_so_far; 364 if ((option = coap_check_option(request, COAP_OPTION_CONTENT_FORMAT, 365 &opt_iter)) != NULL) { 366 example_data_media_type = 367 coap_decode_var_bytes (coap_opt_value (option), 368 coap_opt_length (option)); 369 } 370 else { 371 example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN; 372 } 373 374 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); 375 coap_resource_notify_observers(resource, NULL); 376} 377 378int 379main(int argc, char* argv[]) { 380 coap_context_t *ctx = NULL; /* Set up as normal */ 381 /* ... */ 382 uint16_t cache_ignore_options[] = { COAP_OPTION_BLOCK1, 383 COAP_OPTION_BLOCK2 }; 384 385 /* Initialize libcoap library */ 386 coap_startup(); 387 388 /* Remove (void) definition if variable is used */ 389 (void)argc; 390 (void)argv; 391 392 /* ... */ 393 394 /** Define the options to ignore when setting up cache-keys */ 395 coap_cache_ignore_options(ctx, cache_ignore_options, 396 sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0])); 397 398 /* ... */ 399 coap_cleanup(); 400 401} 402---- 403 404SEE ALSO 405-------- 406*coap_block*(3), *coap_init*(3), *coap_pdu_setup*(3), *coap_resource*(3) 407and *coap_string*(3) 408 409FURTHER INFORMATION 410------------------- 411See 412 413"https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]" 414 415"https://rfc-editor.org/rfc/rfc7959[RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)]" 416 417for further information. 418 419BUGS 420---- 421Please report bugs on the mailing list for libcoap: 422libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at 423https://github.com/obgm/libcoap/issues 424 425AUTHORS 426------- 427The libcoap project <libcoap-developers@lists.sourceforge.net> 428