'\" t
.\" Title: coap_observe
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1
.\" Date: 04/13/2019
.\" Manual: libcoap Manual
.\" Source: coap_observe 4.2.0
.\" Language: English
.\"
.TH "COAP_OBSERVE" "3" "04/13/2019" "coap_observe 4\&.2\&.0" "libcoap Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
coap_observe, coap_resource_set_get_observable, coap_resource_notify_observers, coap_resource_get_uri_path, coap_find_observer, coap_delete_observer, coap_delete_observers \- work with CoAP observe
.SH "SYNOPSIS"
.sp
\fB#include \fR
.sp
\fBvoid coap_resource_set_get_observable(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, int \fR\fB\fImode\fR\fR\fB);\fR
.sp
\fBint coap_resource_notify_observers(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, const const_string_t *\fR\fB\fIquery\fR\fR\fB);\fR
.sp
\fBconst coap_string_t *coap_resource_get_uri_path(coap_resource_t *\fR\fB\fIresource\fR\fR\fB);\fR
.sp
\fBcoap_subscription_t *coap_find_observer(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB, const const_string_t *\fR\fB\fItoken\fR\fR\fB);\fR
.sp
\fBint coap_delete_observer(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB, const coap_binary_t *\fR\fB\fItoken\fR\fR\fB);\fR
.sp
\fBvoid coap_delete_observers(coap_context_t *\fR\fB\fIcontext\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB);\fR
.sp
Link with \fB\-lcoap\-2\fR, \fB\-lcoap\-2\-gnutls\fR, \fB\-lcoap\-2\-openssl\fR or \fB\-lcoap\-2\-tinydtls\fR depending on your (D)TLS library type\&.
.SH "DESCRIPTION"
.sp
RFC 7641 extends the CoAP protocol to be able to monitor the state of a resource over time\&.
.sp
This enables clients to "observe" resources with a defined query, i\&.e\&., to retrieve a representation of a resource and keep this representation updated by the server over a period of time\&.
.sp
The server has to flag a resource as "observable", and then the client has to request in a GET request that it wants to observe this resource by the use of the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_ESTABLISH\&. Optionally, the client can specify query options for the resource\&.
.sp
To remove the "observe" subscription, the client has to issue a GET request with the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_CANCEL\&. Alternatively, the server can remove a subscription by calling \fBcoap_delete_observer\fR() or \fBcoap_delete_observers\fR(), but this does not notify the client that the subscription has been removed\&.
.sp
The underlying library adds in and removes "subscribers" to the resource as appropriate in the server side logic\&.
.sp
Within the server application, it needs to determine that there is a change of state of the resource under observation, and then cause the CoAP library layer to initiate a "fake GET request" so that an observe GET response gets sent back to all the clients that are observing the resource\&. The appropriate GET handler within the server application is called to fill in the response packet with the appropriate information\&. This "fake GET request" is triggered by a call to \fBcoap_resource_notify_observers\fR()\&.
.sp
The call to \fBcoap_run_once\fR() in the main server application i/o loop will do all the necessary processing of sending any outstanding "fake GET requests"\&.
.sp
Whenever the server sends a copy of the state of the "observed" resource to the client, it will use the same token used by the client when the client requested the "observe"\&. The client will receive this observe response in the handler defined by \fBcoap_register_response_handler\fR(3)\&. It is the responsibility of the client application to match the supplied token and update the appropriate internal information\&.
.sp
The \fBcoap_resource_set_get_observable\fR() function enables or disables the observable status of the \fIresource\fR by the setting of \fImode\fR\&. If \fImode\fR is 1, then the \fIresource\fR is observable\&. If \fImode\fR is 0, then the \fIresource\fR is no longer observable\&.
.sp
\fBNOTE:\fR It is not possible for the Unknown Resource, created by \fBcoap_resource_unknown_init\fR(3), to be observable as the Uri\-Path is not known when libcoap creates a "fake GET request"\&. The Unknown Resource PUT handler must create a new resource and mark the resource as "observable" if a specific resource needs to be observable\&. The application must then manage the deleteion of the resource at the appropriate time\&.
.sp
\fBNOTE:\fR The type (confirmable or non\-confirmable) of the triggered observe GET response is determined not by the initial GET request, but independently by the server as per RFC 7641 3\&.5\&. Transmission\&. This is controlled by the flags (one of COAP_RESOURCE_FLAGS_NOTIFY_NON or COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource using \fBcoap_resource_init\fR(3)\&. Furthermore, the server must send at least one "observe" response as confirmable, when generally sending non\-confirmable, every 24 hours\&. libcoap handles this by sending every fifth (COAP_OBS_MAX_NON) response as a confirmable response for detection that the client is still responding\&.
.sp
The \fBcoap_resource_notify_observers\fR() function needs to be called whenever the server application determines that there has been a change to the state of \fIresource\fR, possibly only matching a specific \fIquery\fR if \fIquery\fR is not NULL\&.
.sp
The \fBcoap_resource_get_uri_path\fR() function is used to obtain the UriPath of the \fIresource\fR definion\&.
.sp
The \fBcoap_find_observer\fR() function is used to check whether the current GET request as determined from \fIresource\fR, \fIsession\fR and \fItoken\fR is currently being observed or not\&.
.sp
The \fBcoap_delete_observer\fR() function deletes the specific observer associated with \fIresource\fR, \fIsession\fR and has \fItoken\fR\&.
.sp
The \fBcoap_delete_observers\fR() function is used to delete all observers associated with the \fIsession\fR that is a part of the \fIcontext\fR\&.
.SH "RETURN VALUES"
.sp
The \fBcoap_resource_get_uri_path\fR() function returns the uri_path or NULL if there was a failure\&.
.sp
The \fBcoap_find_observer\fR() function returns the subscription or NULL if there was a failure\&.
.sp
The \fBcoap_resource_set_get_observable\fR() function return 0 on failure, 1 on success\&.
.sp
The \fBcoap_delete_observer\fR() function return 0 on failure, 1 on success\&.
.SH "EXAMPLES"
.sp
\fBSimple Time Server\fR
.sp
.if n \{\
.RS 4
.\}
.nf
#include
coap_resource_t *time_resource = NULL;
static int check_if_time_resource_has_changed(coap_resource_t *resource) {
return 1;
}
/* specific GET "time" handler, called from hnd_get_generic() */
static void
hnd_get_time(coap_context_t *context, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {
unsigned char buf[40];
size_t len;
time_t now;
/* \&.\&.\&. Additional analysis code for resource, request pdu etc\&. \&.\&.\&. */
/* After analysis, generate a suitable response */
/* Note that token, if set, is already in the response pdu */
now = time(NULL);
if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
/* Output secs since Jan 1 1970 */
len = snprintf((char *)buf, sizeof(buf), "%lu", now);
}
else {
/* Output human\-readable time */
struct tm *tmp;
tmp = gmtime(&now);
if (!tmp) {
/* If \*(Aqnow\*(Aq is not valid */
response\->code = COAP_RESPONSE_CODE(404);
return;
}
len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
}
/*
* Invoke coap_add_data_blocked_response() to do all the hard work\&.
*
* Define the format \- COAP_MEDIATYPE_TEXT_PLAIN \- to add in
* Define how long this response is valid for (secs) \- 1 \- to add in\&.
*
* OBSERVE Option added internally if needed within the function
* BLOCK2 Option added internally if output too large
* ETAG Option added internally
*/
coap_add_data_blocked_response(resource, session, request, response, token,
COAP_MEDIATYPE_TEXT_PLAIN, 1,
len,
buf);
/*
* As resource\->code has been updated in coap_add_data_blocked_response(),
* the response pdu will be transmitted by the underlying library\&.
*/
}
/* Generic GET handler */
static void
hnd_get_generic(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {
coap_str_const_t *uri_path = coap_resource_get_uri_path(resource);
if (!uri_path) {
/* Unexpected Failure */
response\->code = COAP_RESPONSE_CODE(400);
return;
}
/* Is this the "time" resource" ? */
if (coap_string_equal(uri_path, coap_make_str_const("time"))) {
hnd_get_time(ctx, resource, session, request, token, query,
response);
return;
}
/* Other resources code */
/* Failure response */
response\->code = COAP_RESPONSE_CODE(400);
}
/* Initialize generic GET handler */
static void
init_resources(coap_context_t *ctx)
{
coap_resource_t *r;
/* Create a resource to return return or update time */
r = coap_resource_init(coap_make_str_const("time"),
COAP_RESOURCE_FLAGS_NOTIFY_CON);
/* We are using a generic GET handler here */
coap_register_handler(r, COAP_REQUEST_GET, hnd_get_generic);
coap_resource_set_get_observable(r, 1);
coap_add_resource(ctx, r);
time_resource = r;
}
int main(int argc, char *argv[]){
coap_context_t *ctx = NULL;
coap_endpoint_t *ep = NULL;
coap_address_t addr;
unsigned wait_ms;
/* Create the libcoap context */
ctx = coap_new_context(NULL);
if (!ctx) {
exit(1);
}
coap_address_init(&addr);
addr\&.addr\&.sa\&.sa_family = AF_INET;
addr\&.addr\&.sin\&.sin_port = ntohs(COAP_DEFAULT_PORT);
ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
/* Other Set up Code */
init_resources(ctx);
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
while (1) {
int result = coap_run_once( ctx, wait_ms );
if ( result < 0 ) {
break;
} else if ( result && (unsigned)result < wait_ms ) {
/* decrement if there is a result wait time returned */
wait_ms \-= result;
} else {
/*
* result == 0, or result >= wait_ms
* (wait_ms could have decremented to a small value, below
* the granularity of the timer in coap_run_once() and hence
* result == 0)
*/
time_t t_now = time(NULL);
if (t_last != t_now) {
/* Happens once per second */
int i;
t_last = t_now;
if (time_resource) {
coap_resource_notify_observers(time_resource, NULL);
}
}
if (result) {
/* result must have been >= wait_ms, so reset wait_ms */
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
}
}
}
exit(0);
}
.fi
.if n \{\
.RE
.\}
.sp
\fBClient Observe Request Setup\fR
.sp
.if n \{\
.RS 4
.\}
.nf
#include
/* Usually, requests are sent confirmable */
static unsigned char msgtype = COAP_MESSAGE_CON;
static unsigned int token = 0;
static coap_pdu_t *
coap_new_request(coap_context_t *context, coap_session_t *session, char request_code,
coap_optlist_t **options, unsigned char *data, size_t length, int observe) {
coap_pdu_t *pdu;
coap_optlist_t *opt;
(void)context;
/* Create the pdu with the appropriate options */
pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session),
coap_session_max_pdu_size(session));
if (!pdu)
return NULL;
/*
* Create uniqueness token for this request for handling unsolicited /
* delayed responses
*/
token++;
if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) {
coap_log(LOG_DEBUG, "cannot add token to request\en");
goto error;
}
if (request_code == COAP_REQUEST_GET && observe) {
/* Indicate that we want to observe this resource */
if (!coap_insert_optlist(options,
coap_new_optlist(COAP_OPTION_OBSERVE,
COAP_OBSERVE_ESTABLISH, NULL)))
goto error;
}
/* \&.\&.\&. Other code / options etc\&. \&.\&.\&. */
/* Add in all the options (after internal sorting) to the pdu */
if (!coap_add_optlist_pdu(pdu, options))
goto error;
if (data && length) {
/* Add in the specified data */
if (!coap_add_data(pdu, length, data))
goto error;
}
return pdu;
error:
coap_delete_pdu(pdu);
return NULL;
}
.fi
.if n \{\
.RE
.\}
.SH "SEE ALSO"
.sp
\fBcoap_attribute\fR(3), \fBcoap_context\fR(3), \fBcoap_handler\fR(3), \fBcoap_pdu_setup\fR(3) and \fBcoap_resource\fR(3)
.SH "FURTHER INFORMATION"
.sp
"RFC7252: The Constrained Application Protocol (CoAP)" "RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)"
.SH "BUGS"
.sp
Please report bugs on the mailing list for libcoap: libcoap\-developers@lists\&.sourceforge\&.net
.SH "AUTHORS"
.sp
The libcoap project