use crate::pools_with_global_extra::PoolsWithGlobalExtra;
use anyhow::anyhow;
use detcd::{DestinationRule, Meta, ServiceState};
use http_pool::body::{VariantBody, variant_body};
use http_pool::http1::HttpPool;
use http_pool::net_pool::{BackendState, Pool, Pools};
use hyper::body::Incoming;
use hyper::{Request, Response};
use std::sync::Arc;
#[macro_export]
macro_rules! to_backend {
(
$pools: ident,
$req: ident,
$service: ident,
$service_id: ident
) => {
let (pool, meta) = get_pool_and_meta(&$pools, &$service)?;
let mut sender = match meta.state {
Some(ServiceState::Fixed) => pool
.clone()
.target(get_fixed_address(&pool, $service_id, meta.instances)?.get_address())
.await
.map_err(|e| anyhow::Error::new(e))?,
_ => pool
.get(parse_rule(&$req, &meta)?)
.await
.map_err(|e| anyhow::Error::new(e))?,
};
let uri = sender
.new_uri($req.uri())
.map_err(|e| anyhow::Error::new(e))?;
*$req.uri_mut() = uri;
sender
.send_request($req.map(|b| variant_body(b)))
.await
.map(|b| b.map(|b| variant_body(b)))
.map_err(|e| anyhow::Error::new(e))
};
}
pub(crate) use to_backend;
pub async fn send_to_backend(
pools: Pools<http_pool::http1::Pool>,
service: String,
service_id: Option<u32>,
mut req: Request<Incoming>,
) -> Result<Response<VariantBody>, anyhow::Error> {
to_backend! {
pools,
req,
service,
service_id
}
}
pub(crate) fn get_pool_and_meta<P: Pool>(
pools: &Pools<P>,
service: &String,
) -> Result<(Arc<P>, Arc<Meta>), anyhow::Error> {
let pool = pools
.get_pool(&service)
.ok_or(anyhow!(format!("service/{} not exist", service)))?;
let meta = pools
.read_global_meta(&service)
.ok_or(anyhow!(format!("service/{} no meta", service)))?;
Ok((pool, meta))
}
pub(crate) fn get_fixed_address<P: Pool>(
pool: &Arc<P>,
service_id: Option<u32>,
instances: u32,
) -> Result<BackendState, anyhow::Error> {
let service_id = service_id
.filter(|&id| id < instances)
.ok_or_else(|| anyhow!("invalid target service id/{:?}", service_id))?;
let bs = pool
.get_backend_by_id(service_id)
.ok_or(anyhow!("target service id/{} not exist", service_id))?;
Ok(bs)
}
pub(crate) fn parse_rule<'a>(
req: &'a Request<Incoming>,
meta: &'a Meta,
) -> Result<&'a str, anyhow::Error> {
let rule = match meta.destination_rule {
DestinationRule::Path(_) => req.uri().path(),
DestinationRule::Header(ref h, _s) => req
.headers()
.get(h)
.ok_or(anyhow!(format!("head/{} not exist", h)))?
.to_str()
.map_err(|_| anyhow!("head/{} not legal", h))?,
};
Ok(rule)
}