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