use axum::extract::rejection::PathRejection;
use axum::extract::Path;
use axum::routing::get;
use axum::Router;
use axum_core::extract::FromRequestParts;
use axum_limit::{Key, Limit, LimitPerDay, LimitPerHour, LimitPerSecond, LimitState};
use http::request::Parts;
use http::{Method, Uri};
use serde::Deserialize;
use std::hash::Hash;
use std::net::{Ipv4Addr, SocketAddr};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let listener = TcpListener::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8080))).await?;
let app = Router::new()
.route(
"/limit-2-per-500-ms-by-method",
get(limit_2_per_500_ms_by_method),
)
.with_state(LimitState::<Method>::default()) .route("/limit-4-per-sec-by-uri", get(limit_4_per_sec_by_uri))
.with_state(LimitState::<Uri>::default()) .route(
"/limit-100-per-hour-by-uri-id",
get(limit_100_per_hour_by_id),
)
.route(
"/limit-10000-per-day-by-uri-id",
get(limit_10000_per_day_by_id),
)
.with_state(LimitState::<(Uri, Id)>::default()); axum::serve(listener, app).await?;
Ok(())
}
async fn limit_2_per_500_ms_by_method(_: Limit<2, 500, Method>) {}
async fn limit_4_per_sec_by_uri(_: LimitPerSecond<4, Uri>) {}
async fn limit_100_per_hour_by_id(
Limit((uri, Path(Data { name, .. }))): LimitPerHour<100, (Uri, Id)>,
) {
println!("{uri}, {name}");
}
async fn limit_10000_per_day_by_id(_: LimitPerDay<10000, (Uri, Id)>) {}
#[derive(Debug, Deserialize)]
struct Data {
id: Id,
name: String,
}
#[derive(Deserialize, Clone, Copy, Hash, Ord, PartialOrd, Eq, PartialEq, Debug)]
struct Id(usize);
#[async_trait::async_trait]
impl<S> FromRequestParts<S> for Data
where
S: Send + Sync,
{
type Rejection = PathRejection;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let path = Path::<Data>::from_request_parts(parts, state).await?;
Ok(path.0)
}
}
impl Key for Id {
type Extractor = Path<Data>;
fn from_extractor(extractor: &Self::Extractor) -> Self {
extractor.id
}
}