upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/components/wisp_relay/rate_limiter.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/wisp_relay/rate_limiter.c')
-rw-r--r--components/wisp_relay/rate_limiter.c98
1 files changed, 98 insertions, 0 deletions
diff --git a/components/wisp_relay/rate_limiter.c b/components/wisp_relay/rate_limiter.c
new file mode 100644
index 0000000..7734e03
--- /dev/null
+++ b/components/wisp_relay/rate_limiter.c
@@ -0,0 +1,98 @@
1#include "rate_limiter.h"
2#include "esp_timer.h"
3#include "esp_log.h"
4#include <string.h>
5
6static const char *TAG = "rate_limiter";
7
8void rate_limiter_init(rate_limiter_t *rl, const rate_config_t *config)
9{
10 memset(rl, 0, sizeof(rate_limiter_t));
11 rl->lock = xSemaphoreCreateMutex();
12 if (config) {
13 memcpy(&rl->config, config, sizeof(rate_config_t));
14 } else {
15 rl->config.events_per_minute = 30;
16 rl->config.reqs_per_minute = 60;
17 }
18}
19
20void rate_limiter_destroy(rate_limiter_t *rl)
21{
22 if (!rl) return;
23 if (rl->lock) {
24 vSemaphoreDelete(rl->lock);
25 rl->lock = NULL;
26 }
27}
28
29static rate_bucket_t* get_bucket(rate_limiter_t *rl, int fd)
30{
31 for (int i = 0; i < RATE_LIMITER_MAX_BUCKETS; i++) {
32 if (rl->buckets[i].active && rl->buckets[i].fd == fd) {
33 return &rl->buckets[i];
34 }
35 }
36 for (int i = 0; i < RATE_LIMITER_MAX_BUCKETS; i++) {
37 if (!rl->buckets[i].active) {
38 rl->buckets[i].fd = fd;
39 rl->buckets[i].active = true;
40 rl->buckets[i].event_count = 0;
41 rl->buckets[i].req_count = 0;
42 rl->buckets[i].window_start = esp_timer_get_time() / 1000000;
43 return &rl->buckets[i];
44 }
45 }
46 return NULL;
47}
48
49bool rate_limiter_check(rate_limiter_t *rl, int fd, rate_type_t type)
50{
51 xSemaphoreTake(rl->lock, portMAX_DELAY);
52
53 rate_bucket_t *bucket = get_bucket(rl, fd);
54 if (!bucket) {
55 xSemaphoreGive(rl->lock);
56 return false;
57 }
58
59 uint32_t now = esp_timer_get_time() / 1000000;
60
61 if (now - bucket->window_start >= 60) {
62 bucket->event_count = 0;
63 bucket->req_count = 0;
64 bucket->window_start = now;
65 }
66
67 bool allowed = true;
68 if (type == RATE_TYPE_EVENT) {
69 if (bucket->event_count >= rl->config.events_per_minute) {
70 ESP_LOGW(TAG, "Rate limited: fd=%d events=%d", fd, bucket->event_count);
71 allowed = false;
72 } else {
73 bucket->event_count++;
74 }
75 } else {
76 if (bucket->req_count >= rl->config.reqs_per_minute) {
77 ESP_LOGW(TAG, "Rate limited: fd=%d reqs=%d", fd, bucket->req_count);
78 allowed = false;
79 } else {
80 bucket->req_count++;
81 }
82 }
83
84 xSemaphoreGive(rl->lock);
85 return allowed;
86}
87
88void rate_limiter_reset(rate_limiter_t *rl, int fd)
89{
90 xSemaphoreTake(rl->lock, portMAX_DELAY);
91 for (int i = 0; i < RATE_LIMITER_MAX_BUCKETS; i++) {
92 if (rl->buckets[i].active && rl->buckets[i].fd == fd) {
93 rl->buckets[i].active = false;
94 break;
95 }
96 }
97 xSemaphoreGive(rl->lock);
98}