Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gcoap: Observe extension server #6469

Merged
merged 3 commits into from
May 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions examples/gcoap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ Build with `Makefile.slip`. Follow the setup instructions in README-slip.md, whi
## Current Status
gcoap includes server and client capability. Available features include:

* Message Type: Supports non-confirmable (NON) messaging. Additionally provides a callback on timeout.
* Observe extension: Provides server-side registration and notifications.
* Server and Client provide helper functions for writing the response/request. See the CoAP topic in the source documentation for details. See the gcoap example for sample implementations.
* Server allows an application to register a 'listener', which includes an array of endpoint paths and function callbacks used to write a response.
* Server listens on a port at startup; defaults to 5683.
* Client operates asynchronously; sends request and then handles response in a user provided callback. Also executes callback on timeout.
* Client operates asynchronously; sends request and then handles response in a user provided callback.
* Client generates token; length defined at compile time.
* Message Type: Supports non-confirmable (NON) messaging.
* Options: Supports Content-Format for response payload.
* Options: Supports Content-Format for payload.


## Example Use
Expand Down
25 changes: 23 additions & 2 deletions examples/gcoap/gcoap_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
#include "od.h"
#include "fmt.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

static void _resp_handler(unsigned req_state, coap_pkt_t* pdu);
static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len);

Expand Down Expand Up @@ -149,15 +152,33 @@ int gcoap_cli_cmd(int argc, char **argv)
argv[4]);
}
printf("gcoap_cli: sending msg ID %u, %u bytes\n", coap_get_id(&pdu),
(unsigned) len);
(unsigned) len);
if (!_send(&buf[0], len, argv[2], argv[3])) {
puts("gcoap_cli: msg send failed");
}
else {
/* send Observe notification for /cli/stats */
switch (gcoap_obs_init(&pdu, &buf[0], GCOAP_PDU_BUF_SIZE,
&_resources[0])) {
case GCOAP_OBS_INIT_OK:
DEBUG("gcoap_cli: creating /cli/stats notification\n");
size_t payload_len = fmt_u16_dec((char *)pdu.payload, req_count);
len = gcoap_finish(&pdu, payload_len, COAP_FORMAT_TEXT);
gcoap_obs_send(&buf[0], len, &_resources[0]);
break;
case GCOAP_OBS_INIT_UNUSED:
DEBUG("gcoap_cli: no observer for /cli/stats\n");
break;
case GCOAP_OBS_INIT_ERR:
DEBUG("gcoap_cli: error initializing /cli/stats notification\n");
break;
}
}
return 0;
}
else {
printf("usage: %s <get|post|put> <addr> <port> <path> [data]\n",
argv[0]);
argv[0]);
return 1;
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/nanocoap/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PKG_NAME=nanocoap
PKG_URL=https:/kaspar030/sock
PKG_VERSION=4035239dce751216038bb2dc8632aa4a734d68a7
PKG_VERSION=1bba8bb16463914266dd5a04fe2a3eac4ee129ae
PKG_LICENSE=LGPL-2.1

.PHONY: all
Expand Down
169 changes: 163 additions & 6 deletions sys/include/net/gcoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
* port, which supports RFC 6282 compression. Internally, gcoap depends on the
* nanocoap package for base level structs and functionality.
*
* gcoap also supports the Observe extension (RFC 7641) for a server. gcoap
* provides functions to generate and send an observe notification that are
* similar to the functions to send a client request.
*
* ## Server Operation ##
*
* gcoap listens for requests on GCOAP_PORT, 5683 by default. You can redefine
Expand Down Expand Up @@ -75,7 +79,7 @@
*
* ### Creating a request ###
*
* Here is the expected sequence for preparing and sending a request:
* Here is the expected sequence to prepare and send a request:
*
* Allocate a buffer and a coap_pkt_t for the request.
*
Expand Down Expand Up @@ -109,6 +113,43 @@
* _content_type_ attributes.
* -# Read the payload, if any.
*
* ## Observe Server Operation
*
* A CoAP client may register for Observe notifications for any resource that
* an application has registered with gcoap. An application does not need to
* take any action to support Observe client registration. However, gcoap
* limits registration for a given resource to a _single_ observer.
*
* An Observe notification is considered a response to the original client
* registration request. So, the Observe server only needs to create and send
* the notification -- no further communication or callbacks are required.
*
* ### Creating a notification ###
*
* Here is the expected sequence to prepare and send a notification:
*
* Allocate a buffer and a coap_pkt_t for the notification, then follow the
* steps below.
*
* -# Call gcoap_obs_init() to initialize the notification for a resource.
* Test the return value, which may indicate there is not an observer for
* the resource. If so, you are done.
* -# Write the notification payload, starting at the updated _payload_ pointer
* in the coap_pkt_t.
* -# Call gcoap_finish(), which updates the packet for the payload.
*
* Finally, call gcoap_obs_send() for the resource.
*
* ### Other considerations ###
*
* By default, the value for the Observe option in a notification is three
* bytes long. For resources that change slowly, this length can be reduced via
* GCOAP_OBS_VALUE_WIDTH.
*
* To cancel a notification, the server expects to receive a GET request with
* the Observe option value set to 1. The server does not support cancellation
* via a reset (RST) response to a non-confirmable notification.
*
* ## Implementation Notes ##
*
* ### Building a packet ###
Expand Down Expand Up @@ -175,6 +216,13 @@ extern "C" {
*/
#define GCOAP_RESP_OPTIONS_BUF (8)

/**
* @brief Size of the buffer used to write options in an Observe notification.
*
* Accommodates Content-Format and Observe.
*/
#define GCOAP_OBS_OPTIONS_BUF (8)

/** @brief Maximum number of requests awaiting a response */
#define GCOAP_REQ_WAITING_MAX (2)

Expand Down Expand Up @@ -225,6 +273,69 @@ extern "C" {
*/
#define GCOAP_MSG_TYPE_INTR (0x1502)

/** @brief Maximum number of Observe clients; use 2 if not defined */
#ifndef GCOAP_OBS_CLIENTS_MAX
#define GCOAP_OBS_CLIENTS_MAX (2)
#endif

/**
* @brief Maximum number of registrations for Observable resources; use 2 if
* not defined
*/
#ifndef GCOAP_OBS_REGISTRATIONS_MAX
#define GCOAP_OBS_REGISTRATIONS_MAX (2)
#endif

/**
* @name States for the memo used to track Observe registrations
* @{
*/
#define GCOAP_OBS_MEMO_UNUSED (0) /**< This memo is unused */
#define GCOAP_OBS_MEMO_IDLE (1) /**< Registration OK; no current activity */
#define GCOAP_OBS_MEMO_PENDING (2) /**< Resource changed; notification pending */
/** @} */

/**
* @brief Width in bytes of the Observe option value for a notification.
*
* This width is used to determine the length of the 'tick' used to measure
* the time between observable changes to a resource. A tick is expressed
* internally as GCOAP_OBS_TICK_EXPONENT, which is the base-2 log value of the
* tick length in microseconds.
*
* The canonical setting for the value width is 3 (exponent 5), which results
* in a tick length of 32 usec, per sec. 3.4, 4.4 of the RFC. Width 2
* (exponent 16) results in a tick length of ~65 msec, and width 1 (exponent
* 24) results in a tick length of ~17 sec.
*
* The tick length must be short enough so that the Observe value strictly
* increases for each new notification. The purpose of the value is to allow a
* client to detect message reordering within the network latency period (128
* sec). For resources that change only slowly, the reduced message length is
* useful when packet size is limited.
*/
#ifndef GCOAP_OBS_VALUE_WIDTH
#define GCOAP_OBS_VALUE_WIDTH (3)
#endif

/** @brief See GCOAP_OBS_VALUE_WIDTH. */
#if (GCOAP_OBS_VALUE_WIDTH == 3)
#define GCOAP_OBS_TICK_EXPONENT (5)
#elif (GCOAP_OBS_VALUE_WIDTH == 2)
#define GCOAP_OBS_TICK_EXPONENT (16)
#elif (GCOAP_OBS_VALUE_WIDTH == 1)
#define GCOAP_OBS_TICK_EXPONENT (24)
#endif

/**
* @name Return values for gcoap_obs_init()
* @{
*/
#define GCOAP_OBS_INIT_OK (0)
#define GCOAP_OBS_INIT_ERR (-1)
#define GCOAP_OBS_INIT_UNUSED (-2)
/** @} */

/**
* @brief A modular collection of resources for a server
*/
Expand Down Expand Up @@ -255,16 +366,29 @@ typedef struct {
msg_t timeout_msg; /**< For response timer */
} gcoap_request_memo_t;

/** @brief Memo for Observe registration and notifications */
typedef struct {
sock_udp_ep_t *observer; /**< Client endpoint; unused if null */
coap_resource_t *resource; /**< Entity being observed */
uint8_t token[GCOAP_TOKENLEN_MAX]; /**< Client token for notifications */
unsigned token_len; /**< Actual length of token attribute */
} gcoap_observe_memo_t;

/**
* @brief Container for the state of gcoap itself
*/
typedef struct {
gcoap_listener_t *listeners; /**< List of registered listeners */
gcoap_listener_t *listeners; /**< List of registered listeners */
gcoap_request_memo_t open_reqs[GCOAP_REQ_WAITING_MAX];
/**< Storage for open requests; if first
byte of an entry is zero, the entry
is available */
uint16_t last_message_id; /**< Last message ID used */
/**< Storage for open requests; if first
byte of an entry is zero, the entry
is available */
uint16_t last_message_id; /**< Last message ID used */
sock_udp_ep_t observers[GCOAP_OBS_CLIENTS_MAX];
/**< Observe clients; allows reuse for
observe memos */
gcoap_observe_memo_t observe_memos[GCOAP_OBS_REGISTRATIONS_MAX];
/**< Observed resource registrations */
} gcoap_state_t;

/**
Expand Down Expand Up @@ -402,6 +526,39 @@ static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf, size_t len,
: -1;
}

/**
* @brief Initializes a CoAP Observe notification packet on a buffer, for the
* observer registered for a resource.
*
* First verifies that an observer has been registered for the resource.
*
* @param[in] pdu Notification metadata
* @param[in] buf Buffer containing the PDU
* @param[in] len Length of the buffer
* @param[in] resource Resource for the notification
*
* @return GCOAP_OBS_INIT_OK on success
* @return GCOAP_OBS_INIT_ERR on error
* @return GCOAP_OBS_INIT_UNUSED if no observer for resource
*/
int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
const coap_resource_t *resource);

/**
* @brief Sends a buffer containing a CoAP Observe notification to the
* observer registered for a resource.
*
* Assumes a single observer for a resource.
*
* @param[in] buf Buffer containing the PDU
* @param[in] len Length of the buffer
* @param[in] resource Resource to send
*
* @return length of the packet
* @return 0 if cannot send
*/
size_t gcoap_obs_send(uint8_t *buf, size_t len, const coap_resource_t *resource);

/**
* @brief Provides important operational statistics.
*
Expand Down
Loading