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       RFC7641 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       NOTE: COAP_RESOURCE_MAX_SUBSCRIBER may have been defined to limit the
54       number of subscribers to a resource when libcoap was built.
55
56       Within the server application, it needs to determine that there is a
57       change of state of the resource under observation, and then cause the
58       CoAP library layer to initiate a "fake GET/FETCH request" so that an
59       observe GET/FETCH response gets sent back to all the clients that are
60       observing the resource. The appropriate GET/FETCH handler within the
61       server application is called to fill in the response packet with the
62       appropriate information. This "fake GET/FETCH request" is triggered by
63       a call to coap_resource_notify_observers().
64
65       The call to coap_io_process() in the main server application i/o loop
66       will do all the necessary processing of sending any outstanding "fake
67       GET/FETCH requests".
68
69       Whenever the server sends a copy of the state of the "observed"
70       resource to the client, it will use the same token used by the client
71       when the client requested the "observe" (or the last token used for a
72       FETCH that spans multiple blocks). The client will receive this observe
73       response in the handler defined by coap_register_response_handler(3)
74       (with the token updated to the initial token used by the client
75       application for a large FETCH). It is the responsibility of the client
76       application to match the supplied token and update the appropriate
77       internal information.
78

FUNCTIONS

80       Function: coap_resource_set_get_observable()
81
82       The coap_resource_set_get_observable() function enables or disables the
83       observable status of the resource by the setting of mode. If mode is 1,
84       then the resource is observable. If mode is 0, then the resource is no
85       longer observable.
86
87       NOTE: It is not possible for the Unknown Resource, created by
88       coap_resource_unknown_init(3), to be observable as the Uri-Path is not
89       known when libcoap creates a "fake GET/FETCH request". The Unknown
90       Resource PUT handler must create a new resource and mark the resource
91       as "observable" if a specific resource needs to be observable. The
92       application must then manage the deletion of the resource at the
93       appropriate time.
94
95       NOTE: The type (confirmable or non-confirmable) of the triggered
96       observe GET response is determined not by the initial GET/FETCH
97       request, but independently by the server as per "RFC7641 3.5.
98       Transmission". This is controlled by the flags (one of
99       COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS
100       or COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource
101       using coap_resource_init(3).
102
103       NOTE: Furthermore, the server must send at least one "observe" response
104       as confirmable, when generally sending non-confirmable, at least every
105       24 hours as per "RFC7641 4.5. Transmission". Libcoap automatically
106       handles this by sending every fifth (COAP_OBS_MAX_NON) response as a
107       confirmable response for detection that the client is still responding
108       unless if COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS is set, which is a
109       "RFC7641 4.5. Transmission" violation, where non-confirmable "observe"
110       responses are always sent as required by some higher layer protocols.
111
112       Function: coap_resource_notify_observers()
113
114       The coap_resource_notify_observers() function needs to be called
115       whenever the server application determines that there has been a change
116       to the state of resource. The query parameter is obsolete and ignored.
117
118       Function: coap_cancel_observe()
119
120       The coap_cancel_observe() function can be used by the client to cancel
121       an observe request that is being tracked. This will cause the
122       appropriate PDU to be sent to the server to cancel the observation,
123       based on the session and token used to set up the observe and the PDU
124       is of type message_type (use COAP_MESSAGE_NON or COAP_MESSAGE_CON).
125
126       Function: coap_session_set_no_observe_cancel()
127
128       The coap_session_set_no_observe_cancel() function can be called by the
129       client to disable calling coap_cancel_observe() when the session is
130       being closed down / freed off. coap_cancel_observe() can still be
131       called directly by the client application.
132

RETURN VALUES

134       coap_resource_notify_observers() returns 0 if not observable or no
135       observers, 1 on success.
136
137       coap_cancel_observe() returns 0 on failure, 1 on success.
138

EXAMPLES

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

SEE ALSO

389       coap_block(3), coap_context(3), coap_handler(3), coap_init(3),
390       coap_pdu_setup(3), coap_resource(3) and coap_session(3)
391

FURTHER INFORMATION

393       See
394
395       "RFC7252: The Constrained Application Protocol (CoAP)"
396
397       "RFC7641: Observing Resources in the Constrained Application Protocol
398       (CoAP)"
399
400       "RFC8132: PATCH and FETCH Methods for the Constrained Application
401       Protocol (CoAP)"
402
403       for further information.
404

BUGS

406       Please report bugs on the mailing list for libcoap:
407       libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
408       https://github.com/obgm/libcoap/issues
409

AUTHORS

411       The libcoap project <libcoap-developers@lists.sourceforge.net>
412
413
414
415coap_observe 4.3.4                10/09/2023                   COAP_OBSERVE(3)
Impressum