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 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
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
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
370 coap_block(3), coap_context(3), coap_handler(3), coap_pdu_setup(3),
371 coap_resource(3) and coap_session(3)
372
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
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
388 The libcoap project <libcoap-developers@lists.sourceforge.net>
389
390
391
392coap_observe 4.3.1 11/24/2022 COAP_OBSERVE(3)