• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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