diff --git a/Cargo.lock b/Cargo.lock index 0e559182438..8cf24f5c1d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -735,14 +735,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "bytes", "futures-util", "http 1.3.0", "http-body 1.0.1", "http-body-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -755,6 +755,40 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +dependencies = [ + "axum-core 0.5.2", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.3.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-core" version = "0.4.5" @@ -775,6 +809,26 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -2164,7 +2218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -4281,6 +4335,7 @@ dependencies = [ name = "http_api" version = "0.1.0" dependencies = [ + "axum 0.8.4", "beacon_chain", "beacon_processor", "bs58 0.4.0", @@ -4324,6 +4379,7 @@ dependencies = [ "types", "warp", "warp_utils", + "warpdrive", ] [[package]] @@ -5123,7 +5179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -5896,6 +5952,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "mdbx-sys" version = "0.11.6-4" @@ -6147,6 +6209,24 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 0.2.12", + "httparse", + "log", + "memchr", + "mime", + "spin", + "version_check", +] + [[package]] name = "multiaddr" version = "0.18.2" @@ -8494,10 +8574,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.223" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac" dependencies = [ + "serde_core", "serde_derive", ] @@ -8532,11 +8613,20 @@ dependencies = [ "serde_urlencoded", ] +[[package]] +name = "serde_core" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.223" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56" dependencies = [ "proc-macro2", "quote", @@ -8555,6 +8645,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a30a8abed938137c7183c173848e3c9b3517f5e038226849a4ecc9b21a4b4e2a" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_repr" version = "0.1.20" @@ -9552,6 +9653,18 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -9603,7 +9716,7 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.7.9", "base64 0.22.1", "bytes", "h2 0.4.8", @@ -9870,6 +9983,25 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.3.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.18.0" @@ -10054,6 +10186,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -10149,6 +10287,7 @@ name = "validator_http_api" version = "0.1.0" dependencies = [ "account_utils", + "axum 0.8.4", "beacon_node_fallback", "bls", "deposit_contract", @@ -10189,6 +10328,7 @@ dependencies = [ "validator_store", "warp", "warp_utils", + "warpdrive", "zeroize", ] @@ -10360,6 +10500,7 @@ dependencies = [ "log", "mime", "mime_guess", + "multer", "percent-encoding", "pin-project", "rustls-pemfile 2.2.0", @@ -10369,6 +10510,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-rustls 0.25.0", + "tokio-tungstenite", "tokio-util", "tower-service", "tracing", @@ -10390,6 +10532,17 @@ dependencies = [ "warp", ] +[[package]] +name = "warpdrive" +version = "0.1.0" +source = "git+http://github.com/macladson/warpdrive?tag=v0.1.0#ebbc144555d27ced2a3964001392d8477776ec33" +dependencies = [ + "axum 0.8.4", + "futures", + "tower 0.5.2", + "warp", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 99543dbfb49..6dba2577d58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,7 @@ alloy-rlp = "0.3.4" anyhow = "1" arbitrary = { version = "1", features = ["derive"] } async-channel = "1.9.0" -axum = "0.7.7" +axum = "0.8" beacon_chain = { path = "beacon_node/beacon_chain" } beacon_node = { path = "beacon_node" } beacon_node_fallback = { path = "validator_client/beacon_node_fallback" } @@ -278,6 +278,7 @@ validator_store = { path = "validator_client/validator_store" } validator_test_rig = { path = "testing/validator_test_rig" } warp = { version = "0.3.7", default-features = false, features = ["tls"] } warp_utils = { path = "common/warp_utils" } +warpdrive = { git = "http://github.com/macladson/warpdrive", tag = "v0.1.0" } workspace_members = { path = "common/workspace_members" } xdelta3 = { git = "http://github.com/sigp/xdelta3-rs", rev = "4db64086bb02e9febb584ba93b9d16bb2ae3825a" } zeroize = { version = "1", features = ["zeroize_derive", "serde"] } diff --git a/beacon_node/http_api/Cargo.toml b/beacon_node/http_api/Cargo.toml index 7dd0d0223f4..b7358b0fa73 100644 --- a/beacon_node/http_api/Cargo.toml +++ b/beacon_node/http_api/Cargo.toml @@ -6,6 +6,7 @@ edition = { workspace = true } autotests = false # using a single test binary compiles faster [dependencies] +axum = { workspace = true } beacon_chain = { workspace = true } beacon_processor = { workspace = true } bs58 = "0.4.0" @@ -48,6 +49,7 @@ tree_hash = { workspace = true } types = { workspace = true } warp = { workspace = true } warp_utils = { workspace = true } +warpdrive = { workspace = true } [dev-dependencies] genesis = { workspace = true } diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 1b18ed50a3f..35e402051d1 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -108,6 +108,10 @@ use warp::sse::Event; use warp::{Filter, Rejection, http::Response}; use warp_utils::{query::multi_key_query, reject::convert_rejection, uor::UnifyingOrFilter}; +use axum::Router; +use futures::FutureExt; +use warpdrive::WarpService; + const API_PREFIX: &str = "eth"; /// A custom type which allows for both unsecured and TLS-enabled HTTP servers. @@ -4901,27 +4905,33 @@ pub fn serve( .with(cors_builder.build()) .boxed(); + let router = Router::new().fallback_service(WarpService::new(routes)); + let http_socket: SocketAddr = SocketAddr::new(config.listen_addr, config.listen_port); + let std_listener = std::net::TcpListener::bind(http_socket) + .map_err(|e| format!("Failed to bind to socket: {}", e))?; + std_listener + .set_nonblocking(true) + .map_err(|e| format!("Failed to set non-blocking: {}", e))?; + let http_server: HttpServer = match config.tls_config { - Some(tls_config) => { - let (socket, server) = warp::serve(routes) - .tls() - .cert_path(tls_config.cert) - .key_path(tls_config.key) - .try_bind_with_graceful_shutdown(http_socket, async { - shutdown.await; - })?; - - info!("HTTP API is being served over TLS"); - - (socket, Box::pin(server)) + Some(_tls_config) => { + unimplemented!("TLS is unsupported") } None => { - let (socket, server) = - warp::serve(routes).try_bind_with_graceful_shutdown(http_socket, async { - shutdown.await; - })?; - (socket, Box::pin(server)) + let listener = tokio::net::TcpListener::from_std(std_listener) + .map_err(|e| format!("Failed to bind to socket: {}", e))?; + + let local_socket = listener + .local_addr() + .map_err(|e| format!("Failed to get local address: {}", e))?; + + let server = axum::serve(listener, router) + .with_graceful_shutdown(shutdown) + .into_future() + .map(|_| ()); + + (local_socket, Box::pin(server)) } }; diff --git a/validator_client/http_api/Cargo.toml b/validator_client/http_api/Cargo.toml index 588aa2ca931..b4bfa14bf3d 100644 --- a/validator_client/http_api/Cargo.toml +++ b/validator_client/http_api/Cargo.toml @@ -10,6 +10,7 @@ path = "src/lib.rs" [dependencies] account_utils = { workspace = true } +axum = { workspace = true } beacon_node_fallback = { workspace = true } bls = { workspace = true } deposit_contract = { workspace = true } @@ -20,6 +21,7 @@ eth2 = { workspace = true } eth2_keystore = { workspace = true } ethereum_serde_utils = { workspace = true } filesystem = { workspace = true } +futures = { workspace = true } graffiti_file = { workspace = true } health_metrics = { workspace = true } initialized_validators = { workspace = true } @@ -48,6 +50,7 @@ validator_services = { workspace = true } validator_store = { workspace = true } warp = { workspace = true } warp_utils = { workspace = true } +warpdrive = { workspace = true } zeroize = { workspace = true } [dev-dependencies] diff --git a/validator_client/http_api/src/lib.rs b/validator_client/http_api/src/lib.rs index 4494fca9574..7fb86f5766f 100644 --- a/validator_client/http_api/src/lib.rs +++ b/validator_client/http_api/src/lib.rs @@ -59,6 +59,11 @@ use warp::{Filter, reply::Response, sse::Event}; use warp_utils::reject::convert_rejection; use warp_utils::task::blocking_json_task; +use axum::Router; +use futures::FutureExt; +use std::future::IntoFuture; +use warpdrive::WarpService; + #[derive(Debug)] pub enum Error { Warp(warp::Error), @@ -155,7 +160,7 @@ pub fn serve( let use_long_timeouts = config.bn_long_timeouts; // Configure CORS. - let cors_builder = { + let warp_cors_builder = { let builder = warp::cors() .allow_methods(vec!["GET", "POST", "PATCH", "DELETE"]) .allow_headers(vec!["Content-Type", "Authorization"]); @@ -1396,20 +1401,36 @@ pub fn serve( .recover(warp_utils::reject::handle_rejection) // Add a `Server` header. .map(|reply| warp::reply::with_header(reply, "Server", &version_with_platform())) - .with(cors_builder.build()); + .with(warp_cors_builder.build()) + .boxed(); - let (listening_socket, server) = warp::serve(routes).try_bind_with_graceful_shutdown( - SocketAddr::new(config.listen_addr, config.listen_port), - async { - shutdown.await; - }, - )?; + let axum_router = Router::new().fallback_service(WarpService::new(routes)); + + let http_socket = SocketAddr::new(config.listen_addr, config.listen_port); + + let std_listener = std::net::TcpListener::bind(http_socket) + .map_err(|e| format!("Failed to bind to socket: {}", e))?; + std_listener + .set_nonblocking(true) + .map_err(|e| format!("Failed to set non-blocking: {}", e))?; + + let listener = tokio::net::TcpListener::from_std(std_listener) + .map_err(|e| Error::Other(format!("Failed to convert listener: {}", e)))?; + + let local_socket = listener + .local_addr() + .map_err(|e| format!("Failed to get local address: {}", e))?; + + let server = axum::serve(listener, axum_router) + .with_graceful_shutdown(shutdown) + .into_future() + .map(|_| ()); info!( - listen_address = listening_socket.to_string(), + listen_address = local_socket.to_string(), ?api_token_path, "HTTP API started" ); - Ok((listening_socket, server)) + Ok((local_socket, server)) }