1COAP_BLOCK(3) libcoap Manual COAP_BLOCK(3)
2
3
4
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
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
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
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
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
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
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
540 coap_init(3) coap_pdu_setup(3), coap_observe(3), and coap_resource(3)
541
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
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
558 The libcoap project <libcoap-developers@lists.sourceforge.net>
559
560
561
562coap_block 4.3.4 10/09/2023 COAP_BLOCK(3)