2022-08-21 16:44:12 +00:00
|
|
|
use actix_governor::KeyExtractor;
|
|
|
|
use actix_governor::PeerIpKeyExtractor;
|
2023-01-27 19:56:44 +00:00
|
|
|
use actix_governor::SimpleKeyExtractionError;
|
|
|
|
use actix_web::HttpResponse;
|
|
|
|
use actix_web::HttpResponseBuilder;
|
2022-08-21 16:44:12 +00:00
|
|
|
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;
|
2023-01-27 19:56:44 +00:00
|
|
|
type KeyExtractionError = SimpleKeyExtractionError<&'static str>;
|
2022-08-21 16:44:12 +00:00
|
|
|
|
|
|
|
fn extract(&self, req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
|
|
|
|
let forwarded_for = req.headers().get("x-forwarded-for");
|
2022-10-15 12:31:54 +00:00
|
|
|
if self.proxied && forwarded_for.is_some() {
|
2023-01-27 19:56:44 +00:00
|
|
|
read_forwareded_for(forwarded_for).map_err(SimpleKeyExtractionError::new)
|
2022-08-21 16:44:12 +00:00
|
|
|
} else {
|
|
|
|
PeerIpKeyExtractor.extract(req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-27 19:56:44 +00:00
|
|
|
fn exceed_rate_limit_response(
|
|
|
|
&self,
|
|
|
|
negative: &NotUntil<QuantaInstant>,
|
|
|
|
mut response: HttpResponseBuilder,
|
|
|
|
) -> HttpResponse {
|
2022-08-21 16:44:12 +00:00
|
|
|
let wait_time = negative
|
|
|
|
.wait_time_from(DefaultClock::default().now())
|
|
|
|
.as_secs();
|
2023-01-27 19:56:44 +00:00
|
|
|
response
|
|
|
|
.content_type(ContentType::plaintext())
|
|
|
|
.body(format!("too many requests, retry in {wait_time}s"))
|
2022-08-21 16:44:12 +00:00
|
|
|
}
|
2023-01-27 19:56:44 +00:00
|
|
|
}
|
2022-08-21 16:44:12 +00:00
|
|
|
|
2023-01-27 19:56:44 +00:00
|
|
|
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")
|
2022-08-21 16:44:12 +00:00
|
|
|
}
|