mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 22:43:50 +01:00
gcoap: integrate nanocoap cache
This commit is contained in:
parent
8750605d26
commit
c5eb53095d
@ -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) {
|
||||
|
||||
@ -27,34 +27,39 @@
|
||||
#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 nanocoap_cache_replacement_strategy_t _replacement_strategy = NULL;
|
||||
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)
|
||||
{
|
||||
nanocoap_cache_entry_t *least = NULL;
|
||||
clist_node_t *it = _cache_list_head.next;
|
||||
clist_node_t *lru_node = clist_lpeek(&_cache_list_head);
|
||||
|
||||
/* no element in the list */
|
||||
if (!it) {
|
||||
if (!lru_node) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
least = container_of(it, nanocoap_cache_entry_t, node);
|
||||
nanocoap_cache_entry_t *lru_ce = container_of(lru_node, nanocoap_cache_entry_t, node);
|
||||
return nanocoap_cache_del(lru_ce);
|
||||
}
|
||||
|
||||
do {
|
||||
it = it->next;
|
||||
nanocoap_cache_entry_t *nit = container_of(it, nanocoap_cache_entry_t, node);
|
||||
if (least->access_time > nit->access_time) {
|
||||
least = nit;
|
||||
}
|
||||
} while (it != _cache_list_head.next);
|
||||
|
||||
return nanocoap_cache_del(least);
|
||||
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)
|
||||
@ -66,9 +71,6 @@ void nanocoap_cache_init(void)
|
||||
for (unsigned i = 0; i < CONFIG_NANOCOAP_CACHE_ENTRIES; i++) {
|
||||
clist_rpush(&_empty_list_head, &_cache_entries[i].node);
|
||||
}
|
||||
|
||||
/* hardcode cache replacement strategy for now */
|
||||
_replacement_strategy = _cache_replacement_lru;
|
||||
}
|
||||
|
||||
size_t nanocoap_cache_used_count(void)
|
||||
@ -125,27 +127,21 @@ static int _compare_cache_keys(clist_node_t *ce, void *arg)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static nanocoap_cache_entry_t *_nanocoap_cache_foreach(const uint8_t *key)
|
||||
{
|
||||
clist_node_t *node = clist_foreach(&_cache_list_head, _compare_cache_keys, (uint8_t *)key);
|
||||
|
||||
if (node != NULL) {
|
||||
return container_of(node, nanocoap_cache_entry_t, node);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
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)
|
||||
{
|
||||
nanocoap_cache_entry_t *ce = _nanocoap_cache_foreach(key);
|
||||
clist_node_t *node = _nanocoap_cache_foreach(key);
|
||||
|
||||
if (ce) {
|
||||
ce->access_time = ztimer_now(ZTIMER_SEC);
|
||||
if (node) {
|
||||
_update_strategy(node);
|
||||
}
|
||||
|
||||
return ce;
|
||||
return container_of(node, nanocoap_cache_entry_t, node);
|
||||
}
|
||||
|
||||
nanocoap_cache_entry_t *nanocoap_cache_request_lookup(const coap_pkt_t *req)
|
||||
@ -217,11 +213,6 @@ int nanocoap_cache_process(const uint8_t *cache_key, unsigned request_method,
|
||||
ETag Option for validation.
|
||||
*/
|
||||
else if (resp->hdr->code == COAP_CODE_CONTENT) {
|
||||
uint32_t now = ztimer_now(ZTIMER_SEC);
|
||||
/* cache entry is stale */
|
||||
if (ce && (ce->max_age < now)) {
|
||||
nanocoap_cache_del(ce);
|
||||
}
|
||||
if (NULL == nanocoap_cache_add_by_key(cache_key, request_method,
|
||||
resp, resp_len)) {
|
||||
/* no space left in the cache? */
|
||||
@ -250,16 +241,20 @@ nanocoap_cache_entry_t *nanocoap_cache_add_by_key(const uint8_t *cache_key,
|
||||
const coap_pkt_t *resp,
|
||||
size_t resp_len)
|
||||
{
|
||||
nanocoap_cache_entry_t *ce;
|
||||
ce = nanocoap_cache_key_lookup(cache_key);
|
||||
nanocoap_cache_entry_t *ce = nanocoap_cache_key_lookup(cache_key);
|
||||
bool add_to_cache = false;
|
||||
|
||||
/* found an already existing cache entry */
|
||||
if (ce) {
|
||||
return ce;
|
||||
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;
|
||||
}
|
||||
|
||||
/* did not find .. get an empty cache container */
|
||||
ce = _nanocoap_cache_pop();
|
||||
if (!ce) {
|
||||
/* did not find .. get an empty cache container */
|
||||
ce = _nanocoap_cache_pop();
|
||||
add_to_cache = true;
|
||||
}
|
||||
|
||||
/* no space left */
|
||||
if (!ce) {
|
||||
@ -269,6 +264,7 @@ nanocoap_cache_entry_t *nanocoap_cache_add_by_key(const uint8_t *cache_key,
|
||||
}
|
||||
/* could remove an entry */
|
||||
ce = _nanocoap_cache_pop();
|
||||
add_to_cache = true;
|
||||
if (!ce) {
|
||||
/* still no free space ? stop trying now */
|
||||
return NULL;
|
||||
@ -281,7 +277,6 @@ nanocoap_cache_entry_t *nanocoap_cache_add_by_key(const uint8_t *cache_key,
|
||||
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->access_time = ztimer_now(ZTIMER_SEC);
|
||||
ce->request_method = request_method;
|
||||
|
||||
/* default value is 60 seconds, if MAX_AGE not present */
|
||||
@ -289,7 +284,9 @@ nanocoap_cache_entry_t *nanocoap_cache_add_by_key(const uint8_t *cache_key,
|
||||
coap_opt_get_uint((coap_pkt_t *)resp, COAP_OPT_MAX_AGE, &max_age);
|
||||
ce->max_age = ztimer_now(ZTIMER_SEC) + max_age;
|
||||
|
||||
clist_rpush(&_cache_list_head, &ce->node);
|
||||
if (add_to_cache) {
|
||||
clist_rpush(&_cache_list_head, &ce->node);
|
||||
}
|
||||
|
||||
return ce;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user