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 - work with CoAP
8 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 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
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
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
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
363 coap_attribute(3), coap_block(3), coap_context(3), coap_handler(3),
364 coap_pdu_setup(3) and coap_resource(3)
365
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
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
381 The libcoap project <libcoap-developers@lists.sourceforge.net>
382
383
384
385coap_observe 4.3.0 07/22/2021 COAP_OBSERVE(3)