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 - Work with CoAP Blocks
9
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
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
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
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
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
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
504 coap_pdu_setup(3), coap_observe(3), and coap_resource(3)
505
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
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
522 The libcoap project <libcoap-developers@lists.sourceforge.net>
523
524
525
526coap_block 4.3.1 11/24/2022 COAP_BLOCK(3)