1COAP_OBSERVE(3) libcoap Manual COAP_OBSERVE(3)
2
3
4
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
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
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
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
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
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
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
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
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
411 The libcoap project <libcoap-developers@lists.sourceforge.net>
412
413
414
415coap_observe 4.3.4 10/09/2023 COAP_OBSERVE(3)