Files
OpenFUT-Core/src/middleware.rs
T
funman300 f6dd41fd41
CI / Build, lint & test (push) Successful in 2m21s
feat: Phase 4 — dev experience complete
Docs:
- .env.example: documents all environment variables with defaults
- CONTRIBUTING.md: dev setup, running tests, code style, design constraints
- MODDING.md: full schema reference for cards, packs, objectives, and SBCs
- .gitea/workflows/ci.yml: Gitea Actions CI (fmt check, clippy, build, test)

Middleware (#50-52):
- Body limit (256 KB) via DefaultBodyLimit on all routes (#50)
- Concurrency limit (256 concurrent requests) via semaphore middleware;
  returns 429 Too Many Requests when at capacity (#51)
- Correlation IDs via tower_http request_id layers; sets x-request-id
  UUID on every request and propagates it to the response headers (#52)

Layer order (outermost → innermost):
  CorsLayer → DefaultBodyLimit → concurrency limit → SetRequestId
  → TraceLayer → PropagateRequestId → routes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 15:53:28 -07:00

33 lines
1.2 KiB
Rust

use axum::{extract::Request, http::StatusCode, middleware::Next, response::Response};
use std::sync::{Arc, LazyLock};
use tokio::sync::Semaphore;
use tower_http::request_id::{MakeRequestId, RequestId};
use uuid::Uuid;
/// Maximum concurrent in-flight requests. Responds 429 when at capacity.
const MAX_CONCURRENT: usize = 256;
static SEMAPHORE: LazyLock<Arc<Semaphore>> =
LazyLock::new(|| Arc::new(Semaphore::new(MAX_CONCURRENT)));
/// Axum middleware that limits concurrent in-flight requests.
/// Returns 429 Too Many Requests when the limit is reached.
pub async fn limit_concurrency(req: Request, next: Next) -> Result<Response, StatusCode> {
let Ok(_permit) = SEMAPHORE.try_acquire() else {
return Err(StatusCode::TOO_MANY_REQUESTS);
};
Ok(next.run(req).await)
}
/// Generates a UUID v4 for each request and attaches it as `x-request-id`.
#[derive(Clone, Copy)]
pub struct MakeRequestUuid;
impl MakeRequestId for MakeRequestUuid {
fn make_request_id<B>(&mut self, _: &http::Request<B>) -> Option<RequestId> {
let id = Uuid::new_v4().to_string();
let header_val = id.parse().ok()?;
Some(RequestId::new(header_val))
}
}