1
0
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:
Cenk Gündoğan 2020-05-13 18:17:20 +02:00
parent 8750605d26
commit c5eb53095d
No known key found for this signature in database
GPG Key ID: A3DBC2F744D484D2
2 changed files with 130 additions and 50 deletions

View File

@ -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) {

View File

@ -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;
}