use std::net::IpAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use common::prelude::*;
use common::config::RateLimitConfig;
use requests::{Request, RequestType};
use scripts::{Repository, Job};
use web::rate_limits::RateLimiter;
use web::responses::Response;
#[derive(Clone)]
pub struct WebApi<A: ProcessorApiTrait<Repository>> {
processor: Arc<Mutex<A>>,
hooks: Arc<Repository>,
locked: Arc<AtomicBool>,
limiter: Arc<Mutex<RateLimiter<IpAddr>>>,
health_enabled: bool,
}
impl<A: ProcessorApiTrait<Repository>> WebApi<A> {
pub fn new(
processor: A,
hooks: Arc<Repository>,
locked: Arc<AtomicBool>,
rate_limit_config: &RateLimitConfig,
health_enabled: bool,
) -> Self {
let limiter = Arc::new(Mutex::new(RateLimiter::new(
rate_limit_config.allowed,
rate_limit_config.interval.as_u64(),
)));
WebApi {
processor: Arc::new(Mutex::new(processor)),
hooks, locked, limiter, health_enabled,
}
}
pub fn process_hook(&self, req: &Request, args: Vec<String>) -> Response {
let hook_name = &args[0];
if self.locked.load(Ordering::Relaxed) {
return Response::Unavailable;
}
if let Ok(r) = req.web() {
let limited = self.limiter.lock().unwrap().is_limited(&r.source);
if let Some(until) = limited {
return Response::TooManyRequests(until);
}
}
let hook;
if let Some(found) = self.hooks.get_by_name(hook_name) {
hook = found;
} else {
return Response::NotFound;
}
let (request_type, provider) = hook.validate(req);
match request_type {
RequestType::Ping => Response::Ok,
RequestType::ExecuteHook => {
let job = Job::new(hook.clone(), provider, req.clone());
self.processor
.lock()
.unwrap()
.queue(job, hook.priority())
.unwrap();
Response::Ok
},
RequestType::Invalid => {
if let Ok(r) = req.web() {
self.limiter.lock().unwrap().increment(r.source);
}
Response::Forbidden
},
}
}
pub fn get_health(&self, _req: &Request, _args: Vec<String>) -> Response {
if self.health_enabled {
Response::HealthStatus(
self.processor.lock().unwrap().health_details().unwrap(),
)
} else {
Response::Forbidden
}
}
}