use std::net::SocketAddr;
use http_body_util::{BodyExt, Empty};
use hyper::body::Buf;
use hyper_util::rt::TokioIo;
use tokio::net::{TcpStream, ToSocketAddrs};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Hyper HTTP client/server error: {0}")]
Hyper(#[from] hyper::Error),
#[error("HTTP library error: {0}")]
Http(#[from] axum::http::Error), #[error("Serde JSON deserialization error: {0}")]
Serde(#[from] serde_json::Error),
}
pub(crate) async fn fetch<T, F>(addrs: T, path: &str) -> Result<(F, SocketAddr), Error>
where
T: ToSocketAddrs, F: serde::de::DeserializeOwned, {
let stream = TcpStream::connect(addrs).await?;
let connected_addr = stream.peer_addr()?;
let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
tokio::spawn(async move {
if let Err(err) = conn.await {
log::warn!("HTTP connection failed: {}", err);
}
});
let url = format!("http://{}{}", connected_addr, path);
let req = hyper::Request::builder()
.uri(url)
.header(hyper::header::HOST, connected_addr.to_string())
.body(Empty::<hyper::body::Bytes>::new())?;
let res = sender.send_request(req).await?;
let body_bytes = res.collect().await?.aggregate();
let data: F = serde_json::from_reader(body_bytes.reader())?;
Ok((data, connected_addr))
}