1// -*- mode:doc; -*- 2// vim: set syntax=asciidoc tw=0 3 4coap_observe(3) 5=============== 6:doctype: manpage 7:man source: coap_observe 8:man version: @PACKAGE_VERSION@ 9:man manual: libcoap Manual 10 11NAME 12---- 13coap_observe, 14coap_resource_set_get_observable, 15coap_resource_notify_observers, 16coap_cancel_observe, 17coap_session_set_no_observe_cancel 18- Work with CoAP observe 19 20SYNOPSIS 21-------- 22*#include <coap@LIBCOAP_API_VERSION@/coap.h>* 23 24*void coap_resource_set_get_observable(coap_resource_t *_resource_, 25int _mode_);* 26 27*int coap_resource_notify_observers(coap_resource_t *_resource_, 28const coap_string_t *_query_);* 29 30*int coap_cancel_observe(coap_session_t *_session_, coap_binary_t *_token_, 31coap_pdu_type_t _message_type_);* 32 33*void coap_session_set_no_observe_cancel(coap_session_t *_session_);* 34 35For specific (D)TLS library support, link with 36*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, 37*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls* 38or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with 39*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. 40 41DESCRIPTION 42----------- 43https://rfc-editor.org/rfc/rfc7641[RFC7641] extends the CoAP protocol to be 44able to monitor the state of a resource over time. 45 46This enables clients to "observe" resources with a defined query, i.e., to 47retrieve a representation of a resource and keep this representation updated 48by the server over a period of time. 49 50The server has to flag a resource as "observable", and then the client has 51to request in a GET request that it wants to observe this resource by the use 52of the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_ESTABLISH. 53Optionally, the client can specify query options for the resource, or by using 54a FETCH request instead of a GET to define a query 55(https://rfc-editor.org/rfc/rfc8132[RFC8132]). 56 57To remove the "observe" subscription, the client has to issue a GET (or FETCH) 58request with the COAP_OPTION_OBSERVE Option with a value of 59COAP_OBSERVE_CANCEL using the same token and other options used for making the 60initial "observe" request. Alternatively, "observe" can be cancelled using 61*coap_cancel_observe*() instead. 62 63The underlying library adds in and removes "subscribers" to "observe" the 64resource as appropriate in the server side logic. 65 66*NOTE:* COAP_RESOURCE_MAX_SUBSCRIBER may have been defined to limit the number 67of subscribers to a resource when libcoap was built. 68 69Within the server application, it needs to determine that there is a change of 70state of the resource under observation, and then cause the CoAP library 71layer to initiate a "fake GET/FETCH request" so that an observe GET/FETCH 72response gets sent back to all the clients that are observing the resource. The 73appropriate GET/FETCH handler within the server application is called to fill 74in the response packet with the appropriate information. This "fake GET/FETCH 75request" is triggered by a call to *coap_resource_notify_observers*(). 76 77The call to *coap_io_process*() in the main server application i/o loop will do 78all the necessary processing of sending any outstanding "fake GET/FETCH 79requests". 80 81Whenever the server sends a copy of the state of the "observed" resource to 82the client, it will use the same token used by the client when the client 83requested the "observe" (or the last token used for a FETCH that spans 84multiple blocks). The client will receive this observe response 85in the handler defined by *coap_register_response_handler*(3) (with the token 86updated to the initial token used by the client application for a large FETCH). 87It is the responsibility of the client application to match the supplied token 88and update the appropriate internal information. 89 90FUNCTIONS 91--------- 92 93*Function: coap_resource_set_get_observable()* 94 95The *coap_resource_set_get_observable*() function enables or disables the 96observable status of the _resource_ by the setting of _mode_. If _mode_ is 971, then the _resource_ is observable. If _mode_ is 0, then the 98_resource_ is no longer observable. 99 100*NOTE:* It is not possible for the Unknown Resource, created by 101*coap_resource_unknown_init*(3), to be observable as the Uri-Path is not known 102when libcoap creates a "fake GET/FETCH request". The Unknown Resource PUT 103handler must create a new resource and mark the resource as "observable" if 104a specific resource needs to be observable. The application must then 105manage the deletion of the resource at the appropriate time. 106 107*NOTE:* The type (confirmable or non-confirmable) of the triggered observe 108GET response is determined not by the initial GET/FETCH request, but 109independently by the server as per 110"https://rfc-editor.org/rfc/rfc7641#section-3.5[RFC7641 3.5. Transmission]". 111This is controlled by the flags (one of COAP_RESOURCE_FLAGS_NOTIFY_NON, 112COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS or COAP_RESOURCE_FLAGS_NOTIFY_CON) 113used when creating the resource using *coap_resource_init*(3). 114 115*NOTE:* Furthermore, the server must send at least one "observe" response as 116confirmable, when generally sending non-confirmable, at least every 24 hours 117as per "https://rfc-editor.org/rfc/rfc7641#section-4.5[RFC7641 1184.5. Transmission]". 119Libcoap automatically handles this by sending every fifth (COAP_OBS_MAX_NON) 120response as a confirmable response for detection that the client is still 121responding unless if COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS is set, which is a 122"https://rfc-editor.org/rfc/rfc7641#section-4.5[RFC7641 4.5. Transmission]" 123violation, where non-confirmable "observe" responses are always sent 124as required by some higher layer protocols. 125 126*Function: coap_resource_notify_observers()* 127 128The *coap_resource_notify_observers*() function needs to be called whenever the 129server application determines that there has been a change to the state of 130_resource_. The _query_ parameter is obsolete and ignored. 131 132*Function: coap_cancel_observe()* 133 134The *coap_cancel_observe*() function can be used by the client to cancel an 135observe request that is being tracked. This will cause the 136appropriate PDU to be sent to the server to cancel the observation, based on 137the _session_ and _token_ used to set up the observe and the PDU is of type 138_message_type_ (use COAP_MESSAGE_NON or COAP_MESSAGE_CON). 139 140*Function: coap_session_set_no_observe_cancel()* 141 142The *coap_session_set_no_observe_cancel*() function can be called by the 143client to disable calling *coap_cancel_observe*() when the _session_ is being 144closed down / freed off. *coap_cancel_observe*() can still be called directly 145by the client application. 146 147RETURN VALUES 148------------- 149*coap_resource_notify_observers*() returns 0 if not observable or 150no observers, 1 on success. 151 152*coap_cancel_observe*() returns 0 on failure, 1 on success. 153 154EXAMPLES 155-------- 156*Simple Time Server* 157 158[source, c] 159---- 160#include <coap@LIBCOAP_API_VERSION@/coap.h> 161 162#include <stdio.h> 163 164coap_resource_t *time_resource = NULL; 165 166/* specific GET "time" handler, called from hnd_get_generic() */ 167 168static void 169hnd_get_time(coap_resource_t *resource, coap_session_t *session, 170const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { 171 172 unsigned char buf[40]; 173 size_t len; 174 time_t now; 175 (void)resource; 176 (void)session; 177 178 /* ... Additional analysis code for resource, request pdu etc. ... */ 179 180 /* After analysis, generate a suitable response */ 181 182 /* Note that token, if set, is already in the response pdu */ 183 184 now = time(NULL); 185 186 if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) { 187 /* Output secs since Jan 1 1970 */ 188 len = snprintf((char *)buf, sizeof(buf), "%lu", now); 189 } 190 else { 191 /* Output human-readable time */ 192 struct tm *tmp; 193 tmp = gmtime(&now); 194 if (!tmp) { 195 /* If 'now' is not valid */ 196 coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); 197 return; 198 } 199 len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp); 200 } 201 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 202 /* 203 * Invoke coap_add_data_large_response() to do all the hard work. 204 * 205 * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in 206 * Define how long this response is valid for (secs) - 1 - to add in. 207 * ETAG Option added internally with unique value as param set to 0 208 * 209 * OBSERVE Option added internally if needed within the function 210 * BLOCK2 Option added internally if output too large 211 * SIZE2 Option added internally 212 */ 213 coap_add_data_large_response(resource, session, request, response, 214 query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0, 215 len, 216 buf, NULL, NULL); 217} 218 219/* Generic GET handler */ 220 221static void 222hnd_get_generic(coap_resource_t *resource, coap_session_t *session, 223const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { 224 225 coap_str_const_t *uri_path = coap_resource_get_uri_path(resource); 226 227 if (!uri_path) { 228 /* Unexpected Failure */ 229 coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); 230 return; 231 } 232 233 /* Is this the "time" resource" ? */ 234 if (coap_string_equal(uri_path, coap_make_str_const("time"))) { 235 hnd_get_time(resource, session, request, query, response); 236 return; 237 } 238 239 /* Other resources code */ 240 241 /* Failure response */ 242 coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); 243} 244 245/* Initialize generic GET handler */ 246 247static void 248init_resources(coap_context_t *ctx) 249{ 250 251 coap_resource_t *r; 252 253 /* Create a resource to return return or update time */ 254 r = coap_resource_init(coap_make_str_const("time"), 255 COAP_RESOURCE_FLAGS_NOTIFY_CON); 256 257 /* We are using a generic GET handler here */ 258 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_generic); 259 260 coap_resource_set_get_observable(r, 1); 261 262 coap_add_resource(ctx, r); 263 time_resource = r; 264 265} 266 267int 268main(int argc, char *argv[]) { 269 270 coap_context_t *ctx = NULL; 271 coap_endpoint_t *ep = NULL; 272 coap_address_t addr; 273 unsigned wait_ms; 274 struct timeval tv_last = {0, 0}; 275 276 /* Initialize libcoap library */ 277 coap_startup(); 278 279 /* Remove (void) definition if variable is used */ 280 (void)argc; 281 (void)argv; 282 283 memset (&tv_last, 0, sizeof(tv_last)); 284 285 /* Create the libcoap context */ 286 ctx = coap_new_context(NULL); 287 if (!ctx) { 288 exit(1); 289 } 290 /* See coap_block(3) */ 291 coap_context_set_block_mode(ctx, 292 COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); 293 294 coap_address_init(&addr); 295 addr.addr.sa.sa_family = AF_INET; 296 addr.addr.sin.sin_port = ntohs(COAP_DEFAULT_PORT); 297 ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP); 298 299 /* Other Set up Code */ 300 301 init_resources(ctx); 302 303 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; 304 305 while (1) { 306 int result = coap_io_process( ctx, wait_ms ); 307 if ( result < 0 ) { 308 break; 309 } else if ( result && (unsigned)result < wait_ms ) { 310 /* decrement if there is a result wait time returned */ 311 wait_ms -= result; 312 } else { 313 /* 314 * result == 0, or result >= wait_ms 315 * (wait_ms could have decremented to a small value, below 316 * the granularity of the timer in coap_io_process() and hence 317 * result == 0) 318 */ 319 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; 320 } 321 if (time_resource) { 322 struct timeval tv_now; 323 if (gettimeofday (&tv_now, NULL) == 0) { 324 if (tv_last.tv_sec != tv_now.tv_sec) { 325 /* Happens once per second */ 326 tv_last = tv_now; 327 coap_resource_notify_observers(time_resource, NULL); 328 } 329 /* need to wait until next second starts if wait_ms is too large */ 330 unsigned next_sec_ms = 1000 - (tv_now.tv_usec / 1000); 331 332 if (next_sec_ms && next_sec_ms < wait_ms) 333 wait_ms = next_sec_ms; 334 } 335 } 336 } 337 coap_free_context(ctx); 338 coap_cleanup(); 339 exit(0); 340 341} 342---- 343 344*Client Observe Request Setup* 345 346[source, c] 347---- 348#include <coap@LIBCOAP_API_VERSION@/coap.h> 349 350/* Usually, requests are sent confirmable */ 351 352static unsigned char msgtype = COAP_MESSAGE_CON; 353 354static unsigned int token = 0; 355 356static coap_pdu_t * 357coap_new_request(coap_context_t *context, coap_session_t *session, char request_code, 358coap_optlist_t **options, unsigned char *data, size_t length, int observe) { 359 360 coap_pdu_t *pdu; 361 /* Remove (void) definition if variable is used */ 362 (void)context; 363 364 /* Create the pdu with the appropriate options */ 365 pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session), 366 coap_session_max_pdu_size(session)); 367 if (!pdu) 368 return NULL; 369 370 /* 371 * Create uniqueness token for this request for handling unsolicited / 372 * delayed responses 373 */ 374 token++; 375 if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) { 376 coap_log_debug("cannot add token to request\n"); 377 goto error; 378 } 379 380 if (request_code == COAP_REQUEST_GET && observe) { 381 /* Indicate that we want to observe this resource */ 382 if (!coap_insert_optlist(options, 383 coap_new_optlist(COAP_OPTION_OBSERVE, 384 COAP_OBSERVE_ESTABLISH, NULL))) 385 goto error; 386 } 387 388 /* ... Other code / options etc. ... */ 389 390 /* Add in all the options (after internal sorting) to the pdu */ 391 if (!coap_add_optlist_pdu(pdu, options)) 392 goto error; 393 394 if (data && length) { 395 /* Add in the specified data */ 396 if (!coap_add_data(pdu, length, data)) 397 goto error; 398 } 399 400 return pdu; 401 402error: 403 404 coap_delete_pdu(pdu); 405 return NULL; 406 407} 408---- 409 410SEE ALSO 411-------- 412*coap_block*(3), *coap_context*(3), *coap_handler*(3), *coap_init*(3), 413*coap_pdu_setup*(3), *coap_resource*(3) and *coap_session*(3) 414 415FURTHER INFORMATION 416------------------- 417See 418 419"https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]" 420 421"https://rfc-editor.org/rfc/rfc7641[RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)]" 422 423"https://rfc-editor.org/rfc/rfc8132[RFC8132: PATCH and FETCH Methods for the Constrained Application Protocol (CoAP)]" 424 425for further information. 426 427 428BUGS 429---- 430Please report bugs on the mailing list for libcoap: 431libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at 432https://github.com/obgm/libcoap/issues 433 434AUTHORS 435------- 436The libcoap project <libcoap-developers@lists.sourceforge.net> 437