diff --git a/apps/freenet-ping/app/tests/run_app.rs b/apps/freenet-ping/app/tests/run_app.rs index d92f5a2fd..109c703ba 100644 --- a/apps/freenet-ping/app/tests/run_app.rs +++ b/apps/freenet-ping/app/tests/run_app.rs @@ -237,7 +237,7 @@ async fn test_node_diagnostics_query() -> TestResult { let config = config_gw.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -248,7 +248,7 @@ async fn test_node_diagnostics_query() -> TestResult { let config = config_node.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -592,7 +592,7 @@ async fn test_ping_multi_node() -> TestResult { let config = config_gw.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -603,7 +603,7 @@ async fn test_ping_multi_node() -> TestResult { let config = config_node1.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -614,7 +614,7 @@ async fn test_ping_multi_node() -> TestResult { let config = config_node2.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -1187,7 +1187,7 @@ async fn test_ping_application_loop() -> TestResult { let config = config_gw.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -1198,7 +1198,7 @@ async fn test_ping_application_loop() -> TestResult { let config = config_node1.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -1209,7 +1209,7 @@ async fn test_ping_application_loop() -> TestResult { let config = config_node2.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -1678,7 +1678,7 @@ async fn test_ping_partially_connected_network() -> TestResult { let config = config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -1693,7 +1693,7 @@ async fn test_ping_partially_connected_network() -> TestResult { let config = config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/apps/freenet-ping/app/tests/run_app_blocked_peers.rs b/apps/freenet-ping/app/tests/run_app_blocked_peers.rs index 4e4c1e822..496859930 100644 --- a/apps/freenet-ping/app/tests/run_app_blocked_peers.rs +++ b/apps/freenet-ping/app/tests/run_app_blocked_peers.rs @@ -181,7 +181,7 @@ async fn run_blocked_peers_test(config: BlockedPeersConfig) -> TestResult { let config = config_gw.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -191,7 +191,7 @@ async fn run_blocked_peers_test(config: BlockedPeersConfig) -> TestResult { let config = config_node1.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -201,7 +201,7 @@ async fn run_blocked_peers_test(config: BlockedPeersConfig) -> TestResult { let config = config_node2.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/apps/freenet-ping/app/tests/run_app_partially_connected_network.rs b/apps/freenet-ping/app/tests/run_app_partially_connected_network.rs index 25e146d03..fd16cdcd6 100644 --- a/apps/freenet-ping/app/tests/run_app_partially_connected_network.rs +++ b/apps/freenet-ping/app/tests/run_app_partially_connected_network.rs @@ -200,7 +200,7 @@ async fn test_ping_partially_connected_network() -> TestResult { let config = config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -218,7 +218,7 @@ async fn test_ping_partially_connected_network() -> TestResult { let config = config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/apps/freenet-ping/app/tests/test_50_node_operations.rs b/apps/freenet-ping/app/tests/test_50_node_operations.rs index 8fc2e8ce3..468dd3114 100644 --- a/apps/freenet-ping/app/tests/test_50_node_operations.rs +++ b/apps/freenet-ping/app/tests/test_50_node_operations.rs @@ -162,7 +162,7 @@ async fn setup_50_node_network() -> TestResult<(Vec, Vec, Contra let config = config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -192,7 +192,7 @@ async fn setup_50_node_network() -> TestResult<(Vec, Vec, Contra let config = config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/apps/freenet-ping/app/tests/test_connection_timing.rs b/apps/freenet-ping/app/tests/test_connection_timing.rs index 793e392bd..c09102cb8 100644 --- a/apps/freenet-ping/app/tests/test_connection_timing.rs +++ b/apps/freenet-ping/app/tests/test_connection_timing.rs @@ -65,7 +65,7 @@ async fn test_connection_timing() -> TestResult { let config = config_gw.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -76,7 +76,7 @@ async fn test_connection_timing() -> TestResult { let config = config_node1.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/apps/freenet-ping/app/tests/test_small_network_get_issue.rs b/apps/freenet-ping/app/tests/test_small_network_get_issue.rs index f01887fb2..8e4f4f5f8 100644 --- a/apps/freenet-ping/app/tests/test_small_network_get_issue.rs +++ b/apps/freenet-ping/app/tests/test_small_network_get_issue.rs @@ -90,7 +90,7 @@ async fn test_small_network_get_failure() -> TestResult { node_config.min_number_of_connections(2); node_config.max_number_of_connections(10); let node = node_config - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -103,7 +103,7 @@ async fn test_small_network_get_failure() -> TestResult { node_config.min_number_of_connections(2); node_config.max_number_of_connections(10); let node = node_config - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -116,7 +116,7 @@ async fn test_small_network_get_failure() -> TestResult { node_config.min_number_of_connections(2); node_config.max_number_of_connections(10); let node = node_config - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/crates/core/src/bin/freenet.rs b/crates/core/src/bin/freenet.rs index a2d8a7dab..0671dc83e 100644 --- a/crates/core/src/bin/freenet.rs +++ b/crates/core/src/bin/freenet.rs @@ -31,7 +31,9 @@ async fn run_local(config: Config) -> anyhow::Result<()> { async fn run_network(config: Config) -> anyhow::Result<()> { tracing::info!("Starting freenet node in network mode"); - let clients = serve_gateway(config.ws_api).await; + let clients = serve_gateway(config.ws_api) + .await + .with_context(|| "failed to start HTTP/WebSocket gateway")?; tracing::info!("Initializing node configuration"); let node_config = NodeConfig::new(config) diff --git a/crates/core/src/node/mod.rs b/crates/core/src/node/mod.rs index 5a8199fec..d80ba4e24 100644 --- a/crates/core/src/node/mod.rs +++ b/crates/core/src/node/mod.rs @@ -1418,7 +1418,7 @@ pub async fn run_local_node( _ => {} } - let (mut gw, mut ws_proxy) = crate::server::serve_gateway_in(socket).await; + let (mut gw, mut ws_proxy) = crate::server::serve_gateway_in(socket).await?; // TODO: use combinator instead // let mut all_clients = diff --git a/crates/core/src/server/mod.rs b/crates/core/src/server/mod.rs index 1e9466a7d..2cede4cee 100644 --- a/crates/core/src/server/mod.rs +++ b/crates/core/src/server/mod.rs @@ -66,14 +66,28 @@ pub(crate) enum HostCallbackResult { }, } -fn serve(socket: SocketAddr, router: axum::Router) { +async fn serve(socket: SocketAddr, router: axum::Router) -> std::io::Result<()> { + let listener = tokio::net::TcpListener::bind(socket).await.map_err(|e| { + if e.kind() == std::io::ErrorKind::AddrInUse { + std::io::Error::new( + std::io::ErrorKind::AddrInUse, + format!( + "Port {} is already in use. Another freenet process may be running. \ + Use 'pkill freenet' to stop it, or specify a different port with --gateway-port.", + socket.port() + ), + ) + } else { + e + } + })?; + tracing::info!("HTTP gateway listening on {}", socket); tokio::spawn(async move { - tracing::info!("HTTP gateway listening on {}", socket); - let listener = tokio::net::TcpListener::bind(socket).await.unwrap(); axum::serve(listener, router).await.map_err(|e| { tracing::error!("Error while running HTTP gateway server: {e}"); }) }); + Ok(()) } pub mod local_node { @@ -101,7 +115,7 @@ pub mod local_node { let (mut gw, gw_router) = HttpGateway::as_router(&socket); let (mut ws_proxy, ws_router) = WebSocketProxy::create_router(gw_router); - serve(socket, ws_router.layer(TraceLayer::new_for_http())); + serve(socket, ws_router.layer(TraceLayer::new_for_http())).await?; // TODO: use combinator instead // let mut all_clients = @@ -199,23 +213,25 @@ pub mod local_node { } } -pub async fn serve_gateway(config: WebsocketApiConfig) -> [BoxedClient; 2] { - let (gw, ws_proxy) = serve_gateway_in(config).await; - [Box::new(gw), Box::new(ws_proxy)] +pub async fn serve_gateway(config: WebsocketApiConfig) -> std::io::Result<[BoxedClient; 2]> { + let (gw, ws_proxy) = serve_gateway_in(config).await?; + Ok([Box::new(gw), Box::new(ws_proxy)]) } /// Serves the gateway and returns the concrete types (for integration testing). /// This allows tests to access internal state like the attested_contracts map. pub async fn serve_gateway_for_test( config: WebsocketApiConfig, -) -> ( +) -> std::io::Result<( http_gateway::HttpGateway, crate::client_events::websocket::WebSocketProxy, -) { +)> { serve_gateway_in(config).await } -pub(crate) async fn serve_gateway_in(config: WebsocketApiConfig) -> (HttpGateway, WebSocketProxy) { +pub(crate) async fn serve_gateway_in( + config: WebsocketApiConfig, +) -> std::io::Result<(HttpGateway, WebSocketProxy)> { let ws_socket = (config.address, config.port).into(); // Create a shared attested_contracts map with token expiration support @@ -234,8 +250,8 @@ pub(crate) async fn serve_gateway_in(config: WebsocketApiConfig) -> (HttpGateway let (ws_proxy, ws_router) = WebSocketProxy::create_router_with_attested_contracts(gw_router, attested_contracts); - serve(ws_socket, ws_router.layer(TraceLayer::new_for_http())); - (gw, ws_proxy) + serve(ws_socket, ws_router.layer(TraceLayer::new_for_http())).await?; + Ok((gw, ws_proxy)) } /// Spawns a background task that periodically removes expired authentication tokens. diff --git a/crates/core/tests/error_notification.rs b/crates/core/tests/error_notification.rs index 392a78019..b266145d9 100644 --- a/crates/core/tests/error_notification.rs +++ b/crates/core/tests/error_notification.rs @@ -379,7 +379,7 @@ async fn test_connection_drop_error_notification() -> anyhow::Result<()> { let config = gateway_config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -391,7 +391,7 @@ async fn test_connection_drop_error_notification() -> anyhow::Result<()> { let config = peer_config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; // Run node until we receive shutdown signal diff --git a/crates/core/tests/operations.rs b/crates/core/tests/operations.rs index 8bf0a80e1..d43463420 100644 --- a/crates/core/tests/operations.rs +++ b/crates/core/tests/operations.rs @@ -2219,7 +2219,7 @@ async fn test_gateway_packet_size_change_after_60s() -> TestResult { let config = config_gw1.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -2230,7 +2230,7 @@ async fn test_gateway_packet_size_change_after_60s() -> TestResult { let config = config_gw2.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -2241,7 +2241,7 @@ async fn test_gateway_packet_size_change_after_60s() -> TestResult { let config = config_client.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -2420,7 +2420,7 @@ async fn test_production_decryption_error_scenario() -> TestResult { let config = config_gw.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -2430,7 +2430,7 @@ async fn test_production_decryption_error_scenario() -> TestResult { let config = config_client.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/crates/core/tests/token_expiration.rs b/crates/core/tests/token_expiration.rs index ff37cc72a..0d1729832 100644 --- a/crates/core/tests/token_expiration.rs +++ b/crates/core/tests/token_expiration.rs @@ -198,6 +198,8 @@ async fn test_token_cleanup_removes_expired_tokens() -> TestResult { let ws_socket = TcpListener::bind("127.0.0.1:0")?; let ws_port = ws_socket.local_addr()?.port(); + // Drop the socket to release the port before the gateway binds to it + drop(ws_socket); let config = WebsocketApiConfig { address: Ipv4Addr::LOCALHOST.into(), @@ -207,7 +209,7 @@ async fn test_token_cleanup_removes_expired_tokens() -> TestResult { }; // Start the gateway server (which spawns the cleanup task) - let (gw, _ws_proxy) = serve_gateway_for_test(config).await; + let (gw, _ws_proxy) = serve_gateway_for_test(config).await?; // Access the attested_contracts map via the test-only method let attested_contracts = gw.attested_contracts(); diff --git a/crates/core/tests/ubertest.rs b/crates/core/tests/ubertest.rs index de7ed05c3..bc19536e2 100644 --- a/crates/core/tests/ubertest.rs +++ b/crates/core/tests/ubertest.rs @@ -301,7 +301,7 @@ async fn test_basic_room_creation() -> anyhow::Result<()> { let config = gw_config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -313,7 +313,7 @@ async fn test_basic_room_creation() -> anyhow::Result<()> { let config = peer_config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -416,7 +416,7 @@ async fn test_app_ubertest() -> anyhow::Result<()> { let config = gw_config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } @@ -463,7 +463,7 @@ async fn test_app_ubertest() -> anyhow::Result<()> { let config = peer_config.build().await?; let node = NodeConfig::new(config.clone()) .await? - .build(serve_gateway(config.ws_api).await) + .build(serve_gateway(config.ws_api).await?) .await?; node.run().await } diff --git a/crates/freenet-macros/src/codegen.rs b/crates/freenet-macros/src/codegen.rs index e6aba8038..7822c4c38 100644 --- a/crates/freenet-macros/src/codegen.rs +++ b/crates/freenet-macros/src/codegen.rs @@ -449,7 +449,7 @@ fn generate_node_builds(args: &FreenetTestArgs) -> TokenStream { let mut node_config = freenet::local_node::NodeConfig::new(built_config.clone()).await?; #connection_tuning let (#node_var, #flush_handle_var) = node_config - .build_with_flush_handle(freenet::server::serve_gateway(built_config.ws_api).await) + .build_with_flush_handle(freenet::server::serve_gateway(built_config.ws_api).await?) .await?; }); }