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