git-trace

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);
}