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_resource_get_uri_path,
8       coap_find_observer, coap_delete_observer, coap_delete_observers - work
9       with CoAP observe
10

SYNOPSIS

12       #include <coap2/coap.h>
13
14       void coap_resource_set_get_observable(coap_resource_t *resource, int
15       mode);
16
17       int coap_resource_notify_observers(coap_resource_t *resource, const
18       const_string_t *query);
19
20       const coap_string_t *coap_resource_get_uri_path(coap_resource_t
21       *resource);
22
23       coap_subscription_t *coap_find_observer(coap_resource_t *resource,
24       coap_session_t *session, const const_string_t *token);
25
26       int coap_delete_observer(coap_resource_t *resource, coap_session_t
27       *session, const coap_binary_t *token);
28
29       void coap_delete_observers(coap_context_t *context, coap_session_t
30       *session);
31
32       Link with -lcoap-2, -lcoap-2-gnutls, -lcoap-2-openssl or
33       -lcoap-2-tinydtls depending on your (D)TLS library type.
34

DESCRIPTION

36       RFC 7641 extends the CoAP protocol to be able to monitor the state of a
37       resource over time.
38
39       This enables clients to "observe" resources with a defined query, i.e.,
40       to retrieve a representation of a resource and keep this representation
41       updated by the server over a period of time.
42
43       The server has to flag a resource as "observable", and then the client
44       has to request in a GET request that it wants to observe this resource
45       by the use of the COAP_OPTION_OBSERVE Option with a value of
46       COAP_OBSERVE_ESTABLISH. Optionally, the client can specify query
47       options for the resource.
48
49       To remove the "observe" subscription, the client has to issue a GET
50       request with the COAP_OPTION_OBSERVE Option with a value of
51       COAP_OBSERVE_CANCEL. Alternatively, the server can remove a
52       subscription by calling coap_delete_observer() or
53       coap_delete_observers(), but this does not notify the client that the
54       subscription has been removed.
55
56       The underlying library adds in and removes "subscribers" to the
57       resource as appropriate in the server side logic.
58
59       Within the server application, it needs to determine that there is a
60       change of state of the resource under observation, and then cause the
61       CoAP library layer to initiate a "fake GET request" so that an observe
62       GET response gets sent back to all the clients that are observing the
63       resource. The appropriate GET handler within the server application is
64       called to fill in the response packet with the appropriate information.
65       This "fake GET request" is triggered by a call to
66       coap_resource_notify_observers().
67
68       The call to coap_run_once() in the main server application i/o loop
69       will do all the necessary processing of sending any outstanding "fake
70       GET requests".
71
72       Whenever the server sends a copy of the state of the "observed"
73       resource to the client, it will use the same token used by the client
74       when the client requested the "observe". The client will receive this
75       observe response in the handler defined by
76       coap_register_response_handler(3). It is the responsibility of the
77       client application to match the supplied token and update the
78       appropriate internal information.
79
80       The coap_resource_set_get_observable() function enables or disables the
81       observable status of the resource by the setting of mode. If mode is 1,
82       then the resource is observable. If mode is 0, then the resource is no
83       longer observable.
84
85       NOTE: It is not possible for the Unknown Resource, created by
86       coap_resource_unknown_init(3), to be observable as the Uri-Path is not
87       known when libcoap creates a "fake GET request". The Unknown Resource
88       PUT handler must create a new resource and mark the resource as
89       "observable" if a specific resource needs to be observable. The
90       application must then manage the deleteion of the resource at the
91       appropriate time.
92
93       NOTE: The type (confirmable or non-confirmable) of the triggered
94       observe GET response is determined not by the initial GET request, but
95       independently by the server as per RFC 7641 3.5. Transmission. This is
96       controlled by the flags (one of COAP_RESOURCE_FLAGS_NOTIFY_NON or
97       COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource using
98       coap_resource_init(3). Furthermore, the server must send at least one
99       "observe" response as confirmable, when generally sending
100       non-confirmable, every 24 hours. libcoap handles this by sending every
101       fifth (COAP_OBS_MAX_NON) response as a confirmable response for
102       detection that the client is still responding.
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, possibly only matching a specific query if
107       query is not NULL.
108
109       The coap_resource_get_uri_path() function is used to obtain the UriPath
110       of the resource definion.
111
112       The coap_find_observer() function is used to check whether the current
113       GET request as determined from resource, session and token is currently
114       being observed or not.
115
116       The coap_delete_observer() function deletes the specific observer
117       associated with resource, session and has token.
118
119       The coap_delete_observers() function is used to delete all observers
120       associated with the session that is a part of the context.
121

RETURN VALUES

123       The coap_resource_get_uri_path() function returns the uri_path or NULL
124       if there was a failure.
125
126       The coap_find_observer() function returns the subscription or NULL if
127       there was a failure.
128
129       The coap_resource_set_get_observable() function return 0 on failure, 1
130       on success.
131
132       The coap_delete_observer() function return 0 on failure, 1 on success.
133

EXAMPLES

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

SEE ALSO

371       coap_attribute(3), coap_context(3), coap_handler(3), coap_pdu_setup(3)
372       and coap_resource(3)
373

FURTHER INFORMATION

375       "RFC7252: The Constrained Application Protocol (CoAP)" "RFC7641:
376       Observing Resources in the Constrained Application Protocol (CoAP)"
377

BUGS

379       Please report bugs on the mailing list for libcoap:
380       libcoap-developers@lists.sourceforge.net
381

AUTHORS

383       The libcoap project <libcoap-developers@lists.sourceforge.net>
384
385
386
387coap_observe 4.2.0                07/25/2019                   COAP_OBSERVE(3)
Impressum