mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #20684 from fabian18/pr/fix_gcoap_observe_response_correlation
sys/net/application_layer/gcoap: fix Observe notifications correlation
This commit is contained in:
commit
1626919da7
@ -9,9 +9,9 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
atxmega-a3bu-xplained \
|
||||
bluepill-stm32f030c8 \
|
||||
chronos \
|
||||
derfmega128 \
|
||||
i-nucleo-lrwan1 \
|
||||
im880b \
|
||||
microduino-corerf \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-c031c6 \
|
||||
@ -26,6 +26,8 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
olimex-msp430-h1611 \
|
||||
olimex-msp430-h2618 \
|
||||
samd10-xmini \
|
||||
saml10-xpro \
|
||||
saml11-xpro \
|
||||
slstk3400a \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
@ -37,4 +39,5 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
waspmote-pro \
|
||||
weact-g030f6 \
|
||||
z1 \
|
||||
zigduino \
|
||||
#
|
||||
|
@ -54,6 +54,9 @@ USEMODULE += shell_cmds_default
|
||||
USEMODULE += uri_parser
|
||||
USEMODULE += ps
|
||||
|
||||
# /rtc resource for regular observe notifications
|
||||
FEATURES_OPTIONAL += periph_rtc
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
@ -81,6 +84,13 @@ DOCKER_ENV_VARS += ZEP_PORT_BASE
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
ifneq (,$(filter periph_rtc,$(USEMODULE)))
|
||||
USEMODULE += event_periodic
|
||||
USEMODULE += event_periodic_callback
|
||||
USEMODULE += event_thread
|
||||
USEMODULE += ztimer_msec
|
||||
endif
|
||||
|
||||
# For now this goes after the inclusion of Makefile.include so Kconfig symbols
|
||||
# are available. Only set configuration via CFLAGS if Kconfig is not being used
|
||||
# for this module.
|
||||
|
@ -8,7 +8,9 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega328p-xplained-mini \
|
||||
atmega8 \
|
||||
atxmega-a3bu-xplained \
|
||||
blackpill-stm32f103c8 \
|
||||
bluepill-stm32f030c8 \
|
||||
bluepill-stm32f103c8 \
|
||||
i-nucleo-lrwan1 \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
@ -16,6 +18,7 @@ BOARD_INSUFFICIENT_MEMORY := \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-f302r8 \
|
||||
nucleo-f303k8 \
|
||||
nucleo-f334r8 \
|
||||
nucleo-l011k4 \
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "net/gcoap.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "od.h"
|
||||
#include "uri_parser.h"
|
||||
@ -53,8 +54,8 @@ static char _proxy_uri[CONFIG_URI_MAX];
|
||||
* completes or times out. */
|
||||
static char _last_req_uri[CONFIG_URI_MAX];
|
||||
|
||||
/* whether this node is currently observing a resource as a client */
|
||||
static bool observing = false;
|
||||
/* Last remote endpoint where an Observe request has been sent to */
|
||||
static sock_udp_ep_t obs_remote;
|
||||
|
||||
/* the token used for observing a remote resource */
|
||||
static uint8_t obs_req_token[GCOAP_TOKENLEN_MAX];
|
||||
@ -190,6 +191,8 @@ static int _print_usage(char **argv)
|
||||
printf(" %s proxy unset\n", argv[0]);
|
||||
printf("Options\n");
|
||||
printf(" -c Send confirmably (defaults to non-confirmable)\n");
|
||||
printf(" -o include Observe registration option\n");
|
||||
printf(" -d include Observe deregistration option\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -300,17 +303,10 @@ int gcoap_cli_cmd(int argc, char **argv)
|
||||
if (code_pos == COAP_METHOD_GET) {
|
||||
if (argc > apos) {
|
||||
if (strcmp(argv[apos], "-o") == 0) {
|
||||
if (observing) {
|
||||
puts("Only one observe supported");
|
||||
return 1;
|
||||
}
|
||||
observe = true;
|
||||
apos++;
|
||||
} else if (strcmp(argv[apos], "-d") == 0) {
|
||||
if (!observing) {
|
||||
puts("Not observing");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[apos], "-d") == 0) {
|
||||
observe = true;
|
||||
apos++;
|
||||
obs_value = COAP_OBS_DEREGISTER;
|
||||
@ -333,21 +329,6 @@ int gcoap_cli_cmd(int argc, char **argv)
|
||||
gcoap_req_init(&pdu, buf, CONFIG_GCOAP_PDU_BUF_SIZE, code_pos, NULL);
|
||||
|
||||
if (observe) {
|
||||
uint8_t *token = coap_get_token(&pdu);
|
||||
if (obs_value == COAP_OBS_REGISTER) {
|
||||
obs_req_tkl = coap_get_token_len(&pdu);
|
||||
/* backup the token of the initial observe registration */
|
||||
memcpy(obs_req_token, token, obs_req_tkl);
|
||||
} else {
|
||||
/* use the token of the registration for deregistration
|
||||
* (manually replace the token set by gcoap_req_init) */
|
||||
memcpy(token, obs_req_token, obs_req_tkl);
|
||||
if (gcoap_obs_req_forget(&remote, obs_req_token, obs_req_tkl)) {
|
||||
printf("could not remove observe request\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
coap_opt_add_uint(&pdu, COAP_OPT_OBSERVE, obs_value);
|
||||
}
|
||||
|
||||
@ -399,9 +380,14 @@ int gcoap_cli_cmd(int argc, char **argv)
|
||||
}
|
||||
else {
|
||||
if (observe) {
|
||||
/* on successful observe request, store that this node is
|
||||
* observing / not observing anymore */
|
||||
observing = obs_value == COAP_OBS_REGISTER;
|
||||
/* forget last Observe token, as only one can be stored in this example */
|
||||
gcoap_obs_req_forget(&obs_remote, obs_req_token, obs_req_tkl);
|
||||
if (obs_value == COAP_OBS_REGISTER) {
|
||||
obs_req_tkl = coap_get_token_len(&pdu);
|
||||
/* backup the token of the initial observe registration */
|
||||
memcpy(obs_req_token, coap_get_token(&pdu), obs_req_tkl);
|
||||
obs_remote = remote;
|
||||
}
|
||||
}
|
||||
/* send Observe notification for /cli/stats */
|
||||
notify_observers();
|
||||
|
@ -23,11 +23,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "event/periodic_callback.h"
|
||||
#include "event/thread.h"
|
||||
#include "fmt.h"
|
||||
#include "net/gcoap.h"
|
||||
#include "net/utils.h"
|
||||
#include "od.h"
|
||||
#include "periph/rtc.h"
|
||||
#include "time_units.h"
|
||||
|
||||
#include "gcoap_example.h"
|
||||
|
||||
@ -60,11 +65,17 @@ static ssize_t _encode_link(const coap_resource_t *resource, char *buf,
|
||||
size_t maxlen, coap_link_encoder_ctx_t *context);
|
||||
static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx);
|
||||
static ssize_t _riot_board_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx);
|
||||
#if IS_USED(MODULE_PERIPH_RTC)
|
||||
static ssize_t _rtc_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx);
|
||||
#endif
|
||||
|
||||
/* CoAP resources. Must be sorted by path (ASCII order). */
|
||||
static const coap_resource_t _resources[] = {
|
||||
{ "/cli/stats", COAP_GET | COAP_PUT, _stats_handler, NULL },
|
||||
{ "/riot/board", COAP_GET, _riot_board_handler, NULL },
|
||||
#if IS_USED(MODULE_PERIPH_RTC)
|
||||
{ "/rtc", COAP_GET, _rtc_handler, NULL },
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char *_link_params[] = {
|
||||
@ -100,6 +111,62 @@ static ssize_t _encode_link(const coap_resource_t *resource, char *buf,
|
||||
return res;
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_PERIPH_RTC)
|
||||
static void _rtc_notify_observers(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
struct tm tm_now;
|
||||
if (rtc_get_time(&tm_now)) {
|
||||
DEBUG_PUTS("gcoap_server: RTC error");
|
||||
return;
|
||||
}
|
||||
size_t len;
|
||||
char str_time[20] = "";
|
||||
uint8_t buf[sizeof(coap_hdr_t) + COAP_TOKEN_LENGTH_MAX + 1 + sizeof(str_time)];
|
||||
coap_pkt_t pdu;
|
||||
const coap_resource_t *rtc_resource = NULL;
|
||||
const gcoap_listener_t *listener = NULL;
|
||||
while ((rtc_resource = gcoap_get_resource_by_path_iterator(&listener, rtc_resource, "/rtc"))) {
|
||||
if (!strcmp(rtc_resource->path, "/rtc")) {
|
||||
break; /* exact match */
|
||||
}
|
||||
}
|
||||
if (rtc_resource) {
|
||||
switch (gcoap_obs_init(&pdu, buf, sizeof(buf), rtc_resource)) {
|
||||
case GCOAP_OBS_INIT_OK:
|
||||
len = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD);
|
||||
memcpy(pdu.payload, str_time, strftime(str_time, sizeof(str_time), "%Y-%m-%d %H:%M:%S", &tm_now));
|
||||
pdu.payload_len = strlen(str_time);
|
||||
len += pdu.payload_len;
|
||||
if (!gcoap_obs_send(buf, len, rtc_resource)) {
|
||||
DEBUG_PUTS("gcoap_server: cannot send /rtc notification");
|
||||
}
|
||||
break;
|
||||
case GCOAP_OBS_INIT_UNUSED:
|
||||
DEBUG_PUTS("gcoap_server: no observer for /rtc");
|
||||
break;
|
||||
case GCOAP_OBS_INIT_ERR:
|
||||
DEBUG_PUTS("gcoap_server: error initializing /rtc notification");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t _rtc_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
struct tm tm_now;
|
||||
rtc_get_time(&tm_now);
|
||||
gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT);
|
||||
size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD);
|
||||
char str_time[20] = "";
|
||||
memcpy(pdu->payload, str_time, strftime(str_time, sizeof(str_time), "%Y-%m-%d %H:%M:%S", &tm_now));
|
||||
pdu->payload_len = strlen(str_time);
|
||||
resp_len += pdu->payload_len;
|
||||
return resp_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Server callback for /cli/stats. Accepts either a GET or a PUT.
|
||||
*
|
||||
@ -202,4 +269,9 @@ void server_init(void)
|
||||
#endif
|
||||
|
||||
gcoap_register_listener(&_listener);
|
||||
#if IS_USED(MODULE_PERIPH_RTC)
|
||||
static event_periodic_callback_t _ev_pcb_rtc;
|
||||
event_periodic_callback_init(&_ev_pcb_rtc, ZTIMER_MSEC, EVENT_PRIO_MEDIUM, _rtc_notify_observers, NULL);
|
||||
event_periodic_callback_start(&_ev_pcb_rtc, 10 * MS_PER_SEC);
|
||||
#endif
|
||||
}
|
||||
|
@ -552,14 +552,35 @@ extern "C" {
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
* @brief Maximum number of Observe clients
|
||||
*
|
||||
* @note As documented in this file, the implementation is limited to one observer per resource.
|
||||
* Therefore, every stored observer is associated with a different resource.
|
||||
* If you have only one observable resource, you could set this value to 1.
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_OBS_CLIENTS_MAX
|
||||
#define CONFIG_GCOAP_OBS_CLIENTS_MAX (2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
* @brief Maximum number of local notifying endpoint addresses
|
||||
*
|
||||
* @note As documented in this file, the implementation is limited to one observer per resource.
|
||||
* Therefore, every stored local endpoint alias is associated with an observation context
|
||||
* of a different resource.
|
||||
* If you have only one observable resource, you could set this value to 1.
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_OBS_NOTIFIERS_MAX
|
||||
#define CONFIG_GCOAP_OBS_NOTIFIERS_MAX (2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
* @brief Maximum number of registrations for Observable resources
|
||||
*
|
||||
* @note As documented in this file, the implementation is limited to one observer per resource.
|
||||
* Therefore, every stored observation context is associated with a different resource.
|
||||
* If you have only one observable resource, you could set this value to 1.
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_OBS_REGISTRATIONS_MAX
|
||||
#define CONFIG_GCOAP_OBS_REGISTRATIONS_MAX (2)
|
||||
@ -839,6 +860,7 @@ struct gcoap_request_memo {
|
||||
*/
|
||||
typedef struct {
|
||||
sock_udp_ep_t *observer; /**< Client endpoint; unused if null */
|
||||
sock_udp_ep_t *notifier; /**< Local endpoint to send notifications */
|
||||
const coap_resource_t *resource; /**< Entity being observed */
|
||||
uint8_t token[GCOAP_TOKENLEN_MAX]; /**< Client token for notifications */
|
||||
uint16_t last_msgid; /**< Message ID of last notification */
|
||||
@ -874,6 +896,22 @@ kernel_pid_t gcoap_init(void);
|
||||
*/
|
||||
void gcoap_register_listener(gcoap_listener_t *listener);
|
||||
|
||||
/**
|
||||
* @brief Iterate through all registered listeners and check for a resource, matching by @p uri_path
|
||||
*
|
||||
* This functions returns resources matching a subpath @see COAP_MATCH_SUBTREE.
|
||||
* If an exact match is required, check with `strncmp()`.
|
||||
*
|
||||
* @param[in, out] last_listener A pointer to NULL for the first call, otherwise the last returned listener
|
||||
* @param[in] last_resource NULL for the first call, otherwise the last returned resource
|
||||
* @param[in] uri_path The URI path to search for
|
||||
*
|
||||
* @return The resource that matches the URI path
|
||||
*/
|
||||
const coap_resource_t *gcoap_get_resource_by_path_iterator(const gcoap_listener_t **last_listener,
|
||||
const coap_resource_t *last_resource,
|
||||
const char *uri_path);
|
||||
|
||||
/**
|
||||
* @brief Initializes a CoAP request PDU on a buffer.
|
||||
*
|
||||
@ -1055,6 +1093,9 @@ static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf,
|
||||
*
|
||||
* First verifies that an observer has been registered for the resource.
|
||||
*
|
||||
* @post If this function returns @see GCOAP_OBS_INIT_OK you have to call
|
||||
* @ref gcoap_obs_send() afterwards to release a mutex.
|
||||
*
|
||||
* @param[out] pdu Notification metadata
|
||||
* @param[out] buf Buffer containing the PDU
|
||||
* @param[in] len Length of the buffer
|
||||
|
@ -2245,7 +2245,7 @@ extern ssize_t coap_well_known_core_default_handler(coap_pkt_t *pkt, \
|
||||
* @return <0 if the resource path sorts before the URI
|
||||
* @return >0 if the resource path sorts after the URI
|
||||
*/
|
||||
int coap_match_path(const coap_resource_t *resource, uint8_t *uri);
|
||||
int coap_match_path(const coap_resource_t *resource, const uint8_t *uri);
|
||||
|
||||
#if defined(MODULE_GCOAP) || defined(DOXYGEN)
|
||||
/**
|
||||
|
@ -29,8 +29,11 @@
|
||||
#include "net/coap.h"
|
||||
#include "net/gcoap.h"
|
||||
#include "net/gcoap/forward_proxy.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/nanocoap.h"
|
||||
#include "net/nanocoap/cache.h"
|
||||
#include "net/sock/async/event.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "mutex.h"
|
||||
#include "random.h"
|
||||
@ -79,8 +82,10 @@ static int _find_resource(gcoap_socket_type_t tl_type,
|
||||
const coap_resource_t **resource_ptr,
|
||||
gcoap_listener_t **listener_ptr);
|
||||
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote);
|
||||
static int _find_obs_memo(gcoap_observe_memo_t **memo, sock_udp_ep_t *remote,
|
||||
coap_pkt_t *pdu);
|
||||
static int _find_notifier(sock_udp_ep_t **notifier, sock_udp_ep_t *local);
|
||||
static int _find_obs_memo(gcoap_observe_memo_t **memo,
|
||||
sock_udp_ep_t *remote, sock_udp_ep_t *local,
|
||||
coap_pkt_t *pdu);
|
||||
static void _find_obs_memo_resource(gcoap_observe_memo_t **memo,
|
||||
const coap_resource_t *resource);
|
||||
|
||||
@ -103,6 +108,8 @@ static void _on_sock_dtls_evt(sock_dtls_t *sock, sock_async_flags_t type, void *
|
||||
static void _dtls_free_up_session(void *arg);
|
||||
#endif
|
||||
|
||||
static char _ipv6_addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
|
||||
/* Internal variables */
|
||||
const coap_resource_t _default_resources[] = {
|
||||
{ "/.well-known/core", COAP_GET, _well_known_core_handler, NULL },
|
||||
@ -129,6 +136,10 @@ typedef struct {
|
||||
sock_udp_ep_t observers[CONFIG_GCOAP_OBS_CLIENTS_MAX];
|
||||
/* Observe clients; allows reuse for
|
||||
observe memos */
|
||||
/**
|
||||
* @brief Local endpoint aliases to send notifications from
|
||||
*/
|
||||
sock_udp_ep_t notifiers[CONFIG_GCOAP_OBS_NOTIFIERS_MAX];
|
||||
gcoap_observe_memo_t observe_memos[CONFIG_GCOAP_OBS_REGISTRATIONS_MAX];
|
||||
/* Observed resource registrations */
|
||||
uint8_t resend_bufs[CONFIG_GCOAP_RESEND_BUFS_MAX][CONFIG_GCOAP_PDU_BUF_SIZE];
|
||||
@ -377,6 +388,22 @@ static void _on_sock_udp_evt(sock_udp_t *sock, sock_async_flags_t type, void *ar
|
||||
}
|
||||
}
|
||||
|
||||
static void _memo_clear_resend_buffer(gcoap_request_memo_t *memo)
|
||||
{
|
||||
uint8_t hdr[GCOAP_HEADER_MAXLEN];
|
||||
if (memo->send_limit >= 0 && memo->msg.data.pdu_buf) {
|
||||
/* store header from retransmission buffer */
|
||||
memcpy(hdr, memo->msg.data.pdu_buf, sizeof(hdr));
|
||||
/* mark referenced retransmission buffer as available again */
|
||||
*memo->msg.data.pdu_buf = 0;
|
||||
/* but store the header to keep the token for Observe notifications */
|
||||
memcpy(memo->msg.hdr_buf, hdr, sizeof(hdr));
|
||||
/* no further retransmissions should be made and
|
||||
gcoap_request_memo_get_hdr() has to know how to get the header for Observe notifications */
|
||||
memo->send_limit = GCOAP_SEND_LIMIT_NON;
|
||||
}
|
||||
}
|
||||
|
||||
/* Processes and evaluates the coap pdu */
|
||||
static void _process_coap_pdu(gcoap_socket_t *sock, sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux,
|
||||
uint8_t *buf, size_t len, bool truncated)
|
||||
@ -518,9 +545,7 @@ static void _process_coap_pdu(gcoap_socket_t *sock, sock_udp_ep_t *remote, sock_
|
||||
memo->resp_handler(memo, &pdu, remote);
|
||||
}
|
||||
|
||||
if (memo->send_limit >= 0) { /* if confirmable */
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend PDU buffer */
|
||||
}
|
||||
_memo_clear_resend_buffer(memo);
|
||||
|
||||
/* The memo must be kept if the response is an observe notification.
|
||||
* Non-2.xx notifications indicate that the associated observe entry
|
||||
@ -657,6 +682,7 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
|
||||
const coap_resource_t *resource = NULL;
|
||||
gcoap_listener_t *listener = NULL;
|
||||
sock_udp_ep_t *observer = NULL;
|
||||
sock_udp_ep_t *notifier = NULL;
|
||||
gcoap_observe_memo_t *memo = NULL;
|
||||
gcoap_observe_memo_t *resource_memo = NULL;
|
||||
|
||||
@ -677,7 +703,7 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
|
||||
|
||||
if (coap_get_observe(pdu) == COAP_OBS_REGISTER) {
|
||||
/* lookup remote+token */
|
||||
int empty_slot = _find_obs_memo(&memo, remote, pdu);
|
||||
int empty_slot = _find_obs_memo(&memo, remote, NULL, pdu);
|
||||
/* validate re-registration request */
|
||||
if (resource_memo != NULL) {
|
||||
if (memo != NULL) {
|
||||
@ -699,18 +725,28 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
|
||||
if ((memo == NULL) && coap_has_observe(pdu)) {
|
||||
/* verify resource not already registered (for another endpoint) */
|
||||
if ((empty_slot >= 0) && (resource_memo == NULL)) {
|
||||
int obs_slot = _find_observer(&observer, remote);
|
||||
int slot = _find_observer(&observer, remote);
|
||||
/* cache new observer */
|
||||
if (observer == NULL) {
|
||||
if (obs_slot >= 0) {
|
||||
observer = &_coap_state.observers[obs_slot];
|
||||
memcpy(observer, remote, sizeof(sock_udp_ep_t));
|
||||
if (slot >= 0) {
|
||||
observer = &_coap_state.observers[slot];
|
||||
} else {
|
||||
DEBUG("gcoap: can't register observer\n");
|
||||
}
|
||||
}
|
||||
if (observer != NULL) {
|
||||
slot = _find_notifier(¬ifier, &aux->local);
|
||||
if (notifier == NULL) {
|
||||
if (slot >= 0) {
|
||||
notifier = &_coap_state.notifiers[slot];
|
||||
} else {
|
||||
DEBUG("gcoap: can't allocate notifier\n");
|
||||
}
|
||||
}
|
||||
if (observer && notifier) {
|
||||
memcpy(observer, remote, sizeof(*remote));
|
||||
memcpy(notifier, &aux->local, sizeof(aux->local));
|
||||
memo = &_coap_state.observe_memos[empty_slot];
|
||||
memo->notifier = notifier;
|
||||
memo->observer = observer;
|
||||
}
|
||||
}
|
||||
@ -732,19 +768,25 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
|
||||
}
|
||||
|
||||
} else if (coap_get_observe(pdu) == COAP_OBS_DEREGISTER) {
|
||||
_find_obs_memo(&memo, remote, pdu);
|
||||
_find_obs_memo(&memo, remote, NULL, pdu);
|
||||
/* clear memo, and clear observer if no other memos */
|
||||
if (memo != NULL) {
|
||||
DEBUG("gcoap: Deregistering observer for: %s\n", memo->resource->path);
|
||||
memo->observer = NULL;
|
||||
memo = NULL;
|
||||
_find_obs_memo(&memo, remote, NULL);
|
||||
if (memo == NULL) {
|
||||
gcoap_observe_memo_t *other_memo = NULL;
|
||||
_find_obs_memo(&other_memo, remote, NULL, NULL);
|
||||
if (other_memo == NULL) {
|
||||
_find_observer(&observer, remote);
|
||||
if (observer != NULL) {
|
||||
observer->family = AF_UNSPEC;
|
||||
}
|
||||
}
|
||||
other_memo = NULL;
|
||||
_find_obs_memo(&other_memo, NULL, memo->notifier, NULL);
|
||||
if (!other_memo) {
|
||||
memo->notifier->family = AF_UNSPEC;
|
||||
}
|
||||
memo->notifier = NULL;
|
||||
}
|
||||
coap_clear_observe(pdu);
|
||||
|
||||
@ -771,6 +813,28 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
static const coap_resource_t *_match_resource_path_iterator(const gcoap_listener_t *listener,
|
||||
const coap_resource_t *last,
|
||||
const uint8_t *uri_path)
|
||||
{
|
||||
size_t i;
|
||||
if (!last) {
|
||||
i = 0;
|
||||
}
|
||||
else {
|
||||
assert(listener);
|
||||
assert(last >= listener->resources);
|
||||
assert(last < listener->resources + listener->resources_len);
|
||||
i = (last - listener->resources) + 1;
|
||||
}
|
||||
for (; i < listener->resources_len; i++) {
|
||||
if (!coap_match_path(&listener->resources[i], uri_path)) {
|
||||
return &listener->resources[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _request_matcher_default(gcoap_listener_t *listener,
|
||||
const coap_resource_t **resource,
|
||||
coap_pkt_t *pdu)
|
||||
@ -788,18 +852,10 @@ static int _request_matcher_default(gcoap_listener_t *listener,
|
||||
coap_method_flags_t method_flag = coap_method2flag(
|
||||
coap_get_code_detail(pdu));
|
||||
|
||||
for (size_t i = 0; i < listener->resources_len; i++) {
|
||||
*resource = &listener->resources[i];
|
||||
|
||||
int res = coap_match_path(*resource, uri);
|
||||
|
||||
/* URI mismatch */
|
||||
if (res != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*resource = NULL;
|
||||
while ((*resource = _match_resource_path_iterator(listener, *resource, uri))) {
|
||||
/* potential match, check for method */
|
||||
if (! ((*resource)->methods & method_flag)) {
|
||||
if (!((*resource)->methods & method_flag)) {
|
||||
/* record wrong method error for next iteration, in case
|
||||
* another resource with the same URI and correct method
|
||||
* exists */
|
||||
@ -837,7 +893,7 @@ static int _find_resource(gcoap_socket_type_t tl_type,
|
||||
gcoap_listener_t *listener = _coap_state.listeners;
|
||||
|
||||
while (listener) {
|
||||
const coap_resource_t *resource;
|
||||
const coap_resource_t *resource = NULL;
|
||||
int res;
|
||||
|
||||
/* only makes sense to check if non-UDP transports are supported,
|
||||
@ -887,7 +943,7 @@ static int _find_resource(gcoap_socket_type_t tl_type,
|
||||
* return Registered request memo, or NULL if not found
|
||||
*/
|
||||
static gcoap_request_memo_t* _find_req_memo_by_token(const sock_udp_ep_t *remote,
|
||||
const uint8_t *token, size_t tkl)
|
||||
const uint8_t *token, size_t tkl)
|
||||
{
|
||||
/* no need to initialize struct; we only care about buffer contents below */
|
||||
coap_pkt_t memo_pdu_data;
|
||||
@ -901,15 +957,36 @@ static gcoap_request_memo_t* _find_req_memo_by_token(const sock_udp_ep_t *remote
|
||||
gcoap_request_memo_t *memo = &_coap_state.open_reqs[i];
|
||||
memo_pdu->hdr = gcoap_request_memo_get_hdr(memo);
|
||||
|
||||
if (coap_get_token_len(memo_pdu) == tkl) {
|
||||
if ((memcmp(token, coap_get_token(memo_pdu), tkl) == 0)
|
||||
&& (sock_udp_ep_equal(&memo->remote_ep, remote)
|
||||
/* Multicast addresses are not considered in matching responses */
|
||||
|| sock_udp_ep_is_multicast(&memo->remote_ep)
|
||||
)) {
|
||||
return memo;
|
||||
/* verbose debug to catch bugs with request/response matching */
|
||||
DEBUG("Seeking memo for remote=%s, tkn=0x%02x%02x%02x%02x%02x%02x%02x%02x, tkl=%"PRIuSIZE"\n",
|
||||
ipv6_addr_to_str(_ipv6_addr_str, (ipv6_addr_t *)&remote->addr.ipv6,
|
||||
IPV6_ADDR_MAX_STR_LEN),
|
||||
token[0], token[1], token[2], token[3], token[4], token[5], token[6], token[7],
|
||||
tkl);
|
||||
|
||||
if (coap_get_token_len(memo_pdu) != tkl) {
|
||||
DEBUG("Token length mismatch %u\n", coap_get_token_len(memo_pdu));
|
||||
continue;
|
||||
}
|
||||
const uint8_t *memo_token = coap_get_token(memo_pdu);
|
||||
if (memcmp(token, memo_token, tkl)) {
|
||||
DEBUG("Token mismatch 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
||||
memo_token[0], memo_token[1], memo_token[2], memo_token[3],
|
||||
memo_token[4], memo_token[5], memo_token[6], memo_token[7]);
|
||||
continue;
|
||||
}
|
||||
if (!sock_udp_ep_equal(&memo->remote_ep, remote)) {
|
||||
if (sock_udp_ep_is_multicast(&memo->remote_ep)) {
|
||||
DEBUG("matching multicast response\n");
|
||||
}
|
||||
else {
|
||||
DEBUG("Remote address mismatch %s\n",
|
||||
ipv6_addr_to_str(_ipv6_addr_str, (ipv6_addr_t *)&memo->remote_ep.addr.ipv6,
|
||||
IPV6_ADDR_MAX_STR_LEN));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return memo;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -972,9 +1049,7 @@ static void _expire_request(gcoap_request_memo_t *memo)
|
||||
req.hdr = gcoap_request_memo_get_hdr(memo);
|
||||
memo->resp_handler(memo, &req, NULL);
|
||||
}
|
||||
if (memo->send_limit != GCOAP_SEND_LIMIT_NON) {
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend buffer */
|
||||
}
|
||||
_memo_clear_resend_buffer(memo);
|
||||
memo->state = GCOAP_MEMO_UNUSED;
|
||||
}
|
||||
else {
|
||||
@ -1003,57 +1078,78 @@ static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t le
|
||||
}
|
||||
|
||||
/*
|
||||
* Find registered observer for a remote address and port.
|
||||
* Find registered observer or notification endpoint for a remote or local address and port.
|
||||
*
|
||||
* observer[out] -- Registered observer, or NULL if not found
|
||||
* remote[in] -- Endpoint to match
|
||||
* out[in,out] -- in: endpoint array to scan, out: found endpoint or NULL if not found
|
||||
* in[in] -- Endpoint to match
|
||||
*
|
||||
* return Index of empty slot, suitable for registering new observer; or -1
|
||||
* return Index of empty slot, suitable for registering new endpoint; or -1
|
||||
* if no empty slots. Undefined if observer found.
|
||||
*/
|
||||
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote)
|
||||
static int _find_endpoint(sock_udp_ep_t **out, sock_udp_ep_t *in, unsigned max)
|
||||
{
|
||||
int empty_slot = -1;
|
||||
*observer = NULL;
|
||||
for (unsigned i = 0; i < CONFIG_GCOAP_OBS_CLIENTS_MAX; i++) {
|
||||
sock_udp_ep_t *ep_array = *out;
|
||||
*out = NULL;
|
||||
for (unsigned i = 0; i < max; i++) {
|
||||
|
||||
if (_coap_state.observers[i].family == AF_UNSPEC) {
|
||||
if (ep_array[i].family == AF_UNSPEC) {
|
||||
empty_slot = i;
|
||||
}
|
||||
else if (sock_udp_ep_equal(&_coap_state.observers[i], remote)) {
|
||||
*observer = &_coap_state.observers[i];
|
||||
else if (sock_udp_ep_equal(&ep_array[i], in)) {
|
||||
*out = &ep_array[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return empty_slot;
|
||||
}
|
||||
|
||||
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote)
|
||||
{
|
||||
*observer = _coap_state.observers;
|
||||
return _find_endpoint(observer, remote, CONFIG_GCOAP_OBS_CLIENTS_MAX);
|
||||
}
|
||||
|
||||
static int _find_notifier(sock_udp_ep_t **notifier, sock_udp_ep_t *local)
|
||||
{
|
||||
*notifier = _coap_state.notifiers;
|
||||
return _find_endpoint(notifier, local, CONFIG_GCOAP_OBS_NOTIFIERS_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find registered observe memo for a remote address and token.
|
||||
*
|
||||
* memo[out] -- Registered observe memo, or NULL if not found
|
||||
* remote[in] -- Endpoint for address to match
|
||||
* remote[in] -- Remote endpoint for address to match if not NULL
|
||||
* local[in] -- Local endpoint for address to match if not NULL
|
||||
* pdu[in] -- PDU for token to match, or NULL to match only on remote address
|
||||
*
|
||||
* return Index of empty slot, suitable for registering new memo; or -1 if no
|
||||
* empty slots. Undefined if memo found.
|
||||
*/
|
||||
static int _find_obs_memo(gcoap_observe_memo_t **memo, sock_udp_ep_t *remote,
|
||||
coap_pkt_t *pdu)
|
||||
static int _find_obs_memo(gcoap_observe_memo_t **memo,
|
||||
sock_udp_ep_t *remote, sock_udp_ep_t *local,
|
||||
coap_pkt_t *pdu)
|
||||
{
|
||||
int empty_slot = -1;
|
||||
*memo = NULL;
|
||||
|
||||
sock_udp_ep_t *remote_observer = NULL;
|
||||
_find_observer(&remote_observer, remote);
|
||||
|
||||
sock_udp_ep_t *local_notifier = NULL;
|
||||
if (remote) {
|
||||
_find_observer(&remote_observer, remote);
|
||||
}
|
||||
if (local) {
|
||||
_find_notifier(&local_notifier, local);
|
||||
}
|
||||
for (unsigned i = 0; i < CONFIG_GCOAP_OBS_REGISTRATIONS_MAX; i++) {
|
||||
if (_coap_state.observe_memos[i].observer == NULL) {
|
||||
empty_slot = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_coap_state.observe_memos[i].observer == remote_observer) {
|
||||
if ((_coap_state.observe_memos[i].observer == remote_observer || !remote_observer) &&
|
||||
(_coap_state.observe_memos[i].notifier == local_notifier || !local_notifier)) {
|
||||
if (pdu == NULL) {
|
||||
*memo = &_coap_state.observe_memos[i];
|
||||
break;
|
||||
@ -1105,10 +1201,19 @@ static void _check_and_expire_obs_memo_last_mid(sock_udp_ep_t *remote,
|
||||
|
||||
if (stale_obs_memo) {
|
||||
stale_obs_memo->observer = NULL; /* clear memo */
|
||||
/* check if no other memo is referencing the same local endpoint ... */
|
||||
gcoap_observe_memo_t *other_memo = NULL;
|
||||
_find_obs_memo(&other_memo, NULL, stale_obs_memo->notifier, NULL);
|
||||
if (!other_memo) {
|
||||
/* ... if not -> also free the notifier entry */
|
||||
stale_obs_memo->notifier->family = AF_UNSPEC;
|
||||
}
|
||||
/* then unreference notifier */
|
||||
stale_obs_memo->notifier = NULL;
|
||||
|
||||
/* check if the observer has more observe memos registered... */
|
||||
stale_obs_memo = NULL;
|
||||
_find_obs_memo(&stale_obs_memo, observer, NULL);
|
||||
_find_obs_memo(&stale_obs_memo, observer, NULL, NULL);
|
||||
if (stale_obs_memo == NULL) {
|
||||
/* ... if not -> also free the observer entry */
|
||||
observer->family = AF_UNSPEC;
|
||||
@ -1357,9 +1462,7 @@ static void _receive_from_cache_cb(void *ctx)
|
||||
if (_cache_build_response(ce, &pdu, _listen_buf, sizeof(_listen_buf)) >= 0) {
|
||||
memo->state = (ce->truncated) ? GCOAP_MEMO_RESP_TRUNC : GCOAP_MEMO_RESP;
|
||||
memo->resp_handler(memo, &pdu, &memo->remote_ep);
|
||||
if (memo->send_limit >= 0) { /* if confirmable */
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend PDU buffer */
|
||||
}
|
||||
_memo_clear_resend_buffer(memo);
|
||||
memo->state = GCOAP_MEMO_UNUSED;
|
||||
}
|
||||
}
|
||||
@ -1553,6 +1656,24 @@ void gcoap_register_listener(gcoap_listener_t *listener)
|
||||
}
|
||||
}
|
||||
|
||||
const coap_resource_t *gcoap_get_resource_by_path_iterator(const gcoap_listener_t **last_listener,
|
||||
const coap_resource_t *last_resource,
|
||||
const char *uri_path)
|
||||
{
|
||||
const gcoap_listener_t *listener = *last_listener ? *last_listener : _coap_state.listeners;
|
||||
const coap_resource_t *resource = last_resource;
|
||||
|
||||
while (listener) {
|
||||
if ((resource = _match_resource_path_iterator(listener, resource, (const uint8_t *)uri_path))) {
|
||||
*last_listener = listener;
|
||||
return resource;
|
||||
}
|
||||
listener = listener->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int gcoap_req_init_path_buffer(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
unsigned code, const char *path, size_t path_len)
|
||||
{
|
||||
@ -1765,9 +1886,7 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len,
|
||||
}
|
||||
if (res <= 0) {
|
||||
if (memo != NULL) {
|
||||
if (msg_type == COAP_TYPE_CON) {
|
||||
*memo->msg.data.pdu_buf = 0; /* clear resend buffer */
|
||||
}
|
||||
_memo_clear_resend_buffer(memo);
|
||||
if (timeout > 0) {
|
||||
event_timeout_clear(&memo->resp_evt_tmout);
|
||||
}
|
||||
@ -1811,9 +1930,11 @@ int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
{
|
||||
gcoap_observe_memo_t *memo = NULL;
|
||||
|
||||
mutex_lock(&_coap_state.lock);
|
||||
_find_obs_memo_resource(&memo, resource);
|
||||
if (memo == NULL) {
|
||||
/* Unique return value to specify there is not an observer */
|
||||
mutex_unlock(&_coap_state.lock);
|
||||
return GCOAP_OBS_INIT_UNUSED;
|
||||
}
|
||||
|
||||
@ -1822,36 +1943,40 @@ int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
ssize_t hdrlen = coap_build_hdr(pdu->hdr, COAP_TYPE_NON, &memo->token[0],
|
||||
memo->token_len, COAP_CODE_CONTENT, msgid);
|
||||
|
||||
if (hdrlen > 0) {
|
||||
coap_pkt_init(pdu, buf, len, hdrlen);
|
||||
|
||||
_add_generated_observe_option(pdu);
|
||||
/* Store message ID of the last notification sent. This is needed
|
||||
* to match a potential RST returned by a client in order to signal
|
||||
* it does not recognize this notification. */
|
||||
memo->last_msgid = msgid;
|
||||
|
||||
return GCOAP_OBS_INIT_OK;
|
||||
}
|
||||
else {
|
||||
if (hdrlen <= 0) {
|
||||
/* reason for negative hdrlen is not defined, so we also are vague */
|
||||
mutex_unlock(&_coap_state.lock);
|
||||
return GCOAP_OBS_INIT_ERR;
|
||||
}
|
||||
|
||||
coap_pkt_init(pdu, buf, len, hdrlen);
|
||||
|
||||
_add_generated_observe_option(pdu);
|
||||
/* Store message ID of the last notification sent. This is needed
|
||||
* to match a potential RST returned by a client in order to signal
|
||||
* it does not recognize this notification. */
|
||||
memo->last_msgid = msgid;
|
||||
|
||||
return GCOAP_OBS_INIT_OK;
|
||||
}
|
||||
|
||||
size_t gcoap_obs_send(const uint8_t *buf, size_t len,
|
||||
const coap_resource_t *resource)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
gcoap_observe_memo_t *memo = NULL;
|
||||
_find_obs_memo_resource(&memo, resource);
|
||||
|
||||
if (memo) {
|
||||
ssize_t bytes = _tl_send(&memo->socket, buf, len, memo->observer, NULL);
|
||||
return (size_t)((bytes > 0) ? bytes : 0);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
sock_udp_aux_tx_t aux = { 0 };
|
||||
if (memo->notifier) {
|
||||
memcpy(&aux.local, memo->notifier, sizeof(*memo->notifier));
|
||||
aux.flags = SOCK_AUX_SET_LOCAL;
|
||||
}
|
||||
ret = _tl_send(&memo->socket, buf, len, memo->observer, &aux);
|
||||
}
|
||||
mutex_unlock(&_coap_state.lock);
|
||||
return ret <= 0 ? 0 : (size_t)ret;
|
||||
}
|
||||
|
||||
uint8_t gcoap_op_state(void)
|
||||
|
@ -184,7 +184,7 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coap_match_path(const coap_resource_t *resource, uint8_t *uri)
|
||||
int coap_match_path(const coap_resource_t *resource, const uint8_t *uri)
|
||||
{
|
||||
assert(resource && uri);
|
||||
int res;
|
||||
|
Loading…
Reference in New Issue
Block a user