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, coap_q_block_is_supported - Work with CoAP
9       Blocks
10

SYNOPSIS

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

DESCRIPTION

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

CALLBACK HANDLER

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

FUNCTIONS

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

RETURN VALUES

292       coap_add_data_large_request(), coap_add_data_large_response(), and
293       coap_get_data_large() return 0 on failure, 1 on success.
294
295       coap_block_build_body() returns the current state of the body’s data
296       (which may have some missing gaps) or NULL on error.
297
298       coap_q_block_is_supported() returns 0 on failure, 1 on success.
299

EXAMPLES

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

SEE ALSO

540       coap_init(3) coap_pdu_setup(3), coap_observe(3), and coap_resource(3)
541

FURTHER INFORMATION

543       See
544
545       "RFC7252: The Constrained Application Protocol (CoAP)"
546
547       "RFC7959: Block-Wise Transfers in the Constrained Application Protocol
548       (CoAP)"
549
550       for further information.
551

BUGS

553       Please report bugs on the mailing list for libcoap:
554       libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
555       https://github.com/obgm/libcoap/issues
556

AUTHORS

558       The libcoap project <libcoap-developers@lists.sourceforge.net>
559
560
561
562coap_block 4.3.4                  10/09/2023                     COAP_BLOCK(3)
Impressum