diff --git a/Cargo.lock b/Cargo.lock index b3b796c031d..eeed878cb39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3271,11 +3271,11 @@ dependencies = [ name = "libp2p-server" version = "0.12.7" dependencies = [ + "axum 0.7.5", "base64 0.22.0", "clap", "futures", "futures-timer", - "hyper 0.14.27", "libp2p", "prometheus-client", "serde", @@ -3748,8 +3748,8 @@ dependencies = [ name = "metrics-example" version = "0.1.0" dependencies = [ + "axum 0.7.5", "futures", - "hyper 0.14.27", "libp2p", "opentelemetry", "opentelemetry-otlp", diff --git a/examples/metrics/Cargo.toml b/examples/metrics/Cargo.toml index e84b4819897..5059bc4085b 100644 --- a/examples/metrics/Cargo.toml +++ b/examples/metrics/Cargo.toml @@ -10,7 +10,7 @@ release = false [dependencies] futures = "0.3.30" -hyper = { version = "0.14", features = ["server", "tcp", "http1"] } +axum = "0.7" libp2p = { path = "../../libp2p", features = ["tokio", "metrics", "ping", "noise", "identify", "tcp", "yamux", "macros"] } opentelemetry = { version = "0.22.0", features = ["metrics"] } opentelemetry-otlp = { version = "0.15.0", features = ["metrics"] } diff --git a/examples/metrics/src/http_service.rs b/examples/metrics/src/http_service.rs index 8c77d724ea3..4a9c9785bb3 100644 --- a/examples/metrics/src/http_service.rs +++ b/examples/metrics/src/http_service.rs @@ -18,109 +18,60 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use hyper::http::StatusCode; -use hyper::service::Service; -use hyper::{Body, Method, Request, Response, Server}; +use axum::extract::State; +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::routing::get; +use axum::Router; use prometheus_client::encoding::text::encode; use prometheus_client::registry::Registry; -use std::future::Future; -use std::pin::Pin; +use std::net::SocketAddr; use std::sync::{Arc, Mutex}; -use std::task::{Context, Poll}; +use tokio::net::TcpListener; const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text;charset=utf-8;version=1.0.0"; pub(crate) async fn metrics_server(registry: Registry) -> Result<(), std::io::Error> { // Serve on localhost. - let addr = ([127, 0, 0, 1], 0).into(); - - let server = Server::bind(&addr).serve(MakeMetricService::new(registry)); - tracing::info!(metrics_server=%format!("http://{}/metrics", server.local_addr())); - if let Err(e) = server.await { - tracing::error!("server error: {}", e); - } + let addr: SocketAddr = ([127, 0, 0, 1], 0).into(); + let service = MetricService::new(registry); + let server = Router::new() + .route("/metrics", get(respond_with_metrics)) + .with_state(service); + let tcp_listener = TcpListener::bind(addr).await?; + let local_addr = tcp_listener.local_addr()?; + tracing::info!(metrics_server=%format!("http://{}/metrics", local_addr)); + axum::serve(tcp_listener, server.into_make_service()).await?; Ok(()) } +#[derive(Clone)] pub(crate) struct MetricService { reg: Arc>, } -type SharedRegistry = Arc>; - -impl MetricService { - fn get_reg(&mut self) -> SharedRegistry { - Arc::clone(&self.reg) - } - fn respond_with_metrics(&mut self) -> Response { - let mut response: Response = Response::default(); - - response.headers_mut().insert( - hyper::header::CONTENT_TYPE, - METRICS_CONTENT_TYPE.try_into().unwrap(), - ); - - let reg = self.get_reg(); - encode(&mut response.body_mut(), ®.lock().unwrap()).unwrap(); - - *response.status_mut() = StatusCode::OK; - - response - } - fn respond_with_404_not_found(&mut self) -> Response { - Response::builder() - .status(StatusCode::NOT_FOUND) - .body("Not found try localhost:[port]/metrics".to_string()) - .unwrap() - } -} - -impl Service> for MetricService { - type Response = Response; - type Error = hyper::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } +async fn respond_with_metrics(state: State) -> impl IntoResponse { + let mut sink = String::new(); + let reg = state.get_reg(); + encode(&mut sink, ®.lock().unwrap()).unwrap(); - fn call(&mut self, req: Request) -> Self::Future { - let req_path = req.uri().path(); - let req_method = req.method(); - let resp = if (req_method == Method::GET) && (req_path == "/metrics") { - // Encode and serve metrics from registry. - self.respond_with_metrics() - } else { - self.respond_with_404_not_found() - }; - Box::pin(async { Ok(resp) }) - } + ( + StatusCode::OK, + [(axum::http::header::CONTENT_TYPE, METRICS_CONTENT_TYPE)], + sink, + ) } -pub(crate) struct MakeMetricService { - reg: SharedRegistry, -} +type SharedRegistry = Arc>; -impl MakeMetricService { - pub(crate) fn new(registry: Registry) -> MakeMetricService { - MakeMetricService { +impl MetricService { + fn new(registry: Registry) -> Self { + Self { reg: Arc::new(Mutex::new(registry)), } } -} - -impl Service for MakeMetricService { - type Response = MetricService; - type Error = hyper::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, _: T) -> Self::Future { - let reg = self.reg.clone(); - let fut = async move { Ok(MetricService { reg }) }; - Box::pin(fut) + fn get_reg(&self) -> SharedRegistry { + Arc::clone(&self.reg) } } diff --git a/misc/server/Cargo.toml b/misc/server/Cargo.toml index 74576ad39f5..14c29b3ebb9 100644 --- a/misc/server/Cargo.toml +++ b/misc/server/Cargo.toml @@ -15,7 +15,7 @@ base64 = "0.22" clap = { version = "4.5.4", features = ["derive"] } futures = "0.3" futures-timer = "3" -hyper = { version = "0.14", features = ["server", "tcp", "http1"] } +axum = "0.7" libp2p = { workspace = true, features = ["autonat", "dns", "tokio", "noise", "tcp", "yamux", "identify", "kad", "ping", "relay", "metrics", "rsa", "macros", "quic", "websocket"] } prometheus-client = { workspace = true } serde = "1.0.197" diff --git a/misc/server/src/http_service.rs b/misc/server/src/http_service.rs index 7905933fbf5..cee1aa96e28 100644 --- a/misc/server/src/http_service.rs +++ b/misc/server/src/http_service.rs @@ -18,115 +18,62 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use hyper::http::StatusCode; -use hyper::service::Service; -use hyper::{Body, Method, Request, Response, Server}; +use axum::extract::State; +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::routing::get; +use axum::Router; use prometheus_client::encoding::text::encode; use prometheus_client::registry::Registry; -use std::future::Future; -use std::pin::Pin; +use std::net::SocketAddr; use std::sync::{Arc, Mutex}; -use std::task::{Context, Poll}; +use tokio::net::TcpListener; const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text;charset=utf-8;version=1.0.0"; pub(crate) async fn metrics_server( registry: Registry, metrics_path: String, -) -> Result<(), hyper::Error> { +) -> Result<(), std::io::Error> { // Serve on localhost. - let addr = ([0, 0, 0, 0], 8888).into(); - - let server = Server::bind(&addr).serve(MakeMetricService::new(registry, metrics_path.clone())); - tracing::info!(metrics_server=%format!("http://{}{}", server.local_addr(), metrics_path)); - server.await?; + let addr: SocketAddr = ([0, 0, 0, 0], 8888).into(); + let service = MetricService::new(registry); + let server = Router::new() + .route(&metrics_path, get(respond_with_metrics)) + .with_state(service); + let tcp_listener = TcpListener::bind(addr).await?; + let local_addr = tcp_listener.local_addr()?; + tracing::info!(metrics_server=%format!("http://{}{}", local_addr, metrics_path)); + axum::serve(tcp_listener, server.into_make_service()).await?; Ok(()) } -pub(crate) struct MetricService { - reg: Arc>, - metrics_path: String, -} -type SharedRegistry = Arc>; +async fn respond_with_metrics(state: State) -> impl IntoResponse { + let mut sink = String::new(); + let reg = state.get_reg(); + encode(&mut sink, ®.lock().unwrap()).unwrap(); -impl MetricService { - fn get_reg(&mut self) -> SharedRegistry { - Arc::clone(&self.reg) - } - fn respond_with_metrics(&mut self) -> Response { - let mut response: Response = Response::default(); - - response.headers_mut().insert( - hyper::header::CONTENT_TYPE, - METRICS_CONTENT_TYPE.try_into().unwrap(), - ); - - let reg = self.get_reg(); - encode(&mut response.body_mut(), ®.lock().unwrap()).unwrap(); - - *response.status_mut() = StatusCode::OK; - - response - } - fn respond_with_404_not_found(&mut self) -> Response { - Response::builder() - .status(StatusCode::NOT_FOUND) - .body(format!( - "Not found try localhost:[port]/{}", - self.metrics_path - )) - .unwrap() - } + ( + StatusCode::OK, + [(axum::http::header::CONTENT_TYPE, METRICS_CONTENT_TYPE)], + sink, + ) } -impl Service> for MetricService { - type Response = Response; - type Error = hyper::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Request) -> Self::Future { - let req_path = req.uri().path(); - let req_method = req.method(); - let resp = if (req_method == Method::GET) && (req_path == self.metrics_path) { - // Encode and serve metrics from registry. - self.respond_with_metrics() - } else { - self.respond_with_404_not_found() - }; - Box::pin(async { Ok(resp) }) - } +#[derive(Clone)] +pub(crate) struct MetricService { + reg: Arc>, } -pub(crate) struct MakeMetricService { - reg: SharedRegistry, - metrics_path: String, -} +type SharedRegistry = Arc>; -impl MakeMetricService { - pub(crate) fn new(registry: Registry, metrics_path: String) -> MakeMetricService { - MakeMetricService { - reg: Arc::new(Mutex::new(registry)), - metrics_path, +impl MetricService { + fn new(reg: Registry) -> Self { + Self { + reg: Arc::new(Mutex::new(reg)), } } -} - -impl Service for MakeMetricService { - type Response = MetricService; - type Error = hyper::Error; - type Future = Pin> + Send>>; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: T) -> Self::Future { - let reg = self.reg.clone(); - let metrics_path = self.metrics_path.clone(); - let fut = async move { Ok(MetricService { reg, metrics_path }) }; - Box::pin(fut) + fn get_reg(&self) -> SharedRegistry { + Arc::clone(&self.reg) } }