Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
COAP_OBSERVE(3)			libcoap	Manual		       COAP_OBSERVE(3)

NAME
       coap_observe, coap_resource_set_get_observable,
       coap_resource_notify_observers, coap_cancel_observe,
       coap_session_set_no_observe_cancel - Work with CoAP observe

SYNOPSIS
       #include	<coap3/coap.h>

       void coap_resource_set_get_observable(coap_resource_t *resource,	int
       mode);

       int coap_resource_notify_observers(coap_resource_t *resource, const
       coap_string_t *query);

       int coap_cancel_observe(coap_session_t *session,	coap_binary_t *token,
       coap_pdu_type_t message_type);

       void coap_session_set_no_observe_cancel(coap_session_t *session);

       For specific (D)TLS library support, link with -lcoap-3-notls,
       -lcoap-3-gnutls,	-lcoap-3-openssl, -lcoap-3-mbedtls, -lcoap-3-wolfssl
       or -lcoap-3-tinydtls. Otherwise,	link with -lcoap-3 to get the default
       (D)TLS library support.

DESCRIPTION
       RFC7641 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, or by using a FETCH request instead of	a GET
       to define a query (RFC8132).

       To remove the "observe" subscription, the client	has to issue a GET (or
       FETCH) request with the COAP_OPTION_OBSERVE Option with a value of
       COAP_OBSERVE_CANCEL using the same token	and other options used for
       making the initial "observe" request. Alternatively, "observe" can be
       cancelled using coap_cancel_observe() instead.

       The underlying library adds in and removes "subscribers"	to "observe"
       the resource as appropriate in the server side logic.

       NOTE: COAP_RESOURCE_MAX_SUBSCRIBER may have been	defined	to limit the
       number of subscribers to	a resource when	libcoap	was built.

       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/FETCH	request" so that an
       observe GET/FETCH response gets sent back to all	the clients that are
       observing the resource. The appropriate GET/FETCH handler within	the
       server application is called to fill in the response packet with	the
       appropriate information.	This "fake GET/FETCH request" is triggered by
       a call to coap_resource_notify_observers().

       The call	to coap_io_process() in	the main server	application i/o	loop
       will do all the necessary processing of sending any outstanding "fake
       GET/FETCH 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" (or the last token used for a
       FETCH that spans	multiple blocks). The client will receive this observe
       response	in the handler defined by coap_register_response_handler(3)
       (with the token updated to the initial token used by the	client
       application for a large FETCH). It is the responsibility	of the client
       application to match the	supplied token and update the appropriate
       internal	information.

FUNCTIONS
       Function: coap_resource_set_get_observable()

       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.

       NOTE: It	is not possible	for the	Unknown	Resource, created by
       coap_resource_unknown_init(3), to be observable as the Uri-Path is not
       known when libcoap creates a "fake GET/FETCH 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	deletion of the	resource at the
       appropriate time.

       NOTE: The type (confirmable or non-confirmable) of the triggered
       observe GET response is determined not by the initial GET/FETCH
       request,	but independently by the server	as per "RFC7641	3.5.
       Transmission". This is controlled by the	flags (one of
       COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS
       or COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource
       using coap_resource_init(3).

       NOTE: Furthermore, the server must send at least	one "observe" response
       as confirmable, when generally sending non-confirmable, at least	every
       24 hours	as per "RFC7641	4.5. Transmission". Libcoap automatically
       handles this by sending every fifth (COAP_OBS_MAX_NON) response as a
       confirmable response for	detection that the client is still responding
       unless if COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS is set, which is	a
       "RFC7641	4.5. Transmission" violation, where non-confirmable "observe"
       responses are always sent as required by	some higher layer protocols.

       Function: coap_resource_notify_observers()

       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. The query parameter is	obsolete and ignored.

       Function: coap_cancel_observe()

       The coap_cancel_observe() function can be used by the client to cancel
       an observe request that is being	tracked. This will cause the
       appropriate PDU to be sent to the server	to cancel the observation,
       based on	the session and	token used to set up the observe and the PDU
       is of type message_type (use COAP_MESSAGE_NON or	COAP_MESSAGE_CON).

       Function: coap_session_set_no_observe_cancel()

       The coap_session_set_no_observe_cancel()	function can be	called by the
       client to disable calling coap_cancel_observe() when the	session	is
       being closed down / freed off. coap_cancel_observe() can	still be
       called directly by the client application.

RETURN VALUES
       coap_resource_notify_observers()	returns	0 if not observable or no
       observers, 1 on success.

       coap_cancel_observe() returns 0 on failure, 1 on	success.

EXAMPLES
       Simple Time Server

	   #include <coap3/coap.h>

	   #include <stdio.h>

	   coap_resource_t *time_resource = NULL;

	   /* specific GET "time" handler, called from hnd_get_generic() */

	   static void
	   hnd_get_time(coap_resource_t	*resource, coap_session_t *session,
			const coap_pdu_t *request, const coap_string_t *query,
			coap_pdu_t *response) {
	     unsigned char buf[40];
	     size_t len;
	     time_t now;
	     (void)resource;
	     (void)session;

	     /*	... 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 'now' is	not valid */
		 coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
		 return;
	       }
	       len = strftime((char *)buf, sizeof(buf),	"%b %d %H:%M:%S", tmp);
	     }
	     coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
	     /*
	      *	Invoke coap_add_data_large_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.
	      *	ETAG Option added internally with unique value as param	set to 0
	      *
	      *	OBSERVE	Option added internally	if needed within the function
	      *	BLOCK2 Option added internally if output too large
	      *	SIZE2 Option added internally
	      */
	     coap_add_data_large_response(resource, session, request, response,
					  query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
					  len,
					  buf, NULL, NULL);
	   }

	   /* Generic GET handler */

	   static void
	   hnd_get_generic(coap_resource_t *resource, coap_session_t *session,
			   const coap_pdu_t *request, const 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 */
	       coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
	       return;
	     }

	     /*	Is this	the "time" resource" ? */
	     if	(coap_string_equal(uri_path, coap_make_str_const("time"))) {
	       hnd_get_time(resource, session, request,	query, response);
	       return;
	     }

	     /*	Other resources	code */

	     /*	Failure	response */
	     coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
	   }

	   /* 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_request_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;
	     struct timeval tv_last = {0, 0};

	     /*	Initialize libcoap library */
	     coap_startup();

	     /*	Remove (void) definition if variable is	used */
	     (void)argc;
	     (void)argv;

	     memset(&tv_last, 0, sizeof(tv_last));

	     /*	Create the libcoap context */
	     ctx = coap_new_context(NULL);
	     if	(!ctx) {
	       exit(1);
	     }
	     /*	See coap_block(3) */
	     coap_context_set_block_mode(ctx,
					 COAP_BLOCK_USE_LIBCOAP	| COAP_BLOCK_SINGLE_BODY);

	     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_io_process(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_io_process() and hence
		  * result == 0)
		  */
		 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
	       }
	       if (time_resource) {
		 struct	timeval	tv_now;
		 if (gettimeofday(&tv_now, NULL) == 0) {
		   if (tv_last.tv_sec != tv_now.tv_sec)	{
		     /*	Happens	once per second	*/
		     tv_last = tv_now;
		     coap_resource_notify_observers(time_resource, NULL);
		   }
		   /* need to wait until next second starts if wait_ms is too large */
		   unsigned next_sec_ms	= 1000 - (tv_now.tv_usec / 1000);

		   if (next_sec_ms && next_sec_ms < wait_ms)
		     wait_ms = next_sec_ms;
		 }
	       }
	     }
	     coap_free_context(ctx);
	     coap_cleanup();
	     exit(0);

	   }

       Client Observe Request Setup

	   #include <coap3/coap.h>

	   /* 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;
	     /*	Remove (void) definition if variable is	used */
	     (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_debug("cannot add token	to request\n");
	       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;

	   }

SEE ALSO
       coap_block(3), coap_context(3), coap_handler(3),	coap_init(3),
       coap_pdu_setup(3), coap_resource(3) and coap_session(3)

FURTHER	INFORMATION
       See

       "RFC7252: The Constrained Application Protocol (CoAP)"

       "RFC7641: Observing Resources in	the Constrained	Application Protocol
       (CoAP)"

       "RFC8132: PATCH and FETCH Methods for the Constrained Application
       Protocol	(CoAP)"

       for further information.

BUGS
       Please raise an issue on	GitHub at
       https://github.com/obgm/libcoap/issues to report	any bugs.

       Please raise a Pull Request at https://github.com/obgm/libcoap/pulls
       for any fixes.

AUTHORS
       The libcoap project <libcoap-developers@lists.sourceforge.net>

coap_observe 4.3.5		  11/03/2025		       COAP_OBSERVE(3)

Want to link to this manual page? Use this URL:
<https://man.freebsd.org/cgi/man.cgi?query=coap_observe&sektion=3&manpath=FreeBSD+Ports+15.0>

home | help