1COAP_BLOCK(3)                   libcoap Manual                   COAP_BLOCK(3)
2
3
4

NAME

6       coap_block, coap_context_set_block_mode, coap_add_data_large_request,
7       coap_add_data_large_response, coap_get_data_large,
8       coap_block_build_body - Work with CoAP Blocks
9

SYNOPSIS

11       #include <coap3/coap.h>
12
13       void coap_context_set_block_mode(coap_context_t *context, uint8_t
14       block_mode);
15
16       int coap_add_data_large_request(coap_session_t *session, coap_pdu_t
17       *pdu, size_t length, const uint8_t *data, coap_release_large_data_t
18       release_func, void *app_ptr);
19
20       int coap_add_data_large_response(coap_resource_t *resource,
21       coap_session_t *session, const coap_pdu_t *request, coap_pdu_t
22       *response, const coap_string_t *query, uint16_t media_type, int maxage,
23       uint64_t etag, size_t length, const uint8_t *data,
24       coap_release_large_data_t release_func, void *app_ptr);
25
26       int coap_get_data_large(const coap_pdu_t *pdu, size_t *length, const
27       uint8_t **_data, size_t *offset, size_t *total);
28
29       coap_binary_t *coap_block_build_body(coap_binary_t *body_data, size_t
30       length, const uint8_t *data, size_t offset, size_t total);
31
32       For specific (D)TLS library support, link with -lcoap-3-notls,
33       -lcoap-3-gnutls, -lcoap-3-openssl, -lcoap-3-mbedtls or
34       -lcoap-3-tinydtls. Otherwise, link with -lcoap-3 to get the default
35       (D)TLS library support.
36

DESCRIPTION

38       Regular setting up of a PDU and transmission is covered in
39       coap_pdu_setup(3) where all the payload data can fit in a single
40       packet. This man page covers how to work with PDUs where the overall
41       body of information may need to be split across several packets by
42       using CoAP Block-Wise Transfers (RFC 7959).
43
44       The block-wise transfers can be controlled by the application, or
45       libcoap is instructed to do all the requests for the next blocks and
46       only present the final body of the result to the application. In
47       summary, the following three ways handle processing a body of data that
48       has to be split across multiple payloads (blocks).
49
50        1. Application does all the work
51
52           It is the responsibility of the application to analyze each block
53           transmission at receipt and then generate the next request as per
54           RFC 7959. In this case, coap_context_set_block_mode() function must
55           not be called to maintain backward compatability with applications
56           that did the block handling within the application.
57
58        2. Application sees individual blocks
59
60           By calling coap_context_set_block_mode(context,
61           COAP_BLOCK_USE_LIBCOAP) and using the appropriate functions, the
62           requests for the next block of data is handled automatically by the
63           libcoap layer. Each individual block of data is presented to the
64           application for processing.
65
66           By calling coap_get_data_large(), the application can determine if
67           this is the first block or not (using offset value), whether the
68           first block is all the data (offset = 0, length = total) and
69           whether this is the last block (offset + length = total). It is the
70           responsibility of the application to re-assemble the individual
71           blocks into a single body of data.
72
73           NOTE: total is only an approximation (it will be > offset + length)
74           until the final block is received.
75
76           If this is the request handler in a server, the server still needs
77           to return a COAP_RESPONSE_CODE_CONTINUE 2.31 (Continue) response
78           code if the received data is not for the final block, otherwise a
79           COAP_RESPONSE_CODE_CREATED 2.01 (Created) or
80           COAP_RESPONSE_CODE_CHANGED 2.04 (Changed) should be returned.
81
82        3. Application only sees all of the body
83
84           By calling coap_context_set_block_mode(context,
85           COAP_BLOCK_USE_LIBCOAP|COAP_BLOCK_SINGLE_BODY) and using the
86           appropriate functions, the requests for all the blocks of data is
87           handled automatically by the libcoap layer. Only the complete body
88           of the data is presented to the application, unless there is an
89           error.
90
91           coap_get_data_large() will only return the entire body of data
92           (offset always 0, length = total) and there is no need to
93           re-assemble individual blocks into a large body of data.
94
95           In RAM constrained environments, option 2 may be the preferred
96           method.
97
98       This man page focuses on getting libcoap to do all the work, not how to
99       do it all in the application.
100
101       However, if the client supplies a Block1 or Block2 Option in the PDU
102       where the block number is not 0, this is assumed to be a random access
103       request and any other blocks will not be requested by libcoap even if
104       instructed otherwise.
105
106       The functions that are named _large are intended as replacements for
107       the equivalent functions as described in coap_pdu_setup(3).
108

CALLBACK HANDLER

110       Callback Type: coap_release_large_data_t
111
112           /**
113            * Callback handler for de-allocating the data based on @p app_ptr provided to
114            * coap_add_data_large_*() functions following transmission of the supplied
115            * data.
116            *
117            * @param session The session that this data is associated with
118            * @param app_ptr The application provided pointer to the
119            *                coap_add_data_large_*() functions
120            */
121           typedef void (*coap_release_large_data_t)(coap_session_t *session,
122                                                     void *app_ptr);
123

FUNCTIONS

125       Function: coap_context_set_block_mode()
126
127       The coap_context_set_block_mode() function is used to set up the
128       context level block_mode block handling bits for supporting RFC7959.
129       block_mode flows down to a session when a session is created and if the
130       peer does not support the respective block mode, an appropriate bit may
131       get disabled in the session block_mode.
132
133           #define COAP_BLOCK_USE_LIBCOAP  0x01 /* Use libcoap to do block requests */
134           #define COAP_BLOCK_SINGLE_BODY  0x02 /* Deliver the data as a single body */
135
136       block_mode is an or’d set of zero or more COAP_BLOCK_* definitions.
137
138       If COAP_BLOCK_USE_LIBCOAP is not set, then everything works as per
139       Option 1 above.
140
141       If COAP_BLOCK_SINGLE_BODY is set, then the entire body of data is
142       presented to the receiving handler, otherwise each individual block is
143       presented on arrival. To obtain the data, length and current offset,
144       coap_get_data_large() must be used instead of coap_get_data(). It may
145       be appropriate not to set COAP_BLOCK_SINGLE_BODY if there are RAM
146       limitations.
147
148       NOTE: It is the responsibility of the receiving application to
149       re-assemble the data as appropriate (e.g., using
150       coap_block_build_body()) if COAP_BLOCK_SINGLE_BODY is not set.
151
152       NOTE: If COAP_BLOCK_SINGLE_BODY is not set, then the CoAP server on
153       receiving request data that is split over multiple data blocks must
154       respond with COAP_RESPONSE_CODE_CONTINUE 2.31 (Continue) response code
155       if the received data is not for the final block, otherwise a
156       COAP_RESPONSE_CODE_CREATED 2.01 (Created) or COAP_RESPONSE_CODE_CHANGED
157       2.04 (Changed) should be returned.
158
159       If COAP_BLOCK_USE_LIBCOAP is set, then any PDUs presented to the
160       application handlers will get the tokens set back to the initiating
161       token so that requests can be matched with responses even if different
162       tokens had to be used for the series of packet interchanges.
163       Furthermore, if COAP_BLOCK_SINGLE_BODY is set, then the PDU that
164       presents the entire body will have any BlockX option removed.
165
166       NOTE: COAP_BLOCK_USE_LIBCOAP must be set if libcoap is to do all the
167       block tracking and requesting, otherwise the application will have to
168       do all of this work (the default if coap_context_set_block_mode() is
169       not called).
170
171       Function: coap_add_data_large_request()
172
173       The coap_add_data_large_request() function is similar to
174       coap_add_data(), but supports the transmission of data that has a body
175       size that is potentially larger than can be fitted into a single client
176       request PDU. The specified payload data of length length is associated
177       with the session with the first block of data added to the PDU pdu
178       along with the appropriate CoAP options such as Block1, Size1 and
179       Request-Tag if the data does not fit in a single PDU.
180
181       When the block receipt has been acknowledged by the peer, the library
182       will then send the next block of data until all the data has been
183       transmitted.
184
185       The data passed to the function coap_add_data_large_request() must
186       exist until all blocks have been transmitted. The callback function
187       release_func can be used to release storage that has been dynamically
188       allocated to hold the transmit data. If not NULL, the callback function
189       is called once the final block of data has been transmitted. The
190       user-defined parameter app_ptr is the same value that was passed to
191       coap_add_data_large_request().
192
193       NOTE: This function must only be called once per pdu.
194
195       NOTE: Options cannot be added to the pdu after
196       coap_add_data_large_request() is called.
197
198       Function: coap_add_data_large_response()
199
200       The coap_add_data_large_response() function is responsible for handling
201       the server’s large responses to requests.
202       coap_add_data_large_response() should be used as a direct replacement
203       for coap_add_data() if it is possible that the length of data will not
204       fit in a single server’s response pdu. This function adds in the
205       initial part of the payload data of length length to the PDU pdu.
206
207       The data passed to the function coap_add_data_large_response() must
208       exist until all blocks have been transmitted. The callback function
209       release_func can be used to release storage that has been dynamically
210       allocated to hold the transmit data. If not NULL, the callback function
211       is called once the final block of data has been transmitted. The
212       user-defined parameter app_ptr is the same value that was passed to
213       coap_add_data_large_response().
214
215       It also adds in the appropriate CoAP options such as Block2, Size2 and
216       ETag to handle block-wise transfer if the data does not fit in a single
217       PDU.
218
219       resource, query, session, request, and response are the same parameters
220       as in the called resource handler that invokes
221       coap_add_data_large_response(). If etag is 0, then a unique ETag value
222       will be generated, else is the ETag value to use. The media_type is for
223       the format of the data and maxage defines the lifetime of the response.
224       If maxage is set to -1, then the Max-Age option does not get included
225       (which indicates the default value of 60 seconds according to RFC
226       7252).
227
228       The application request handler for the resource is only called once
229       instead of potentially multiple times.
230
231       NOTE: This function must only be called once per pdu.
232
233       NOTE: Options cannot be added to the pdu after
234       coap_add_data_large_request() is called.
235
236       Function: coap_get_data_large()
237
238       The coap_get_data_large() function is used abstract from the pdu
239       information about the received data by updating length with the length
240       of data available, data with a pointer to where the data is located,
241       offset with where this block of data starts and total with the total
242       amount of data. offset will always be zero if block_mode includes
243       COAP_BLOCK_SINGLE_BODY. All of the body’s data has been received if
244       "offset + length == total".
245
246       NOTE: total is potentially only an indication of the total size of the
247       body and is only exact when all of the data has been received.
248
249       Function: coap_block_build_body()
250
251       The coap_block_build_body() function is used to re-assemble the
252       received data as returned by coap_get_data_large() into a single blob
253       of data. Data from data of length length starting from offset offset is
254       added to body_data. The resultant state of body_data is returned. If
255       body_data is NULL, or total is larger than the current size of
256       body_data, then body_data is re-allocated and returned. If there is an
257       error, body_data gets de-allocated.
258
259       If block_mode (as set by coap_context_set_block_mode()) includes
260       COAP_BLOCK_SINGLE_BODY, then the request/response handler will only get
261       called once with the entire body containing the data from all of the
262       individual blocks. If there is a change of data during the blocks
263       receipt (e.g., ETag value changes), then the entire set of data is
264       re-requested and the partial body dropped.
265

RETURN VALUES

267       The coap_add_data_large_request(), coap_add_data_large_response(), and
268       coap_get_data_large() functions return 0 on failure, 1 on success.
269
270       The coap_block_build_body() returns the current state of the body’s
271       data (which may have some missing gaps) or NULL on error.
272

EXAMPLES

274       Setup PDU and Transmit
275
276           #include <coap3/coap.h>
277
278           static int
279           build_send_pdu(coap_context_t *context, coap_session_t *session,
280           uint8_t msgtype, uint8_t request_code, const char *uri, const char *query,
281           unsigned char *data, size_t length, int observe) {
282
283             coap_pdu_t *pdu;
284             uint8_t buf[1024];
285             size_t buflen;
286             uint8_t *sbuf = buf;
287             int res;
288             coap_optlist_t *optlist_chain = NULL;
289             /* Remove (void) definition if variable is used */
290             (void)context;
291
292             /* Create the pdu with the appropriate options */
293             pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session),
294                                 coap_session_max_pdu_size(session));
295             if (!pdu)
296               return 0;
297
298             /*
299              * Create unique token for this request for handling unsolicited /
300              * delayed responses
301              */
302             coap_session_new_token(session, &buflen, buf);
303             if (!coap_add_token(pdu, buflen, buf)) {
304               coap_log(LOG_DEBUG, "cannot add token to request\n");
305               goto error;
306             }
307
308             if (uri) {
309               /* Add in the URI options */
310               buflen = sizeof(buf);
311               res = coap_split_path((const uint8_t*)uri, strlen(uri), sbuf, &buflen);
312               while (res--) {
313                 if (!coap_insert_optlist(&optlist_chain,
314                                          coap_new_optlist(COAP_OPTION_URI_PATH,
315                                   coap_opt_length(sbuf), coap_opt_value(sbuf))))
316                   goto error;
317                 sbuf += coap_opt_size(sbuf);
318               }
319             }
320
321             if (query) {
322               /* Add in the QUERY options */
323               buflen = sizeof(buf);
324               res = coap_split_query((const uint8_t*)query, strlen(query), sbuf, &buflen);
325               while (res--) {
326                 if (!coap_insert_optlist(&optlist_chain,
327                                          coap_new_optlist(COAP_OPTION_URI_QUERY,
328                                   coap_opt_length(sbuf), coap_opt_value(sbuf))))
329                   goto error;
330                 sbuf += coap_opt_size(sbuf);
331               }
332             }
333
334             if (request_code == COAP_REQUEST_GET && observe) {
335               /* Indicate that we want to observe this resource */
336               if (!coap_insert_optlist(&optlist_chain,
337                                        coap_new_optlist(COAP_OPTION_OBSERVE,
338                                          coap_encode_var_safe(buf, sizeof(buf),
339                                          COAP_OBSERVE_ESTABLISH), buf)
340                                        ))
341                 goto error;
342             }
343
344             /* ... Other code / options etc. ... */
345
346             /* Add in all the options (after internal sorting) to the pdu */
347             if (!coap_add_optlist_pdu(pdu, &optlist_chain))
348               goto error;
349
350             if (data && length) {
351               /* Add in the specified data */
352               if (!coap_add_data_large_request(session, pdu, length, data, NULL, NULL))
353                 goto error;
354             }
355
356             if (coap_send(session, pdu) == COAP_INVALID_MID)
357               goto error;
358             return 1;
359
360           error:
361
362             if (pdu)
363               coap_delete_pdu(pdu);
364             return 0;
365
366           }
367
368           int main(int argc, char *argv[]) {
369             coap_context_t *context = NULL;
370             coap_session_t *session = NULL;
371             unsigned char *data = NULL;
372             size_t data_length = 0;
373
374             (void)argc;
375             (void)argv;
376
377             /* ... Set up context, session etc. ... */
378
379             /* Set up using libcoap to do the block work */
380             coap_context_set_block_mode(context,
381                                         COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
382
383             /* ... Other code etc. ... */
384
385             /* .. build data and define data_length ... */
386
387             build_send_pdu(context, session, COAP_MESSAGE_CON, COAP_REQUEST_PUT,
388                            "/example/uri", NULL, data, data_length, 0);
389
390             /* ... Other code etc. ... */
391
392             return 0;
393           }
394
395       Resource Request Handler Response PDU Update
396
397           #include <coap3/coap.h>
398
399           #include <stdio.h>
400
401           static void
402           hnd_get_time(coap_resource_t *resource, coap_session_t *session,
403           const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
404
405             unsigned char buf[40];
406             size_t len;
407             time_t now;
408
409             /* ... Additional analysis code for resource, request pdu etc.  ... */
410
411             /* After analysis, generate a failure response and return if needed */
412
413             now = time(NULL);
414
415             if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
416               /* Output secs since Jan 1 1970 */
417               len = snprintf((char *)buf, sizeof(buf), "%lu", now);
418             }
419             else {
420               /* Output human-readable time */
421               struct tm *tmp;
422               tmp = gmtime(&now);
423               if (!tmp) {
424                 /* If 'now' is not valid */
425                 coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
426                 return;
427               }
428               len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
429             }
430             coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
431             /*
432              * Invoke coap_add_data_large_response() to do all the hard work.
433              * [A good practice, even though ins this case, the amount of data is small]
434              *
435              * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
436              * Define how long this response is valid for (secs) - 1 - to add in.
437              *
438              * Observe Option added internally if needed within the function
439              * Block2 Option added internally if output too large
440              * Size2 Option added internally
441              * ETag Option added internally
442              */
443             coap_add_data_large_response(resource, session, request, response,
444                                          query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
445                                          len,
446                                          buf,
447                                          NULL, NULL);
448             /*
449              * When request handler returns, the response pdu will get automatically
450              * sent, unless the pdu code is not updated and this is a NON or TCP based
451              * request.
452              */
453           }
454
455           int main(int argc, char *argv[]) {
456             coap_context_t *context = NULL;
457             coap_resource_t *r;
458             coap_resource_t *time_resource;
459             int not_exit = 1;
460
461             (void)argc;
462             (void)argv;
463
464             /* ... Set up context etc. ... */
465
466             /* Set up using libcoap to do the block work */
467             coap_context_set_block_mode(context,
468                                         COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
469
470             /* Create a resource to return time */
471             r = coap_resource_init(coap_make_str_const("time"),
472                                    COAP_RESOURCE_FLAGS_NOTIFY_CON);
473             coap_resource_set_get_observable(r, 1);
474             coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_time);
475
476             /* Document resource for 'time' request */
477             coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
478             coap_add_attr(r, coap_make_str_const("title"),
479                           coap_make_str_const("\"Internal Clock\""), 0);
480             coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"secs\""),
481                           0);
482             coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""),
483                           0);
484
485             coap_add_resource(context, r);
486             time_resource = r;
487
488             /* ... Loop waiting for incoming traffic ... */
489             while (!not_exit) {
490               coap_io_process(context, 1000);
491
492               /* Cause a notification to anyone Observing 'time' */
493               coap_resource_notify_observers(time_resource, NULL);
494             }
495
496             /* Clean up */
497
498             coap_free_context(context);
499             coap_cleanup();
500
501           }
502

SEE ALSO

504       coap_pdu_setup(3), coap_observe(3), and coap_resource(3)
505

FURTHER INFORMATION

507       See
508
509       "RFC7252: The Constrained Application Protocol (CoAP)"
510
511       "RFC7959: Block-Wise Transfers in the Constrained Application Protocol
512       (CoAP)"
513
514       for further information.
515

BUGS

517       Please report bugs on the mailing list for libcoap:
518       libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
519       https://github.com/obgm/libcoap/issues
520

AUTHORS

522       The libcoap project <libcoap-developers@lists.sourceforge.net>
523
524
525
526coap_block 4.3.1                  11/24/2022                     COAP_BLOCK(3)
Impressum