Class: Rubino::API::Middleware::RateLimit
- Inherits:
-
Object
- Object
- Rubino::API::Middleware::RateLimit
- Defined in:
- lib/rubino/api/middleware/rate_limit.rb
Overview
Token-bucket rate limiter, applied BEFORE Auth so that the open endpoints (/v1/health, /v1/metrics) get their own per-IP ceiling and cannot be flooded by an unauthenticated client. Authenticated requests are keyed by the bearer token so a single API key cannot saturate the process by spraying connections from many IPs.
Buckets refill linearly over a 60-second window. Storage is a single in-memory hash with monotonic timestamps; safe for a single-process deployment. Multi-process / multi-host needs a shared backend (Redis, etc.) — defer until we actually scale out.
On exceed: 429 with the canonical error envelope
{ error: { code: "rate_limited", message: "...",
details: { retry_after_seconds: N } } }
and a Retry-After header so well-behaved clients can back off without parsing the body.
Constant Summary collapse
- DEFAULT_UNAUTH_PER_MINUTE =
60- DEFAULT_AUTH_PER_MINUTE =
600- WINDOW_SECONDS =
60.0
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, clock: nil) ⇒ RateLimit
constructor
A new instance of RateLimit.
Constructor Details
#initialize(app, clock: nil) ⇒ RateLimit
Returns a new instance of RateLimit.
29 30 31 32 33 34 |
# File 'lib/rubino/api/middleware/rate_limit.rb', line 29 def initialize(app, clock: nil) @app = app @clock = clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) } @buckets = {} @mutex = Mutex.new end |
Instance Method Details
#call(env) ⇒ Object
36 37 38 39 40 41 42 43 44 |
# File 'lib/rubino/api/middleware/rate_limit.rb', line 36 def call(env) return @app.call(env) unless enabled? key, capacity = bucket_for(env) allowed, retry_after = consume(key, capacity) return too_many(retry_after) unless allowed @app.call(env) end |