mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #20266 from benpicco/nanocoap_reply_separate
nanocoap_sock: implement separate response
This commit is contained in:
commit
3f41494e59
@ -47,6 +47,13 @@ endif
|
|||||||
HIGH_MEMORY_BOARDS := native native64 same54-xpro mcb2388
|
HIGH_MEMORY_BOARDS := native native64 same54-xpro mcb2388
|
||||||
|
|
||||||
ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS)))
|
ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS)))
|
||||||
|
# enable separate response
|
||||||
|
USEMODULE += nanocoap_server_separate
|
||||||
|
USEMODULE += event_callback
|
||||||
|
USEMODULE += event_thread
|
||||||
|
USEMODULE += event_timeout_ztimer
|
||||||
|
|
||||||
|
# enable fileserver
|
||||||
USEMODULE += nanocoap_fileserver
|
USEMODULE += nanocoap_fileserver
|
||||||
USEMODULE += vfs_default
|
USEMODULE += vfs_default
|
||||||
|
|
||||||
|
@ -10,8 +10,12 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "event/callback.h"
|
||||||
|
#include "event/timeout.h"
|
||||||
|
#include "event/thread.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "net/nanocoap.h"
|
#include "net/nanocoap.h"
|
||||||
|
#include "net/nanocoap_sock.h"
|
||||||
#include "hashes/sha256.h"
|
#include "hashes/sha256.h"
|
||||||
#include "kernel_defines.h"
|
#include "kernel_defines.h"
|
||||||
|
|
||||||
@ -182,6 +186,45 @@ NANOCOAP_RESOURCE(sha256) {
|
|||||||
.path = "/sha256", .methods = COAP_POST, .handler = _sha256_handler
|
.path = "/sha256", .methods = COAP_POST, .handler = _sha256_handler
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* separate response requires an event thread to execute it */
|
||||||
|
#ifdef MODULE_EVENT_THREAD
|
||||||
|
static nanocoap_server_response_ctx_t _separate_ctx;
|
||||||
|
|
||||||
|
static void _send_response(void *ctx)
|
||||||
|
{
|
||||||
|
const char response[] = "This is a delayed response.";
|
||||||
|
|
||||||
|
puts("_separate_handler(): send delayed response");
|
||||||
|
nanocoap_server_send_separate(ctx, COAP_CODE_CONTENT, COAP_TYPE_NON,
|
||||||
|
response, sizeof(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context)
|
||||||
|
{
|
||||||
|
static event_timeout_t event_timeout;
|
||||||
|
static event_callback_t event_timed = EVENT_CALLBACK_INIT(_send_response, &_separate_ctx);
|
||||||
|
|
||||||
|
if (event_timeout_is_pending(&event_timeout)) {
|
||||||
|
puts("_separate_handler(): response already scheduled");
|
||||||
|
return coap_build_reply(pkt, COAP_CODE_SERVICE_UNAVAILABLE, buf, len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("_separate_handler(): send ACK, schedule response");
|
||||||
|
|
||||||
|
nanocoap_server_prepare_separate(&_separate_ctx, pkt, context);
|
||||||
|
|
||||||
|
event_timeout_ztimer_init(&event_timeout, ZTIMER_MSEC, EVENT_PRIO_MEDIUM,
|
||||||
|
&event_timed.super);
|
||||||
|
event_timeout_set(&event_timeout, 1 * MS_PER_SEC);
|
||||||
|
|
||||||
|
return coap_build_empty_ack(pkt, (void *)buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
NANOCOAP_RESOURCE(separate) {
|
||||||
|
.path = "/separate", .methods = COAP_GET, .handler = _separate_handler,
|
||||||
|
};
|
||||||
|
#endif /* MODULE_EVENT_THREAD */
|
||||||
|
|
||||||
/* we can also include the fileserver module */
|
/* we can also include the fileserver module */
|
||||||
#ifdef MODULE_NANOCOAP_FILESERVER
|
#ifdef MODULE_NANOCOAP_FILESERVER
|
||||||
#include "net/nanocoap/fileserver.h"
|
#include "net/nanocoap/fileserver.h"
|
||||||
|
@ -529,6 +529,11 @@ ifneq (,$(filter nanocoap_server_auto_init,$(USEMODULE)))
|
|||||||
USEMODULE += nanocoap_server
|
USEMODULE += nanocoap_server
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter nanocoap_server_separate,$(USEMODULE)))
|
||||||
|
USEMODULE += nanocoap_server
|
||||||
|
USEMODULE += sock_aux_local
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter nanocoap_server,$(USEMODULE)))
|
ifneq (,$(filter nanocoap_server,$(USEMODULE)))
|
||||||
USEMODULE += nanocoap_resources
|
USEMODULE += nanocoap_resources
|
||||||
USEMODULE += nanocoap_sock
|
USEMODULE += nanocoap_sock
|
||||||
|
@ -115,6 +115,11 @@ void event_timeout_clear(event_timeout_t *event_timeout);
|
|||||||
*/
|
*/
|
||||||
static inline bool event_timeout_is_pending(const event_timeout_t *event_timeout)
|
static inline bool event_timeout_is_pending(const event_timeout_t *event_timeout)
|
||||||
{
|
{
|
||||||
|
if (event_timeout->clock == NULL || event_timeout->queue == NULL ||
|
||||||
|
event_timeout->event == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return ztimer_is_set(event_timeout->clock, &event_timeout->timer)
|
return ztimer_is_set(event_timeout->clock, &event_timeout->timer)
|
||||||
|| event_is_queued(event_timeout->queue, event_timeout->event);
|
|| event_is_queued(event_timeout->queue, event_timeout->event);
|
||||||
}
|
}
|
||||||
|
@ -338,6 +338,9 @@ void coap_request_ctx_init(coap_request_ctx_t *ctx, sock_udp_ep_t *remote);
|
|||||||
struct _coap_request_ctx {
|
struct _coap_request_ctx {
|
||||||
const coap_resource_t *resource; /**< resource of the request */
|
const coap_resource_t *resource; /**< resource of the request */
|
||||||
sock_udp_ep_t *remote; /**< remote endpoint of the request */
|
sock_udp_ep_t *remote; /**< remote endpoint of the request */
|
||||||
|
#if defined(MODULE_SOCK_AUX_LOCAL) || DOXYGEN
|
||||||
|
sock_udp_ep_t *local; /**< local endpoint of the request */
|
||||||
|
#endif
|
||||||
#if defined(MODULE_GCOAP) || DOXYGEN
|
#if defined(MODULE_GCOAP) || DOXYGEN
|
||||||
/**
|
/**
|
||||||
* @brief transport the packet was received over
|
* @brief transport the packet was received over
|
||||||
@ -1969,6 +1972,22 @@ ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token,
|
|||||||
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
|
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
|
||||||
uint8_t *rbuf, unsigned rlen, unsigned payload_len);
|
uint8_t *rbuf, unsigned rlen, unsigned payload_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build empty reply to CoAP request
|
||||||
|
*
|
||||||
|
* This function can be used to create an empty ACK so that a later, separate
|
||||||
|
* response can be sent independently.
|
||||||
|
*
|
||||||
|
* If the request was non-confirmable, this will generate nothing.
|
||||||
|
*
|
||||||
|
* @param[in] pkt packet to reply to
|
||||||
|
* @param[out] ack buffer to write reply to
|
||||||
|
*
|
||||||
|
* @returns size of reply packet on success
|
||||||
|
* @returns -ENOSPC if @p rbuf too small
|
||||||
|
*/
|
||||||
|
ssize_t coap_build_empty_ack(coap_pkt_t *pkt, coap_hdr_t *ack);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle incoming CoAP request
|
* @brief Handle incoming CoAP request
|
||||||
*
|
*
|
||||||
|
@ -221,6 +221,55 @@ typedef struct {
|
|||||||
uint8_t blksize; /**< CoAP blocksize exponent */
|
uint8_t blksize; /**< CoAP blocksize exponent */
|
||||||
} coap_block_request_t;
|
} coap_block_request_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Context from CoAP request for separate response
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
sock_udp_ep_t remote; /**< remote to send response to */
|
||||||
|
#if defined(MODULE_SOCK_AUX_LOCAL) || DOXYGEN
|
||||||
|
sock_udp_ep_t local; /**< local from which to send response */
|
||||||
|
#endif
|
||||||
|
uint8_t token[COAP_TOKEN_LENGTH_MAX]; /**< request token */
|
||||||
|
uint8_t tkl; /**< request token length */
|
||||||
|
uint8_t no_response; /**< no-response bitmap */
|
||||||
|
} nanocoap_server_response_ctx_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepare the context for a separate response
|
||||||
|
*
|
||||||
|
* This function serializes the CoAP request information so that
|
||||||
|
* a separate response can be generated outside the CoAP handler.
|
||||||
|
*
|
||||||
|
* The CoAP handler should then respond with an empty ACK by calling
|
||||||
|
* @ref coap_build_empty_ack
|
||||||
|
*
|
||||||
|
* @param[out] ctx Context information for separate response
|
||||||
|
* @param[in] pkt CoAP packet to which the response will be generated
|
||||||
|
* @param[in] req Context of the CoAP request
|
||||||
|
*/
|
||||||
|
void nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx,
|
||||||
|
coap_pkt_t *pkt, const coap_request_ctx_t *req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a separate response to a CoAP request
|
||||||
|
*
|
||||||
|
* This sends a response to a CoAP request outside the CoAP handler
|
||||||
|
*
|
||||||
|
* @pre @ref nanocoap_server_prepare_separate has been called on @p ctx
|
||||||
|
* inside the CoAP handler
|
||||||
|
*
|
||||||
|
* @param[in] ctx Context information for the CoAP response
|
||||||
|
* @param[in] code CoAP response code
|
||||||
|
* @param[in] type Response type, may be `COAP_TYPE_NON`
|
||||||
|
* @param[in] payload Response payload
|
||||||
|
* @param[in] len Payload length
|
||||||
|
*
|
||||||
|
* @returns 0 on success
|
||||||
|
* negative error (see @ref sock_udp_sendv_aux)
|
||||||
|
*/
|
||||||
|
int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx,
|
||||||
|
unsigned code, unsigned type,
|
||||||
|
const void *payload, size_t len);
|
||||||
/**
|
/**
|
||||||
* @brief Get next consecutive message ID for use when building a new
|
* @brief Get next consecutive message ID for use when building a new
|
||||||
* CoAP request.
|
* CoAP request.
|
||||||
|
@ -626,6 +626,18 @@ ssize_t coap_reply_simple(coap_pkt_t *pkt,
|
|||||||
return header_len + payload_len;
|
return header_len + payload_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t coap_build_empty_ack(coap_pkt_t *pkt, coap_hdr_t *ack)
|
||||||
|
{
|
||||||
|
if (coap_get_type(pkt) != COAP_TYPE_CON) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_build_hdr(ack, COAP_TYPE_ACK, NULL, 0,
|
||||||
|
COAP_CODE_EMPTY, ntohs(pkt->hdr->id));
|
||||||
|
|
||||||
|
return sizeof(*ack);
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
|
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
|
||||||
uint8_t *rbuf, unsigned rlen, unsigned payload_len)
|
uint8_t *rbuf, unsigned rlen, unsigned payload_len)
|
||||||
{
|
{
|
||||||
@ -675,9 +687,6 @@ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
|
|||||||
|
|
||||||
coap_build_hdr((coap_hdr_t *)rbuf, type, coap_get_token(pkt), tkl, code,
|
coap_build_hdr((coap_hdr_t *)rbuf, type, coap_get_token(pkt), tkl, code,
|
||||||
ntohs(pkt->hdr->id));
|
ntohs(pkt->hdr->id));
|
||||||
coap_hdr_set_type((coap_hdr_t *)rbuf, type);
|
|
||||||
coap_hdr_set_code((coap_hdr_t *)rbuf, code);
|
|
||||||
|
|
||||||
len += payload_len;
|
len += payload_len;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
@ -813,7 +813,7 @@ ssize_t nanocoap_get_blockwise_url_to_buf(const char *url,
|
|||||||
|
|
||||||
int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
|
int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
|
||||||
{
|
{
|
||||||
nanocoap_sock_t sock;
|
sock_udp_t sock;
|
||||||
sock_udp_ep_t remote;
|
sock_udp_ep_t remote;
|
||||||
coap_request_ctx_t ctx = {
|
coap_request_ctx_t ctx = {
|
||||||
.remote = &remote,
|
.remote = &remote,
|
||||||
@ -823,7 +823,7 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
|
|||||||
local->port = COAP_PORT;
|
local->port = COAP_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t res = sock_udp_create(&sock.udp, local, NULL, 0);
|
ssize_t res = sock_udp_create(&sock, local, NULL, 0);
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -838,7 +838,7 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
|
|||||||
aux_in_ptr = &aux_in;
|
aux_in_ptr = &aux_in;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
res = sock_udp_recv_aux(&sock.udp, buf, bufsize, SOCK_NO_TIMEOUT,
|
res = sock_udp_recv_aux(&sock, buf, bufsize, SOCK_NO_TIMEOUT,
|
||||||
&remote, aux_in_ptr);
|
&remote, aux_in_ptr);
|
||||||
if (res <= 0) {
|
if (res <= 0) {
|
||||||
DEBUG("nanocoap: error receiving UDP packet %" PRIdSIZE "\n", res);
|
DEBUG("nanocoap: error receiving UDP packet %" PRIdSIZE "\n", res);
|
||||||
@ -849,10 +849,6 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
|
|||||||
DEBUG("nanocoap: error parsing packet\n");
|
DEBUG("nanocoap: error parsing packet\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((res = coap_handle_req(&pkt, buf, bufsize, &ctx)) <= 0) {
|
|
||||||
DEBUG("nanocoap: error handling request %" PRIdSIZE "\n", res);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sock_udp_aux_tx_t *aux_out_ptr = NULL;
|
sock_udp_aux_tx_t *aux_out_ptr = NULL;
|
||||||
#ifdef MODULE_SOCK_AUX_LOCAL
|
#ifdef MODULE_SOCK_AUX_LOCAL
|
||||||
@ -865,8 +861,14 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
|
|||||||
if (!sock_udp_ep_is_multicast(&aux_in.local)) {
|
if (!sock_udp_ep_is_multicast(&aux_in.local)) {
|
||||||
aux_out_ptr = &aux_out;
|
aux_out_ptr = &aux_out;
|
||||||
}
|
}
|
||||||
|
ctx.local = &aux_in.local;
|
||||||
#endif
|
#endif
|
||||||
sock_udp_send_aux(&sock.udp, buf, res, &remote, aux_out_ptr);
|
if ((res = coap_handle_req(&pkt, buf, bufsize, &ctx)) <= 0) {
|
||||||
|
DEBUG("nanocoap: error handling request %" PRIdSIZE "\n", res);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_udp_send_aux(&sock, buf, res, &remote, aux_out_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -904,3 +906,66 @@ void auto_init_nanocoap_server(void)
|
|||||||
|
|
||||||
nanocoap_server_start(&local);
|
nanocoap_server_start(&local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nanocoap_server_prepare_separate(nanocoap_server_response_ctx_t *ctx,
|
||||||
|
coap_pkt_t *pkt, const coap_request_ctx_t *req)
|
||||||
|
{
|
||||||
|
ctx->tkl = coap_get_token_len(pkt);
|
||||||
|
memcpy(ctx->token, coap_get_token(pkt), ctx->tkl);
|
||||||
|
memcpy(&ctx->remote, req->remote, sizeof(ctx->remote));
|
||||||
|
#ifdef MODULE_SOCK_AUX_LOCAL
|
||||||
|
assert(req->local);
|
||||||
|
memcpy(&ctx->local, req->local, sizeof(ctx->local));
|
||||||
|
#endif
|
||||||
|
uint32_t no_response = 0;
|
||||||
|
coap_opt_get_uint(pkt, COAP_OPT_NO_RESPONSE, &no_response);
|
||||||
|
ctx->no_response = no_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nanocoap_server_send_separate(const nanocoap_server_response_ctx_t *ctx,
|
||||||
|
unsigned code, unsigned type,
|
||||||
|
const void *payload, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t rbuf[sizeof(coap_hdr_t) + COAP_TOKEN_LENGTH_MAX + 1];
|
||||||
|
assert(type != COAP_TYPE_ACK);
|
||||||
|
assert(type != COAP_TYPE_CON); /* TODO: add support */
|
||||||
|
|
||||||
|
const uint8_t no_response_index = (code >> 5) - 1;
|
||||||
|
/* If the handler code misbehaved here, we'd face UB otherwise */
|
||||||
|
assert(no_response_index < 7);
|
||||||
|
|
||||||
|
const uint8_t mask = 1 << no_response_index;
|
||||||
|
if (ctx->no_response & mask) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
iolist_t data = {
|
||||||
|
.iol_base = (void *)payload,
|
||||||
|
.iol_len = len,
|
||||||
|
};
|
||||||
|
|
||||||
|
iolist_t head = {
|
||||||
|
.iol_next = &data,
|
||||||
|
.iol_base = rbuf,
|
||||||
|
};
|
||||||
|
head.iol_len = coap_build_hdr((coap_hdr_t *)rbuf, type,
|
||||||
|
ctx->token, ctx->tkl,
|
||||||
|
code, random_uint32());
|
||||||
|
if (len) {
|
||||||
|
rbuf[head.iol_len++] = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_udp_aux_tx_t *aux_out_ptr = NULL;
|
||||||
|
#ifdef MODULE_SOCK_AUX_LOCAL
|
||||||
|
/* make sure we reply with the same address that the request was
|
||||||
|
* destined for -- except in the multicast case */
|
||||||
|
sock_udp_aux_tx_t aux_out = {
|
||||||
|
.flags = SOCK_AUX_SET_LOCAL,
|
||||||
|
.local = ctx->local,
|
||||||
|
};
|
||||||
|
if (!sock_udp_ep_is_multicast(&ctx->local)) {
|
||||||
|
aux_out_ptr = &aux_out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return sock_udp_sendv_aux(NULL, &head, &ctx->remote, aux_out_ptr);
|
||||||
|
}
|
||||||
|
@ -425,6 +425,7 @@ ssize_t sock_udp_sendv_aux(sock_udp_t *sock,
|
|||||||
if ((aux != NULL) && (aux->flags & SOCK_AUX_SET_LOCAL)) {
|
if ((aux != NULL) && (aux->flags & SOCK_AUX_SET_LOCAL)) {
|
||||||
local.family = aux->local.family;
|
local.family = aux->local.family;
|
||||||
local.netif = aux->local.netif;
|
local.netif = aux->local.netif;
|
||||||
|
src_port = aux->local.port;
|
||||||
memcpy(&local.addr, &aux->local.addr, sizeof(local.addr));
|
memcpy(&local.addr, &aux->local.addr, sizeof(local.addr));
|
||||||
|
|
||||||
aux->flags &= ~SOCK_AUX_SET_LOCAL;
|
aux->flags &= ~SOCK_AUX_SET_LOCAL;
|
||||||
|
Loading…
Reference in New Issue
Block a user