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