36#include <shared_mutex>
37#include <unordered_map>
57 std::chrono::seconds
window = std::chrono::seconds(1);
98 [[nodiscard]]
bool allow(std::string_view client_id) {
99 std::unique_lock<std::shared_mutex> lock(
mutex_);
101 auto now = std::chrono::steady_clock::now();
108 std::string key(client_id);
118 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
121 double tokens_to_add = elapsed.count() *
149 [[nodiscard]]
bool allow(std::string_view client_id, std::string_view session_id) {
150 if (session_id.empty()) {
151 return allow(client_id);
153 std::string composite_key;
154 composite_key.reserve(client_id.size() + 1 + session_id.size());
155 composite_key.append(client_id);
156 composite_key.push_back(
':');
157 composite_key.append(session_id);
158 return allow(std::string_view(composite_key));
167 [[nodiscard]]
bool would_allow(std::string_view client_id)
const {
168 std::shared_lock<std::shared_mutex> lock(
mutex_);
170 auto it =
buckets_.find(std::string(client_id));
175 auto now = std::chrono::steady_clock::now();
176 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
177 now - it->second.last_refill);
179 double tokens_to_add = elapsed.count() *
182 double available = std::min(
184 it->second.tokens + tokens_to_add);
186 return available >= 1.0;
196 std::shared_lock<std::shared_mutex> lock(
mutex_);
198 auto it =
buckets_.find(std::string(client_id));
203 auto now = std::chrono::steady_clock::now();
204 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
205 now - it->second.last_refill);
207 double tokens_to_add = elapsed.count() *
212 it->second.tokens + tokens_to_add);
219 void reset(std::string_view client_id) {
220 std::unique_lock<std::shared_mutex> lock(
mutex_);
221 buckets_.erase(std::string(client_id));
228 std::unique_lock<std::shared_mutex> lock(
mutex_);
237 std::shared_lock<std::shared_mutex> lock(
mutex_);
246 std::unique_lock<std::shared_mutex> lock(
mutex_);
255 std::shared_lock<std::shared_mutex> lock(
mutex_);
267 auto since_cleanup = std::chrono::duration_cast<std::chrono::seconds>(
270 if (since_cleanup < std::chrono::seconds(60)) {
278 auto since_refill = std::chrono::duration_cast<std::chrono::seconds>(
279 now - it->second.last_refill);
332 std::memory_order_acq_rel,
333 std::memory_order_acquire)) {
364 [[nodiscard]]
size_t current() const noexcept {
372 [[nodiscard]]
size_t max() const noexcept {
380 void set_max(
size_t max_connections)
noexcept {
429 other.accepted_ =
false;
433 if (
this != &other) {
437 other.accepted_ =
false;
457 explicit operator bool() const noexcept {
489 size_t max_per_client = 10,
490 size_t max_total = 1000)
505 std::unique_lock<std::mutex> lock(
mutex_);
506 std::string key(client_id);
526 std::unique_lock<std::mutex> lock(
mutex_);
527 std::string key(client_id);
532 if (it->second == 0) {
545 std::unique_lock<std::mutex> lock(
mutex_);
RAII guard for connection limiting.
connection_limiter * limiter_
connection_guard & operator=(const connection_guard &)=delete
bool accepted() const noexcept
Check if connection was accepted.
connection_guard & operator=(connection_guard &&other) noexcept
connection_guard(connection_limiter &limiter)
Construct guard and try to accept connection.
connection_guard(connection_guard &&other) noexcept
Movable.
void release() noexcept
Release the connection early.
connection_guard(const connection_guard &)=delete
Non-copyable.
Connection count limiter.
bool try_accept() noexcept
Try to accept a connection.
void on_disconnect() noexcept
Unregister a connection.
connection_limiter(size_t max_connections=1000)
Construct connection limiter.
size_t max() const noexcept
Get maximum connection limit.
void set_max(size_t max_connections) noexcept
Set maximum connection limit.
std::atomic< size_t > current_connections_
bool can_accept() const noexcept
Check if a new connection can be accepted.
size_t available() const noexcept
Get available connection slots.
size_t current() const noexcept
Get current connection count.
void on_connect() noexcept
Register a new connection.
bool at_capacity() const noexcept
Check if at capacity.
Per-client connection limiter.
void release(std::string_view client_id)
Release a connection from a client.
size_t client_connections(std::string_view client_id) const
Get connection count for a client.
std::unordered_map< std::string, size_t > client_connections_
per_client_connection_limiter(size_t max_per_client=10, size_t max_total=1000)
Construct limiter.
bool try_accept(std::string_view client_id)
Try to accept a connection from a client.
size_t total_connections() const noexcept
Get total connection count.
connection_limiter total_limiter_
Token bucket rate limiter.
rate_limiter(rate_limiter_config config={})
Construct rate limiter with configuration.
size_t client_count() const
Get number of tracked clients.
void set_config(rate_limiter_config config)
Update configuration.
bool allow(std::string_view client_id)
Check if request should be allowed.
void reset(std::string_view client_id)
Reset rate limit for a specific client.
rate_limiter_config config() const
Get current configuration.
bool would_allow(std::string_view client_id) const
Check if request would be allowed (without consuming token)
void reset_all()
Reset all rate limits.
rate_limiter_config config_
void maybe_cleanup(std::chrono::steady_clock::time_point now)
bool allow(std::string_view client_id, std::string_view session_id)
Check if request should be allowed (session-aware)
std::unordered_map< std::string, bucket > buckets_
std::chrono::steady_clock::time_point last_cleanup_
double remaining_tokens(std::string_view client_id) const
Get remaining tokens for a client.
Main namespace for all Network System components.
std::chrono::steady_clock::time_point last_refill
Configuration for rate limiter.
size_t burst_size
Maximum burst size (token bucket capacity)
std::chrono::seconds stale_timeout
Stale entry expiration time.
size_t max_requests_per_second
Maximum requests per second.
bool auto_cleanup
Enable automatic cleanup of stale entries.
std::chrono::seconds window
Time window for rate calculation.