Skip to content

Commit

Permalink
nanocoap_sock: implement observe client
Browse files Browse the repository at this point in the history
  • Loading branch information
benpicco committed Jan 28, 2025
1 parent 139404d commit 1a6805d
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
6 changes: 6 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,12 @@ ifneq (,$(filter nanocoap_link_format,$(USEMODULE)))
USEMODULE += fmt
endif

ifneq (,$(filter nanocoap_sock_observe,$(USEMODULE)))
USEMODULE += event_thread
USEMODULE += sock_async_event
USEMODULE += nanocoap_sock
endif

ifneq (,$(filter nanocoap_vfs,$(USEMODULE)))
USEMODULE += nanocoap_sock
USEMODULE += vfs
Expand Down
50 changes: 50 additions & 0 deletions sys/include/net/nanocoap_sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ extern "C" {
#define CONFIG_NANOCOAP_SOCK_BLOCK_TOKEN (0)
#endif

/**
* @brief Event priority for nanoCoAP sock events (e.g. used by `nanocoap_sock_observe`)
*/
#ifndef CONFIG_NANOCOAP_SOCK_EVENT_PRIO
# define CONFIG_NANOCOAP_SOCK_EVENT_PRIO EVENT_PRIO_MEDIUM
#endif

/**
* @brief NanoCoAP socket types
*/
Expand Down Expand Up @@ -224,6 +231,15 @@ typedef struct {
uint8_t blksize; /**< CoAP blocksize exponent */
} coap_block_request_t;

/**
* @brief Observe Client helper struct
*/
typedef struct {
coap_request_cb_t cb; /**< user callback function */
void *arg; /**< callback function argument */
nanocoap_sock_t sock; /**< socket used for the request */
} coap_observe_client_t;

/**
* @brief Context from CoAP request for separate response
*/
Expand Down Expand Up @@ -550,6 +566,40 @@ static inline void nanocoap_sock_close(nanocoap_sock_t *sock)
sock_udp_close(&sock->udp);
}

/**
* @brief Observe a CoAP resource behind a URL (via GET)
*
* @note This requires the `nanocoap_sock_observe` module.
*
* @param[in] url URL to subscribe to
* @param[out] ctx nanoCoAP observe context
* @param[in] cb callback function called for every resource update
* @param[in] arg callback function argument
*
* @returns Number of bytes send on success
* @retval -EPROTONOSUPPORT registration failed
* @retval <0 other error
*/
ssize_t nanocoap_sock_observe_url(const char *url, coap_observe_client_t *ctx,
coap_request_cb_t cb, void *arg);
/**
* @brief Stop observing a CoAP resource
*
* @note This requires the `nanocoap_sock_observe` module.
*
* @param[in] url URL to unsubscribe subscribe from
* @param[out] ctx nanoCoAP observe context that was previously used with
* @see nanocoap_sock_observe_url
*
* @pre @p nanocoap_sock_observe_url has been called on the same arguments
* before and the Observation has not yet been cancelled yet.
*
* @returns >=0 on success
* @returns <0 on error
*/
ssize_t nanocoap_sock_unobserve_url(const char *url, coap_observe_client_t *ctx);

/**
* @brief Simple synchronous CoAP (confirmable) GET
*
Expand Down
118 changes: 118 additions & 0 deletions sys/net/application_layer/nanocoap/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@
#include <stdio.h>

#include "container.h"
#include "event/thread.h"
#include "net/credman.h"
#include "net/nanocoap.h"
#include "net/nanocoap_sock.h"
#ifdef MODULE_SOCK_ASYNC_EVENT
#include "net/sock/async/event.h"
#endif
#include "net/sock/udp.h"
#include "net/sock/util.h"
#include "random.h"
Expand Down Expand Up @@ -463,6 +467,120 @@ ssize_t nanocoap_sock_get(nanocoap_sock_t *sock, const char *path, void *buf, si
return _sock_get(sock, path, COAP_TYPE_CON, buf, len);
}

#ifdef MODULE_NANOCOAP_SOCK_OBSERVE
static void _async_udp_handler(sock_udp_t *sock, sock_async_flags_t type, void *arg)
{
if (!(type & SOCK_ASYNC_MSG_RECV)) {
return;
}

coap_pkt_t pkt;
void *payload, *ctx = NULL;
coap_observe_client_t *obs = arg;
ssize_t res = sock_udp_recv_buf(sock, &payload, &ctx, 0, NULL);
if (res <= 0) {
return;
}

/* parse response */
if (coap_parse(&pkt, payload, res) < 0) {
DEBUG("nanocoap: error parsing packet\n");
goto out;
}

DEBUG("nanocoap: response code=%i\n", coap_get_code_decimal(&pkt));
switch (coap_get_type(&pkt)) {
case COAP_TYPE_CON:
_send_ack(&obs->sock, &pkt);
/* fall-through */
case COAP_TYPE_NON:
obs->cb(obs->arg, &pkt);
break;
default:
DEBUG("nanocoap: ignore observe pkt of invalid type %u\n", coap_get_type(&pkt));
break;
}
out:
/* release data */
sock_udp_recv_buf(sock, &payload, &ctx, 0, NULL);
}

static int _observe_reg_wrapper(void *arg, coap_pkt_t *pkt)
{
coap_observe_client_t *obs = arg;
bool registered = coap_find_option(pkt, COAP_OPT_OBSERVE);
int res = obs->cb(obs->arg, pkt);

return registered ? res : -EPROTONOSUPPORT;
}

static ssize_t _get_observe(coap_observe_client_t *ctx, const char *path,
bool unregister)
{
/* buffer for CoAP header */
uint8_t buffer[CONFIG_NANOCOAP_BLOCK_HEADER_MAX];
uint8_t *pktpos = buffer;

coap_pkt_t pkt = {
.hdr = (void *)pktpos,
};

uint16_t lastonum = 0;

pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET,
nanocoap_sock_next_msg_id(&ctx->sock));
pktpos += coap_opt_put_observe(pktpos, lastonum, unregister);
lastonum = COAP_OPT_OBSERVE;
pktpos += coap_opt_put_uri_pathquery(pktpos, &lastonum, path);

pkt.payload = pktpos;
pkt.payload_len = 0;

return nanocoap_sock_request_cb(&ctx->sock, &pkt, _observe_reg_wrapper, ctx);
}

ssize_t nanocoap_sock_observe_url(const char *url, coap_observe_client_t *ctx,
coap_request_cb_t cb, void *arg)
{
int res = nanocoap_sock_url_connect(url, &ctx->sock);
if (res) {
return res;
}
ctx->cb = cb;
ctx->arg = arg;

res = _get_observe(ctx, sock_urlpath(url), false);
if (res >= 0) {
sock_udp_event_init(&ctx->sock.udp, CONFIG_NANOCOAP_SOCK_EVENT_PRIO,
_async_udp_handler, ctx);
}
else {
nanocoap_sock_close(&ctx->sock);
ctx->cb = NULL;
}

return res;
}

ssize_t nanocoap_sock_unobserve_url(const char *url, coap_observe_client_t *ctx)
{
if (ctx->cb == NULL) {
return -ENOTCONN;
}

int res = _get_observe(ctx, sock_urlpath(url), true);

/* we expect no observe option in the response */
if (res == -EPROTONOSUPPORT) {
res = 0;
}

nanocoap_sock_close(&ctx->sock);
ctx->cb = NULL;
return res;
}
#endif /* MODULE_NANOCOAP_SOCK_OBSERVE */

ssize_t _sock_put_post(nanocoap_sock_t *sock, const char *path, unsigned code,
uint8_t type, const void *request, size_t len,
void *response, size_t max_len)
Expand Down

0 comments on commit 1a6805d

Please sign in to comment.