From 747205fb937ddb2b4c044ccd2862929a38a89dc9 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Fri, 9 Mar 2018 17:15:23 +0000 Subject: [PATCH] man/coap_observe.txt.in: New man page covering Observe usage --- man/coap_observe.txt.in | 372 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 man/coap_observe.txt.in diff --git a/man/coap_observe.txt.in b/man/coap_observe.txt.in new file mode 100644 index 00000000..db07c6a1 --- /dev/null +++ b/man/coap_observe.txt.in @@ -0,0 +1,372 @@ +// -*- mode:doc; -*- +// vim: set syntax=asciidoc,tw=0: + +coap_observe(3) +================= +:doctype: manpage +:man source: coap_observe +:man version: @PACKAGE_VERSION@ +:man manual: libcoap Manual + +NAME +---- +coap_observe, coap_resource_set_get_observable, coap_resource_notify_observers, +coap_resource_get_uri_path, coap_find_observer - work with CoAP observe + +SYNOPSIS +-------- +*#include * + +*void coap_resource_set_get_observable(coap_resource_t *_resource_, +int _mode_);* + +*int coap_resource_notify_observers(coap_resource_t *_resource_, +const str *_query_);* + +*const str *coap_resource_get_uri_path(coap_resource_t *_resource_);* + +*coap_subscription_t *coap_find_observer(coap_resource_t *_resource_, +coap_session_t *_session_, const str *_token_);* + +Link with -lcoap-@LIBCOAP_API_VERSION@. + +DESCRIPTION +----------- +RFC 7641 extends the CoAP protocol to be able to monitor the state of a +resource over time. + +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. + +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. + +The underlying library adds in and removes "subscribers" to the resource as +appropriate in the server side logic. + +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 unsolicited 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. + +The call to coap_write() in the main server application i/o loop will do all +the necessary processing of sending any outstanding "fake GET requests". + +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 unsolicited response +in the handler defined by coap_register_response_handler(). It is the +responsibility of the client application to match the supplied token and +update the appropriate internal information. + +The *coap_resource_set_get_observable*() function enables or disables the +observable status of the _resource_ by the setting of _mode_. If _mode_ is +1, then the _resource_ is observable. If _mode_ is 0, then the +_resource_ is no longer observable. + +The *coap_resource_notify_observers*() function needs to be called whenever the +server application determines that there has been a change to the state of +_resource_, possibly only matching a specific _query_ if _query_ is not NULL. + +The *coap_resource_get_uri_path*() function is used to obtain the UriPath of +the _resource_ definion. + +The *coap_find_observer*() function is used to check whether the current GET +request as determined from _resource_, _session_ and _token_ is currently +being observed or not. + +RETURN VALUES +------------- +The *coap_resource_get_uri_path*() function +returns the uri_path or NULL if there was a failure. + +The *coap_find_observer*() function +returns the subscription or NULL if there was a failure. + +The *coap_resource_set_get_observable*() function return 0 on failure, 1 on +success. + +EXAMPLES +-------- +*Server Initialize Resources* + +[source, c] +-- +#include + +external coap_resource_t *time_resource; + +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((unsigned char *)"time", 4, + 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 = 1; + +} +-- + +*Server GET Callback Handlers* + +[source, c] +-- +#include + +/* specific GET "time" handler, called from hnd_get_generic() */ + +static void +hnd_get_time(coap_context_t *ctx, coap_resource_t *resource, +coap_session_t *session, coap_pdu_t *request, str *token, str *query, +coap_pdu_t *response) { + + unsigned char buf[40]; + size_t len; + time_t now; + coap_tick_t t; + + /* Define what the response code is going to be */ + response->code = COAP_RESPONSE_CODE(205); + + /* If the resource is being Observed, return with Observe set */ + if (coap_find_observer(resource, session, token)) { + coap_add_option(response, + COAP_OPTION_OBSERVE, + coap_encode_var_bytes(buf, resource->observe), buf); + } + + /* Response is in plain text format */ + coap_add_option(response, + COAP_OPTION_CONTENT_FORMAT, + coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf); + + /* Report on how long this response is valid for */ + coap_add_option(response, + COAP_OPTION_MAXAGE, + coap_encode_var_bytes(buf, 0x01), buf); + + now = time(NULL); + + if (query != NULL && memcmp(query->s, "secs", min(4, query->length)) == 0) { + /* Output secs since Jan 1 1970 */ + len = snprintf((char *)buf, sizeof(buf), "%lu", now); + coap_add_data(response, len, buf); + } else { + /* Output human-readable time */ + struct tm *tmp; + tmp = gmtime(&now); + len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp); + coap_add_data(response, len, buf); + } + +} + +static void +hnd_get_generic(coap_context_t *ctx, coap_resource_t *resource, +coap_session_t *session, coap_pdu_t *request, str *token, str *query, +coap_pdu_t *response) { + + const str *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 (!memcmp((char*)uri_path->s, "time", sizeof("time")) { + return hnd_get_time(ctx, resource, session, request, token, query, + response); + } + + /* Other resources code */ + + /* Failure response */ + response->code = COAP_RESPONSE_CODE(400); + +} +-- + +*Server Main Loop* + +[source, c] +-- +#include + +coap_resource_t *time_resource = NULL; + +int main(int argc, char *argv[]){ + + coap_contest_t *ctx = NULL; + unsigned wait_ms; + + /* 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 ((unsigned)result < wait_ms) { + wait_ms -= result; + } else { + if (check_if_time_resource_has_changed(time_resource)) { + coap_resource_notify_observers(time_resource, NULL); + } + wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; + } + } +} +-- + +*Client Request Setup* + +[source, c] +-- +#include + +unsigned char msgtype = COAP_MESSAGE_CON; /* usually, requests are sent confirmable */ + +typedef struct coap_list_t { + struct coap_list_t *next; + char data[]; +} coap_list_t; + +static int coap_insert(coap_list_t **head, coap_list_t *node) { + + if (!node) { + coap_log(LOG_WARNING, "node not specified\n"); + } else { + /* must append at the list end to avoid re-ordering of + * options during sort */ + LL_APPEND((*head), node); + } + + return node != NULL; + +} + +static coap_list_t * new_option_node(uint16_t key, size_t length, +unsigned char *data) { + + coap_list_t *node; + + node = coap_malloc(sizeof(coap_list_t) + sizeof(coap_option) + length); + + if (node) { + coap_option *option; + option = (coap_option *)(node->data); + COAP_OPTION_KEY(*option) = key; + COAP_OPTION_LENGTH(*option) = length; + memcpy(COAP_OPTION_DATA(*option), data, length); + } else { + coap_log(LOG_DEBUG, "new_option_node: malloc\n"); + } + + return node; + +} + +static int order_opts(void *a, void *b) { + + coap_option *o1, *o2; + + if (!a || !b) + return a < b ? -1 : 1; + + o1 = (coap_option *)(((coap_list_t *)a)->data); + o2 = (coap_option *)(((coap_list_t *)b)->data); + + return (COAP_OPTION_KEY(*o1) < COAP_OPTION_KEY(*o2)) + ? -1 + : (COAP_OPTION_KEY(*o1) != COAP_OPTION_KEY(*o2)); + +} + +static coap_pdu_t * +coap_new_request(coap_context_t *ctx, coap_session_t *session, char m, +coap_list_t **options, unsigned char *data, size_t length, int observe) { + + static unsigned int token = 0; + coap_pdu_t *pdu; + coap_list_t *opt; + (void)ctx; + + if (!(pdu = coap_new_pdu(session))) + return NULL; + + pdu->type = msgtype; + pdu->tid = coap_new_message_id(session); + pdu->code = m; + + /* Create uniqueness for this request */ + token++; + if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) { + coap_log(LOG_DEBUG, "cannot add token to request\n"); + } + + if (m == COAP_REQUEST_GET && observe) { + /* Indicate that we want to observe this resource */ + coap_insert(options, new_option_node(COAP_OPTION_OBSERVE, + COAP_OBSERVE_ESTABLISH, NULL)); + } + + if (options) { + /* sort options for delta encoding */ + LL_SORT((*options), order_opts); + + LL_FOREACH((*options), opt) { + coap_option *o = (coap_option *)(opt->data); + coap_add_option(pdu, + COAP_OPTION_KEY(*o), + COAP_OPTION_LENGTH(*o), + COAP_OPTION_DATA(*o)); + } + } + + if (length) { + coap_add_data(pdu, length, data); + } + + return pdu; + +} +-- + +SEE ALSO +-------- +*coap_attribute*(3), *coap_context*(3), *coap_handler*(3) and +*coap_resource*(3) + +FURTHER INFORMATION +------------------- +"RFC7252: The Constrained Application Protocol (CoAP)" +"RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)" + +BUGS +---- +Please report bugs on the mailing list for libcoap: +libcoap-developers@lists.sourceforge.net + +AUTHORS +------- +The libcoap project