8#include <ascii-chat/network/rate_limit/memory.h>
9#include <ascii-chat/common.h>
10#include <ascii-chat/log/logging.h>
11#include <ascii-chat/util/time.h>
12#include <ascii-chat/platform/abstraction.h>
13#include <ascii-chat/uthash/uthash.h>
40static void make_key(
const char *ip_address, rate_event_type_t event_type,
char *key,
size_t key_size) {
41 safe_snprintf(key, key_size,
"%s:%d", ip_address, event_type);
47static void add_timestamp(
rate_event_t *event, uint64_t timestamp) {
50 event->timestamps[
event->count++] = timestamp;
53 event->timestamps[
event->head] = timestamp;
54 event->head = (
event->head + 1) % event->
capacity;
61static uint32_t count_events_in_window(
rate_event_t *event, uint64_t window_start_ms) {
64 for (
size_t i = 0; i <
event->count; i++) {
76static void cleanup_old_events(
rate_event_t *event, uint64_t cutoff_ns) {
80 for (
size_t read_idx = 0; read_idx <
event->count; read_idx++) {
81 if (event->
timestamps[read_idx] >= cutoff_ns) {
82 event->timestamps[write_idx++] =
event->timestamps[read_idx];
86 event->count = write_idx;
90static asciichat_error_t memory_check(
void *backend_data,
const char *ip_address, rate_event_type_t event_type,
91 const rate_limit_config_t *config,
bool *allowed) {
99 uint64_t window_start_ns = now_ns - ((uint64_t)limit->window_secs * NS_PER_SEC_INT);
103 make_key(ip_address, event_type, key,
sizeof(key));
105 mutex_lock(&backend->lock);
109 HASH_FIND_STR(backend->events, key, event);
111 uint32_t event_count = 0;
115 event_count = count_events_in_window(event, window_start_ns);
118 mutex_unlock(&backend->lock);
121 *allowed = (event_count < limit->max_events);
124 log_warn(
"Rate limit exceeded for %s (event: %s, count: %u/%u)", ip_address,
131static asciichat_error_t memory_record(
void *backend_data,
const char *ip_address, rate_event_type_t event_type) {
139 make_key(ip_address, event_type, key,
sizeof(key));
141 mutex_lock(&backend->
lock);
145 HASH_FIND_STR(backend->
events, key, event);
151 mutex_unlock(&backend->
lock);
152 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate rate event");
155 memset(event, 0,
sizeof(*event));
156 SAFE_STRNCPY(event->
key, key,
sizeof(event->
key));
159 event->capacity = 100;
160 event->timestamps = SAFE_MALLOC(
sizeof(uint64_t) * event->
capacity, uint64_t *);
163 mutex_unlock(&backend->
lock);
164 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate timestamp buffer");
167 HASH_ADD_STR(backend->
events, key, event);
171 add_timestamp(event, now_ns);
173 mutex_unlock(&backend->
lock);
179static asciichat_error_t memory_cleanup(
void *backend_data, uint32_t max_age_secs) {
183 if (max_age_secs == 0) {
184 max_age_secs = SEC_PER_HOUR;
189 uint64_t cutoff_ns = now_ns - ((uint64_t)max_age_secs * NS_PER_SEC_INT);
191 mutex_lock(&backend->
lock);
193 size_t total_removed = 0;
196 HASH_ITER(hh, backend->
events, event, tmp) {
197 size_t before_count =
event->
count;
200 cleanup_old_events(event, cutoff_ns);
202 total_removed += (before_count -
event->count);
205 if (event->
count == 0) {
206 HASH_DEL(backend->
events, event);
212 mutex_unlock(&backend->
lock);
214 if (total_removed > 0) {
215 log_debug(
"Cleaned up %zu old rate events", total_removed);
221static void memory_destroy(
void *backend_data) {
229 HASH_ITER(hh, backend->
events, event, tmp) {
230 HASH_DEL(backend->
events, event);
242 log_error(
"Failed to allocate memory backend");
246 memset(backend, 0,
sizeof(*backend));
249 log_error(
"Failed to initialize mutex");
254 log_debug(
"Memory rate limiter backend initialized");
259 .check = memory_check,
260 .record = memory_record,
261 .cleanup = memory_cleanup,
262 .destroy = memory_destroy,
void * memory_backend_create(void)
const rate_limiter_backend_ops_t memory_backend_ops
struct rate_event_s rate_event_t
Rate event record in memory.
const char * rate_limiter_event_type_string(rate_event_type_t event_type)
Get event type string for logging.
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
mutex_t lock
Mutex for thread safety.
rate_event_t * events
Hash table of rate events.
Rate event record in memory.
char key[256]
Hash key: "ip_address:event_type".
size_t count
Number of events in buffer.
UT_hash_handle hh
uthash handle
size_t capacity
Buffer capacity.
size_t head
Head index (oldest event)
uint64_t * timestamps
Array of timestamps (circular buffer)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
int mutex_init(mutex_t *mutex)
int mutex_destroy(mutex_t *mutex)
uint64_t time_get_realtime_ns(void)