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 - work with CoAP
8       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       For specific (D)TLS library support, link with -lcoap-3-notls,
23       -lcoap-3-gnutls, -lcoap-3-openssl, -lcoap-3-mbedtls or
24       -lcoap-3-tinydtls. Otherwise, link with -lcoap-3 to get the default
25       (D)TLS library support.
26

DESCRIPTION

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

RETURN VALUES

115       The coap_resource_set_get_observable() function return 0 on failure, 1
116       on success.
117
118       The coap_cancel_observe() function return 0 on failure, 1 on success.
119

EXAMPLES

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

SEE ALSO

363       coap_attribute(3), coap_block(3), coap_context(3), coap_handler(3),
364       coap_pdu_setup(3) and coap_resource(3)
365

FURTHER INFORMATION

367       "RFC7252: The Constrained Application Protocol (CoAP)"
368
369       "RFC7641: Observing Resources in the Constrained Application Protocol
370       (CoAP)"
371
372       "RFC8132: PATCH and FETCH Methods for the Constrained Application
373       Protocol (CoAP)"
374

BUGS

376       Please report bugs on the mailing list for libcoap:
377       libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
378       https://github.com/obgm/libcoap/issues
379

AUTHORS

381       The libcoap project <libcoap-developers@lists.sourceforge.net>
382
383
384
385coap_observe 4.3.0                01/20/2022                   COAP_OBSERVE(3)
Impressum