• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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