mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
gcoap_forward_proxy: only leave upstream validation for caching
Most of the caching operation was moved to the client code. Since the forward proxy is using that code for upstream messaging, interacting with the cache directly is not necessary anymore. The only cache-related thing necessary for the proxy is validating ETags from upstream. However, that can be done by just looking at the ETags from the upstream response (which may or may not have come from the cache).
This commit is contained in:
parent
728c7d6088
commit
70d0d7f624
@ -27,10 +27,10 @@
|
||||
|
||||
typedef struct {
|
||||
bool in_use;
|
||||
bool validating;
|
||||
uint8_t req_etag_len;
|
||||
sock_udp_ep_t ep;
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
uint8_t cache_key[CONFIG_NANOCOAP_CACHE_KEY_LENGTH];
|
||||
uint8_t req_etag[COAP_ETAG_LENGTH_MAX];
|
||||
#endif
|
||||
} client_ep_t;
|
||||
|
||||
@ -59,78 +59,6 @@ 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)
|
||||
{
|
||||
if ((pdu->hdr->code == COAP_METHOD_GET) || (pdu->hdr->code == COAP_METHOD_FETCH)) {
|
||||
uint8_t *req_etag;
|
||||
/* Searching for more ETags might become necessary in the future */
|
||||
ssize_t req_etag_len = coap_opt_get_opaque(pdu, COAP_OPT_ETAG, &req_etag);
|
||||
|
||||
if (req_etag_len > 0) {
|
||||
/* ETag found, validate from cache entry */
|
||||
uint8_t *cache_etag;
|
||||
ssize_t cache_etag_len = coap_opt_get_opaque(&ce->response_pkt, COAP_OPT_ETAG,
|
||||
&cache_etag);
|
||||
|
||||
if ((cache_etag_len == req_etag_len) &&
|
||||
(memcmp(req_etag, cache_etag, req_etag_len) == 0)) {
|
||||
gcoap_resp_init(pdu, buf, len, COAP_CODE_VALID);
|
||||
coap_opt_add_opaque(pdu, COAP_OPT_ETAG, req_etag, req_etag_len);
|
||||
return coap_get_total_hdr_len(pdu);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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,
|
||||
nanocoap_cache_entry_t **ce)
|
||||
{
|
||||
(void) cep;
|
||||
|
||||
uint8_t cache_key[SHA256_DIGEST_LENGTH];
|
||||
ztimer_now_t now = ztimer_now(ZTIMER_SEC);
|
||||
nanocoap_cache_key_generate(pdu, cache_key);
|
||||
*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)
|
||||
@ -141,7 +69,7 @@ static client_ep_t *_allocate_client_ep(sock_udp_ep_t *ep)
|
||||
cep++) {
|
||||
if (!cep->in_use) {
|
||||
cep->in_use = true;
|
||||
cep->validating = false;
|
||||
cep->req_etag_len = 0U;
|
||||
memcpy(&cep->ep, ep, sizeof(*ep));
|
||||
return cep;
|
||||
}
|
||||
@ -270,58 +198,41 @@ static void _forward_resp_handler(const gcoap_request_memo_t *memo,
|
||||
{
|
||||
(void) remote; /* this is the origin server */
|
||||
client_ep_t *cep = (client_ep_t *)memo->context;
|
||||
size_t buf_len = (pdu->payload - (uint8_t *)pdu->hdr) + pdu->payload_len;
|
||||
|
||||
|
||||
if (memo->state == GCOAP_MEMO_RESP) {
|
||||
if (!IS_USED(MODULE_NANOCOAP_CACHE) ||
|
||||
/* only forward 2.03 Valid, if client endpoint sent an ETag to validate cached content */
|
||||
(pdu->hdr->code != COAP_CODE_VALID) || cep->validating) {
|
||||
/* forward the response packet as-is to the client */
|
||||
gcoap_forward_proxy_dispatch((uint8_t *)pdu->hdr,
|
||||
(pdu->payload -
|
||||
(uint8_t *)pdu->hdr + pdu->payload_len),
|
||||
&cep->ep);
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
/* if response is a 2.03, but client endpoint did not send an ETag to validate content */
|
||||
if ((pdu->hdr->code == COAP_CODE_VALID) && !cep->validating) {
|
||||
nanocoap_cache_entry_t *ce = NULL;
|
||||
/* req_tag in cep is pre-processor guarded so we need to as well */
|
||||
if (cep->req_etag_len > 0) {
|
||||
uint8_t *resp_etag;
|
||||
|
||||
if ((ce = nanocoap_cache_key_lookup(cep->cache_key))) {
|
||||
/* update max_age from response and send cached response */
|
||||
uint32_t max_age = 60;
|
||||
|
||||
coap_opt_get_uint(pdu, COAP_OPT_MAX_AGE, &max_age);
|
||||
ce->max_age = ztimer_now(ZTIMER_SEC) + max_age;
|
||||
/* 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);
|
||||
uint8_t *buf = (uint8_t *)pdu->hdr;
|
||||
size_t len = pdu->payload_len + header_len_req;
|
||||
gcoap_resp_init(pdu, buf, len, ce->response_pkt.hdr->code);
|
||||
unsigned opt_payload_len = ce->response_len - header_len_cached;
|
||||
|
||||
memcpy((buf + header_len_req),
|
||||
(ce->response_buf + header_len_cached),
|
||||
opt_payload_len);
|
||||
gcoap_forward_proxy_dispatch(buf, header_len_req + opt_payload_len, &cep->ep);
|
||||
}
|
||||
else {
|
||||
/* cache entry to be validated cached out while trying to validate */
|
||||
/* TODO: re-request real response (without ETag) */
|
||||
/* check if we can just send 2.03 Valid instead */
|
||||
if ((cep->req_etag_len == coap_opt_get_opaque(pdu, COAP_OPT_ETAG, &resp_etag)) &&
|
||||
(memcmp(cep->req_etag, resp_etag, cep->req_etag_len) == 0)) {
|
||||
gcoap_resp_init(pdu, (uint8_t *)pdu->hdr, buf_len, COAP_CODE_VALID);
|
||||
coap_opt_add_opaque(pdu, COAP_OPT_ETAG, cep->req_etag, cep->req_etag_len);
|
||||
coap_opt_finish(pdu, COAP_OPT_FINISH_NONE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
coap_pkt_t req;
|
||||
|
||||
req.hdr = gcoap_request_memo_get_hdr(memo);
|
||||
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);
|
||||
}
|
||||
/* we do not need to check if valid came from upstream as this is already automatically
|
||||
* converted by the client-side to the cached response */
|
||||
#endif
|
||||
/* else forward the response packet as-is to the client */
|
||||
}
|
||||
else if (memo->state == GCOAP_MEMO_RESP_TRUNC) {
|
||||
/* the response was truncated, so there should be enough space
|
||||
* to allocate an empty error message instead (with a potential Observe option) if not,
|
||||
* _listen_buf is _way_ too short ;-) */
|
||||
assert(buf_len >= (sizeof(*pdu->hdr) + 4U));
|
||||
gcoap_resp_init(pdu, (uint8_t *)pdu->hdr, buf_len, COAP_CODE_INTERNAL_SERVER_ERROR);
|
||||
coap_opt_finish(pdu, COAP_OPT_FINISH_NONE);
|
||||
}
|
||||
/* don't use buf_len here, in case the above `gcoap_resp_init`s changed `pdu` */
|
||||
gcoap_forward_proxy_dispatch((uint8_t *)pdu->hdr,
|
||||
(pdu->payload -
|
||||
(uint8_t *)pdu->hdr + pdu->payload_len),
|
||||
&cep->ep);
|
||||
_free_client_ep(cep);
|
||||
}
|
||||
|
||||
@ -348,38 +259,44 @@ static int _gcoap_forward_proxy_add_uri_path(coap_pkt_t *pkt,
|
||||
static int _gcoap_forward_proxy_copy_options(coap_pkt_t *pkt,
|
||||
coap_pkt_t *client_pkt,
|
||||
client_ep_t *cep,
|
||||
uri_parser_result_t *urip,
|
||||
nanocoap_cache_entry_t *ce)
|
||||
uri_parser_result_t *urip)
|
||||
{
|
||||
/* copy all options from client_pkt to pkt */
|
||||
coap_optpos_t opt = {0, 0};
|
||||
uint8_t *value;
|
||||
bool uri_path_added = false, etag_added = false;
|
||||
bool uri_path_added = false;
|
||||
bool etag_added = false;
|
||||
|
||||
for (int i = 0; i < client_pkt->options_len; i++) {
|
||||
ssize_t optlen = coap_opt_get_next(client_pkt, &opt, &value, !i);
|
||||
/* wrt to ETag: we always have at least the Proxy-URI option in the client_pkt, so
|
||||
* we should hit at least once (and its opt_num is also >= COAP_OPT_ETAG) */
|
||||
/* wrt to ETag option slack: we always have at least the Proxy-URI option in the client_pkt,
|
||||
* so we should hit at least once (and it's opt_num is also >= COAP_OPT_ETAG) */
|
||||
if (optlen >= 0) {
|
||||
/* Add ETag before any larger opt num, but skip old ETag if ETag in cache */
|
||||
if (!etag_added && (opt.opt_num >= COAP_OPT_ETAG)) {
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE) && ce) {
|
||||
uint8_t *etag;
|
||||
/* Searching for more ETags might become necessary in the future */
|
||||
ssize_t etag_len = coap_opt_get_opaque(&ce->response_pkt, COAP_OPT_ETAG, &etag);
|
||||
|
||||
if (etag_len > 0) {
|
||||
coap_opt_add_opaque(pkt, COAP_OPT_ETAG, etag, etag_len);
|
||||
}
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE) && !etag_added && (opt.opt_num >= COAP_OPT_ETAG)) {
|
||||
static const uint8_t tmp[COAP_ETAG_LENGTH_MAX] = { 0 };
|
||||
/* add slack to maybe add an ETag on stale cache hit later, as is done in gcoap_req_send()
|
||||
* (which we circumvented in _gcoap_forward_proxy_via_coap()) */
|
||||
if (coap_opt_add_opaque(pkt, COAP_OPT_ETAG, tmp, sizeof(tmp))) {
|
||||
etag_added = true;
|
||||
}
|
||||
}
|
||||
/* skip original ETag of request, otherwise we might accidentally fill the cache
|
||||
* with 2.03 Valid responses which would require additional handling */
|
||||
#if IS_USED(MODULE_NANOCOAP_CACHE)
|
||||
/* req_tag in cep is pre-processor guarded so we need to as well */
|
||||
if (opt.opt_num == COAP_OPT_ETAG) {
|
||||
cep->validating = true;
|
||||
if (cep->req_etag_len == 0) {
|
||||
/* TODO: what to do on multiple ETags? */
|
||||
cep->req_etag_len = (uint8_t)optlen;
|
||||
memcpy(cep->req_etag, value, optlen);
|
||||
}
|
||||
/* skip original ETag of request, otherwise we might accidentally fill the cache
|
||||
* with 2.03 Valid responses which would require additional handling.
|
||||
* For upstream validation, gcoap_req_send() will add an ETag, if the response
|
||||
* was in cache */
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
(void)cep;
|
||||
#endif
|
||||
/* add URI-PATH before any larger opt num */
|
||||
if (!uri_path_added && (opt.opt_num > COAP_OPT_URI_PATH)) {
|
||||
if (_gcoap_forward_proxy_add_uri_path(pkt, urip) == -EINVAL) {
|
||||
@ -410,8 +327,7 @@ static int _gcoap_forward_proxy_copy_options(coap_pkt_t *pkt,
|
||||
|
||||
static int _gcoap_forward_proxy_via_coap(coap_pkt_t *client_pkt,
|
||||
client_ep_t *client_ep,
|
||||
uri_parser_result_t *urip,
|
||||
nanocoap_cache_entry_t *ce)
|
||||
uri_parser_result_t *urip)
|
||||
{
|
||||
coap_pkt_t pkt;
|
||||
sock_udp_ep_t origin_server_ep;
|
||||
@ -447,7 +363,7 @@ static int _gcoap_forward_proxy_via_coap(coap_pkt_t *client_pkt,
|
||||
}
|
||||
|
||||
/* copy all options from client_pkt to pkt */
|
||||
len = _gcoap_forward_proxy_copy_options(&pkt, client_pkt, client_ep, urip, ce);
|
||||
len = _gcoap_forward_proxy_copy_options(&pkt, client_pkt, client_ep, urip);
|
||||
|
||||
if (len == -EINVAL) {
|
||||
return -EINVAL;
|
||||
@ -466,27 +382,11 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt,
|
||||
ssize_t optlen = 0;
|
||||
|
||||
client_ep_t *cep = _allocate_client_ep(client);
|
||||
nanocoap_cache_entry_t *ce = NULL;
|
||||
|
||||
if (!cep) {
|
||||
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,
|
||||
&ce);
|
||||
/* 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) {
|
||||
@ -505,7 +405,7 @@ int gcoap_forward_proxy_request_process(coap_pkt_t *pkt,
|
||||
|
||||
/* target is using CoAP */
|
||||
if (!strncmp("coap", urip.scheme, urip.scheme_len)) {
|
||||
int res = _gcoap_forward_proxy_via_coap(pkt, cep, &urip, ce);
|
||||
int res = _gcoap_forward_proxy_via_coap(pkt, cep, &urip);
|
||||
if (res < 0) {
|
||||
_free_client_ep(cep);
|
||||
return -EINVAL;
|
||||
|
@ -1337,14 +1337,13 @@ kernel_pid_t gcoap_init(void)
|
||||
/* randomize initial value */
|
||||
atomic_init(&_coap_state.next_message_id, (unsigned)random_uint32());
|
||||
|
||||
if (IS_USED(MODULE_NANOCOAP_CACHE)) {
|
||||
nanocoap_cache_init();
|
||||
}
|
||||
/* initialize the forward proxy operation, if compiled */
|
||||
if (IS_ACTIVE(MODULE_GCOAP_FORWARD_PROXY)) {
|
||||
gcoap_forward_proxy_init();
|
||||
}
|
||||
/* gcoap_forward_proxy_init() also initializes nanocoap_cache_init() */
|
||||
else if (IS_USED(MODULE_NANOCOAP_CACHE)) {
|
||||
nanocoap_cache_init();
|
||||
}
|
||||
|
||||
return _pid;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user