#[cfg(test)]
mod tests;
use crate::application::Application;
use crate::middleware::Middleware;
use crate::request::Request;
use crate::response::Response;
use crate::server::ConnectionInfo;
pub use rws_http::ip_filter::*;
enum FilterMode {
Allow,
Deny,
}
pub struct IpFilter {
mode: FilterMode,
ranges: Vec<IpRange>,
}
impl IpFilter {
pub fn allow(entries: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
Self::from_entries(FilterMode::Allow, entries)
}
pub fn deny(entries: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
Self::from_entries(FilterMode::Deny, entries)
}
fn from_entries(mode: FilterMode, entries: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
let ranges = entries.into_iter().filter_map(|e| IpRange::parse(e.as_ref())).collect();
IpFilter { mode, ranges }
}
fn matches(&self, ip: &str) -> bool {
match parse_ip(ip) {
Some(addr) => self.ranges.iter().any(|r| (addr & r.mask) == r.network),
None => false,
}
}
}
#[doc(hidden)]
pub fn matches(entry: &str, ip: &str) -> bool {
IpFilter::deny([entry]).matches(ip)
}
impl Middleware for IpFilter {
fn accepts_streamed_body(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
true
}
fn handle(
&self,
request: &Request,
connection: &ConnectionInfo,
next: &dyn Application,
) -> Result<Response, String> {
use crate::error::{AppError, IntoResponse};
let matched = self.matches(&client_ip(request, &connection.client.ip));
match self.mode {
FilterMode::Allow if !matched => Ok(AppError::Forbidden.into_response()),
FilterMode::Deny if matched => Ok(AppError::Forbidden.into_response()),
_ => next.execute(request, connection),
}
}
fn runs_inline(&self, _request: &Request, _connection: &ConnectionInfo) -> bool {
true
}
}