romele / git-trace public
62 commits · 1 branch · 1 tag · 1 contributor (top: Romele) · first commit 3d ago · last push 12m ago
src/ratelimit.c
#include "ratelimit.h" #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define RL_TABLE 256 struct entry { char ip[64]; int fails; time_t last; }; static struct entry tab[RL_TABLE]; static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; /* overridable for tests: GIT_TRACE_RL_MAXFAIL / GIT_TRACE_RL_WINDOW */ static int cfg(const char *name, int def) { const char *v = getenv(name); int n = v ? atoi(v) : 0; return n > 0 ? n : def; } static int window(void) { return cfg("GIT_TRACE_RL_WINDOW", 600); } static int maxfail(void) { return cfg("GIT_TRACE_RL_MAXFAIL", 20); } static struct entry *slot(const char *ip) { struct entry *free_e = NULL, *old = &tab[0]; time_t now = time(NULL); for (int i = 0; i < RL_TABLE; i++) { struct entry *e = &tab[i]; if (!e->ip[0]) { if (!free_e) free_e = e; continue; } if (now - e->last > window()) e->fails = 0; if (strcmp(e->ip, ip) == 0) return e; if (e->last < old->last) old = e; } struct entry *e = free_e ? free_e : old; /* evict LRU when full */ snprintf(e->ip, sizeof e->ip, "%s", ip); e->fails = 0; e->last = now; return e; } int rl_check(const char *ip) { pthread_mutex_lock(&mtx); struct entry *e = slot(ip); int out = 0; if (e->fails >= maxfail()) out = -1; else if (e->fails >= 3) { int d = 100 << (e->fails - 3); /* 200,400,800... ms */ out = d > 5000 ? 5000 : d; /* capped at 5 s */ } pthread_mutex_unlock(&mtx); return out; } void rl_fail(const char *ip) { pthread_mutex_lock(&mtx); struct entry *e = slot(ip); e->fails++; e->last = time(NULL); pthread_mutex_unlock(&mtx); } void rl_ok(const char *ip) { pthread_mutex_lock(&mtx); for (int i = 0; i < RL_TABLE; i++) if (strcmp(tab[i].ip, ip) == 0) tab[i].ip[0] = '\0'; pthread_mutex_unlock(&mtx); }