diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 4fcebb123d9e3..b3620d75300da 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -97,6 +97,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_max_response_size: None, rpc_id_provider: None, rpc_max_subs_per_conn: None, + rpc_allow_fallback_to_random_port: true, ws_max_out_buffer_capacity: None, prometheus_config: None, telemetry_endpoints: None, diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index a8839642ddc26..03d2dd849c7f3 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -90,6 +90,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_max_response_size: None, rpc_id_provider: None, rpc_max_subs_per_conn: None, + rpc_allow_fallback_to_random_port: true, ws_max_out_buffer_capacity: None, prometheus_config: None, telemetry_endpoints: None, diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 35181d83f805f..58fb19950c2b8 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -116,6 +116,11 @@ pub struct RunCmd { #[arg(long)] pub rpc_max_subscriptions_per_connection: Option, + /// Use random port if port configured for Websocket or HTTP is already in use + /// Default is true. + #[clap(long)] + pub rpc_allow_fallback_to_random_port: Option, + /// Expose Prometheus exporter on all interfaces. /// /// Default is local. @@ -465,6 +470,10 @@ impl CliConfiguration for RunCmd { Ok(self.rpc_max_subscriptions_per_connection) } + fn rpc_allow_fallback_to_random_port(&self) -> Result { + Ok(self.rpc_allow_fallback_to_random_port.unwrap_or(true)) + } + fn ws_max_out_buffer_capacity(&self) -> Result> { Ok(self.ws_max_out_buffer_capacity) } diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 77689708a231f..74683cb2f8759 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -365,6 +365,11 @@ pub trait CliConfiguration: Sized { Ok(None) } + /// Use random port if port configured for Websocket or HTTP is already in use + fn rpc_allow_fallback_to_random_port(&self) -> Result { + Ok(true) + } + /// Get maximum WS output buffer capacity. fn ws_max_out_buffer_capacity(&self) -> Result> { Ok(None) @@ -544,6 +549,7 @@ pub trait CliConfiguration: Sized { rpc_max_response_size: self.rpc_max_response_size()?, rpc_id_provider: None, rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, + rpc_allow_fallback_to_random_port: self.rpc_allow_fallback_to_random_port()?, ws_max_out_buffer_capacity: self.ws_max_out_buffer_capacity()?, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, diff --git a/client/rpc-servers/src/lib.rs b/client/rpc-servers/src/lib.rs index 7eb825e169bfa..75a447e4aea5c 100644 --- a/client/rpc-servers/src/lib.rs +++ b/client/rpc-servers/src/lib.rs @@ -79,7 +79,7 @@ impl WsConfig { /// Start HTTP server listening on given address. pub async fn start_http( - addrs: [SocketAddr; 2], + addrs: &[SocketAddr], cors: Option<&Vec>, max_payload_in_mb: Option, max_payload_out_mb: Option, @@ -95,7 +95,7 @@ pub async fn start_http( if let Some(cors) = cors { // Whitelist listening address. // NOTE: set_allowed_hosts will whitelist both ports but only one will used. - acl = acl.set_allowed_hosts(format_allowed_hosts(&addrs[..]))?; + acl = acl.set_allowed_hosts(format_allowed_hosts(addrs))?; acl = acl.set_allowed_origins(cors)?; }; @@ -107,30 +107,40 @@ pub async fn start_http( .custom_tokio_runtime(rt); let rpc_api = build_rpc_api(rpc_api); - let (handle, addr) = if let Some(metrics) = metrics { + let server_start_result = if let Some(metrics) = metrics { let middleware = RpcMiddleware::new(metrics, "http".into()); let builder = builder.set_middleware(middleware); - let server = builder.build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) + builder.build(addrs).await.and_then(|server| { + let addr = server.local_addr(); + Ok((server.start(rpc_api)?, addr)) + }) } else { - let server = builder.build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) + builder.build(addrs).await.and_then(|server| { + let addr = server.local_addr(); + Ok((server.start(rpc_api)?, addr)) + }) }; - log::info!( - "Running JSON-RPC HTTP server: addr={}, allowed origins={:?}", - addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), - cors - ); - - Ok(handle) + match server_start_result { + Ok((handle, addr)) => { + log::info!( + "Running JSON-RPC HTTP server: addr={}, allowed origins={:?}", + addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), + cors + ); + + Ok(handle) + }, + Err(error) => { + log::info!("Error on starting JSON-RPC HTTP server: {}", error); + Err(error.into()) + }, + } } /// Start WS server listening on given address. pub async fn start_ws( - addrs: [SocketAddr; 2], + addrs: &[SocketAddr], cors: Option<&Vec>, ws_config: WsConfig, metrics: Option, @@ -146,7 +156,7 @@ pub async fn start_ws( if let Some(cors) = cors { // Whitelist listening address. // NOTE: set_allowed_hosts will whitelist both ports but only one will used. - acl = acl.set_allowed_hosts(format_allowed_hosts(&addrs[..]))?; + acl = acl.set_allowed_hosts(format_allowed_hosts(addrs))?; acl = acl.set_allowed_origins(cors)?; }; @@ -166,25 +176,35 @@ pub async fn start_ws( }; let rpc_api = build_rpc_api(rpc_api); - let (handle, addr) = if let Some(metrics) = metrics { + let server_start_result = if let Some(metrics) = metrics { let middleware = RpcMiddleware::new(metrics, "ws".into()); let builder = builder.set_middleware(middleware); - let server = builder.build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) + builder.build(addrs).await.and_then(|server| { + let addr = server.local_addr(); + Ok((server.start(rpc_api)?, addr)) + }) } else { - let server = builder.build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) + builder.build(addrs).await.and_then(|server| { + let addr = server.local_addr(); + Ok((server.start(rpc_api)?, addr)) + }) }; - log::info!( - "Running JSON-RPC WS server: addr={}, allowed origins={:?}", - addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), - cors - ); - - Ok(handle) + match server_start_result { + Ok((handle, addr)) => { + log::info!( + "Running JSON-RPC WS server: addr={}, allowed origins={:?}", + addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), + cors + ); + + Ok(handle) + }, + Err(error) => { + log::info!("Error on starting JSON-RPC WS server: {}", error); + Err(error.into()) + }, + } } fn format_allowed_hosts(addrs: &[SocketAddr]) -> Vec { diff --git a/client/service/src/config.rs b/client/service/src/config.rs index bca0697bcbd08..ea289893fa09d 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -113,6 +113,8 @@ pub struct Configuration { /// /// Default: 1024. pub rpc_max_subs_per_conn: Option, + /// Use random port if port configured for Websocket or HTTP is already in use + pub rpc_allow_fallback_to_random_port: bool, /// Maximum size of the output buffer capacity for websocket connections. pub ws_max_out_buffer_capacity: Option, /// Prometheus endpoint configuration. `None` if disabled. diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 091b4bbe9fe5f..c639561535a14 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -345,16 +345,20 @@ where let ws_addr = config .rpc_ws .unwrap_or_else(|| "127.0.0.1:9944".parse().expect("valid sockaddr; qed")); - let ws_addr2 = random_port(ws_addr); + let ws_addrs = [ws_addr, random_port(ws_addr)]; + let ws_addrs = + if config.rpc_allow_fallback_to_random_port { &ws_addrs[..] } else { &ws_addrs[..1] }; + let http_addr = config .rpc_http .unwrap_or_else(|| "127.0.0.1:9933".parse().expect("valid sockaddr; qed")); - let http_addr2 = random_port(http_addr); + let http_addrs = [http_addr, random_port(http_addr)]; + let http_addrs = + if config.rpc_allow_fallback_to_random_port { &http_addrs[..] } else { &http_addrs[..1] }; let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; - let http_fut = sc_rpc_server::start_http( - [http_addr, http_addr2], + http_addrs, config.rpc_cors.as_ref(), max_request_size, http_max_response_size, @@ -371,7 +375,7 @@ where }; let ws_fut = sc_rpc_server::start_ws( - [ws_addr, ws_addr2], + ws_addrs, config.rpc_cors.as_ref(), ws_config, metrics, diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 5d29d34a3cbf2..ed8151fff1e96 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -250,6 +250,7 @@ fn node_config< rpc_max_response_size: None, rpc_id_provider: None, rpc_max_subs_per_conn: None, + rpc_allow_fallback_to_random_port: true, ws_max_out_buffer_capacity: None, prometheus_config: None, telemetry_endpoints: None,