118 lines
3.3 KiB
C
118 lines
3.3 KiB
C
#include "sentry_ratelimiter.h"
|
|
#include "sentry_alloc.h"
|
|
#include "sentry_slice.h"
|
|
#include "sentry_utils.h"
|
|
|
|
#define MAX_RATE_LIMITS 4
|
|
|
|
struct sentry_rate_limiter_s {
|
|
uint64_t disabled_until[MAX_RATE_LIMITS];
|
|
};
|
|
|
|
sentry_rate_limiter_t *
|
|
sentry__rate_limiter_new(void)
|
|
{
|
|
sentry_rate_limiter_t *rl = SENTRY_MAKE(sentry_rate_limiter_t);
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_ANY] = 0;
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_ERROR] = 0;
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_SESSION] = 0;
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_TRANSACTION] = 0;
|
|
return rl;
|
|
}
|
|
|
|
bool
|
|
sentry__rate_limiter_update_from_header(
|
|
sentry_rate_limiter_t *rl, const char *sentry_header)
|
|
{
|
|
sentry_slice_t slice = sentry__slice_from_str(sentry_header);
|
|
|
|
while (true) {
|
|
slice = sentry__slice_trim(slice);
|
|
uint64_t retry_after = 0;
|
|
if (!sentry__slice_consume_uint64(&slice, &retry_after)) {
|
|
return false;
|
|
}
|
|
retry_after *= 1000;
|
|
retry_after += sentry__monotonic_time();
|
|
|
|
if (!sentry__slice_consume_if(&slice, ':')) {
|
|
return false;
|
|
}
|
|
|
|
sentry_slice_t categories = sentry__slice_split_at(slice, ':');
|
|
if (categories.len == 0) {
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_ANY] = retry_after;
|
|
}
|
|
|
|
while (categories.len > 0) {
|
|
sentry_slice_t category = sentry__slice_split_at(categories, ';');
|
|
if (sentry__slice_eqs(category, "error")) {
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_ERROR] = retry_after;
|
|
} else if (sentry__slice_eqs(category, "session")) {
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_SESSION] = retry_after;
|
|
} else if (sentry__slice_eqs(category, "transaction")) {
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_TRANSACTION]
|
|
= retry_after;
|
|
}
|
|
|
|
categories = sentry__slice_advance(categories, category.len);
|
|
sentry__slice_consume_if(&categories, ';');
|
|
}
|
|
|
|
size_t next = sentry__slice_find(slice, ',');
|
|
if (next != (size_t)-1) {
|
|
slice = sentry__slice_advance(slice, next + 1);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
sentry__rate_limiter_update_from_http_retry_after(
|
|
sentry_rate_limiter_t *rl, const char *retry_after)
|
|
{
|
|
sentry_slice_t slice = sentry__slice_from_str(retry_after);
|
|
uint64_t eta = 60;
|
|
sentry__slice_consume_uint64(&slice, &eta);
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_ANY]
|
|
= sentry__monotonic_time() + eta * 1000;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
sentry__rate_limiter_update_from_429(sentry_rate_limiter_t *rl)
|
|
{
|
|
rl->disabled_until[SENTRY_RL_CATEGORY_ANY]
|
|
= sentry__monotonic_time() + 60 * 1000;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
sentry__rate_limiter_is_disabled(const sentry_rate_limiter_t *rl, int category)
|
|
{
|
|
uint64_t now = sentry__monotonic_time();
|
|
return rl->disabled_until[SENTRY_RL_CATEGORY_ANY] > now
|
|
|| rl->disabled_until[category] > now;
|
|
}
|
|
|
|
void
|
|
sentry__rate_limiter_free(sentry_rate_limiter_t *rl)
|
|
{
|
|
if (!rl) {
|
|
return;
|
|
}
|
|
sentry_free(rl);
|
|
}
|
|
|
|
#ifdef SENTRY_UNITTEST
|
|
uint64_t
|
|
sentry__rate_limiter_get_disabled_until(
|
|
const sentry_rate_limiter_t *rl, int category)
|
|
{
|
|
return rl->disabled_until[category];
|
|
}
|
|
#endif
|