use std::sync::Arc;
use plecto_host::{HttpRequest, HttpResponse, RequestTrace};
use crate::ActiveConfig;
use crate::chain::{self, ChainOutcome, RequestBodyOutcome, ResponseOutcome};
use crate::route::{self, RouteInfo};
#[derive(Clone)]
pub struct ConfigSnapshot {
config: Arc<ActiveConfig>,
trace: RequestTrace,
}
impl ConfigSnapshot {
pub(crate) fn new(config: Arc<ActiveConfig>, trace: RequestTrace) -> Self {
Self { config, trace }
}
pub fn on_request(&self, request: HttpRequest) -> ChainOutcome {
chain::dispatch_request(&self.config.resolved_chain, request, &self.trace)
}
pub fn on_response(&self, request: &HttpRequest, response: HttpResponse) -> ResponseOutcome {
chain::dispatch_response(&self.config.resolved_chain, request, response, &self.trace)
}
#[allow(clippy::indexing_slicing)]
pub fn find_route(&self, request: &HttpRequest) -> Option<RouteInfo> {
let parts = route::RequestParts {
authority: &request.authority,
path: &request.path,
method: &request.method,
headers: &request.headers,
};
let index = route::select(&self.config.routes, &parts)?;
debug_assert!(index < self.config.routes.len());
let r = &self.config.routes[index];
Some(RouteInfo {
index,
backends: r.backends.clone(),
strip_prefix: r.strip_prefix.clone(),
has_filters: !r.filters.is_empty(),
reads_body: r.reads_body,
rate_limit: r.rate_limit.clone(),
upgrade: r.upgrade.clone(),
compression: r.compression.clone(),
})
}
pub fn dispatch_request(&self, route: usize, request: HttpRequest) -> ChainOutcome {
match self.config.routes.get(route) {
Some(r) => chain::dispatch_request(&r.resolved_chain, request, &self.trace),
None => ChainOutcome::Respond(no_route_response()),
}
}
pub fn dispatch_request_body(&self, route: usize, body: Vec<u8>) -> RequestBodyOutcome {
match self.config.routes.get(route) {
Some(r) => chain::dispatch_request_body(&r.resolved_chain, body, &self.trace),
None => RequestBodyOutcome::Forward(body),
}
}
pub fn dispatch_response(
&self,
route: usize,
request: &HttpRequest,
response: HttpResponse,
) -> ResponseOutcome {
match self.config.routes.get(route) {
Some(r) => chain::dispatch_response(&r.resolved_chain, request, response, &self.trace),
None => ResponseOutcome::Forward(response),
}
}
pub fn config_version(&self) -> &str {
&self.config.hash
}
pub fn traceparent(&self) -> String {
self.trace.to_traceparent()
}
}
impl crate::Control {
pub fn snapshot(&self) -> ConfigSnapshot {
self.snapshot_with_trace(RequestTrace::root())
}
pub fn snapshot_with_trace(&self, trace: RequestTrace) -> ConfigSnapshot {
ConfigSnapshot::new(self.active.load_full(), trace)
}
pub fn on_request(&self, request: HttpRequest) -> ChainOutcome {
self.snapshot().on_request(request)
}
pub fn on_response(&self, request: &HttpRequest, response: HttpResponse) -> ResponseOutcome {
self.snapshot().on_response(request, response)
}
}
fn no_route_response() -> HttpResponse {
HttpResponse {
status: 404,
headers: vec![plecto_host::Header {
name: "x-plecto-fault".to_string(),
value: b"no-route".to_vec(),
}],
body: b"no route".to_vec(),
}
}