• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// -*- mode:doc; -*-
2// vim: set syntax=asciidoc tw=0
3
4coap_block(3)
5=============
6:doctype: manpage
7:man source:   coap_block
8:man version:  @PACKAGE_VERSION@
9:man manual:   libcoap Manual
10
11NAME
12----
13coap_block,
14coap_context_set_block_mode,
15coap_add_data_large_request,
16coap_add_data_large_response,
17coap_get_data_large,
18coap_block_build_body,
19coap_q_block_is_supported
20- Work with CoAP Blocks
21
22SYNOPSIS
23--------
24*#include <coap@LIBCOAP_API_VERSION@/coap.h>*
25
26*void coap_context_set_block_mode(coap_context_t *_context_,
27uint8_t _block_mode_);*
28
29*int coap_add_data_large_request(coap_session_t *_session_,
30coap_pdu_t *_pdu_, size_t _length_, const uint8_t *_data_,
31coap_release_large_data_t _release_func_, void *_app_ptr_);*
32
33*int coap_add_data_large_response(coap_resource_t *_resource_,
34coap_session_t *_session_, const coap_pdu_t *_request_, coap_pdu_t *_response_,
35const coap_string_t *query, uint16_t _media_type_, int _maxage_,
36uint64_t etag, size_t _length_, const uint8_t *_data_,
37coap_release_large_data_t _release_func_, void *_app_ptr_);*
38
39*int coap_get_data_large(const coap_pdu_t *_pdu_, size_t *_length,
40const uint8_t **_data_, size_t *_offset_, size_t *_total_);*
41
42*coap_binary_t *coap_block_build_body(coap_binary_t *_body_data_,
43size_t _length_, const uint8_t *_data_, size_t _offset_, size_t _total_);*
44
45*int coap_q_block_is_supported(void);*
46
47For specific (D)TLS library support, link with
48*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*,
49*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*
50or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*.   Otherwise, link with
51*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support.
52
53DESCRIPTION
54-----------
55Regular setting up of a PDU and transmission is covered in *coap_pdu_setup*(3)
56where all the payload data can fit in a single packet.  This man page covers
57how to work with PDUs where the overall body of information may need to be
58split across several packets by using CoAP Block-Wise Transfers
59(https://rfc-editor.org/rfc/rfc7959[RFC7959] and
60https://rfc-editor.org/rfc/rfc9177[RFC9177]).
61
62The block-wise transfers can be controlled by the application, or libcoap is
63instructed to do all the requests for the next blocks and only present the
64final body of the result to the application.  In summary, the following three
65ways handle processing a body of data that has to be split across multiple
66payloads (blocks).
67
681. Application does all the work +
69It is the responsibility of the application to analyze each block transmission
70at receipt and then generate the next request as per
71https://rfc-editor.org/rfc/rfc7959[RFC7959].  In this case,
72*coap_context_set_block_mode*() function must not be called to maintain
73backward compatibility with applications that did the block handling within the
74application.
75
762. Application sees individual blocks +
77By calling *coap_context_set_block_mode(context, COAP_BLOCK_USE_LIBCOAP)* and
78using the appropriate functions, the requests for the next block of data is
79handled automatically by the libcoap layer.  Each individual block of data is
80presented to the application for processing. +
81By calling *coap_get_data_large*(), the application can determine if this is
82the first block or not (using _offset_ value), whether the first block is all
83the data (_offset_ = 0, _length_ = _total_) and whether this is the last block
84(_offset_ + _length_ = _total_). It is the responsibility of the application to
85re-assemble the individual blocks into a single body of data. +
86*NOTE:* _total_ is only an approximation (it will be > _offset_ + _length_)
87until the final block is received. +
88If this is the request handler in a server, the server still needs to return a
89COAP_RESPONSE_CODE_CONTINUE 2.31 (Continue) response code if the received data
90is not for the final block, otherwise a COAP_RESPONSE_CODE_CREATED 2.01
91(Created) or COAP_RESPONSE_CODE_CHANGED 2.04 (Changed) should be returned.
92
933. Application only sees all of the body +
94By calling *coap_context_set_block_mode(context,
95COAP_BLOCK_USE_LIBCOAP|COAP_BLOCK_SINGLE_BODY)* and using the appropriate
96functions, the requests for all the blocks of data is handled automatically by
97the libcoap layer.  Only the complete body of the data is presented to the
98application, unless there is an error. +
99*coap_get_data_large*() will only return the entire body of data (_offset_
100always 0, _length_ = _total_) and there is no need to re-assemble individual
101blocks into a large body of data. +
102In RAM constrained environments, option 2 may be the preferred method.
103
104This man page focuses on getting libcoap to do all the work, not how to do it
105all in the application.
106
107However, if the client supplies a Block1 or Block2 Option in the PDU where the
108block number is not 0, this is assumed to be a random access request and any
109other blocks will not be requested by libcoap even if instructed otherwise.
110
111The functions that are named *_large* are intended as replacements for the
112equivalent functions as described in *coap_pdu_setup*(3).
113
114CALLBACK HANDLER
115----------------
116
117*Callback Type: coap_release_large_data_t*
118
119[source, c]
120----
121/**
122 * Callback handler for de-allocating the data based on @p app_ptr provided to
123 * coap_add_data_large_*() functions following transmission of the supplied
124 * data.
125 *
126 * @param session The session that this data is associated with
127 * @param app_ptr The application provided pointer to the
128 *                coap_add_data_large_*() functions
129 */
130typedef void (*coap_release_large_data_t)(coap_session_t *session,
131                                          void *app_ptr);
132----
133
134FUNCTIONS
135---------
136
137*Function: coap_context_set_block_mode()*
138
139The *coap_context_set_block_mode*() function is used to set up the _context_
140level _block_mode_ block handling bits for supporting
141https://rfc-editor.org/rfc/rfc7959[RFC7959] _block_mode_
142flows down to a session when a session is created and if the peer does not
143support the respective block mode, an appropriate bit may get disabled in the
144session _block_mode_.
145
146[source, c]
147----
148#define COAP_BLOCK_USE_LIBCOAP   0x01 /* Use libcoap to do block requests */
149#define COAP_BLOCK_SINGLE_BODY   0x02 /* Deliver the data as a single body */
150#define COAP_BLOCK_TRY_Q_BLOCK   0x04 /* Try Q-Block method */
151#define COAP_BLOCK_USE_M_Q_BLOCK 0x08 /* Use M bit when recovering Q-Block2 */
152#define COAP_BLOCK_NO_PREEMPTIVE_RTAG 0x10 /* Don't use pre-emptive Request-Tags */
153----
154_block_mode_ is an or'd set of zero or more COAP_BLOCK_* definitions.
155
156If *COAP_BLOCK_USE_LIBCOAP* is not set, then everything works as per Option 1
157above.
158
159If *COAP_BLOCK_SINGLE_BODY* is set, then the entire body of data is presented to
160the receiving handler, otherwise each individual block is presented on arrival.
161To obtain the data, length and current offset, *coap_get_data_large*() must
162be used instead of *coap_get_data*().  It may be appropriate not to set
163*COAP_BLOCK_SINGLE_BODY* if there are RAM limitations.
164
165*NOTE:* It is the responsibility of the receiving application to re-assemble
166the _data_ as appropriate (e.g., using *coap_block_build_body*()) if
167*COAP_BLOCK_SINGLE_BODY* is not set.
168
169*NOTE:* If *COAP_BLOCK_SINGLE_BODY* is not set, then the CoAP server on
170receiving
171request data that is split over multiple data blocks must respond with
172COAP_RESPONSE_CODE_CONTINUE 2.31 (Continue) response code if the received data
173is not for the final block, otherwise a COAP_RESPONSE_CODE_CREATED 2.01
174(Created) or COAP_RESPONSE_CODE_CHANGED 2.04 (Changed) should be returned.
175
176To indicate support for Q-Block-1 and Q-Block2, *COAP_BLOCK_TRY_Q_BLOCK* needs
177to be set on both the client and server.  *COAP_BLOCK_SINGLE_BODY* is assumed to
178be set if using Q-Block as the data will always be presented as a single body.
179If *COAP_BLOCK_USE_M_Q_BLOCK* is defined, then the 'M' bit version of recovery
180will be used if possible.
181
182If *COAP_BLOCK_USE_LIBCOAP* is set, then any PDUs presented to the application
183handlers will get the tokens set back to the initiating token so that requests
184can be matched with responses even if different tokens had to be used for the
185series of packet interchanges.  Furthermore, if *COAP_BLOCK_SINGLE_BODY* is set,
186then the PDU that presents the entire body will have any BlockX or Q-BlockX
187option removed.
188
189*NOTE:* *COAP_BLOCK_USE_LIBCOAP* must be set if libcoap is to do all the
190block tracking and requesting, otherwise the application will have to do all
191of this work (the default if *coap_context_set_block_mode*() is not called).
192
193If *COAP_BLOCK_NO_PREEMPTIVE_RTAG* is set, then Request-Tag options are only
194sent when a large amount of data is being sent to the server using the Block1
195option.  Otherwise, a Request-Tag option is sent with any request (apart from
196DELETE) on the off chance that there may be multiple Block2 based
197responses for multiple requests to the same resource that need to be
198differentiated between.
199
200*Function: coap_add_data_large_request()*
201
202The *coap_add_data_large_request*() function is similar to *coap_add_data*(),
203but supports the transmission of data that has a body size that is potentially
204larger than can be fitted into a single client request PDU. The specified
205payload _data_ of length _length_ is associated with the _session_ with the
206first block of data added to the PDU _pdu_ along with the appropriate CoAP
207options such as (Q-)Block1, Size1 and Request-Tag if the data does not fit in
208a single PDU.
209
210When the block receipt has been acknowledged by the peer, the library
211will then send the next block of data until all the data has been transmitted.
212
213The _data_ passed to the
214function *coap_add_data_large_request*() must exist until all blocks have been
215transmitted. The callback function _release_func_ can be used to release
216storage that has been dynamically allocated to hold the transmit data. If not
217NULL, the callback function is called once the final block of _data_ has been
218transmitted. The user-defined parameter _app_ptr_ is the same value that was
219passed to *coap_add_data_large_request*().
220
221*NOTE:* This function must only be called once per _pdu_.
222
223*NOTE:* Options cannot be added to the _pdu_ after
224coap_add_data_large_request() is called.
225
226*Function: coap_add_data_large_response()*
227
228The *coap_add_data_large_response*() function is responsible for handling
229the server's large responses to requests. *coap_add_data_large_response*()
230should be used as a direct replacement for *coap_add_data*() if it is possible
231that the _length_ of _data_ will not fit in a single server's response pdu.
232This function adds in the initial part of the payload _data_ of length
233_length_ to the PDU _pdu_.
234
235The _data_ passed to the function
236*coap_add_data_large_response*() must exist until all blocks have been
237transmitted. The callback function _release_func_ can be used to release
238storage that has been dynamically allocated to hold the transmit data. If not
239NULL, the callback function is called once the final block of _data_ has been
240transmitted. The user-defined parameter _app_ptr_ is the same value that was
241passed to *coap_add_data_large_response*().
242
243It also adds in the appropriate CoAP options such as Block2, Size2 and ETag to
244handle block-wise transfer if the data does not fit in a single PDU.
245
246_resource_, _query_, _session_, _request_, and _response_ are the same
247parameters as in the called resource handler that invokes
248*coap_add_data_large_response*(). If _etag_ is 0, then a unique ETag value will
249be generated, else is the ETag value to use.
250The _media_type_ is for the format of the _data_ and _maxage_ defines the
251lifetime of the response.  If _maxage_ is set to -1,  then the Max-Age option
252does not get included (which indicates the default value of 60 seconds
253according to
254"https://rfc-editor.org/rfc/rfc7252#section-5.6.1[RFC7252 5.6.1. Freshness
255Model]").
256
257The application request handler for the resource is only called once instead of
258potentially multiple times.
259
260*NOTE:* This function must only be called once per _pdu_.
261
262*NOTE:* Options cannot be added to the _pdu_ after
263coap_add_data_large_request() is called.
264
265*Function: coap_get_data_large()*
266
267The *coap_get_data_large*() function is used abstract from the _pdu_
268information about the received data by updating _length_ with the length of
269data available, _data_ with a pointer to where the data is located, _offset_
270with where this block of data starts and _total_ with the total amount of data.
271_offset_ will always be zero if block_mode includes COAP_BLOCK_SINGLE_BODY.
272All of the body's data has been received if "_offset_ + _length_ == _total_".
273
274*NOTE:* _total_ is potentially only an indication of the total size of the
275body and is only exact when all of the data has been received.
276
277*Function: coap_block_build_body()*
278
279The *coap_block_build_body*() function is used to re-assemble the received
280data as returned by *coap_get_data_large*() into a single blob of data. Data
281from _data_ of length _length_ starting from offset _offset_ is added to
282_body_data_.  The resultant state of _body_data_ is returned. If _body_data_
283is NULL, or _total_ is larger than the current size of _body_data_, then
284_body_data_ is re-allocated and returned.  If there is an error, _body_data_
285gets de-allocated.
286
287If _block_mode_ (as set by *coap_context_set_block_mode*()) includes
288COAP_BLOCK_SINGLE_BODY, then the request/response handler will only get called
289once with the entire body containing the data from all of the individual
290blocks. If there is a change of data during the blocks receipt (e.g., ETag
291value changes), then the entire set of data is re-requested and the partial
292body dropped.
293
294*Function: coap_q_block_is_supported()*
295
296The *coap_q_block_is_supported*() function is used to determine whether
297libcoap has been build with Q-Block support or not.
298
299RETURN VALUES
300-------------
301*coap_add_data_large_request*(), *coap_add_data_large_response*(), and
302*coap_get_data_large*() return 0 on failure, 1 on success.
303
304*coap_block_build_body*() returns the current state of the body's data
305(which may have some missing gaps) or NULL on error.
306
307*coap_q_block_is_supported*() returns 0 on failure, 1 on success.
308
309EXAMPLES
310--------
311*Setup PDU and Transmit*
312
313[source, c]
314----
315#include <coap@LIBCOAP_API_VERSION@/coap.h>
316
317static int
318build_send_pdu(coap_context_t *context, coap_session_t *session,
319uint8_t msgtype, uint8_t request_code, const char *uri, const char *query,
320unsigned char *data, size_t length, int observe) {
321
322  coap_pdu_t *pdu;
323  uint8_t buf[1024];
324  size_t buflen;
325  uint8_t *sbuf = buf;
326  int res;
327  coap_optlist_t *optlist_chain = NULL;
328  /* Remove (void) definition if variable is used */
329  (void)context;
330
331  /* Create the pdu with the appropriate options */
332  pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session),
333                      coap_session_max_pdu_size(session));
334  if (!pdu)
335    return 0;
336
337  /*
338   * Create unique token for this request for handling unsolicited /
339   * delayed responses
340   */
341  coap_session_new_token(session, &buflen, buf);
342  if (!coap_add_token(pdu, buflen, buf)) {
343    coap_log_debug("cannot add token to request\n");
344    goto error;
345  }
346
347  if (uri) {
348    /* Add in the URI options */
349    buflen = sizeof(buf);
350    res = coap_split_path((const uint8_t*)uri, strlen(uri), sbuf, &buflen);
351    while (res--) {
352      if (!coap_insert_optlist(&optlist_chain,
353                               coap_new_optlist(COAP_OPTION_URI_PATH,
354                        coap_opt_length(sbuf), coap_opt_value(sbuf))))
355        goto error;
356      sbuf += coap_opt_size(sbuf);
357    }
358  }
359
360  if (query) {
361    /* Add in the QUERY options */
362    buflen = sizeof(buf);
363    res = coap_split_query((const uint8_t*)query, strlen(query), sbuf, &buflen);
364    while (res--) {
365      if (!coap_insert_optlist(&optlist_chain,
366                               coap_new_optlist(COAP_OPTION_URI_QUERY,
367                        coap_opt_length(sbuf), coap_opt_value(sbuf))))
368        goto error;
369      sbuf += coap_opt_size(sbuf);
370    }
371  }
372
373  if (request_code == COAP_REQUEST_GET && observe) {
374    /* Indicate that we want to observe this resource */
375    if (!coap_insert_optlist(&optlist_chain,
376                             coap_new_optlist(COAP_OPTION_OBSERVE,
377                               coap_encode_var_safe(buf, sizeof(buf),
378                               COAP_OBSERVE_ESTABLISH), buf)
379                             ))
380      goto error;
381  }
382
383  /* ... Other code / options etc. ... */
384
385  /* Add in all the options (after internal sorting) to the pdu */
386  if (!coap_add_optlist_pdu(pdu, &optlist_chain))
387    goto error;
388
389  if (data && length) {
390    /* Add in the specified data */
391    if (!coap_add_data_large_request(session, pdu, length, data, NULL, NULL))
392      goto error;
393  }
394
395  if (coap_send(session, pdu) == COAP_INVALID_MID)
396    goto error;
397  return 1;
398
399error:
400
401  if (pdu)
402    coap_delete_pdu(pdu);
403  return 0;
404
405}
406
407int
408main(int argc, char *argv[]) {
409  coap_context_t *context = NULL;
410  coap_session_t *session = NULL;
411  unsigned char *data = NULL;
412  size_t data_length = 0;
413
414  (void)argc;
415  (void)argv;
416
417  /* Initialize libcoap library */
418  coap_startup();
419
420  /* ... Set up context, session etc. ... */
421
422  /* Set up using libcoap to do the block work */
423  coap_context_set_block_mode(context,
424                              COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
425
426  /* ... Other code etc. ... */
427
428  /* .. build data and define data_length ... */
429
430  build_send_pdu(context, session, COAP_MESSAGE_CON, COAP_REQUEST_PUT,
431                 "/example/uri", NULL, data, data_length, 0);
432
433  /* ... Other code etc. ... */
434
435  coap_cleanup();
436  return 0;
437}
438----
439
440*Resource Request Handler Response PDU Update*
441
442[source, c]
443----
444#include <coap@LIBCOAP_API_VERSION@/coap.h>
445
446#include <stdio.h>
447
448static void
449hnd_get_time(coap_resource_t *resource, coap_session_t *session,
450const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
451
452  unsigned char buf[40];
453  size_t len;
454  time_t now;
455
456  /* ... Additional analysis code for resource, request pdu etc.  ... */
457
458  /* After analysis, generate a failure response and return if needed */
459
460  now = time(NULL);
461
462  if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
463    /* Output secs since Jan 1 1970 */
464    len = snprintf((char *)buf, sizeof(buf), "%lu", now);
465  }
466  else {
467    /* Output human-readable time */
468    struct tm *tmp;
469    tmp = gmtime(&now);
470    if (!tmp) {
471      /* If 'now' is not valid */
472      coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
473      return;
474    }
475    len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
476  }
477  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
478  /*
479   * Invoke coap_add_data_large_response() to do all the hard work.
480   * [A good practice, even though ins this case, the amount of data is small]
481   *
482   * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
483   * Define how long this response is valid for (secs) - 1 - to add in.
484   *
485   * Observe Option added internally if needed within the function
486   * Block2 Option added internally if output too large
487   * Size2 Option added internally
488   * ETag Option added internally
489   */
490  coap_add_data_large_response(resource, session, request, response,
491                               query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
492                               len,
493                               buf,
494                               NULL, NULL);
495  /*
496   * When request handler returns, the response pdu will get automatically
497   * sent, unless the pdu code is not updated and this is a NON or TCP based
498   * request.
499   */
500}
501
502int
503main(int argc, char *argv[]) {
504  coap_context_t *context = NULL;
505  coap_resource_t *r;
506  coap_resource_t *time_resource;
507  int not_exit = 1;
508
509  /* Initialize libcoap library */
510  coap_startup();
511
512  (void)argc;
513  (void)argv;
514
515  /* ... Set up context etc. ... */
516
517  /* Set up using libcoap to do the block work */
518  coap_context_set_block_mode(context,
519                              COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
520
521  /* Create a resource to return time */
522  r = coap_resource_init(coap_make_str_const("time"),
523                         COAP_RESOURCE_FLAGS_NOTIFY_CON);
524  coap_resource_set_get_observable(r, 1);
525  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_time);
526
527  /* Document resource for 'time' request */
528  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
529  coap_add_attr(r, coap_make_str_const("title"),
530                coap_make_str_const("\"Internal Clock\""), 0);
531  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"secs\""),
532                0);
533  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""),
534                0);
535
536  coap_add_resource(context, r);
537  time_resource = r;
538
539  /* ... Loop waiting for incoming traffic ... */
540  while (!not_exit) {
541    coap_io_process(context, 1000);
542
543    /* Cause a notification to anyone Observing 'time' */
544    coap_resource_notify_observers(time_resource, NULL);
545  }
546
547  /* Clean up */
548
549  coap_free_context(context);
550  coap_cleanup();
551
552}
553----
554
555SEE ALSO
556--------
557*coap_init*(3) *coap_pdu_setup*(3), *coap_observe*(3), and *coap_resource*(3)
558
559FURTHER INFORMATION
560-------------------
561See
562
563"https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]"
564
565"https://rfc-editor.org/rfc/rfc7959[RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)]"
566
567for further information.
568
569BUGS
570----
571Please report bugs on the mailing list for libcoap:
572libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
573https://github.com/obgm/libcoap/issues
574
575AUTHORS
576-------
577The libcoap project <libcoap-developers@lists.sourceforge.net>
578