Skip to content

Commit 8dc3833

Browse files
committed
update rate limiter to support more than one limited action
1 parent f165456 commit 8dc3833

File tree

9 files changed

+200
-86
lines changed

9 files changed

+200
-86
lines changed

src/app.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::downloads_counter::DownloadsCounter;
1010
use crate::email::Emails;
1111
use crate::github::{GitHubClient, RealGitHubClient};
1212
use crate::metrics::{InstanceMetrics, ServiceMetrics};
13+
use crate::rate_limiter::RateLimiter;
1314
use crate::storage::Storage;
1415
use axum::extract::{FromRef, FromRequestParts, State};
1516
use diesel::r2d2;
@@ -68,6 +69,9 @@ pub struct App {
6869

6970
/// In-flight request counters for the `balance_capacity` middleware.
7071
pub balance_capacity: BalanceCapacityState,
72+
73+
/// Rate limit select actions.
74+
pub rate_limiter: RateLimiter,
7175
}
7276

7377
impl App {
@@ -178,6 +182,7 @@ impl App {
178182
http_client,
179183
fastboot_client,
180184
balance_capacity: Default::default(),
185+
rate_limiter: RateLimiter::new(config.rate_limiter.clone()),
181186
config,
182187
}
183188
}

src/config/server.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ use anyhow::{anyhow, Context};
22
use ipnetwork::IpNetwork;
33
use oauth2::{ClientId, ClientSecret};
44

5-
use crate::rate_limiter::RateLimiter;
5+
use crate::rate_limiter::{LimitedAction, RateLimiterConfig};
66
use crate::{env, env_optional, Env};
77

88
use super::base::Base;
99
use super::database_pools::DatabasePools;
1010
use crate::config::balance_capacity::BalanceCapacityConfig;
1111
use crate::storage::StorageConfig;
1212
use http::HeaderValue;
13-
use std::collections::HashSet;
13+
use std::collections::{HashMap, HashSet};
1414
use std::net::IpAddr;
1515
use std::time::Duration;
1616

@@ -30,7 +30,7 @@ pub struct Server {
3030
pub gh_client_secret: ClientSecret,
3131
pub max_upload_size: u64,
3232
pub max_unpack_size: u64,
33-
pub rate_limiter: RateLimiter,
33+
pub rate_limiter: HashMap<LimitedAction, RateLimiterConfig>,
3434
pub new_version_rate_limit: Option<u32>,
3535
pub blocked_traffic: Vec<(String, Vec<String>)>,
3636
pub max_allowed_page_offset: u32,
@@ -140,6 +140,24 @@ impl Default for Server {
140140
.map(|s| s.parse().expect("SERVER_THREADS was not a valid number"))
141141
.ok();
142142

143+
// Dynamically load the configuration for all the rate limiting actions. See
144+
// `src/rate_limiter.rs` for their definition.
145+
let mut rate_limiter = HashMap::new();
146+
for action in LimitedAction::VARIANTS {
147+
let env_var_key = action.env_var_key();
148+
rate_limiter.insert(
149+
*action,
150+
RateLimiterConfig {
151+
rate: Duration::from_secs(
152+
env_optional(&format!("RATE_LIMITER_{env_var_key}_RATE_SECONDS"))
153+
.unwrap_or_else(|| action.default_rate_seconds()),
154+
),
155+
burst: env_optional(&format!("RATE_LIMITER_{env_var_key}_BURST"))
156+
.unwrap_or_else(|| action.default_burst()),
157+
},
158+
);
159+
}
160+
143161
Server {
144162
db: DatabasePools::full_from_environment(&base),
145163
storage: StorageConfig::from_environment(),
@@ -153,7 +171,7 @@ impl Default for Server {
153171
gh_client_secret: ClientSecret::new(env("GH_CLIENT_SECRET")),
154172
max_upload_size: 10 * 1024 * 1024, // 10 MB default file upload size limit
155173
max_unpack_size: 512 * 1024 * 1024, // 512 MB max when decompressed
156-
rate_limiter: Default::default(),
174+
rate_limiter,
157175
new_version_rate_limit: env_optional("MAX_NEW_VERSIONS_DAILY"),
158176
blocked_traffic: blocked_traffic(),
159177
max_allowed_page_offset: env_optional("WEB_MAX_ALLOWED_PAGE_OFFSET").unwrap_or(200),

src/controllers/krate/publish.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
137137
};
138138

139139
let license_file = new_crate.license_file.as_deref();
140-
let krate = persist.create_or_update(conn, user.id, Some(&app.config.rate_limiter))?;
140+
let krate = persist.create_or_update(conn, user.id, Some(&app.rate_limiter))?;
141141

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

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub mod github;
4646
pub mod headers;
4747
pub mod metrics;
4848
pub mod middleware;
49-
mod rate_limiter;
49+
pub mod rate_limiter;
5050
pub mod schema;
5151
#[macro_use]
5252
pub mod sql;

src/models/krate.rs

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

1919
use crate::models::helpers::with_count::*;
20-
use crate::rate_limiter::RateLimiter;
20+
use crate::rate_limiter::{LimitedAction, RateLimiter};
2121
use crate::schema::*;
2222
use crate::sql::canon_crate_name;
2323

@@ -119,7 +119,7 @@ impl<'a> NewCrate<'a> {
119119
// first so we know whether to add an owner
120120
if let Some(krate) = self.save_new_crate(conn, uploader)? {
121121
if let Some(rate_limit) = rate_limit {
122-
rate_limit.check_rate_limit(uploader, conn)?;
122+
rate_limit.check_rate_limit(uploader, LimitedAction::PublishNew, conn)?;
123123
}
124124
return Ok(krate);
125125
}

0 commit comments

Comments
 (0)