Kurlyk
Loading...
Searching...
No Matches
HttpRateLimiter.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _KURLYK_HTTP_RATE_LIMITER_HPP_INCLUDED
3#define _KURLYK_HTTP_RATE_LIMITER_HPP_INCLUDED
4
7
8namespace kurlyk {
9
17 public:
18
24 long create_limit(long requests_per_period, long period_ms) {
25 std::lock_guard<std::mutex> lock(m_mutex);
26 long id = m_next_id++;
27 m_limits[id] = LimitData{
28 requests_per_period,
29 period_ms,
30 0,
31 std::chrono::steady_clock::now()
32 };
33 return id;
34 }
35
40 bool remove_limit(long limit_id) {
41 std::lock_guard<std::mutex> lock(m_mutex);
42 return m_limits.erase(limit_id) > 0;
43 }
44
52 bool allow_request(long general_rate_limit_id, long specific_rate_limit_id) {
53 std::lock_guard<std::mutex> lock(m_mutex);
54 auto general_it = m_limits.find(general_rate_limit_id);
55 auto specific_it = m_limits.find(specific_rate_limit_id);
56 if (general_it == m_limits.end() &&
57 specific_it == m_limits.end()) return true;
58
59 auto now = std::chrono::steady_clock::now();
60 bool general_limit_allowed = true;
61 bool specific_limit_allowed = true;
62
63 if (general_it != m_limits.end()) {
64 auto& limit_data = general_it->second;
65 general_limit_allowed = check_limit(limit_data, now);
66 }
67
68 if (specific_it != m_limits.end()) {
69 auto& limit_data = specific_it->second;
70 specific_limit_allowed = check_limit(limit_data, now);
71 }
72
73 if (general_limit_allowed && specific_limit_allowed) {
74 // All limits allow the request, now update counts
75 if (general_it != m_limits.end()) {
76 auto& limit_data = general_it->second;
77 update_limit(limit_data, now);
78 }
79 if (specific_it != m_limits.end()) {
80 auto& limit_data = specific_it->second;
81 update_limit(limit_data, now);
82 }
83 return true;
84 }
85
86 // Request is not allowed under one or more limits
87 return false;
88 }
89
99 template<typename Duration = std::chrono::milliseconds>
100 Duration time_until_next_allowed(long general_rate_limit_id, long specific_rate_limit_id) {
101 std::lock_guard<std::mutex> lock(m_mutex);
102 auto now = std::chrono::steady_clock::now();
103 Duration max_delay{0};
104
105 auto it = m_limits.find(general_rate_limit_id);
106 if (it != m_limits.end()) {
107 max_delay = std::max(max_delay, time_until_limit_allows<Duration>(it->second, now));
108 }
109
110 it = m_limits.find(specific_rate_limit_id);
111 if (it != m_limits.end()) {
112 max_delay = std::max(max_delay, time_until_limit_allows<Duration>(it->second, now));
113 }
114
115 return max_delay;
116 }
117
125 template<typename Duration = std::chrono::milliseconds>
127 std::lock_guard<std::mutex> lock(m_mutex);
128 auto now = std::chrono::steady_clock::now();
129
130 Duration min_delay = Duration::max();
131
132 for (const auto& pair : m_limits) {
133 const auto& limit = pair.second;
134 Duration delay = time_until_limit_allows<Duration>(limit, now);
135 if (delay.count() > 0 && delay < min_delay) {
136 min_delay = delay;
137 }
138 }
139
140 return (min_delay == Duration::max()) ? Duration{0} : min_delay;
141 }
142
143 private:
144 using time_point_t = std::chrono::steady_clock::time_point;
145
154
155 std::mutex m_mutex;
156 long m_next_id = 1;
157 std::unordered_map<long, LimitData> m_limits;
158
163 bool check_limit(LimitData& limit_data, const time_point_t& now) {
164 auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(now - limit_data.start_time);
165
166 // If period has elapsed, the count will reset upon update
167 if (elapsed_time.count() >= limit_data.period_ms) {
168 return true; // Period elapsed, limit can be reset
169 }
170
171 // Check if under limit or if no limit is set (0 requests per period means unlimited)
172 if (limit_data.count < limit_data.requests_per_period ||
173 limit_data.requests_per_period == 0) {
174 return true;
175 }
176 return false;
177 }
178
182 void update_limit(LimitData& limit_data, const time_point_t& now) {
183 auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(now - limit_data.start_time);
184
185 // Reset count if period has elapsed
186 if (elapsed_time.count() >= limit_data.period_ms) {
187 limit_data.start_time = now;
188 limit_data.count = 0;
189 }
190
191 ++limit_data.count;
192 }
193
203 template<typename Duration>
204 Duration time_until_limit_allows(const LimitData& limit_data, const time_point_t& now) const {
205 if (limit_data.requests_per_period == 0) return Duration{0};
206 auto elapsed = std::chrono::duration_cast<Duration>(now - limit_data.start_time);
207 auto period_duration = std::chrono::duration_cast<Duration>(std::chrono::milliseconds(limit_data.period_ms));
208 if (elapsed >= period_duration ||
209 limit_data.count < limit_data.requests_per_period) {
210 return Duration{0};
211 }
212 return period_duration - elapsed;
213 }
214 }; // HttpRateLimiter
215
216} // namespace kurlyk
217
218#endif // _KURLYK_HTTP_RATE_LIMITER_HPP_INCLUDED
Manages rate limits for HTTP requests, ensuring compliance with set limits.
std::unordered_map< long, LimitData > m_limits
Map storing rate limit configurations.
bool remove_limit(long limit_id)
Removes a rate limit with the specified ID.
bool allow_request(long general_rate_limit_id, long specific_rate_limit_id)
Checks if a request is allowed under the specified general and specific rate limits.
std::chrono::steady_clock::time_point time_point_t
long m_next_id
Next available unique ID for rate limits.
Duration time_until_next_allowed(long general_rate_limit_id, long specific_rate_limit_id)
Calculates the delay until the next request is allowed under the specified rate limits.
bool check_limit(LimitData &limit_data, const time_point_t &now)
Checks if a request is allowed under the specified rate limit without updating the count.
Duration time_until_any_limit_allows()
Finds the shortest delay among all active rate limits.
void update_limit(LimitData &limit_data, const time_point_t &now)
Updates the request count for the specified rate limit.
long create_limit(long requests_per_period, long period_ms)
Creates a new rate limit with specified parameters.
Duration time_until_limit_allows(const LimitData &limit_data, const time_point_t &now) const
Calculates the delay required before a request is allowed under a single rate limit.
std::mutex m_mutex
Mutex to protect shared data.
Primary namespace for the Kurlyk library, encompassing initialization, request management,...
Stores data for an individual rate limit.
time_point_t start_time
Start time of the current rate period.
long count
Requests made in the current period.
long requests_per_period
Max requests allowed in the period.
long period_ms
Duration of the period in milliseconds.