63 lines
2.0 KiB
Rust
63 lines
2.0 KiB
Rust
use actix_governor::KeyExtractor;
|
|
use actix_governor::PeerIpKeyExtractor;
|
|
use actix_governor::SimpleKeyExtractionError;
|
|
use actix_web::HttpResponse;
|
|
use actix_web::HttpResponseBuilder;
|
|
use actix_web::{dev::ServiceRequest, http::header::ContentType};
|
|
use governor::clock::{Clock, DefaultClock, QuantaInstant};
|
|
use governor::NotUntil;
|
|
use std::net::IpAddr;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct ForwardedPeerIpKeyExtractor {
|
|
pub proxied: bool,
|
|
}
|
|
|
|
impl KeyExtractor for ForwardedPeerIpKeyExtractor {
|
|
type Key = IpAddr;
|
|
type KeyExtractionError = SimpleKeyExtractionError<&'static str>;
|
|
|
|
fn extract(&self, req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
|
|
let forwarded_for = req.headers().get("x-forwarded-for");
|
|
let mut ip = if self.proxied && forwarded_for.is_some() {
|
|
read_forwareded_for(forwarded_for).map_err(SimpleKeyExtractionError::new)?
|
|
} else {
|
|
PeerIpKeyExtractor.extract(req)?
|
|
};
|
|
|
|
// only keep the first /56 for ipv6 addresses
|
|
// mask 0xffff_ffff_ffff_ff00_0000_0000_0000_0000
|
|
if let IpAddr::V6(ipv6) = ip {
|
|
let mut octets = ipv6.octets();
|
|
octets[7..16].fill(0);
|
|
ip = IpAddr::V6(octets.into());
|
|
}
|
|
|
|
Ok(ip)
|
|
}
|
|
|
|
fn exceed_rate_limit_response(
|
|
&self,
|
|
negative: &NotUntil<QuantaInstant>,
|
|
mut response: HttpResponseBuilder,
|
|
) -> HttpResponse {
|
|
let wait_time = negative
|
|
.wait_time_from(DefaultClock::default().now())
|
|
.as_secs();
|
|
response
|
|
.content_type(ContentType::plaintext())
|
|
.body(format!("too many requests, retry in {wait_time}s"))
|
|
}
|
|
}
|
|
|
|
fn read_forwareded_for(
|
|
forwarded_for: Option<&actix_web::http::header::HeaderValue>,
|
|
) -> Result<IpAddr, &'static str> {
|
|
forwarded_for
|
|
.unwrap()
|
|
.to_str()
|
|
.map_err(|_| "x-forwarded-for contains invalid header value")?
|
|
.parse::<IpAddr>()
|
|
.map_err(|_| "x-forwarded-for contains invalid ip adress")
|
|
}
|