ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
rate_limit.h File Reference

🚦 Rate limiting API with pluggable backends More...

Go to the source code of this file.

Data Structures

struct  rate_limit_config_t
 One sliding-window rate-limit policy. More...
struct  rate_limiter_backend_ops_t
 Backend callbacks implementing rate-limit checks, recording, cleanup, and destruction. More...

Typedefs

typedef struct rate_limiter_s rate_limiter_t
 Opaque rate limiter handle.

Enumerations

enum  rate_event_type_t {
  RATE_EVENT_SESSION_CREATE = 0 , RATE_EVENT_SESSION_LOOKUP = 1 , RATE_EVENT_SESSION_JOIN = 2 , RATE_EVENT_CONNECTION = 3 ,
  RATE_EVENT_IMAGE_FRAME = 4 , RATE_EVENT_AUDIO = 5 , RATE_EVENT_PING = 6 , RATE_EVENT_CLIENT_JOIN = 7 ,
  RATE_EVENT_CONTROL = 8 , RATE_EVENT_HTTP_REQUEST = 9 , RATE_EVENT_MAX
}
 One sliding-window rate-limit policy. More...

Functions

rate_limiter_t * rate_limiter_create_memory (void)
 Create in-memory rate limiter.
rate_limiter_t * rate_limiter_create_sqlite (const char *db_path)
 Create SQLite-backed rate limiter.
void rate_limiter_set_sqlite_db (rate_limiter_t *limiter, void *db)
 Set SQLite database handle for rate limiter.
void rate_limiter_destroy (rate_limiter_t *limiter)
 Destroy rate limiter and free resources.
asciichat_error_t rate_limiter_check (rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
 Check if an event from an IP address should be rate limited.
asciichat_error_t rate_limiter_record (rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type)
 Record a rate limit event.
asciichat_error_t rate_limiter_prune (rate_limiter_t *limiter, uint32_t max_age_secs)
 Clean up old rate limit events.
const char * rate_limiter_event_type_string (rate_event_type_t event_type)
 Get event type string for logging/database storage.

Variables

const rate_limit_config_t DEFAULT_RATE_LIMITS [RATE_EVENT_MAX]
 Default rate limits for each event type.

Detailed Description

🚦 Rate limiting API with pluggable backends

Supports two backends:

  • Memory: Thread-safe in-memory tracking with uthash (for ascii-chat server)
  • SQLite: Persistent database tracking (for acds discovery server)

Example usage:

// Create in-memory rate limiter
// Or SQLite-backed limiter
// Check rate limit
bool allowed = false;
rate_limiter_check(limiter, "192.168.1.100", RATE_EVENT_SESSION_CREATE, NULL, &allowed);
if (allowed) {
rate_limiter_record(limiter, "192.168.1.100", RATE_EVENT_SESSION_CREATE);
// Process request
} else {
// Reject request
}
// Cleanup (call periodically)
rate_limiter_prune(limiter, 3600);
// Destroy
asciichat_error_t rate_limiter_record(rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type)
Record a rate limit event.
Definition rate_limit.c:162
void rate_limiter_destroy(rate_limiter_t *limiter)
Destroy rate limiter and free resources.
Definition rate_limit.c:126
rate_limiter_t * rate_limiter_create_memory(void)
Create in-memory rate limiter.
Definition rate_limit.c:83
rate_limiter_t * rate_limiter_create_sqlite(const char *db_path)
Create SQLite-backed rate limiter.
Definition rate_limit.c:100
asciichat_error_t rate_limiter_check(rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
Check if an event from an IP address should be rate limited.
Definition rate_limit.c:138
asciichat_error_t rate_limiter_prune(rate_limiter_t *limiter, uint32_t max_age_secs)
Clean up old rate limit events.
Definition rate_limit.c:178
@ RATE_EVENT_SESSION_CREATE
Session creation.
Definition rate_limit.h:50
struct rate_limiter_s rate_limiter_t
Opaque rate limiter handle.
Definition rate_limit.h:83

Definition in file rate_limit.h.

Typedef Documentation

◆ rate_limiter_t

Opaque rate limiter handle.

Backend implementation is hidden from users.

Definition at line 83 of file rate_limit.h.

Enumeration Type Documentation

◆ rate_event_type_t

One sliding-window rate-limit policy.

Backend callbacks implementing rate-limit checks, recording, cleanup, and destruction

Rate limit event types

Enumerator
RATE_EVENT_SESSION_CREATE 

Session creation.

RATE_EVENT_SESSION_LOOKUP 

Session lookup.

RATE_EVENT_SESSION_JOIN 

Session join.

RATE_EVENT_CONNECTION 

New connection.

RATE_EVENT_IMAGE_FRAME 

Image frame from client (PACKET_TYPE_IMAGE_FRAME).

RATE_EVENT_AUDIO 

Audio packet (PACKET_TYPE_AUDIO, PACKET_TYPE_AUDIO_BATCH).

RATE_EVENT_PING 

Ping/pong keepalive (PACKET_TYPE_PING, PACKET_TYPE_PONG).

RATE_EVENT_CLIENT_JOIN 

Client join request (PACKET_TYPE_CLIENT_JOIN).

RATE_EVENT_CONTROL 

Control packets (CAPABILITIES, STREAM_START/STOP, LEAVE).

RATE_EVENT_HTTP_REQUEST 

HTTP requests from a client identity.

RATE_EVENT_MAX 

Sentinel value.

Definition at line 48 of file rate_limit.h.

48 {
49 // ACDS discovery server events
53
54 // ascii-chat server events
58 RATE_EVENT_PING = 6,
62
rate_event_type_t
One sliding-window rate-limit policy.
Definition rate_limit.h:48
@ RATE_EVENT_SESSION_LOOKUP
Session lookup.
Definition rate_limit.h:51
@ RATE_EVENT_CONTROL
Control packets (CAPABILITIES, STREAM_START/STOP, LEAVE).
Definition rate_limit.h:60
@ RATE_EVENT_SESSION_JOIN
Session join.
Definition rate_limit.h:52
@ RATE_EVENT_CONNECTION
New connection.
Definition rate_limit.h:55
@ RATE_EVENT_PING
Ping/pong keepalive (PACKET_TYPE_PING, PACKET_TYPE_PONG).
Definition rate_limit.h:58
@ RATE_EVENT_AUDIO
Audio packet (PACKET_TYPE_AUDIO, PACKET_TYPE_AUDIO_BATCH).
Definition rate_limit.h:57
@ RATE_EVENT_MAX
Sentinel value.
Definition rate_limit.h:63
@ RATE_EVENT_IMAGE_FRAME
Image frame from client (PACKET_TYPE_IMAGE_FRAME).
Definition rate_limit.h:56
@ RATE_EVENT_CLIENT_JOIN
Client join request (PACKET_TYPE_CLIENT_JOIN).
Definition rate_limit.h:59
@ RATE_EVENT_HTTP_REQUEST
HTTP requests from a client identity.
Definition rate_limit.h:61

Function Documentation

◆ rate_limiter_check()

asciichat_error_t rate_limiter_check ( rate_limiter_t * limiter,
const char * ip_address,
rate_event_type_t event_type,
const rate_limit_config_t * config,
bool * allowed )

Check if an event from an IP address should be rate limited.

Uses sliding window: counts events in the last window_secs seconds.

Parameters
limiterRate limiter instance
ip_addressIP address string (IPv4 or IPv6)
event_typeType of event
configRate limit configuration (NULL = use defaults)
[out]allowedTrue if event is allowed, false if rate limited
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 138 of file rate_limit.c.

139 {
140 ASCII_OTEL_SCOPE(RATE_LIMITER_CHECK);
141 if (!limiter || !limiter->ops || !limiter->ops->check) {
142 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rate limiter");
143 }
144
145 if (!ip_address || !allowed) {
146 return SET_ERRNO(ERROR_INVALID_PARAM, "ip_address or allowed is NULL");
147 }
148
149 if (event_type >= RATE_EVENT_MAX) {
150 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event_type: %d", event_type);
151 }
152
153 ASCII_OTEL_ATTR_STRING("rate_limit.event", rate_limiter_event_type_string(event_type));
154 asciichat_error_t result = limiter->ops->check(limiter->backend_data, ip_address, event_type, config, allowed);
155 if (result == ASCIICHAT_OK)
156 ASCII_OTEL_ATTR_INT("rate_limit.allowed", *allowed);
157 if (result == ASCIICHAT_OK && !*allowed)
159 return result;
160}
#define SET_ERRNO(code,...)
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
@ STATS_COUNTER_RATE_LIMIT_REJECTIONS
void stats_counter_add(stats_scope_t *scope, stats_counter_id_t id, uint64_t amount)
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72
#define ASCII_OTEL_ATTR_INT(key, value)
Definition otel.h:67
#define ASCII_OTEL_ATTR_STRING(key, value)
Definition otel.h:66
const char * rate_limiter_event_type_string(rate_event_type_t event_type)
Get event type string for logging.
Definition rate_limit.c:194
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80
asciichat_error_t(* check)(void *backend_data, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
Definition rate_limit.h:93
void * backend_data
Backend-specific data.
Definition rate_limit.c:76
const rate_limiter_backend_ops_t * ops
Backend operations.
Definition rate_limit.c:75

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ASCII_OTEL_SCOPE, ASCIICHAT_OK, rate_limiter_s::backend_data, rate_limiter_backend_ops_t::check, ERROR_INVALID_PARAM, rate_limiter_s::ops, RATE_EVENT_MAX, rate_limiter_event_type_string(), SET_ERRNO, stats_counter_add(), STATS_COUNTER_RATE_LIMIT_REJECTIONS, and stats_runtime_scope().

Referenced by check_and_record_rate_limit(), and http_server_dispatch_http3().

◆ rate_limiter_create_memory()

rate_limiter_t * rate_limiter_create_memory ( void )

Create in-memory rate limiter.

Thread-safe implementation using uthash and mutexes. Suitable for ascii-chat server where persistence is not needed.

Returns
Rate limiter instance or NULL on failure

Definition at line 83 of file rate_limit.c.

83 {
84 rate_limiter_t *limiter = malloc(sizeof(rate_limiter_t));
85 if (!limiter) {
86 log_error("Failed to allocate rate limiter");
87 return NULL;
88 }
89
91 if (!limiter->backend_data) {
92 free(limiter);
93 return NULL;
94 }
95
96 limiter->ops = &memory_backend_ops;
97 return limiter;
98}
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
void * memory_backend_create(void)
Create memory backend instance.
Definition memory.c:256
const rate_limiter_backend_ops_t memory_backend_ops
Memory backend operations vtable.
Definition memory.c:276

References rate_limiter_s::backend_data, log_error, memory_backend_create(), memory_backend_ops, and rate_limiter_s::ops.

Referenced by http_server_init().

◆ rate_limiter_create_sqlite()

rate_limiter_t * rate_limiter_create_sqlite ( const char * db_path)

Create SQLite-backed rate limiter.

Persistent implementation using SQLite database. Suitable for acds discovery server where persistence is needed.

Parameters
db_pathPath to SQLite database (NULL = externally managed database)
Returns
Rate limiter instance or NULL on failure

Definition at line 100 of file rate_limit.c.

100 {
101 rate_limiter_t *limiter = malloc(sizeof(rate_limiter_t));
102 if (!limiter) {
103 log_error("Failed to allocate rate limiter");
104 return NULL;
105 }
106
107 limiter->backend_data = sqlite_backend_create(db_path);
108 if (!limiter->backend_data) {
109 free(limiter);
110 return NULL;
111 }
112
113 limiter->ops = &sqlite_backend_ops;
114 return limiter;
115}
void * sqlite_backend_create(const char *db_path)
Create SQLite backend instance.
Definition sqlite.c:165
const rate_limiter_backend_ops_t sqlite_backend_ops
SQLite backend operations vtable.
Definition sqlite.c:194

References rate_limiter_s::backend_data, log_error, rate_limiter_s::ops, sqlite_backend_create(), and sqlite_backend_ops.

Referenced by acds_server_init().

◆ rate_limiter_destroy()

void rate_limiter_destroy ( rate_limiter_t * limiter)

Destroy rate limiter and free resources.

Parameters
limiterRate limiter instance (NULL-safe)

Definition at line 126 of file rate_limit.c.

126 {
127 if (!limiter) {
128 return;
129 }
130
131 if (limiter->ops && limiter->ops->destroy) {
132 limiter->ops->destroy(limiter->backend_data);
133 }
134
135 free(limiter);
136}
void(* destroy)(void *backend_data)
Definition rate_limit.h:100

References rate_limiter_s::backend_data, rate_limiter_backend_ops_t::destroy, and rate_limiter_s::ops.

Referenced by acds_server_shutdown(), http_server_destroy(), and http_server_init().

◆ rate_limiter_event_type_string()

const char * rate_limiter_event_type_string ( rate_event_type_t event_type)

Get event type string for logging/database storage.

Parameters
event_typeEvent type enum value
Returns
Event type name string (e.g., "session_create", "connection")

Get event type string for logging/database storage.

Helper: Get event type string for logging.

Definition at line 194 of file rate_limit.c.

194 {
195 if (event_type >= RATE_EVENT_MAX) {
196 return "unknown";
197 }
198 return event_type_strings[event_type];
199}

References RATE_EVENT_MAX.

Referenced by rate_limiter_check().

◆ rate_limiter_prune()

asciichat_error_t rate_limiter_prune ( rate_limiter_t * limiter,
uint32_t max_age_secs )

Clean up old rate limit events.

Deletes events older than the specified age to prevent unbounded growth. Should be called periodically (e.g., every 5 minutes).

Parameters
limiterRate limiter instance
max_age_secsDelete events older than this (0 = use default 1 hour)
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 178 of file rate_limit.c.

178 {
179 ASCII_OTEL_SCOPE(RATE_LIMITER_PRUNE);
180 if (!limiter || !limiter->ops || !limiter->ops->cleanup) {
181 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rate limiter");
182 }
183
184 return limiter->ops->cleanup(limiter->backend_data, max_age_secs);
185}
asciichat_error_t(* cleanup)(void *backend_data, uint32_t max_age_secs)
Definition rate_limit.h:98

References ASCII_OTEL_SCOPE, rate_limiter_s::backend_data, rate_limiter_backend_ops_t::cleanup, ERROR_INVALID_PARAM, rate_limiter_s::ops, and SET_ERRNO.

◆ rate_limiter_record()

asciichat_error_t rate_limiter_record ( rate_limiter_t * limiter,
const char * ip_address,
rate_event_type_t event_type )

Record a rate limit event.

Should be called after rate_limiter_check() returns allowed=true.

Parameters
limiterRate limiter instance
ip_addressIP address string
event_typeType of event
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 162 of file rate_limit.c.

162 {
163 if (!limiter || !limiter->ops || !limiter->ops->record) {
164 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rate limiter");
165 }
166
167 if (!ip_address) {
168 return SET_ERRNO(ERROR_INVALID_PARAM, "ip_address is NULL");
169 }
170
171 if (event_type >= RATE_EVENT_MAX) {
172 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event_type: %d", event_type);
173 }
174
175 return limiter->ops->record(limiter->backend_data, ip_address, event_type);
176}
asciichat_error_t(* record)(void *backend_data, const char *ip_address, rate_event_type_t event_type)
Definition rate_limit.h:96

References rate_limiter_s::backend_data, ERROR_INVALID_PARAM, rate_limiter_s::ops, RATE_EVENT_MAX, rate_limiter_backend_ops_t::record, and SET_ERRNO.

Referenced by check_and_record_rate_limit(), and http_server_dispatch_http3().

◆ rate_limiter_set_sqlite_db()

void rate_limiter_set_sqlite_db ( rate_limiter_t * limiter,
void * db )

Set SQLite database handle for rate limiter.

For SQLite-backed rate limiters where the database lifecycle is managed externally (e.g., ACDS manages its own database). Must be called after rate_limiter_create_sqlite(NULL).

Parameters
limiterRate limiter instance (must be SQLite backend)
dbSQLite database handle

Definition at line 117 of file rate_limit.c.

117 {
118 if (!limiter || !limiter->backend_data) {
119 return;
120 }
121
122 // Call the SQLite backend function to set the database handle
123 sqlite_backend_set_db(limiter->backend_data, (sqlite3 *)db);
124}
void sqlite_backend_set_db(void *backend_data, sqlite3 *db)
Set SQLite database handle for backend.
Definition sqlite.c:187

References rate_limiter_s::backend_data, and sqlite_backend_set_db().

Referenced by acds_server_init().

Variable Documentation

◆ DEFAULT_RATE_LIMITS

const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
extern

Default rate limits for each event type.

Definition at line 33 of file rate_limit.c.

33 {
34// ACDS discovery server limits
35#ifdef NDEBUG
36 // Release mode: production limits (144 FPS video, 172 FPS audio)
37 [RATE_EVENT_SESSION_CREATE] = {.max_events = 10, .window_secs = 60}, // 10 creates per minute
38 [RATE_EVENT_SESSION_LOOKUP] = {.max_events = 30, .window_secs = 60}, // 30 lookups per minute
39 [RATE_EVENT_SESSION_JOIN] = {.max_events = 20, .window_secs = 60}, // 20 joins per minute
40 [RATE_EVENT_CONNECTION] = {.max_events = 50, .window_secs = 60}, // 50 connections per minute
41 [RATE_EVENT_IMAGE_FRAME] = {.max_events = 8640, .window_secs = 60}, // 8640 frames/min = 144 FPS
42 [RATE_EVENT_AUDIO] = {.max_events = 10320, .window_secs = 60}, // 10320 packets/min = 172 FPS
43 [RATE_EVENT_PING] = {.max_events = 120, .window_secs = 60}, // 120 pings/min = 2 Hz max
44 [RATE_EVENT_CLIENT_JOIN] = {.max_events = 10, .window_secs = 60}, // 10 joins per minute
45 [RATE_EVENT_CONTROL] = {.max_events = 100, .window_secs = 60}, // 100 control packets/min
46 [RATE_EVENT_HTTP_REQUEST] = {.max_events = 120, .window_secs = 60}, // 120 HTTP requests/minute
47#else
48 // Debug mode: slightly relaxed limits for development/testing (1.5x production limits)
49 [RATE_EVENT_SESSION_CREATE] = {.max_events = 15, .window_secs = 60}, // 15 creates per minute
50 [RATE_EVENT_SESSION_LOOKUP] = {.max_events = 45, .window_secs = 60}, // 45 lookups per minute
51 [RATE_EVENT_SESSION_JOIN] = {.max_events = 30, .window_secs = 60}, // 30 joins per minute
52 [RATE_EVENT_CONNECTION] = {.max_events = 75, .window_secs = 60}, // 75 connections per minute
53 [RATE_EVENT_IMAGE_FRAME] = {.max_events = 12960, .window_secs = 60}, // 12960 frames/min = 216 FPS
54 [RATE_EVENT_AUDIO] = {.max_events = 15480, .window_secs = 60}, // 15480 packets/min = 258 FPS
55 [RATE_EVENT_PING] = {.max_events = 180, .window_secs = 60}, // 180 pings/min = 3 Hz
56 [RATE_EVENT_CLIENT_JOIN] = {.max_events = 25, .window_secs = 60}, // 25 joins per minute (for testing reconnects)
57 [RATE_EVENT_CONTROL] = {.max_events = 150, .window_secs = 60}, // 150 control packets/min
58 [RATE_EVENT_HTTP_REQUEST] = {.max_events = 120, .window_secs = 60}, // 120 HTTP requests/minute
59#endif
60};

Referenced by http_server_init().