perf: OnceLock for env var, Arc<CurrentUser> in auth, pre-compute query lowercase

- rate_limit: cache OXICLOUD_TRUST_PROXY_HEADERS in OnceLock<bool> to avoid
  syscall on every request (~500ns → ~1ns)
- auth middleware: insert Arc<CurrentUser> instead of bare CurrentUser;
  all 5 extractors now clone Arc (~1ns) instead of 4 Strings (~60-100ns)
- search_service: pre-compute query.to_lowercase() once before loops,
  eliminating N redundant heap allocations per search
This commit is contained in:
Diocrafts
2026-03-07 11:23:56 +01:00
parent e4bcab0488
commit 9f08460027
8 changed files with 796 additions and 42 deletions
+15 -13
View File
@@ -47,7 +47,7 @@ where
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
parts
.extensions
.get::<CurrentUser>()
.get::<Arc<CurrentUser>>()
.map(|cu| AuthUser {
id: cu.id.clone(),
username: cu.username.clone(),
@@ -58,6 +58,8 @@ where
}
// Implement FromRequestParts for CurrentUser — full user extractor from extensions
// The middleware inserts Arc<CurrentUser>; this extractor cheaply clones the Arc
// (~1 ns atomic increment) instead of deep-cloning 4 Strings (~60-100 ns).
impl<S> FromRequestParts<S> for CurrentUser
where
S: Send + Sync,
@@ -67,8 +69,8 @@ where
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
parts
.extensions
.get::<CurrentUser>()
.cloned()
.get::<Arc<CurrentUser>>()
.map(|arc| (**arc).clone())
.ok_or(AuthError::UserNotFound)
}
}
@@ -83,7 +85,7 @@ where
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
parts
.extensions
.get::<CurrentUser>()
.get::<Arc<CurrentUser>>()
.map(|cu| CurrentUserId(cu.id.clone()))
.ok_or(AuthError::UserNotFound)
}
@@ -104,7 +106,7 @@ where
Ok(OptionalUserId(
parts
.extensions
.get::<CurrentUser>()
.get::<Arc<CurrentUser>>()
.map(|cu| cu.id.clone()),
))
}
@@ -122,7 +124,7 @@ where
type Rejection = Infallible;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
Ok(OptionalAuthUser(parts.extensions.get::<CurrentUser>().map(
Ok(OptionalAuthUser(parts.extensions.get::<Arc<CurrentUser>>().map(
|cu| AuthUser {
id: cu.id.clone(),
username: cu.username.clone(),
@@ -214,12 +216,12 @@ pub async fn auth_middleware(
"Token validated successfully for user: {}",
claims.username
);
let current_user = CurrentUser {
let current_user = Arc::new(CurrentUser {
id: claims.sub,
username: claims.username,
email: claims.email,
role: claims.role,
};
});
request.extensions_mut().insert(current_user);
return Ok(next.run(request).await);
}
@@ -260,12 +262,12 @@ pub async fn auth_middleware(
"App password authentication successful for user: {}",
uname
);
let current_user = CurrentUser {
let current_user = Arc::new(CurrentUser {
id: user_id,
username: uname,
email,
role,
};
});
request.extensions_mut().insert(current_user);
return Ok(next.run(request).await);
}
@@ -301,12 +303,12 @@ pub async fn auth_middleware(
match token_service.validate_token(&token_str) {
Ok(claims) => {
tracing::debug!("Cookie token validated for user: {}", claims.username);
let current_user = CurrentUser {
let current_user = Arc::new(CurrentUser {
id: claims.sub,
username: claims.username,
email: claims.email,
role: claims.role,
};
});
request.extensions_mut().insert(current_user);
request.extensions_mut().insert(CookieAuthenticated);
return Ok(next.run(request).await);
@@ -336,7 +338,7 @@ pub async fn auth_middleware(
/// `CurrentUser` being present in the request extensions.
pub async fn require_admin(request: Request, next: Next) -> Response {
// Get the CurrentUser inserted by auth_middleware
if let Some(current_user) = request.extensions().get::<CurrentUser>() {
if let Some(current_user) = request.extensions().get::<Arc<CurrentUser>>() {
if current_user.role == "admin" {
tracing::debug!("Admin access granted for user: {}", current_user.username);
return next.run(request).await;
+10 -4
View File
@@ -20,9 +20,13 @@ use axum::{
};
use moka::sync::Cache;
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
/// Cached value of `OXICLOUD_TRUST_PROXY_HEADERS` env var.
/// Read once on first access, never again — avoids a syscall per request.
static TRUST_PROXY: OnceLock<bool> = OnceLock::new();
/// A simple sliding-window counter keyed by IP address.
///
/// Each key lives for `window` seconds; every request increments the counter.
@@ -95,9 +99,11 @@ impl RateLimiter {
/// proxy in front of the app, an attacker can spoof these headers to bypass
/// rate limiting.
pub fn extract_client_ip<B>(req: &Request<B>) -> String {
let trust_proxy = std::env::var("OXICLOUD_TRUST_PROXY_HEADERS")
.map(|v| v == "true" || v == "1")
.unwrap_or(false);
let trust_proxy = *TRUST_PROXY.get_or_init(|| {
std::env::var("OXICLOUD_TRUST_PROXY_HEADERS")
.map(|v| v == "true" || v == "1")
.unwrap_or(false)
});
let headers = req.headers();