route-util 0.1.0

route utils
Documentation
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)))?;

    // 获取meta
    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)
}