pub mod backend; pub mod caioclookup; pub mod events; use crate::bodystream::ToPublicResponse; use crate::err::Error; use crate::gather::gather_get_json_generic; use crate::gather::SubRes; use crate::gather::Tag; use crate::requests::accepts_json_or_all; use crate::response; use crate::ReqCtx; use futures_util::Future; use http::Method; use http::Response; use http::StatusCode; use httpclient::body_empty; use httpclient::read_body_bytes; use httpclient::IntoBody; use httpclient::Requ; use httpclient::StreamResponse; use httpclient::ToJsonBody; use hyper::body::Incoming; use netpod::log::*; use netpod::req_uri_to_url; use netpod::ChannelSearchQuery; use netpod::ChannelSearchResult; use netpod::NodeStatus; use netpod::NodeStatusSub; use netpod::ProxyConfig; use netpod::ServiceVersion; use serde_json::Value as JsVal; use std::collections::VecDeque; use std::pin::Pin; use std::time::Duration; use url::Url; // TODO model channel search according to StatusNodesRecursive. // Make sure that backend handling is correct: // The aggregator asks all backends, except if the user specifies some backend // in which case it should only go to the matching backends. // The aggregators and leaf nodes behind should as well not depend on backend, // but simply answer all matching. pub async fn channel_search(req: Requ, ctx: &ReqCtx, proxy_config: &ProxyConfig) -> Result { let (head, _body) = req.into_parts(); let inpurl = req_uri_to_url(&head.uri)?; let query = ChannelSearchQuery::from_url(&inpurl)?; let mut urls = Vec::new(); let mut tags = Vec::new(); let mut bodies = Vec::new(); for pb in &proxy_config.backends { if if let Some(b) = &query.backend { pb.name.contains(b) } else { true } { match Url::parse(&format!("{}/api/4/search/channel", pb.url)) { Ok(mut url) => { query.append_to_url(&mut url); tags.push(url.to_string()); bodies.push(None); urls.push(url); } Err(_) => return Err(Error::with_msg(format!("parse error for: {:?}", pb))), } } } let nt = |tag: String, res: Response| { let fut = async { let (_head, body) = res.into_parts(); let body = read_body_bytes(body).await?; //info!("got a result {:?}", body); let res: ChannelSearchResult = match serde_json::from_slice(&body) { Ok(k) => k, Err(_) => { let msg = format!("can not parse result tag {} {}", tag, String::from_utf8_lossy(&body)); error!("{}", msg); return Err(Error::with_msg_no_trace(msg)); } }; info!("from {} len {} {:?}", tag, res.channels.len(), res.channels); let ret = SubRes { tag, status: StatusCode::OK, val: res, }; Ok(ret) }; Box::pin(fut) as Pin + Send>> }; let ft = |all: Vec<(Tag, Result, Error>)>| { let mut res = Vec::new(); for (_tag, j) in all { match j { Ok(j) => { for k in j.val.channels { res.push(k); } } Err(e) => { warn!("{e}"); } } } let res = ChannelSearchResult { channels: res }; Ok(res) }; let ret = gather_get_json_generic( http::Method::GET, urls, bodies, tags, nt, ft, Duration::from_millis(3000), ctx, ) .await?; Ok(ret) } pub struct ChannelSearchAggHandler {} impl ChannelSearchAggHandler { pub fn handler(req: &Requ) -> Option { if req.uri().path() == "/api/4/search/channel" { Some(Self {}) } else { None } } pub async fn handle(&self, req: Requ, ctx: &ReqCtx, node_config: &ProxyConfig) -> Result { if req.method() == Method::GET { if accepts_json_or_all(req.headers()) { match channel_search(req, ctx, node_config).await { Ok(item) => Ok(response(StatusCode::OK).body(ToJsonBody::from(&item).into_body())?), Err(e) => { warn!("ChannelConfigHandler::handle: got error from channel_config: {e:?}"); Ok(e.to_public_response()) } } } else { Ok(response(StatusCode::BAD_REQUEST).body(body_empty())?) } } else { Ok(response(StatusCode::METHOD_NOT_ALLOWED).body(body_empty())?) } } } pub struct StatusNodesRecursive {} impl StatusNodesRecursive { pub fn handler(req: &Requ) -> Option { if req.uri().path() == crate::api4::status::StatusNodesRecursive::path() { Some(Self {}) } else { None } } pub async fn handle( &self, req: Requ, ctx: &ReqCtx, proxy_config: &ProxyConfig, service_version: &ServiceVersion, ) -> Result { match self.status(req, ctx, proxy_config, service_version).await { Ok(status) => { let ret = response(StatusCode::OK).body(ToJsonBody::from(&status).into_body())?; Ok(ret) } Err(e) => { error!("StatusNodesRecursive sees: {e}"); let ret = crate::bodystream::ToPublicResponse::to_public_response(&e); Ok(ret) } } } async fn status( &self, _req: Requ, ctx: &ReqCtx, proxy_config: &ProxyConfig, service_version: &ServiceVersion, ) -> Result { let path = crate::api4::status::StatusNodesRecursive::path(); let mut bodies = Vec::new(); let mut urls = Vec::new(); let mut tags = Vec::new(); for sub in &proxy_config.status_subs { match Url::parse(&format!("{}{}", sub.url, path)) { Ok(url) => { bodies.push(None); tags.push(sub.url.to_string()); urls.push(url); } Err(e) => return Err(Error::with_msg_no_trace(format!("parse error for: {sub:?} {e:?}"))), } } let nt = |tag: String, res: Response| { let fut = async { let (_head, body) = res.into_parts(); let body = read_body_bytes(body).await?; let res: JsVal = match serde_json::from_slice(&body) { Ok(k) => k, Err(e) => { error!("{e}"); let msg = format!( "gather sub responses, can not parse result: {} {}", String::from_utf8_lossy(&body), e, ); error!("{}", msg); return Err(Error::with_msg_no_trace(msg)); } }; let ret = SubRes { tag, status: StatusCode::OK, val: res, }; Ok(ret) }; Box::pin(fut) as Pin + Send>> }; let ft = |all: Vec<(Tag, Result, Error>)>| { let mut subs = VecDeque::new(); for (tag, sr) in all { match sr { Ok(sr) => { let s: Result = serde_json::from_value(sr.val).map_err(daqbuf_err::Error::from); let sub = NodeStatusSub { url: tag.0, status: s }; subs.push_back(sub); } Err(e) => { let sub = NodeStatusSub { url: tag.0, status: Err(daqbuf_err::Error::from(e)), }; subs.push_back(sub); } } } let ret = NodeStatus { name: format!("{}:{}", proxy_config.name, proxy_config.port), version: service_version.to_string(), is_sf_databuffer: false, is_archiver_engine: false, is_archiver_appliance: false, database_size: None, archiver_appliance_status: None, subs, }; Ok(ret) }; let ret = gather_get_json_generic( http::Method::GET, urls, bodies, tags, nt, ft, Duration::from_millis(1200), ctx, ) .await?; Ok(ret) } }