// Hello!
//
// to run this example simply do:
// cargo run --example http-server
//
// After which you can open a new terminal and do:
// curl http://127.0.0.1:3000
//
// Send a bunch of requests through curl and follow what happens!
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use rate_gate::Limiter;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::time::Duration;
async fn handle_request(
_: Request
,
limiter: Arc>>,
) -> Result, Infallible> {
// Get the IP address of the request (for simplicity, use "127.0.0.1" as a mock)
let entity_ip = "127.0.0.1".to_string();
let mut limiter_lock = limiter.lock().unwrap();
// Check if the entity is rate-limited
match limiter_lock.is_entity_limited(&entity_ip) {
Some(true) => Ok(Response::new(Body::from("Request allowed\n"))),
Some(false) => Ok(Response::new(Body::from("Rate limit exceeded\n"))),
None => {
// Add a new entity if it's not already tracked
limiter_lock.add_limited_entity(entity_ip.clone(), 5, Duration::from_secs(10));
Ok(Response::new(Body::from("Request allowed (first time)\n")))
}
}
}
#[tokio::main]
async fn main() {
let limiter = Arc::new(Mutex::new(Limiter::new()));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
// Create a service that wraps the limiter with the HTTP request handler
let make_svc = make_service_fn(move |_conn| {
let limiter = Arc::clone(&limiter);
async move {
Ok::<_, Infallible>(service_fn(move |req| {
handle_request(req, Arc::clone(&limiter))
}))
}
});
let server = Server::bind(&addr).serve(make_svc);
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("Server error: {}", e);
}
}