mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #13889 from cgundogan/pr/coca
nanocoap: cache for CoAP responses
This commit is contained in:
commit
02fb040d9c
1
dist/tools/doccheck/exclude_patterns
vendored
1
dist/tools/doccheck/exclude_patterns
vendored
@ -14273,6 +14273,7 @@ sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_ACCEPT \(macro definiti
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_BLOCK1 \(macro definition\) of group net_coap is not documented\.
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_BLOCK2 \(macro definition\) of group net_coap is not documented\.
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_CONTENT_FORMAT \(macro definition\) of group net_coap is not documented\.
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_MAX_AGE \(macro definition\) of group net_coap is not documented\.
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_LOCATION_PATH \(macro definition\) of group net_coap is not documented\.
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_LOCATION_QUERY \(macro definition\) of group net_coap is not documented\.
|
||||
sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_OBSERVE \(macro definition\) of group net_coap is not documented\.
|
||||
|
@ -662,6 +662,11 @@ ifneq (,$(filter nanocoap_sock,$(USEMODULE)))
|
||||
USEMODULE += ztimer_msec
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nanocoap_cache,$(USEMODULE)))
|
||||
USEMODULE += ztimer_sec
|
||||
USEMODULE += hashes
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nanocoap_%,$(USEMODULE)))
|
||||
USEMODULE += nanocoap
|
||||
endif
|
||||
|
@ -40,6 +40,7 @@ extern "C" {
|
||||
#define COAP_OPT_LOCATION_PATH (8)
|
||||
#define COAP_OPT_URI_PATH (11)
|
||||
#define COAP_OPT_CONTENT_FORMAT (12)
|
||||
#define COAP_OPT_MAX_AGE (14)
|
||||
#define COAP_OPT_URI_QUERY (15)
|
||||
#define COAP_OPT_ACCEPT (17)
|
||||
#define COAP_OPT_LOCATION_QUERY (20)
|
||||
|
234
sys/include/net/nanocoap/cache.h
Normal file
234
sys/include/net/nanocoap/cache.h
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2020 HAW Hamburg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_nanocoap_cache Nanocoap-Cache implementation
|
||||
* @ingroup net_nanocoap
|
||||
* @brief A cache implementation for nanocoap response messages
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief nanocoap-cache API
|
||||
*
|
||||
* @author Cenk Gündoğan <cenk.guendogan@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#ifndef NET_NANOCOAP_CACHE_H
|
||||
#define NET_NANOCOAP_CACHE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include "clist.h"
|
||||
#include "net/nanocoap.h"
|
||||
#include "hashes/sha256.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The number of maximum cache entries.
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_CACHE_ENTRIES
|
||||
#define CONFIG_NANOCOAP_CACHE_ENTRIES (8)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The length of the cache key in bytes.
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_CACHE_KEY_LENGTH
|
||||
#define CONFIG_NANOCOAP_CACHE_KEY_LENGTH (8)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Size of the buffer to store responses in the cache.
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE
|
||||
#define CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE (128)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Cache container that holds a @p coap_pkt_t struct.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief needed for clist_t, must be the first struct member!
|
||||
*/
|
||||
clist_node_t node;
|
||||
|
||||
/**
|
||||
* @brief the calculated cache key, see nanocoap_cache_key_generate().
|
||||
*/
|
||||
uint8_t cache_key[CONFIG_NANOCOAP_CACHE_KEY_LENGTH];
|
||||
|
||||
/**
|
||||
* @brief packet representation of the response
|
||||
*/
|
||||
coap_pkt_t response_pkt;
|
||||
|
||||
/**
|
||||
* @brief buffer to hold the response message.
|
||||
*/
|
||||
uint8_t response_buf[CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE];
|
||||
|
||||
size_t response_len; /**< length of the message in @p response */
|
||||
|
||||
unsigned request_method; /**< the method of the initial request */
|
||||
|
||||
/**
|
||||
* @brief absolute system time in seconds until which this cache entry
|
||||
* is considered valid.
|
||||
*/
|
||||
ztimer_now_t max_age;
|
||||
} nanocoap_cache_entry_t;
|
||||
|
||||
/**
|
||||
* @brief Typedef for the cache replacement strategy on full cache list.
|
||||
*
|
||||
* @return 0 on successfully replacing a cache element
|
||||
* @return -1 on error
|
||||
*/
|
||||
typedef int (*nanocoap_cache_replacement_strategy_t)(void);
|
||||
|
||||
/**
|
||||
* @brief Typedef for the cache update strategy on element access.
|
||||
*
|
||||
* @param[in] node The accessed node.
|
||||
*
|
||||
* @return 0 on successfully updating the element
|
||||
* @return -1 on error
|
||||
*/
|
||||
typedef int (*nanocoap_cache_update_strategy_t)(clist_node_t *node);
|
||||
|
||||
/**
|
||||
* @brief Initializes the internal state of the nanocoap cache.
|
||||
*/
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
void nanocoap_cache_init(void);
|
||||
#else
|
||||
static inline void nanocoap_cache_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Returns the number of cached entries.
|
||||
*
|
||||
* @return Number of cached entries
|
||||
*/
|
||||
size_t nanocoap_cache_used_count(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of unused cache entries.
|
||||
*
|
||||
* @return Number of unused cache entries
|
||||
*/
|
||||
size_t nanocoap_cache_free_count(void);
|
||||
|
||||
/**
|
||||
* @brief Determines if a response is cacheable and modifies the cache
|
||||
* as reflected in RFC7252, Section 5.9.
|
||||
|
||||
* @param[in] cache_key The cache key of the request
|
||||
* @param[in] request_method The method of the initial request
|
||||
* @param[in] resp The response to operate on
|
||||
* @param[in] resp_len The actual length of the response in @p resp
|
||||
*
|
||||
* @return 0 on successfully handling the response
|
||||
* @return -1 on error
|
||||
*/
|
||||
int nanocoap_cache_process(const uint8_t *cache_key, unsigned request_method,
|
||||
const coap_pkt_t *resp, size_t resp_len);
|
||||
/**
|
||||
* @brief Creates a new or gets an existing cache entry using the
|
||||
* request packet.
|
||||
*
|
||||
* @param[in] req The request to calculate the cache-key
|
||||
* @param[in] resp The response to add to the cache
|
||||
* @param[in] resp_len The actual length of the response message in @p resp
|
||||
*
|
||||
* @return The previously existing or newly added cache entry on success
|
||||
* @return NULL, if there is no space left
|
||||
*/
|
||||
nanocoap_cache_entry_t *nanocoap_cache_add_by_req(const coap_pkt_t *req,
|
||||
const coap_pkt_t *resp,
|
||||
size_t resp_len);
|
||||
|
||||
/**
|
||||
* @brief Creates a new or gets an existing cache entry using the cache key.
|
||||
*
|
||||
* @param[in] cache_key The cache key of the request
|
||||
* @param[in] request_method The method of the initial request
|
||||
* @param[in] resp The response to add to the cache
|
||||
* @param[in] resp_len The actual length of the response in @p resp
|
||||
*
|
||||
* @return The previously existing or newly added cache entry on success
|
||||
* @return NULL, if there is no space left
|
||||
*/
|
||||
nanocoap_cache_entry_t *nanocoap_cache_add_by_key(const uint8_t *cache_key,
|
||||
unsigned request_method,
|
||||
const coap_pkt_t *resp,
|
||||
size_t resp_len);
|
||||
|
||||
/**
|
||||
* @brief Performs a cache lookup based on the @p req.
|
||||
*
|
||||
* @param[in] req The request to calculate the cache-key
|
||||
*
|
||||
* @return An existing cache entry on cache hit
|
||||
* @return NULL on cache miss
|
||||
*/
|
||||
nanocoap_cache_entry_t *nanocoap_cache_request_lookup(const coap_pkt_t *req);
|
||||
|
||||
/**
|
||||
* @brief Performs a cache lookup based on the cache key of a request.
|
||||
*
|
||||
* @param[in] cache_key The cache key of a request
|
||||
*
|
||||
* @return An existing cache entry on cache hit
|
||||
* @return NULL on cache miss
|
||||
*/
|
||||
nanocoap_cache_entry_t *nanocoap_cache_key_lookup(const uint8_t *cache_key);
|
||||
|
||||
/**
|
||||
* @brief Deletes the provided cache entry @p ce.
|
||||
*
|
||||
* @param[in] ce The cache entry to delete
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 if entry is not available in the cache
|
||||
*/
|
||||
int nanocoap_cache_del(const nanocoap_cache_entry_t *ce);
|
||||
|
||||
/**
|
||||
* @brief Generates a cache key based on the request @p req.
|
||||
*
|
||||
* @param[in] req The request to generate the cache key from
|
||||
* @param[out] cache_key The generated cache key
|
||||
*/
|
||||
void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key);
|
||||
|
||||
/**
|
||||
* @brief Compares two cache keys.
|
||||
*
|
||||
* @param[in] cache_key1 The first cache key in the comparison
|
||||
* @param[in] cache_key2 The second cache key in the comparison
|
||||
*
|
||||
* @return 0 if cache keys are equal
|
||||
* @return <0 or 0> (see memcmp()) for unequal cache keys
|
||||
*/
|
||||
ssize_t nanocoap_cache_key_compare(uint8_t *cache_key1, uint8_t *cache_key2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* NET_NANOCOAP_CACHE_H */
|
||||
/** @} */
|
@ -16,6 +16,7 @@
|
||||
#include "net/gcoap.h"
|
||||
#include "net/gcoap/forward_proxy.h"
|
||||
#include "uri_parser.h"
|
||||
#include "net/nanocoap/cache.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
@ -23,6 +24,9 @@
|
||||
typedef struct {
|
||||
int in_use;
|
||||
sock_udp_ep_t ep;
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
uint8_t cache_key[CONFIG_NANOCOAP_CACHE_KEY_LENGTH];
|
||||
#endif
|
||||
} client_ep_t;
|
||||
|
||||
static uint8_t proxy_req_buf[CONFIG_GCOAP_PDU_BUF_SIZE];
|
||||
@ -50,6 +54,58 @@ gcoap_listener_t forward_proxy_listener = {
|
||||
void gcoap_forward_proxy_init(void)
|
||||
{
|
||||
gcoap_register_listener(&forward_proxy_listener);
|
||||
|
||||
/* initialize the nanocoap cache operation, if compiled */
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE)) {
|
||||
nanocoap_cache_init();
|
||||
}
|
||||
}
|
||||
|
||||
static int _cache_build_response(nanocoap_cache_entry_t *ce,
|
||||
coap_pkt_t *pdu,
|
||||
uint8_t *buf,
|
||||
size_t len)
|
||||
{
|
||||
/* Use the same code from the cached content. Use other header
|
||||
* fields from the incoming request */
|
||||
gcoap_resp_init(pdu, buf, len, ce->response_pkt.hdr->code);
|
||||
/* copy all options and possible payload from the cached response
|
||||
* to the new response */
|
||||
unsigned header_len_req = coap_get_total_hdr_len(pdu);
|
||||
unsigned header_len_cached = coap_get_total_hdr_len(&ce->response_pkt);
|
||||
unsigned opt_payload_len = ce->response_len - header_len_cached;
|
||||
|
||||
memcpy((buf + header_len_req),
|
||||
(ce->response_buf + header_len_cached),
|
||||
opt_payload_len);
|
||||
return header_len_req + opt_payload_len;
|
||||
}
|
||||
|
||||
static int _cache_lookup_and_process(coap_pkt_t *pdu,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
client_ep_t *cep)
|
||||
{
|
||||
(void) cep;
|
||||
|
||||
uint8_t cache_key[SHA256_DIGEST_LENGTH];
|
||||
ztimer_now_t now = ztimer_now(ZTIMER_SEC);
|
||||
nanocoap_cache_key_generate(pdu, cache_key);
|
||||
nanocoap_cache_entry_t *ce = nanocoap_cache_key_lookup(cache_key);
|
||||
|
||||
/* cache hit, methods are equal, and cache entry is not stale */
|
||||
if (ce &&
|
||||
(ce->request_method == coap_get_code(pdu)) &&
|
||||
(ce->max_age > now)) {
|
||||
/* use response from cache */
|
||||
return _cache_build_response(ce, pdu, buf, len);
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
memcpy(cep->cache_key, cache_key, CONFIG_NANOCOAP_CACHE_KEY_LENGTH);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static client_ep_t *_allocate_client_ep(sock_udp_ep_t *ep)
|
||||
@ -91,24 +147,25 @@ static int _request_matcher_forward_proxy(gcoap_listener_t *listener,
|
||||
static ssize_t _forward_proxy_handler(coap_pkt_t *pdu, uint8_t *buf,
|
||||
size_t len, void *ctx)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
sock_udp_ep_t *remote = (sock_udp_ep_t *)ctx;
|
||||
|
||||
int proxy_res = gcoap_forward_proxy_request_process(pdu, remote);
|
||||
pdu_len = gcoap_forward_proxy_request_process(pdu, remote);
|
||||
|
||||
/* Out of memory, reply with 5.00 */
|
||||
if (proxy_res == -ENOMEM) {
|
||||
if (pdu_len == -ENOMEM) {
|
||||
return gcoap_response(pdu, buf, len, COAP_CODE_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
/* Proxy-Uri malformed, reply with 4.02 */
|
||||
else if (proxy_res == -EINVAL) {
|
||||
else if (pdu_len == -EINVAL) {
|
||||
return gcoap_response(pdu, buf, len, COAP_CODE_BAD_OPTION);
|
||||
}
|
||||
/* scheme not supported */
|
||||
else if (proxy_res == -EPERM) {
|
||||
else if (pdu_len == -EPERM) {
|
||||
return gcoap_response(pdu, buf, len, COAP_CODE_PROXYING_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
static bool _parse_endpoint(sock_udp_ep_t *remote,
|
||||
@ -194,6 +251,19 @@ static void _forward_resp_handler(const gcoap_request_memo_t *memo,
|
||||
(pdu->payload -
|
||||
(uint8_t *)pdu->hdr + pdu->payload_len),
|
||||
&cep->ep);
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
coap_pkt_t req;
|
||||
if (memo->send_limit == GCOAP_SEND_LIMIT_NON) {
|
||||
req.hdr = (coap_hdr_t *) &memo->msg.hdr_buf[0];
|
||||
}
|
||||
else {
|
||||
req.hdr = (coap_hdr_t *) memo->msg.data.pdu_buf;
|
||||
}
|
||||
|
||||
size_t pdu_len = pdu->payload_len +
|
||||
(pdu->payload - (uint8_t *)pdu->hdr);
|
||||
nanocoap_cache_process(cep->cache_key, coap_get_code(&req), pdu, pdu_len);
|
||||
#endif
|
||||
}
|
||||
_free_client_ep(cep);
|
||||
}
|
||||
@ -312,7 +382,6 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt,
|
||||
sock_udp_ep_t *client) {
|
||||
char *uri;
|
||||
uri_parser_result_t urip;
|
||||
|
||||
ssize_t optlen = 0;
|
||||
|
||||
client_ep_t *cep = _allocate_client_ep(client);
|
||||
@ -321,6 +390,20 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE)) {
|
||||
int pdu_len = _cache_lookup_and_process(pkt,
|
||||
(uint8_t *)pkt->hdr,
|
||||
CONFIG_GCOAP_PDU_BUF_SIZE,
|
||||
cep);
|
||||
/* if a valid cache entry was found, then pdu_len contains the
|
||||
* length of that response message */
|
||||
if (pdu_len > 0) {
|
||||
_free_client_ep(cep);
|
||||
return pdu_len;
|
||||
}
|
||||
/* if there was no cache hit, then we continue forwarding */
|
||||
}
|
||||
|
||||
optlen = coap_get_proxy_uri(pkt, &uri);
|
||||
|
||||
if (optlen < 0) {
|
||||
|
@ -35,4 +35,24 @@ config NANOCOAP_QS_MAX
|
||||
int "Maximum length of a query string written to a message"
|
||||
default 64
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_NANOCOAP_CACHE
|
||||
bool "Configure Nanocoap Cache module"
|
||||
depends on USEMODULE_NANOCOAP_CACHE
|
||||
|
||||
if KCONFIG_USEMODULE_NANOCOAP_CACHE
|
||||
|
||||
config NANOCOAP_CACHE_ENTRIES
|
||||
int "Number of maximum cache entries"
|
||||
default 8
|
||||
|
||||
config NANOCOAP_CACHE_KEY_LENGTH
|
||||
int "The length of the cache key in bytes"
|
||||
default 8
|
||||
|
||||
config NANOCOAP_CACHE_RESPONSE_SIZE
|
||||
int "Size of the buffer to store responses in the cache"
|
||||
default 128
|
||||
|
||||
endif # KCONFIG_USEMODULE_NANOCOAP_CACHE
|
||||
|
||||
endif # KCONFIG_USEMODULE_NANOCOAP
|
||||
|
321
sys/net/application_layer/nanocoap/cache.c
Normal file
321
sys/net/application_layer/nanocoap/cache.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (C) 2020 HAW Hamburg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_nanocoap_cache
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of common functions for the nanocoap-cache
|
||||
*
|
||||
* @author Cenk Gündoğan <cenk.guendogan@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "net/nanocoap/cache.h"
|
||||
#include "hashes/sha256.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
static int _cache_replacement_lru(void);
|
||||
static int _cache_update_lru(clist_node_t *node);
|
||||
|
||||
static clist_node_t _cache_list_head = { NULL };
|
||||
static clist_node_t _empty_list_head = { NULL };
|
||||
|
||||
static nanocoap_cache_entry_t _cache_entries[CONFIG_NANOCOAP_CACHE_ENTRIES];
|
||||
|
||||
static const nanocoap_cache_replacement_strategy_t _replacement_strategy = _cache_replacement_lru;
|
||||
static const nanocoap_cache_update_strategy_t _update_strategy = _cache_update_lru;
|
||||
|
||||
static int _cache_replacement_lru(void)
|
||||
{
|
||||
clist_node_t *lru_node = clist_lpeek(&_cache_list_head);
|
||||
|
||||
/* no element in the list */
|
||||
if (!lru_node) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nanocoap_cache_entry_t *lru_ce = container_of(lru_node, nanocoap_cache_entry_t, node);
|
||||
return nanocoap_cache_del(lru_ce);
|
||||
}
|
||||
|
||||
static int _cache_update_lru(clist_node_t *node)
|
||||
{
|
||||
if (clist_remove(&_cache_list_head, node)) {
|
||||
/* Move an accessed node to the end of the list. Least
|
||||
* recently used nodes are at the beginning of this list */
|
||||
clist_rpush(&_cache_list_head, node);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void nanocoap_cache_init(void)
|
||||
{
|
||||
_cache_list_head.next = NULL;
|
||||
_empty_list_head.next = NULL;
|
||||
memset(_cache_entries, 0, sizeof(_cache_entries));
|
||||
/* construct list of empty entries */
|
||||
for (unsigned i = 0; i < CONFIG_NANOCOAP_CACHE_ENTRIES; i++) {
|
||||
clist_rpush(&_empty_list_head, &_cache_entries[i].node);
|
||||
}
|
||||
}
|
||||
|
||||
size_t nanocoap_cache_used_count(void)
|
||||
{
|
||||
return clist_count(&_cache_list_head);
|
||||
}
|
||||
|
||||
size_t nanocoap_cache_free_count(void)
|
||||
{
|
||||
return clist_count(&_empty_list_head);
|
||||
}
|
||||
|
||||
void nanocoap_cache_key_generate(const coap_pkt_t *req, uint8_t *cache_key)
|
||||
{
|
||||
sha256_context_t ctx;
|
||||
sha256_init(&ctx);
|
||||
|
||||
coap_optpos_t opt = {0, 0};
|
||||
uint8_t *value;
|
||||
|
||||
for (unsigned i = 0; i < req->options_len; i++) {
|
||||
ssize_t optlen = coap_opt_get_next(req, &opt, &value, !i);
|
||||
if (optlen >= 0) {
|
||||
/* skip NoCacheKey,
|
||||
see https://tools.ietf.org/html/rfc7252#section-5.4.6 */
|
||||
if ((opt.opt_num & 0x1E) == 0x1C) {
|
||||
continue;
|
||||
}
|
||||
sha256_update(&ctx, value, optlen);
|
||||
}
|
||||
}
|
||||
switch (req->hdr->code) {
|
||||
case COAP_METHOD_FETCH:
|
||||
sha256_update(&ctx, req->payload, req->payload_len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sha256_final(&ctx, cache_key);
|
||||
}
|
||||
|
||||
ssize_t nanocoap_cache_key_compare(uint8_t *cache_key1, uint8_t *cache_key2)
|
||||
{
|
||||
return memcmp(cache_key1, cache_key2, CONFIG_NANOCOAP_CACHE_KEY_LENGTH);
|
||||
}
|
||||
|
||||
static int _compare_cache_keys(clist_node_t *ce, void *arg)
|
||||
{
|
||||
nanocoap_cache_entry_t *iterator = container_of(ce, nanocoap_cache_entry_t, node);
|
||||
uint8_t *cache_key = (uint8_t *) arg;
|
||||
|
||||
if (!memcmp(iterator->cache_key, cache_key, CONFIG_NANOCOAP_CACHE_KEY_LENGTH)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static clist_node_t *_nanocoap_cache_foreach(const uint8_t *key)
|
||||
{
|
||||
return clist_foreach(&_cache_list_head, _compare_cache_keys, (uint8_t *)key);
|
||||
}
|
||||
|
||||
nanocoap_cache_entry_t *nanocoap_cache_key_lookup(const uint8_t *key)
|
||||
{
|
||||
clist_node_t *node = _nanocoap_cache_foreach(key);
|
||||
|
||||
if (node) {
|
||||
_update_strategy(node);
|
||||
}
|
||||
|
||||
return container_of(node, nanocoap_cache_entry_t, node);
|
||||
}
|
||||
|
||||
nanocoap_cache_entry_t *nanocoap_cache_request_lookup(const coap_pkt_t *req)
|
||||
{
|
||||
uint8_t cache_key[SHA256_DIGEST_LENGTH];
|
||||
nanocoap_cache_entry_t *ce;
|
||||
|
||||
/* generate cache key */
|
||||
nanocoap_cache_key_generate(req, cache_key);
|
||||
|
||||
/* check if cache key already exists */
|
||||
ce = nanocoap_cache_key_lookup(cache_key);
|
||||
|
||||
return ce;
|
||||
}
|
||||
|
||||
int nanocoap_cache_process(const uint8_t *cache_key, unsigned request_method,
|
||||
const coap_pkt_t *resp, size_t resp_len)
|
||||
{
|
||||
nanocoap_cache_entry_t *ce;
|
||||
ce = nanocoap_cache_key_lookup(cache_key);
|
||||
|
||||
/* This response is not cacheable. */
|
||||
if (resp->hdr->code == COAP_CODE_CREATED) {
|
||||
/* NO OP */
|
||||
}
|
||||
/* This response is not cacheable. However, a cache MUST mark any
|
||||
stored response for the deleted resource as not fresh.
|
||||
*/
|
||||
else if (resp->hdr->code == COAP_CODE_DELETED) {
|
||||
if (ce) {
|
||||
/* set max_age to now(), so that the cache is considered
|
||||
* stale immdiately */
|
||||
ce->max_age = ztimer_now(ZTIMER_SEC);
|
||||
}
|
||||
}
|
||||
/* When a cache that recognizes and processes the ETag response
|
||||
option receives a 2.03 (Valid) response, it MUST update the
|
||||
stored response with the value of the Max-Age Option included
|
||||
in the response (explicitly, or implicitly as a default value;
|
||||
see also Section 5.6.2). For each type of Safe-to-Forward
|
||||
option present in the response, the (possibly empty) set of
|
||||
options of this type that are present in the stored response
|
||||
MUST be replaced with the set of options of this type in the
|
||||
response received. (Unsafe options may trigger similar
|
||||
option-specific processing as defined by the option.)
|
||||
*/
|
||||
else if (resp->hdr->code == COAP_CODE_VALID) {
|
||||
if (ce) {
|
||||
/* refresh max_age() */
|
||||
uint32_t max_age = 60;
|
||||
coap_opt_get_uint((coap_pkt_t *)resp, COAP_OPT_MAX_AGE, &max_age);
|
||||
ce->max_age = ztimer_now(ZTIMER_SEC) + max_age;
|
||||
}
|
||||
/* TODO: handle the copying of the new options (if changed) */
|
||||
}
|
||||
/* This response is not cacheable. However, a cache MUST mark any
|
||||
stored response for the changed resource as not fresh.
|
||||
*/
|
||||
else if (resp->hdr->code == COAP_CODE_CHANGED) {
|
||||
if (ce) {
|
||||
/* set max_age to now(), so that the cache is considered
|
||||
* stale immdiately */
|
||||
ce->max_age = ztimer_now(ZTIMER_SEC);
|
||||
}
|
||||
}
|
||||
/* This response is cacheable: Caches can use the Max-Age Option
|
||||
to determine freshness and (if present) the
|
||||
ETag Option for validation.
|
||||
*/
|
||||
else if (resp->hdr->code == COAP_CODE_CONTENT) {
|
||||
if (NULL == nanocoap_cache_add_by_key(cache_key, request_method,
|
||||
resp, resp_len)) {
|
||||
/* no space left in the cache? */
|
||||
return -1;
|
||||
}
|
||||
/* TODO: ETAG handling */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static nanocoap_cache_entry_t *_nanocoap_cache_pop(void)
|
||||
{
|
||||
clist_node_t *node;
|
||||
|
||||
node = clist_lpop(&_empty_list_head);
|
||||
if (node != NULL) {
|
||||
return container_of(node, nanocoap_cache_entry_t, node);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nanocoap_cache_entry_t *nanocoap_cache_add_by_key(const uint8_t *cache_key,
|
||||
unsigned request_method,
|
||||
const coap_pkt_t *resp,
|
||||
size_t resp_len)
|
||||
{
|
||||
nanocoap_cache_entry_t *ce = nanocoap_cache_key_lookup(cache_key);
|
||||
bool add_to_cache = false;
|
||||
|
||||
if (resp_len > CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE) {
|
||||
DEBUG("nanocoap_cache: response too large to cache (%lu > %d)\n",
|
||||
(long unsigned)resp_len, CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!ce) {
|
||||
/* did not find .. get an empty cache container */
|
||||
ce = _nanocoap_cache_pop();
|
||||
add_to_cache = true;
|
||||
}
|
||||
|
||||
/* no space left */
|
||||
if (!ce) {
|
||||
/* could not remove any entry */
|
||||
if (_replacement_strategy()) {
|
||||
return NULL;
|
||||
}
|
||||
/* could remove an entry */
|
||||
ce = _nanocoap_cache_pop();
|
||||
add_to_cache = true;
|
||||
if (!ce) {
|
||||
/* still no free space ? stop trying now */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ce->cache_key, cache_key, CONFIG_NANOCOAP_CACHE_KEY_LENGTH);
|
||||
memcpy(&ce->response_pkt, resp, sizeof(coap_pkt_t));
|
||||
memcpy(&ce->response_buf, resp->hdr, resp_len);
|
||||
ce->response_pkt.hdr = (coap_hdr_t *) ce->response_buf;
|
||||
ce->response_pkt.payload = ce->response_buf + (resp->payload - ((uint8_t *)resp->hdr));
|
||||
ce->response_len = resp_len;
|
||||
ce->request_method = request_method;
|
||||
|
||||
/* default value is 60 seconds, if MAX_AGE not present */
|
||||
uint32_t max_age = 60;
|
||||
coap_opt_get_uint((coap_pkt_t *)resp, COAP_OPT_MAX_AGE, &max_age);
|
||||
ce->max_age = ztimer_now(ZTIMER_SEC) + max_age;
|
||||
|
||||
if (add_to_cache) {
|
||||
clist_rpush(&_cache_list_head, &ce->node);
|
||||
}
|
||||
|
||||
return ce;
|
||||
}
|
||||
|
||||
nanocoap_cache_entry_t *nanocoap_cache_add_by_req(const coap_pkt_t *req,
|
||||
const coap_pkt_t *resp,
|
||||
size_t resp_len)
|
||||
{
|
||||
uint8_t cache_key[SHA256_DIGEST_LENGTH];
|
||||
|
||||
/* generate cache key */
|
||||
nanocoap_cache_key_generate(req, cache_key);
|
||||
|
||||
return nanocoap_cache_add_by_key(cache_key,
|
||||
coap_get_code((coap_pkt_t *)req),
|
||||
resp,
|
||||
resp_len);
|
||||
}
|
||||
|
||||
int nanocoap_cache_del(const nanocoap_cache_entry_t *ce)
|
||||
{
|
||||
clist_node_t *entry = clist_find(&_cache_list_head, &ce->node);
|
||||
|
||||
if (entry) {
|
||||
clist_remove(&_cache_list_head, entry);
|
||||
memset(entry, 0, sizeof(nanocoap_cache_entry_t));
|
||||
clist_rpush(&_empty_list_head, entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
1
tests/unittests/tests-nanocoap_cache/Makefile
Normal file
1
tests/unittests/tests-nanocoap_cache/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
1
tests/unittests/tests-nanocoap_cache/Makefile.include
Normal file
1
tests/unittests/tests-nanocoap_cache/Makefile.include
Normal file
@ -0,0 +1 @@
|
||||
USEMODULE += nanocoap_cache
|
275
tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.c
Normal file
275
tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Cenk Gündoğan
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "embUnit.h"
|
||||
|
||||
#include "net/nanocoap/cache.h"
|
||||
#include "ztimer.h"
|
||||
#include "hashes/sha256.h"
|
||||
|
||||
#include "unittests-constants.h"
|
||||
#include "tests-nanocoap_cache.h"
|
||||
#define _BUF_SIZE (128U)
|
||||
|
||||
static void test_nanocoap_cache__cachekey(void)
|
||||
{
|
||||
uint8_t digest1[SHA256_DIGEST_LENGTH];
|
||||
uint8_t digest2[SHA256_DIGEST_LENGTH];
|
||||
uint8_t buf1[_BUF_SIZE];
|
||||
uint8_t buf2[_BUF_SIZE];
|
||||
coap_pkt_t pkt1;
|
||||
coap_pkt_t pkt2;
|
||||
uint16_t msgid = 0xABCD;
|
||||
uint8_t token[2] = {0xDA, 0xEC};
|
||||
char path[] = "/time";
|
||||
char path2[] = "/time2";
|
||||
size_t len;
|
||||
|
||||
/* 1. packet */
|
||||
len = coap_build_hdr((coap_hdr_t *)&buf1[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_METHOD_GET, msgid);
|
||||
coap_pkt_init(&pkt1, &buf1[0], sizeof(buf1), len);
|
||||
coap_opt_add_string(&pkt1, COAP_OPT_URI_PATH, &path[0], '/');
|
||||
coap_opt_finish(&pkt1, COAP_OPT_FINISH_NONE);
|
||||
|
||||
/* 2. packet */
|
||||
len = coap_build_hdr((coap_hdr_t *)&buf2[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_METHOD_GET, msgid);
|
||||
coap_pkt_init(&pkt2, &buf2[0], sizeof(buf2), len);
|
||||
coap_opt_add_string(&pkt2, COAP_OPT_URI_PATH, &path[0], '/');
|
||||
coap_opt_finish(&pkt2, COAP_OPT_FINISH_NONE);
|
||||
|
||||
nanocoap_cache_key_generate((const coap_pkt_t *) &pkt1, digest1);
|
||||
nanocoap_cache_key_generate((const coap_pkt_t *) &pkt2, digest2);
|
||||
|
||||
/* compare 1. and 2. packet */
|
||||
TEST_ASSERT_EQUAL_INT(0, nanocoap_cache_key_compare(digest1, digest2));
|
||||
|
||||
/* 3. packet */
|
||||
len = coap_build_hdr((coap_hdr_t *)&buf2[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_METHOD_GET, msgid);
|
||||
coap_pkt_init(&pkt2, &buf2[0], sizeof(buf2), len);
|
||||
coap_opt_add_string(&pkt2, COAP_OPT_URI_PATH, &path2[0], '/');
|
||||
coap_opt_finish(&pkt2, COAP_OPT_FINISH_NONE);
|
||||
|
||||
nanocoap_cache_key_generate((const coap_pkt_t *) &pkt2, digest2);
|
||||
|
||||
/* compare 1. and 3. packet */
|
||||
TEST_ASSERT_EQUAL_INT(-1, nanocoap_cache_key_compare(digest1, digest2));
|
||||
/* compare 3. and 1. packet */
|
||||
TEST_ASSERT_EQUAL_INT(1, nanocoap_cache_key_compare(digest2, digest1));
|
||||
}
|
||||
static void test_nanocoap_cache__add(void)
|
||||
{
|
||||
uint8_t buf[_BUF_SIZE];
|
||||
uint8_t rbuf[_BUF_SIZE];
|
||||
coap_pkt_t req, resp;
|
||||
|
||||
uint16_t msgid = 0xABCD;
|
||||
uint8_t token[2] = {0xDA, 0xEC};
|
||||
char path[16];
|
||||
nanocoap_cache_entry_t *c = NULL;
|
||||
size_t len;
|
||||
uint8_t temp_cache_key[CONFIG_NANOCOAP_CACHE_KEY_LENGTH];
|
||||
|
||||
/* initialize the nanocoap cache */
|
||||
nanocoap_cache_init();
|
||||
|
||||
/* add more entries to test LRU replacement */
|
||||
for (unsigned i = 0; i < CONFIG_NANOCOAP_CACHE_ENTRIES + 4; i++) {
|
||||
if (i < CONFIG_NANOCOAP_CACHE_ENTRIES) {
|
||||
TEST_ASSERT_EQUAL_INT(CONFIG_NANOCOAP_CACHE_ENTRIES - i,
|
||||
nanocoap_cache_free_count());
|
||||
TEST_ASSERT_EQUAL_INT(i, nanocoap_cache_used_count());
|
||||
}
|
||||
else {
|
||||
TEST_ASSERT_EQUAL_INT(0, nanocoap_cache_free_count());
|
||||
TEST_ASSERT_EQUAL_INT(CONFIG_NANOCOAP_CACHE_ENTRIES,
|
||||
nanocoap_cache_used_count());
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/path_%u", i);
|
||||
|
||||
/* request */
|
||||
len = coap_build_hdr((coap_hdr_t *)&buf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_METHOD_GET, msgid);
|
||||
coap_pkt_init(&req, &buf[0], sizeof(buf), len);
|
||||
coap_opt_add_string(&req, COAP_OPT_URI_PATH, &path[0], '/');
|
||||
coap_opt_finish(&req, COAP_OPT_FINISH_NONE);
|
||||
|
||||
/* response */
|
||||
len = coap_build_hdr((coap_hdr_t *)&rbuf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_CODE_205, msgid);
|
||||
coap_pkt_init(&resp, &rbuf[0], sizeof(rbuf), len);
|
||||
coap_opt_finish(&resp, COAP_OPT_FINISH_NONE);
|
||||
|
||||
/* add a fake response buffer with fake response length */
|
||||
c = nanocoap_cache_add_by_req((const coap_pkt_t *)&req,
|
||||
(const coap_pkt_t *)&req,
|
||||
CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
|
||||
/* keep a pointer to the first cache entry for testing the LRU
|
||||
replacement */
|
||||
if (i == 0) {
|
||||
memcpy(temp_cache_key, c->cache_key,
|
||||
CONFIG_NANOCOAP_CACHE_KEY_LENGTH);
|
||||
}
|
||||
|
||||
if (i == CONFIG_NANOCOAP_CACHE_ENTRIES-1) {
|
||||
/* lookup the first entry to update its list position */
|
||||
nanocoap_cache_key_lookup(temp_cache_key);
|
||||
}
|
||||
}
|
||||
|
||||
/* the first entry should NOT be cached out, because we updated
|
||||
* its list position */
|
||||
c = nanocoap_cache_key_lookup(temp_cache_key);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
}
|
||||
|
||||
static void test_nanocoap_cache__del(void)
|
||||
{
|
||||
uint8_t buf[_BUF_SIZE];
|
||||
uint8_t rbuf[_BUF_SIZE];
|
||||
coap_pkt_t req, resp;
|
||||
|
||||
uint16_t msgid = 0xABCD;
|
||||
uint8_t token[2] = {0xDA, 0xEC};
|
||||
char *path = "/path";
|
||||
nanocoap_cache_entry_t *c = NULL;
|
||||
size_t len;
|
||||
int res;
|
||||
|
||||
/* initialize the nanocoap cache */
|
||||
nanocoap_cache_init();
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(CONFIG_NANOCOAP_CACHE_ENTRIES,
|
||||
nanocoap_cache_free_count());
|
||||
TEST_ASSERT_EQUAL_INT(0, nanocoap_cache_used_count());
|
||||
|
||||
/* request */
|
||||
len = coap_build_hdr((coap_hdr_t *)&buf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_METHOD_GET, msgid);
|
||||
coap_pkt_init(&req, &buf[0], sizeof(buf), len);
|
||||
coap_opt_add_string(&req, COAP_OPT_URI_PATH, &path[0], '/');
|
||||
coap_opt_finish(&req, COAP_OPT_FINISH_NONE);
|
||||
|
||||
/* response */
|
||||
len = coap_build_hdr((coap_hdr_t *)&rbuf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_CODE_205, msgid);
|
||||
coap_pkt_init(&resp, &rbuf[0], sizeof(rbuf), len);
|
||||
coap_opt_finish(&resp, COAP_OPT_FINISH_NONE);
|
||||
|
||||
c = nanocoap_cache_add_by_req((const coap_pkt_t *)&req,
|
||||
(const coap_pkt_t *)&resp,
|
||||
CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(CONFIG_NANOCOAP_CACHE_ENTRIES - 1,
|
||||
nanocoap_cache_free_count());
|
||||
TEST_ASSERT_EQUAL_INT(1, nanocoap_cache_used_count());
|
||||
|
||||
/* delete previously added cache entry */
|
||||
res = nanocoap_cache_del(c);
|
||||
TEST_ASSERT_EQUAL_INT(0, res);
|
||||
TEST_ASSERT_EQUAL_INT(CONFIG_NANOCOAP_CACHE_ENTRIES,
|
||||
nanocoap_cache_free_count());
|
||||
TEST_ASSERT_EQUAL_INT(0, nanocoap_cache_used_count());
|
||||
}
|
||||
|
||||
static void test_nanocoap_cache__max_age(void)
|
||||
{
|
||||
uint8_t buf[_BUF_SIZE];
|
||||
uint8_t rbuf[_BUF_SIZE];
|
||||
coap_pkt_t req, resp;
|
||||
|
||||
uint16_t msgid = 0xABCD;
|
||||
uint8_t token[2] = {0xDA, 0xEC};
|
||||
char *path = "/path";
|
||||
nanocoap_cache_entry_t *c = NULL;
|
||||
size_t len;
|
||||
uint32_t now;
|
||||
|
||||
/* initialize the nanocoap cache */
|
||||
nanocoap_cache_init();
|
||||
|
||||
/* request */
|
||||
len = coap_build_hdr((coap_hdr_t *)&buf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_METHOD_GET, msgid);
|
||||
coap_pkt_init(&req, &buf[0], sizeof(buf), len);
|
||||
coap_opt_add_string(&req, COAP_OPT_URI_PATH, &path[0], '/');
|
||||
coap_opt_finish(&req, COAP_OPT_FINISH_NONE);
|
||||
|
||||
/* response with max-age 30 sec */
|
||||
len = coap_build_hdr((coap_hdr_t *)&rbuf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_CODE_205, msgid);
|
||||
coap_pkt_init(&resp, &rbuf[0], sizeof(rbuf), len);
|
||||
coap_opt_add_uint(&resp, COAP_OPT_MAX_AGE, 30);
|
||||
coap_opt_finish(&resp, COAP_OPT_FINISH_NONE);
|
||||
|
||||
c = nanocoap_cache_add_by_req((const coap_pkt_t *)&req,
|
||||
(const coap_pkt_t *)&resp,
|
||||
CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE);
|
||||
|
||||
/* the absolute time of max-age should be at approx. now + 30 sec
|
||||
(1 sec buffer) */
|
||||
now = ztimer_now(ZTIMER_SEC);
|
||||
TEST_ASSERT(c->max_age < (now + 31));
|
||||
|
||||
/* delete previously added cache entry */
|
||||
nanocoap_cache_del(c);
|
||||
|
||||
/* response with max-age 60 sec (default, if option is missing) */
|
||||
len = coap_build_hdr((coap_hdr_t *)&rbuf[0], COAP_TYPE_NON,
|
||||
&token[0], 2, COAP_CODE_205, msgid);
|
||||
coap_pkt_init(&resp, &rbuf[0], sizeof(rbuf), len);
|
||||
coap_opt_finish(&resp, COAP_OPT_FINISH_NONE);
|
||||
|
||||
now = ztimer_now(ZTIMER_SEC);
|
||||
|
||||
c = nanocoap_cache_add_by_req((const coap_pkt_t *)&req,
|
||||
(const coap_pkt_t *)&resp,
|
||||
CONFIG_NANOCOAP_CACHE_RESPONSE_SIZE);
|
||||
|
||||
/* the absolute time of max-age should be at approx. now + 60 sec
|
||||
(1 sec buffer) */
|
||||
now = ztimer_now(ZTIMER_SEC);
|
||||
TEST_ASSERT(c->max_age < (now + 61));
|
||||
}
|
||||
|
||||
Test *tests_nanocoap_cache_tests(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_nanocoap_cache__add),
|
||||
new_TestFixture(test_nanocoap_cache__del),
|
||||
new_TestFixture(test_nanocoap_cache__cachekey),
|
||||
new_TestFixture(test_nanocoap_cache__max_age),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(nanocoap_cache_entry_tests, NULL, NULL, fixtures);
|
||||
|
||||
return (Test *)&nanocoap_cache_entry_tests;
|
||||
}
|
||||
|
||||
void tests_nanocoap_cache(void)
|
||||
{
|
||||
TESTS_RUN(tests_nanocoap_cache_tests());
|
||||
}
|
||||
/** @} */
|
37
tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.h
Normal file
37
tests/unittests/tests-nanocoap_cache/tests-nanocoap_cache.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2020 HAW Hamburg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup unittests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unit tests for the nanocoap-cache module
|
||||
*
|
||||
* @author Cenk Gündoğan <cenk.guendogan@haw-hamburg.de>
|
||||
*/
|
||||
#ifndef TESTS_NANOCOAP_CACHE_H
|
||||
#define TESTS_NANOCOAP_CACHE_H
|
||||
|
||||
#include "embUnit.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The entry point of this test suite.
|
||||
*/
|
||||
void tests_nanocoap_cache(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TESTS_NANOCOAP_CACHE_H */
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user