use async_trait::async_trait; use base64::Engine; use base64::engine::general_purpose::STANDARD; use http::HeaderName; use log::{debug, info}; use pingora::{Error, ErrorType}; use pingora::prelude::{HttpPeer, ProxyHttp, Result, Session}; use super::service::HostConfig; pub struct ProxyApp { host_configs: Vec, } impl ProxyApp { pub fn new(host_configs: Vec) -> Self { ProxyApp { host_configs } } } #[async_trait] impl ProxyHttp for ProxyApp { type CTX = (); fn new_ctx(&self) {} async fn upstream_peer(&self, session: &mut Session, _ctx: &mut ()) -> Result> { let host_header = session .get_header(HeaderName::from_static("host")) .unwrap() .to_str() .expect("get host from http header failed"); debug!("host header: {host_header}"); if host_header == "localhost" || host_header.starts_with("localhost:") { return Err(Error::new(ErrorType::CustomCode("bad host", 400))); } let host_config = self .host_configs .iter() .find(|x| x.proxy_hostname == host_header) .unwrap_or_else(|| panic!("find config for: {} failed", host_header)); let proxy_to = HttpPeer::new( host_config.proxy_addr.as_str(), host_config.proxy_tls, host_config.proxy_hostname.clone(), ); let peer = Box::new(proxy_to); Ok(peer) } async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result where Self::CTX: Send + Sync, { let request_header = session.req_header(); let mut req = String::with_capacity(512); req.push_str(request_header.method.as_str()); req.push(' '); req.push_str(&request_header.uri.to_string()); req.push(' '); req.push_str(&format!("{:?}\n", request_header.version)); let header_len = request_header.headers.len(); request_header.headers.iter().enumerate().for_each(|(i, (n, v))| { req.push_str( &format!("{}: {}{}", n.as_str(), v.to_str().unwrap_or("ERROR!BAD-VALUE!"), if i < header_len - 1 { "\n" } else { "" } ) ); }); let body = match session.read_request_body().await { Ok(Some(body_bytes)) => Some(STANDARD.encode(body_bytes)), _ => None, }; info!("Request:\n{}\n\n{}", req, body.unwrap_or_else(|| "".into())); Ok(false) } }