1
0
Fork 1
mirror of https://github.com/MingweiSamuel/Riven.git synced 2025-03-27 00:53:16 -07:00
Riven/src/req/rate_limit.rs
2019-10-14 01:00:20 -07:00

84 lines
2.6 KiB
Rust

use std::cmp;
use std::time::{
Duration,
Instant,
};
use parking_lot::{
RwLock,
};
use super::token_bucket::{
TokenBucket,
VectorTokenBucket,
};
use super::rate_limit_type::RateLimitType;
pub struct RateLimit {
rate_limit_type: RateLimitType,
// Buckets for this rate limit (synchronized).
// Almost always read, written only when rate limit rates are updated
// from API response.
// TODO: Question of writer starvation.
buckets: RwLock<Vec<VectorTokenBucket>>,
// Set to when we can retry if a retry-after header is received.
retry_after: Option<Instant>,
}
impl RateLimit {
/// Header specifying which RateLimitType caused a 429.
const HEADER_XRATELIMITTYPE: &'static str = "X-Rate-Limit-Type";
/// Header specifying retry after time in seconds after a 429.
const HEADER_RETRYAFTER: &'static str = "Retry-After";
pub fn new(rate_limit_type: RateLimitType) -> Self {
let initial_bucket = VectorTokenBucket::new(Duration::from_secs(1), 1);
RateLimit {
rate_limit_type: rate_limit_type,
// Rate limit before getting from response: 1/s.
buckets: RwLock::new(vec![initial_bucket]),
retry_after: None,
}
}
pub fn get_both_or_delay(app_rate_limit: &Self, method_rate_limit: &Self) -> Option<Duration> {
// Check retry after.
let retry_after_delay = app_rate_limit.get_retry_after_delay()
.and_then(|a| method_rate_limit.get_retry_after_delay().map(|m| cmp::max(a, m)));
if retry_after_delay.is_some() {
return retry_after_delay
}
// Check buckets.
let app_buckets = app_rate_limit.buckets.read();
let method_buckets = method_rate_limit.buckets.read();
for bucket in app_buckets.iter().chain(method_buckets.iter()) {
let delay = bucket.get_delay();
if delay.is_some() {
return delay;
}
}
// Success.
for bucket in app_buckets.iter().chain(method_buckets.iter()) {
bucket.get_tokens(1);
}
None
}
pub fn get_retry_after_delay(&self) -> Option<Duration> {
self.retry_after.and_then(|i| Instant::now().checked_duration_since(i))
}
pub fn on_response(&self, _response: &reqwest::Response) {
unimplemented!();
}
}
#[cfg(test)]
mod tests {
use super::*;
fn send_sync() {
fn is_send_sync<T: Send + Sync>() {}
is_send_sync::<RateLimit>();
}
}