Skip to content

Commit 466afb6

Browse files
committed
refactor RateLimiter to work with multiple actions
1 parent acc2654 commit 466afb6

File tree

9 files changed

+183
-115
lines changed

9 files changed

+183
-115
lines changed

src/app.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::downloads_counter::DownloadsCounter;
88
use crate::email::Emails;
99
use crate::github::GitHubClient;
1010
use crate::metrics::{InstanceMetrics, ServiceMetrics};
11+
use crate::rate_limiter::RateLimiter;
1112
use diesel::r2d2;
1213
use oauth2::basic::BasicClient;
1314
use reqwest::blocking::Client;
@@ -43,6 +44,8 @@ pub struct App {
4344
/// Metrics related to this specific instance of the service
4445
pub instance_metrics: InstanceMetrics,
4546

47+
pub rate_limiter: RateLimiter,
48+
4649
/// A configured client for outgoing HTTP requests
4750
///
4851
/// In production this shares a single connection pool across requests. In tests
@@ -165,6 +168,7 @@ impl App {
165168
read_only_replica_database: replica_database,
166169
github,
167170
github_oauth,
171+
rate_limiter: RateLimiter::new(config.rate_limiter.clone()),
168172
config,
169173
downloads_counter: DownloadsCounter::new(),
170174
emails: Emails::from_environment(),

src/config.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use crate::rate_limiter::RateLimiter;
1+
use crate::rate_limiter::{LimitedAction, RateLimiterConfig};
22
use crate::{env, env_optional, uploaders::Uploader, Env};
3+
use std::collections::HashMap;
4+
use std::time::Duration;
35

46
mod base;
57
mod database_pools;
@@ -16,7 +18,6 @@ pub struct Server {
1618
pub gh_base_url: String,
1719
pub max_upload_size: u64,
1820
pub max_unpack_size: u64,
19-
pub publish_rate_limit: RateLimiter,
2021
pub blocked_traffic: Vec<(String, Vec<String>)>,
2122
pub max_allowed_page_offset: u32,
2223
pub page_offset_ua_blocklist: Vec<String>,
@@ -27,6 +28,7 @@ pub struct Server {
2728
pub metrics_authorization_token: Option<String>,
2829
pub use_test_database_pool: bool,
2930
pub instance_metrics_log_every_seconds: Option<u64>,
31+
pub rate_limiter: HashMap<LimitedAction, RateLimiterConfig>,
3032
}
3133

3234
impl Default for Server {
@@ -64,12 +66,34 @@ impl Default for Server {
6466
.split(',')
6567
.map(ToString::to_string)
6668
.collect();
69+
6770
let page_offset_ua_blocklist = match env_optional::<String>("WEB_PAGE_OFFSET_UA_BLOCKLIST")
6871
{
6972
None => vec![],
7073
Some(s) if s.is_empty() => vec![],
7174
Some(s) => s.split(',').map(String::from).collect(),
7275
};
76+
77+
// Dynamically load the configuration for all the rate limiting actions. See
78+
// `src/rate_limiter.rs` for their definition.
79+
let mut rate_limiter = HashMap::new();
80+
for action in LimitedAction::VARIANTS {
81+
rate_limiter.insert(
82+
*action,
83+
RateLimiterConfig {
84+
rate: Duration::from_secs(
85+
env_optional(&format!(
86+
"RATE_LIMITER_{}_RATE_SECONDS",
87+
action.env_var_key()
88+
))
89+
.unwrap_or_else(|| action.default_rate_seconds()),
90+
),
91+
burst: env_optional(&format!("RATE_LIMITER_{}_BURST", action.env_var_key()))
92+
.unwrap_or_else(|| action.default_burst()),
93+
},
94+
);
95+
}
96+
7397
Server {
7498
db: DatabasePools::full_from_environment(),
7599
base: Base::from_environment(),
@@ -79,7 +103,6 @@ impl Default for Server {
79103
gh_base_url: "https://api.github.com".to_string(),
80104
max_upload_size: 10 * 1024 * 1024, // 10 MB default file upload size limit
81105
max_unpack_size: 512 * 1024 * 1024, // 512 MB max when decompressed
82-
publish_rate_limit: Default::default(),
83106
blocked_traffic: blocked_traffic(),
84107
max_allowed_page_offset: env_optional("WEB_MAX_ALLOWED_PAGE_OFFSET").unwrap_or(200),
85108
page_offset_ua_blocklist,
@@ -96,6 +119,7 @@ impl Default for Server {
96119
metrics_authorization_token: dotenv::var("METRICS_AUTHORIZATION_TOKEN").ok(),
97120
use_test_database_pool: false,
98121
instance_metrics_log_every_seconds: env_optional("INSTANCE_METRICS_LOG_EVERY_SECONDS"),
122+
rate_limiter,
99123
}
100124
}
101125
}

src/controllers/krate/publish.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
107107
};
108108

109109
let license_file = new_crate.license_file.as_deref();
110-
let krate =
111-
persist.create_or_update(&conn, user.id, Some(&app.config.publish_rate_limit))?;
110+
let krate = persist.create_or_update(&conn, user.id, Some(&app.rate_limiter))?;
112111

113112
let owners = krate.owners(&conn)?;
114113
if user.rights(req.app(), &owners)? < Rights::Publish {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub mod git;
4444
pub mod github;
4545
pub mod metrics;
4646
pub mod middleware;
47-
mod rate_limiter;
47+
pub mod rate_limiter;
4848
pub mod render;
4949
pub mod schema;
5050
pub mod tasks;

src/models/krate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::models::{
1515
use crate::util::errors::{cargo_err, AppResult};
1616

1717
use crate::models::helpers::with_count::*;
18-
use crate::rate_limiter::RateLimiter;
18+
use crate::rate_limiter::{LimitedAction, RateLimiter};
1919
use crate::schema::*;
2020

2121
#[derive(Debug, Queryable, Identifiable, Associations, Clone, Copy)]
@@ -109,7 +109,7 @@ impl<'a> NewCrate<'a> {
109109
// first so we know whether to add an owner
110110
if let Some(krate) = self.save_new_crate(conn, uploader)? {
111111
if let Some(rate_limit) = rate_limit {
112-
rate_limit.check_rate_limit(uploader, conn)?;
112+
rate_limit.check_rate_limit(uploader, LimitedAction::PublishNew, conn)?;
113113
}
114114
return Ok(krate);
115115
}

0 commit comments

Comments
 (0)