1COAP_OBSERVE(3)                 libcoap Manual                 COAP_OBSERVE(3)
2
3
4

NAME

6       coap_observe, coap_resource_set_get_observable,
7       coap_resource_notify_observers, coap_cancel_observe,
8       coap_session_set_no_observe_cancel - work with CoAP observe
9

SYNOPSIS

11       #include <coap3/coap.h>
12
13       void coap_resource_set_get_observable(coap_resource_t *resource, int
14       mode);
15
16       int coap_resource_notify_observers(coap_resource_t *resource, const
17       coap_string_t *query);
18
19       int coap_cancel_observe(coap_session_t *session, coap_binary_t *token,
20       coap_pdu_type_t message_type);
21
22       void coap_session_set_no_observe_cancel(coap_session_t *session);
23
24       For specific (D)TLS library support, link with -lcoap-3-notls,
25       -lcoap-3-gnutls, -lcoap-3-openssl, -lcoap-3-mbedtls or
26       -lcoap-3-tinydtls. Otherwise, link with -lcoap-3 to get the default
27       (D)TLS library support.
28

DESCRIPTION

30       RFC 7641 extends the CoAP protocol to be able to monitor the state of a
31       resource over time.
32
33       This enables clients to "observe" resources with a defined query, i.e.,
34       to retrieve a representation of a resource and keep this representation
35       updated by the server over a period of time.
36
37       The server has to flag a resource as "observable", and then the client
38       has to request in a GET request that it wants to observe this resource
39       by the use of the COAP_OPTION_OBSERVE Option with a value of
40       COAP_OBSERVE_ESTABLISH. Optionally, the client can specify query
41       options for the resource, or by using a FETCH request instead of a GET
42       to define a query (RFC8132).
43
44       To remove the "observe" subscription, the client has to issue a GET (or
45       FETCH) request with the COAP_OPTION_OBSERVE Option with a value of
46       COAP_OBSERVE_CANCEL using the same token and other options used for
47       making the initial "observe" request. Alternatively, "observe" can be
48       cancelled using coap_cancel_observe() instead.
49
50       The underlying library adds in and removes "subscribers" to "observe"
51       the resource as appropriate in the server side logic.
52
53       Within the server application, it needs to determine that there is a
54       change of state of the resource under observation, and then cause the
55       CoAP library layer to initiate a "fake GET/FETCH request" so that an
56       observe GET/FETCH response gets sent back to all the clients that are
57       observing the resource. The appropriate GET/FETCH handler within the
58       server application is called to fill in the response packet with the
59       appropriate information. This "fake GET/FETCH request" is triggered by
60       a call to coap_resource_notify_observers().
61
62       The call to coap_io_process() in the main server application i/o loop
63       will do all the necessary processing of sending any outstanding "fake
64       GET/FETCH requests".
65
66       Whenever the server sends a copy of the state of the "observed"
67       resource to the client, it will use the same token used by the client
68       when the client requested the "observe". The client will receive this
69       observe response in the handler defined by
70       coap_register_response_handler(3). It is the responsibility of the
71       client application to match the supplied token and update the
72       appropriate internal information.
73
74       The coap_resource_set_get_observable() function enables or disables the
75       observable status of the resource by the setting of mode. If mode is 1,
76       then the resource is observable. If mode is 0, then the resource is no
77       longer observable.
78
79       NOTE: It is not possible for the Unknown Resource, created by
80       coap_resource_unknown_init(3), to be observable as the Uri-Path is not
81       known when libcoap creates a "fake GET/FETCH request". The Unknown
82       Resource PUT handler must create a new resource and mark the resource
83       as "observable" if a specific resource needs to be observable. The
84       application must then manage the deletion of the resource at the
85       appropriate time.
86
87       NOTE: The type (confirmable or non-confirmable) of the triggered
88       observe GET response is determined not by the initial GET/FETCH
89       request, but independently by the server as per
90       https://tools.ietf.org/html/rfc7641#section-3.5. This is controlled by
91       the flags (one of COAP_RESOURCE_FLAGS_NOTIFY_NON,
92       COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS or
93       COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource using
94       coap_resource_init(3).
95
96       NOTE: Furthermore, the server must send at least one "observe" response
97       as confirmable, when generally sending non-confirmable, at least every
98       24 hours as per https://tools.ietf.org/html/rfc7641#section-4.5.
99       Libcoap automatically handles this by sending every fifth
100       (COAP_OBS_MAX_NON) response as a confirmable response for detection
101       that the client is still responding unless if
102       COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS is set, which is a RFC7641
103       violation, where non-confirmable "observe" responses are always sent as
104       required by some higher layer protocols.
105
106       The coap_resource_notify_observers() function needs to be called
107       whenever the server application determines that there has been a change
108       to the state of resource. The query parameter is obsolete and ignored.
109
110       The coap_cancel_observe() function can be used by the client to cancel
111       an observe request that is being tracked. This will cause the
112       appropriate PDU to be sent to the server to cancel the observation,
113       based on the session and token used to set up the observe and the PDU
114       is of type message_type (use COAP_MESSAGE_NON or COAP_MESSAGE_CON).
115
116       The coap_session_set_no_observe_cancel() function can be called by the
117       client to disable calling coap_cancel_observe() when the session is
118       being closed down / freed off. coap_cancel_observe() can still be
119       called directly by the client application.
120

RETURN VALUES

122       The coap_resource_set_get_observable() function return 0 on failure, 1
123       on success.
124
125       The coap_cancel_observe() function return 0 on failure, 1 on success.
126

EXAMPLES

128       Simple Time Server
129
130           #include <coap3/coap.h>
131
132           #include <stdio.h>
133
134           coap_resource_t *time_resource = NULL;
135
136           /* specific GET "time" handler, called from hnd_get_generic() */
137
138           static void
139           hnd_get_time(coap_resource_t *resource, coap_session_t *session,
140           const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
141
142             unsigned char buf[40];
143             size_t len;
144             time_t now;
145             (void)resource;
146             (void)session;
147
148             /* ... Additional analysis code for resource, request pdu etc.  ... */
149
150             /* After analysis, generate a suitable response */
151
152             /* Note that token, if set, is already in the response pdu */
153
154             now = time(NULL);
155
156             if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
157               /* Output secs since Jan 1 1970 */
158               len = snprintf((char *)buf, sizeof(buf), "%lu", now);
159             }
160             else {
161               /* Output human-readable time */
162               struct tm *tmp;
163               tmp = gmtime(&now);
164               if (!tmp) {
165                 /* If 'now' is not valid */
166                 coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
167                 return;
168               }
169               len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
170             }
171             coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
172             /*
173              * Invoke coap_add_data_large_response() to do all the hard work.
174              *
175              * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
176              * Define how long this response is valid for (secs) - 1 - to add in.
177              * ETAG Option added internally with unique value as param set to 0
178              *
179              * OBSERVE Option added internally if needed within the function
180              * BLOCK2 Option added internally if output too large
181              * SIZE2 Option added internally
182              */
183             coap_add_data_large_response(resource, session, request, response,
184                                          query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
185                                          len,
186                                          buf, NULL, NULL);
187           }
188
189           /* Generic GET handler */
190
191           static void
192           hnd_get_generic(coap_resource_t *resource, coap_session_t *session,
193           const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
194
195             coap_str_const_t *uri_path = coap_resource_get_uri_path(resource);
196
197             if (!uri_path) {
198               /* Unexpected Failure */
199               coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
200               return;
201             }
202
203             /* Is this the "time" resource" ? */
204             if (coap_string_equal(uri_path, coap_make_str_const("time"))) {
205               hnd_get_time(resource, session, request, query, response);
206               return;
207             }
208
209             /* Other resources code */
210
211             /* Failure response */
212             coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
213           }
214
215           /* Initialize generic GET handler */
216
217           static void
218           init_resources(coap_context_t *ctx)
219           {
220
221             coap_resource_t *r;
222
223             /* Create a resource to return return or update time */
224             r = coap_resource_init(coap_make_str_const("time"),
225                                    COAP_RESOURCE_FLAGS_NOTIFY_CON);
226
227             /* We are using a generic GET handler here */
228             coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_generic);
229
230             coap_resource_set_get_observable(r, 1);
231
232             coap_add_resource(ctx, r);
233             time_resource = r;
234
235           }
236
237           int main(int argc, char *argv[]){
238
239             coap_context_t *ctx = NULL;
240             coap_endpoint_t *ep = NULL;
241             coap_address_t addr;
242             unsigned wait_ms;
243             struct timeval tv_last = {0, 0};
244             /* Remove (void) definition if variable is used */
245             (void)argc;
246             (void)argv;
247
248             memset (&tv_last, 0, sizeof(tv_last));
249
250             /* Create the libcoap context */
251             ctx = coap_new_context(NULL);
252             if (!ctx) {
253               exit(1);
254             }
255             /* See coap_block(3) */
256             coap_context_set_block_mode(ctx,
257                                         COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
258
259             coap_address_init(&addr);
260             addr.addr.sa.sa_family = AF_INET;
261             addr.addr.sin.sin_port = ntohs(COAP_DEFAULT_PORT);
262             ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
263
264             /* Other Set up Code */
265
266             init_resources(ctx);
267
268             wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
269
270             while (1) {
271               int result = coap_io_process( ctx, wait_ms );
272               if ( result < 0 ) {
273                 break;
274               } else if ( result && (unsigned)result < wait_ms ) {
275                 /* decrement if there is a result wait time returned */
276                 wait_ms -= result;
277               } else {
278                 /*
279                  * result == 0, or result >= wait_ms
280                  * (wait_ms could have decremented to a small value, below
281                  * the granularity of the timer in coap_io_process() and hence
282                  * result == 0)
283                  */
284                 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
285               }
286               if (time_resource) {
287                 struct timeval tv_now;
288                 if (gettimeofday (&tv_now, NULL) == 0) {
289                   if (tv_last.tv_sec != tv_now.tv_sec) {
290                     /* Happens once per second */
291                     tv_last = tv_now;
292                     coap_resource_notify_observers(time_resource, NULL);
293                   }
294                   /* need to wait until next second starts if wait_ms is too large */
295                   unsigned next_sec_ms = 1000 - (tv_now.tv_usec / 1000);
296
297                   if (next_sec_ms && next_sec_ms < wait_ms)
298                     wait_ms = next_sec_ms;
299                 }
300               }
301             }
302             exit(0);
303
304           }
305
306       Client Observe Request Setup
307
308           #include <coap3/coap.h>
309
310           /* Usually, requests are sent confirmable */
311
312           static unsigned char msgtype = COAP_MESSAGE_CON;
313
314           static unsigned int token = 0;
315
316           static coap_pdu_t *
317           coap_new_request(coap_context_t *context, coap_session_t *session, char request_code,
318           coap_optlist_t **options, unsigned char *data, size_t length, int observe) {
319
320             coap_pdu_t *pdu;
321             /* Remove (void) definition if variable is used */
322             (void)context;
323
324             /* Create the pdu with the appropriate options */
325             pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session),
326                                 coap_session_max_pdu_size(session));
327             if (!pdu)
328               return NULL;
329
330             /*
331              * Create uniqueness token for this request for handling unsolicited /
332              * delayed responses
333              */
334             token++;
335             if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) {
336               coap_log(LOG_DEBUG, "cannot add token to request\n");
337               goto error;
338             }
339
340             if (request_code == COAP_REQUEST_GET && observe) {
341               /* Indicate that we want to observe this resource */
342               if (!coap_insert_optlist(options,
343                                        coap_new_optlist(COAP_OPTION_OBSERVE,
344                                                    COAP_OBSERVE_ESTABLISH, NULL)))
345                 goto error;
346             }
347
348             /* ... Other code / options etc. ... */
349
350             /* Add in all the options (after internal sorting) to the pdu */
351             if (!coap_add_optlist_pdu(pdu, options))
352               goto error;
353
354             if (data && length) {
355               /* Add in the specified data */
356               if (!coap_add_data(pdu, length, data))
357                 goto error;
358             }
359
360             return pdu;
361
362           error:
363
364             coap_delete_pdu(pdu);
365             return NULL;
366
367           }
368

SEE ALSO

370       coap_block(3), coap_context(3), coap_handler(3), coap_pdu_setup(3),
371       coap_resource(3) and coap_session(3)
372

FURTHER INFORMATION

374       "RFC7252: The Constrained Application Protocol (CoAP)"
375
376       "RFC7641: Observing Resources in the Constrained Application Protocol
377       (CoAP)"
378
379       "RFC8132: PATCH and FETCH Methods for the Constrained Application
380       Protocol (CoAP)"
381

BUGS

383       Please report bugs on the mailing list for libcoap:
384       libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
385       https://github.com/obgm/libcoap/issues
386

AUTHORS

388       The libcoap project <libcoap-developers@lists.sourceforge.net>
389
390
391
392coap_observe 4.3.1                01/19/2023                   COAP_OBSERVE(3)
Impressum