• 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
19- Work with CoAP Blocks
20
21SYNOPSIS
22--------
23*#include <coap@LIBCOAP_API_VERSION@/coap.h>*
24
25*void coap_context_set_block_mode(coap_context_t *_context_,
26uint8_t _block_mode_);*
27
28*int coap_add_data_large_request(coap_session_t *_session_,
29coap_pdu_t *_pdu_, size_t _length_, const uint8_t *_data_,
30coap_release_large_data_t _release_func_, void *_app_ptr_);*
31
32*int coap_add_data_large_response(coap_resource_t *_resource_,
33coap_session_t *_session_, const coap_pdu_t *_request_, coap_pdu_t *_response_,
34const coap_string_t *query, uint16_t _media_type_, int _maxage_,
35uint64_t etag, size_t _length_, const uint8_t *_data_,
36coap_release_large_data_t _release_func_, void *_app_ptr_);*
37
38*int coap_get_data_large(const coap_pdu_t *_pdu_, size_t *_length,
39const uint8_t **_data_, size_t *_offset_, size_t *_total_);*
40
41*coap_binary_t *
42coap_block_build_body(coap_binary_t *_body_data_, size_t _length_,
43const uint8_t *_data_, size_t _offset_, size_t _total_);*
44
45For specific (D)TLS library support, link with
46*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*,
47*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*
48or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*.   Otherwise, link with
49*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support.
50
51DESCRIPTION
52-----------
53Regular setting up of a PDU and transmission is covered in coap_pdu_setup(3)
54where all the payload data can fit into a single packet.  This man page covers
55how to work with PDUs where the overall body of information may need to be
56split across several packets by using CoAP Block-Wise Transfers (RFC 7959).
57
58The block-wise transfers can be controlled by the application, or libcoap is
59instructed to do all the requests for the next blocks and only present the
60final body of the result to the application.  This man page focuses on getting
61libcoap to do all the work, not how to do it all in the application.
62
63However, if the client supplies a BLOCK1 or BLOCK2 Option in the PDU where the
64block number is not 0, this is assumed to be a random access request and any
65other blocks will not be requested by libcoap even if instructed otherwise.
66
67The functions that are named *_large* are intended as replacements for the
68equivalent functions as described in coap_pdu_setup(3).
69
70The *coap_context_set_block_mode*() function is used to set up the _context_
71level _block_mode_ block handling bits for supporting RFC7959. _block_mode_
72flows down to a session when a session is created and if the peer does not
73support the respective block mode, an appropriate bit may get disabled in the
74session _block_mode_.
75
76[source, c]
77----
78#define COAP_BLOCK_USE_LIBCOAP  0x01 /* Use libcoap to do block requests */
79#define COAP_BLOCK_SINGLE_BODY  0x02 /* Deliver the data as a single body */
80----
81_block_mode_ is an or'd set of zero or more COAP_BLOCK_* definitions.
82
83If COAP_BLOCK_SINGLE_BODY is set, then the entire body of data is presented to
84the receiving handler, otherwise each individual block is presented on arrival.
85To obtain the data, length and current offset, *coap_get_data_large*() must
86be used instead of *coap_get_data*().  It may be appropriate not to set
87COAP_BLOCK_SINGLE_BODY if there are RAM limitations.
88
89*NOTE:* It is the responsibility of the receiving application to re-assemble
90the _data_ as appropriate (using *coap_block_build_body*()) if
91COAP_BLOCK_SINGLE_BODY is not set.
92
93*NOTE:* If COAP_BLOCK_SINGLE_BODY is not set, then the CoAP server on receiving
94request data split over multiple blocks data must respond with 2.31 (more data
95still to come), 2.01 or 2.04 (all data successfully received) as appropriate.
96
97If COAP_BLOCK_USE_LIBCOAP is set, then any PDUs presented to the application
98handlers will get the tokens set back to the initiating token so that requests
99can be matched with responses even if different tokens had to be used for the
100series of packet interchanges.  Furthermore, if COAP_BLOCK_SINGLE_BODY is set,
101then the PDU that presents the entire body will have any BLOCKx option removed.
102
103*NOTE:* COAP_BLOCK_USE_LIBCOAP must be set if libcoap is to do all the
104block tracking and requesting, otherwise the application will have to do all
105of this work (the default if *coap_context_set_block_mode*() is not called).
106
107[source, c]
108----
109/**
110 * Callback handler for de-allocating the data based on @p app_ptr provided to
111 * coap_add_data_large_*() functions following transmission of the supplied
112 * data.
113 *
114 * @param session The session that this data is associated with
115 * @param app_ptr The application provided pointer to the
116 *                coap_add_data_large_*() functions
117 */
118typedef void (*coap_release_large_data_t)(coap_session_t *session,
119                                          void *app_ptr);
120----
121
122The *coap_add_data_large_request*() function is similar to *coap_add_data*(),
123but supports the transmission of data that has a body size that is potentially
124larger than can be fitted into a single client request PDU. The specified
125payload _data_ of length _length_ is associated with the _session_ with the
126first block of data added to the PDU _pdu_ along with the appropriate CoAP
127options such as BLOCK1, and SIZE1 if the data does not fit into
128a single PDU.  When the block has been acknowledged by the peer, the library
129will then send the next block of data until all the data has been transmitted.
130This function must only be called once per _pdu_.  When the final block is
131transmitted, the callback function _release_func_ (if not NULL) with the user
132defined parameter of _app_ptr_ is called so that the data can be released.
133
134The *coap_add_data_large_response*() function is responsible for handling
135the server's large responses to requests. *coap_add_data_large_response*()
136should be used as a direct replacement for *coap_add_data*() if it is possible
137that the _length_ of _data_ will not fit into a single server's response pdu.
138This function adds in the initial part of the payload _data_ of length
139_length_ to the PDU _pdu_. _release_func_ (if not NULL) and _app_ptr_ are
140used for releasing the data when the body transfer is complete.  It also adds
141in the appropriate CoAP options such as BLOCK2, SIZE2 and ETAG to handle
142Block-Wise transfer if the data does not fit into a single PDU.
143_resource_, _query_, _session_, _request_, and _response_ are the same
144parameters as in the called resource handler that invokes
145*coap_add_data_large_response*(). If _etag_ is 0, then a unique ETag value will
146be generated, else is the ETag value to use.
147The _media_type_ is for the format of the _data_ and _maxage_ defines the
148lifetime of the response.  If _maxage_ is set to -1,  then the MAXAGE option
149does not get included (which indicates the default value of 60 seconds
150according to RFC 7252). This function must only be called once per _pdu_.
151The application handler for the resource is only called once instead of
152potentially multiple times.
153
154The *coap_get_data_large*() function is used abstract from the _pdu_
155information about the received data by updating _length_ with the length of
156data available, _data_ with a pointer to where the data is located, _offset_
157with where this block of data starts and _total_ with the total amount of data.
158_offset_ will always be zero if block_mode includes COAP_BLOCK_SINGLE_BODY.
159All of the body's data has been received if "_offset_ + _length_ == _total_".
160
161*NOTE:* _total_ is potentially only an indication of the total size of the
162body and is only exact when all of the data has been received.
163
164The *coap_block_build_body*() function is used to re-assemble the received
165data as returned by *coap_get_data_large*() into a single blob of data. Data
166from _data_ of length _length_ starting from offset _offset_ is added to
167_body_data_.  The resultant state of _body_data_ is returned. If _body_data_
168is NULL, or _total_ is larger than the current size of _body_data_, then
169_body_data_ is re-allocated and returned.  If there is an error, _body_data_
170gets de-allocated.
171
172If _block_mode_ (as set by *coap_context_set_block_mode*()) includes
173COAP_BLOCK_SINGLE_BODY is used, then the response handler will only get called
174once with the entire body containing the data from all of the individual
175blocks. If there is a change of data during the blocks receipt (e.g. ETag
176value changes), then the entire set of data is re-requested and the partial
177body dropped.
178
179RETURN VALUES
180-------------
181The *coap_add_data_large_request*(), *coap_add_data_large_response*(), and
182*coap_get_data_large*() functions return 0 on failure, 1 on success.
183
184The  *coap_block_build_body*() returns the current state of the body's data
185(which may have some missing gaps) or NULL on error.
186
187EXAMPLES
188--------
189*Setup PDU and Transmit*
190
191[source, c]
192----
193#include <coap@LIBCOAP_API_VERSION@/coap.h>
194
195static int
196build_send_pdu(coap_context_t *context, coap_session_t *session,
197uint8_t msgtype, uint8_t request_code, const char *uri, const char *query,
198unsigned char *data, size_t length, int observe) {
199
200  coap_pdu_t *pdu;
201  uint8_t buf[1024];
202  size_t buflen;
203  uint8_t *sbuf = buf;
204  int res;
205  coap_optlist_t *optlist_chain = NULL;
206  /* Remove (void) definition if variable is used */
207  (void)context;
208
209  /* Create the pdu with the appropriate options */
210  pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session),
211                      coap_session_max_pdu_size(session));
212  if (!pdu)
213    return 0;
214
215  /*
216   * Create unique token for this request for handling unsolicited /
217   * delayed responses
218   */
219  coap_session_new_token(session, &buflen, buf);
220  if (!coap_add_token(pdu, buflen, buf)) {
221    coap_log(LOG_DEBUG, "cannot add token to request\n");
222    goto error;
223  }
224
225  if (uri) {
226    /* Add in the URI options */
227    buflen = sizeof(buf);
228    res = coap_split_path((const uint8_t*)uri, strlen(uri), sbuf, &buflen);
229    while (res--) {
230      if (!coap_insert_optlist(&optlist_chain,
231                               coap_new_optlist(COAP_OPTION_URI_PATH,
232                        coap_opt_length(sbuf), coap_opt_value(sbuf))))
233        goto error;
234      sbuf += coap_opt_size(sbuf);
235    }
236  }
237
238  if (query) {
239    /* Add in the QUERY options */
240    buflen = sizeof(buf);
241    res = coap_split_query((const uint8_t*)query, strlen(query), sbuf, &buflen);
242    while (res--) {
243      if (!coap_insert_optlist(&optlist_chain,
244                               coap_new_optlist(COAP_OPTION_URI_QUERY,
245                        coap_opt_length(sbuf), coap_opt_value(sbuf))))
246        goto error;
247      sbuf += coap_opt_size(sbuf);
248    }
249  }
250
251  if (request_code == COAP_REQUEST_GET && observe) {
252    /* Indicate that we want to observe this resource */
253    if (!coap_insert_optlist(&optlist_chain,
254                             coap_new_optlist(COAP_OPTION_OBSERVE,
255                               coap_encode_var_safe(buf, sizeof(buf),
256                               COAP_OBSERVE_ESTABLISH), buf)
257                             ))
258      goto error;
259  }
260
261  /* ... Other code / options etc. ... */
262
263  /* Add in all the options (after internal sorting) to the pdu */
264  if (!coap_add_optlist_pdu(pdu, &optlist_chain))
265    goto error;
266
267  if (data && length) {
268    /* Add in the specified data */
269    if (!coap_add_data_large_request(session, pdu, length, data, NULL, NULL))
270      goto error;
271  }
272
273  if (coap_send(session, pdu) == COAP_INVALID_MID)
274    goto error;
275  return 1;
276
277error:
278
279  if (pdu)
280    coap_delete_pdu(pdu);
281  return 0;
282
283}
284
285int main(int argc, char *argv[]) {
286  coap_context_t *context = NULL;
287  coap_session_t *session = NULL;
288  unsigned char *data = NULL;
289  size_t data_length = 0;
290
291  (void)argc;
292  (void)argv;
293
294  /* ... Set up context, session etc. ... */
295
296  /* Set up using libcoap to do the block work */
297  coap_context_set_block_mode(context,
298                              COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
299
300  /* ... Other code etc. ... */
301
302  /* .. build data and define data_length ... */
303
304  build_send_pdu(context, session, COAP_MESSAGE_CON, COAP_REQUEST_PUT,
305                 "/example/uri", NULL, data, data_length, 0);
306
307  /* ... Other code etc. ... */
308
309  return 0;
310}
311----
312
313*Resource Handler Response PDU Update*
314
315[source, c]
316----
317#include <coap@LIBCOAP_API_VERSION@/coap.h>
318
319#include <stdio.h>
320
321static void
322hnd_get_time(coap_resource_t *resource, coap_session_t *session,
323const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
324
325  unsigned char buf[40];
326  size_t len;
327  time_t now;
328
329  /* Note that request may be NULL if triggered by an observe response */
330
331  /* ... Additional analysis code for resource, request pdu etc.  ... */
332
333  /* After analysis, generate a failure response and return if needed */
334
335  now = time(NULL);
336
337  if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
338    /* Output secs since Jan 1 1970 */
339    len = snprintf((char *)buf, sizeof(buf), "%lu", now);
340  }
341  else {
342    /* Output human-readable time */
343    struct tm *tmp;
344    tmp = gmtime(&now);
345    if (!tmp) {
346      /* If 'now' is not valid */
347      coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
348      return;
349    }
350    len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
351  }
352  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
353  /*
354   * Invoke coap_add_data_large_response() to do all the hard work.
355   * [A good practice, even though ins this case, the amount of data is small]
356   *
357   * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
358   * Define how long this response is valid for (secs) - 1 - to add in.
359   *
360   * OBSERVE Option added internally if needed within the function
361   * BLOCK2 Option added internally if output too large
362   * SIZE2 Option added internally
363   * ETAG Option added internally
364   */
365  coap_add_data_large_response(resource, session, request, response,
366                               query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
367                               len,
368                               buf,
369                               NULL, NULL);
370}
371
372int main(int argc, char *argv[]) {
373  coap_context_t *context = NULL;
374  coap_resource_t *r;
375
376  (void)argc;
377  (void)argv;
378
379  /* ... Set up context etc. ... */
380
381  /* Set up using libcoap to do the block work */
382  coap_context_set_block_mode(context,
383                              COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
384
385  /* Create a resource to return time */
386  r = coap_resource_init(coap_make_str_const("time"),
387                         COAP_RESOURCE_FLAGS_NOTIFY_CON);
388  coap_resource_set_get_observable(r, 1);
389  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
390
391  /* Document resource for 'time' request */
392  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
393  coap_add_attr(r, coap_make_str_const("title"),
394                coap_make_str_const("\"Internal Clock\""), 0);
395  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"secs\""),
396                0);
397  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""),
398                0);
399
400  coap_add_resource(context, r);
401
402  /* ... Loop waiting for incoming traffic ... */
403
404}
405----
406
407SEE ALSO
408--------
409*coap_pdu_setup*(3), *coap_observe*(3), and *coap_resource*(3)
410
411FURTHER INFORMATION
412-------------------
413See
414
415"RFC7252: The Constrained Application Protocol (CoAP)"
416
417"RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)"
418
419for further information.
420
421See https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers
422for the current set of defined CoAP Options.
423
424BUGS
425----
426Please report bugs on the mailing list for libcoap:
427libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
428https://github.com/obgm/libcoap/issues
429
430AUTHORS
431-------
432The libcoap project <libcoap-developers@lists.sourceforge.net>
433