From 24ad74c0f7114600e0ceb810e6d5529e72b66c1e Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Fri, 27 May 2022 00:40:32 +0400 Subject: [PATCH 1/4] Allow using a different scheme for peer RPC connections and use ws by default --- crates/humanode-peer/src/cli/config.rs | 11 +++++++- crates/humanode-peer/src/cli/params.rs | 17 ++++++++++--- crates/humanode-peer/src/rpc_url.rs | 35 +++++++++++++++++++------- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/crates/humanode-peer/src/cli/config.rs b/crates/humanode-peer/src/cli/config.rs index 727fb55ac..69cdfdb04 100644 --- a/crates/humanode-peer/src/cli/config.rs +++ b/crates/humanode-peer/src/cli/config.rs @@ -94,13 +94,22 @@ fn rpc_url_from_params(params: &BioauthFlowParams, rpc_port: Option) -> Rpc return RpcUrl::Unset; } if params.rpc_url_ngrok_detect { + let mut scheme_override = None; + if !params.rpc_url_ngrok_scheme_override.is_empty() { + scheme_override = Some(params.rpc_url_ngrok_scheme_override.clone()); + } return RpcUrl::DetectFromNgrok { tunnel_name: params.rpc_url_ngrok_detect_from.clone(), + scheme_override, }; } if let Some(rpc_endpoint_port) = rpc_port { - return RpcUrl::LocalhostWithPort { rpc_endpoint_port }; + let scheme = params.rpc_url_scheme.as_deref().unwrap_or("ws").into(); + return RpcUrl::LocalhostWithPort { + rpc_endpoint_port, + scheme, + }; } RpcUrl::Unset diff --git a/crates/humanode-peer/src/cli/params.rs b/crates/humanode-peer/src/cli/params.rs index 9053898e0..f6b92f594 100644 --- a/crates/humanode-peer/src/cli/params.rs +++ b/crates/humanode-peer/src/cli/params.rs @@ -10,21 +10,32 @@ pub struct BioauthFlowParams { /// The URL to pass to the web app to connect to the node RPC. /// If not passed, a URL with `localhost` and the HTTP RPC port will be used. - #[clap(long, value_name = "RPC_URL", conflicts_with_all = &["rpc-url-ngrok-detect", "rpc-url-unset"])] + #[clap(long, value_name = "RPC_URL", conflicts_with_all = &["rpc-url-scheme", "rpc-url-ngrok-detect", "rpc-url-unset"])] pub rpc_url: Option, + /// The scheme of the URL to pass to the web app to connect to the node RPC when the URL is + /// not set explicitly via other commands. + /// If not passed, a URL with `localhost` and the HTTP RPC port will be used. + #[clap(long, value_name = "RPC_URL_SCHEME", conflicts_with_all = &["rpc-url", "rpc-url-ngrok-detect", "rpc-url-unset"])] + pub rpc_url_scheme: Option, + /// Detect RPC URL from ngrok. - #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-unset"])] + #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-scheme", "rpc-url-unset"])] pub rpc_url_ngrok_detect: bool, /// Explicitly unset the RPC URL. - #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-ngrok-detect"])] + #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-scheme", "rpc-url-ngrok-detect"])] pub rpc_url_unset: bool, /// The tunnel name at ngrok to detect RPC URL from, if ngrok is used to detect the RPC URL. #[clap(long, value_name = "TUNNEL_NAME", default_value = "command_line")] pub rpc_url_ngrok_detect_from: String, + /// The scheme to use in the RPC URL instead of the original one, if ngrok is used to detect + /// the RPC URL. + #[clap(long, value_name = "SCHEME", default_value = "wss")] + pub rpc_url_ngrok_scheme_override: String, + /// The URL of robonode to authenticate with. #[clap(long, value_name = "ROBONODE_URL")] pub robonode_url: Option, diff --git a/crates/humanode-peer/src/rpc_url.rs b/crates/humanode-peer/src/rpc_url.rs index 92db539f7..048c99b5c 100644 --- a/crates/humanode-peer/src/rpc_url.rs +++ b/crates/humanode-peer/src/rpc_url.rs @@ -13,13 +13,17 @@ pub enum RpcUrl { /// The URL is to be constructed from the RPC endpoint port. /// This is typically used as a fallback. LocalhostWithPort { - /// The port of the RPC enpoint our peer binds the socket to. + /// The port of the RPC endpoint our peer binds the socket to. rpc_endpoint_port: u16, + /// The scheme to use for the RPC URL. + scheme: String, }, /// Detect the RPC URL from ngrok. DetectFromNgrok { /// The tunnel name to get the public URL from. tunnel_name: String, + /// The scheme to use instead of the one in public URL. + scheme_override: Option, }, } @@ -49,19 +53,28 @@ impl RpcUrlResolver { match val { RpcUrl::Unset => Err(Cow::Borrowed("RPC URL was not set")), RpcUrl::Set(url) => Ok(Cow::Borrowed(url)), - RpcUrl::LocalhostWithPort { rpc_endpoint_port } => { - Ok(format!("http://localhost:{}", rpc_endpoint_port).into()) - } - RpcUrl::DetectFromNgrok { tunnel_name } => { - Ok(self.detect_from_ngrok(&*tunnel_name).await?.into()) - } + RpcUrl::LocalhostWithPort { + rpc_endpoint_port, + scheme, + } => Ok(format!("{}://localhost:{}", scheme, rpc_endpoint_port).into()), + RpcUrl::DetectFromNgrok { + tunnel_name, + scheme_override, + } => Ok(self + .detect_from_ngrok(&*tunnel_name, scheme_override.as_deref()) + .await? + .into()), } } /// Detect the RPC URL from the `ngrok`'s API. /// Returns the public URL from the specified tunnel. /// Assumes that the tunnel has a proper protocol and points to a proper port. - async fn detect_from_ngrok(&self, tunnel_name: &str) -> Result { + async fn detect_from_ngrok( + &self, + tunnel_name: &str, + scheme_override: Option<&str>, + ) -> Result { let mut attempts_left = 10; let res = loop { let tunnel_name = std::borrow::Cow::Owned(tunnel_name.to_owned()); @@ -81,6 +94,10 @@ impl RpcUrlResolver { Err(err) => return Err(err.to_string()), } }; - Ok(res.public_url) + let mut public_url = res.public_url; + if let Some(scheme_override) = scheme_override { + public_url = public_url.replacen("https", scheme_override, 1); + } + Ok(public_url) } } From 62219d1cb5001734528bff7622be16d95036533d Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Fri, 27 May 2022 01:15:27 +0400 Subject: [PATCH 2/4] Change the cli logic such that user only can specify the preference of the scheme --- crates/humanode-peer/src/cli/config.rs | 50 +++++++++++++++++--------- crates/humanode-peer/src/cli/params.rs | 36 ++++++++++++------- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/crates/humanode-peer/src/cli/config.rs b/crates/humanode-peer/src/cli/config.rs index 69cdfdb04..20c5c9447 100644 --- a/crates/humanode-peer/src/cli/config.rs +++ b/crates/humanode-peer/src/cli/config.rs @@ -2,7 +2,7 @@ use sc_chain_spec::get_extension; -use super::{params, BioauthFlowParams}; +use super::{params, BioauthFlowParams, RpcUrlSchemePreference}; use crate::{ chain_spec::Extensions, configuration::{self, Configuration}, @@ -28,8 +28,9 @@ pub trait CliConfigurationExt: SubstrateCliConfigurationProvider { .unwrap_or_default(); let bioauth_flow = self.bioauth_params().map(|params| { - let rpc_port = substrate.rpc_http.map(|v| v.port()); - let rpc_url = rpc_url_from_params(params, rpc_port); + let rpc_http_port = substrate.rpc_http.map(|v| v.port()); + let rpc_ws_port = substrate.rpc_ws.map(|v| v.port()); + let rpc_url = rpc_url_from_params(params, rpc_http_port, rpc_ws_port); configuration::BioauthFlow { rpc_url_resolver: Default::default(), @@ -86,7 +87,11 @@ impl SubstrateCliConfigurationProvider for T { } /// Construct an RPC URL from the bioauth flow params and an RPC endpoint port. -fn rpc_url_from_params(params: &BioauthFlowParams, rpc_port: Option) -> RpcUrl { +fn rpc_url_from_params( + params: &BioauthFlowParams, + rpc_http_port: Option, + rpc_ws_port: Option, +) -> RpcUrl { if let Some(val) = ¶ms.rpc_url { return RpcUrl::Set(val.clone()); } @@ -94,23 +99,36 @@ fn rpc_url_from_params(params: &BioauthFlowParams, rpc_port: Option) -> Rpc return RpcUrl::Unset; } if params.rpc_url_ngrok_detect { - let mut scheme_override = None; - if !params.rpc_url_ngrok_scheme_override.is_empty() { - scheme_override = Some(params.rpc_url_ngrok_scheme_override.clone()); - } + let scheme_override = match params.rpc_url_scheme_preference { + RpcUrlSchemePreference::NoPreference | RpcUrlSchemePreference::Ws => Some("wss".into()), + RpcUrlSchemePreference::Http => None, + }; return RpcUrl::DetectFromNgrok { tunnel_name: params.rpc_url_ngrok_detect_from.clone(), scheme_override, }; } - if let Some(rpc_endpoint_port) = rpc_port { - let scheme = params.rpc_url_scheme.as_deref().unwrap_or("ws").into(); - return RpcUrl::LocalhostWithPort { - rpc_endpoint_port, - scheme, - }; + match ( + ¶ms.rpc_url_scheme_preference, + rpc_http_port, + rpc_ws_port, + ) { + // Try WebSocket first if the user has no preference. + (RpcUrlSchemePreference::Ws | RpcUrlSchemePreference::NoPreference, _, Some(port)) => { + RpcUrl::LocalhostWithPort { + rpc_endpoint_port: port, + scheme: "ws".into(), + } + } + // Try HTTP second if the user has no preference. + (RpcUrlSchemePreference::Http | RpcUrlSchemePreference::NoPreference, Some(port), _) => { + RpcUrl::LocalhostWithPort { + rpc_endpoint_port: port, + scheme: "http".into(), + } + } + // If everything fails - fallback to unset. + _ => RpcUrl::Unset, } - - RpcUrl::Unset } diff --git a/crates/humanode-peer/src/cli/params.rs b/crates/humanode-peer/src/cli/params.rs index f6b92f594..ec095ca11 100644 --- a/crates/humanode-peer/src/cli/params.rs +++ b/crates/humanode-peer/src/cli/params.rs @@ -1,5 +1,16 @@ //! Shared CLI parameters. +/// Possible RPC URL scheme preference options. +#[derive(Debug, clap::ArgEnum, Clone)] +pub enum RpcUrlSchemePreference { + /// Prefer HTTP (http or https). + Http, + /// Prefer WebSocket (ws or wss). + Ws, + /// No preference, use opinionated defaults. + NoPreference, +} + /// Shared CLI parameters used to configure bioauth flow. #[derive(Debug, clap::Parser, Clone)] pub struct BioauthFlowParams { @@ -10,32 +21,31 @@ pub struct BioauthFlowParams { /// The URL to pass to the web app to connect to the node RPC. /// If not passed, a URL with `localhost` and the HTTP RPC port will be used. - #[clap(long, value_name = "RPC_URL", conflicts_with_all = &["rpc-url-scheme", "rpc-url-ngrok-detect", "rpc-url-unset"])] + #[clap(long, value_name = "RPC_URL", conflicts_with_all = &["rpc-url-scheme-preference", "rpc-url-ngrok-detect", "rpc-url-unset"])] pub rpc_url: Option, - /// The scheme of the URL to pass to the web app to connect to the node RPC when the URL is - /// not set explicitly via other commands. - /// If not passed, a URL with `localhost` and the HTTP RPC port will be used. - #[clap(long, value_name = "RPC_URL_SCHEME", conflicts_with_all = &["rpc-url", "rpc-url-ngrok-detect", "rpc-url-unset"])] - pub rpc_url_scheme: Option, + /// What RPC URL scheme to prefer. + #[clap( + long, + arg_enum, + value_name = "RPC_URL_SCHEME_PREFERENCE", + default_value = "no-preference", + conflicts_with_all = &["rpc-url", "rpc-url-unset"] + )] + pub rpc_url_scheme_preference: RpcUrlSchemePreference, /// Detect RPC URL from ngrok. - #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-scheme", "rpc-url-unset"])] + #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-unset"])] pub rpc_url_ngrok_detect: bool, /// Explicitly unset the RPC URL. - #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-scheme", "rpc-url-ngrok-detect"])] + #[clap(long, conflicts_with_all = &["rpc-url", "rpc-url-scheme-preference", "rpc-url-ngrok-detect"])] pub rpc_url_unset: bool, /// The tunnel name at ngrok to detect RPC URL from, if ngrok is used to detect the RPC URL. #[clap(long, value_name = "TUNNEL_NAME", default_value = "command_line")] pub rpc_url_ngrok_detect_from: String, - /// The scheme to use in the RPC URL instead of the original one, if ngrok is used to detect - /// the RPC URL. - #[clap(long, value_name = "SCHEME", default_value = "wss")] - pub rpc_url_ngrok_scheme_override: String, - /// The URL of robonode to authenticate with. #[clap(long, value_name = "ROBONODE_URL")] pub robonode_url: Option, From ea171975e600c411408e693219df705cfdc440cd Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Fri, 27 May 2022 01:32:42 +0400 Subject: [PATCH 3/4] More advanced autodetection of the WebSocket at ngrok --- crates/humanode-peer/src/cli/config.rs | 7 ++++--- crates/humanode-peer/src/rpc_url.rs | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/humanode-peer/src/cli/config.rs b/crates/humanode-peer/src/cli/config.rs index 20c5c9447..812fc536d 100644 --- a/crates/humanode-peer/src/cli/config.rs +++ b/crates/humanode-peer/src/cli/config.rs @@ -99,13 +99,14 @@ fn rpc_url_from_params( return RpcUrl::Unset; } if params.rpc_url_ngrok_detect { - let scheme_override = match params.rpc_url_scheme_preference { - RpcUrlSchemePreference::NoPreference | RpcUrlSchemePreference::Ws => Some("wss".into()), + let ws_rpc_endpoint_port = match params.rpc_url_scheme_preference { + // If there's no preference - try switching to WebSocket if it's available. + RpcUrlSchemePreference::NoPreference | RpcUrlSchemePreference::Ws => rpc_ws_port, RpcUrlSchemePreference::Http => None, }; return RpcUrl::DetectFromNgrok { tunnel_name: params.rpc_url_ngrok_detect_from.clone(), - scheme_override, + ws_rpc_endpoint_port, }; } diff --git a/crates/humanode-peer/src/rpc_url.rs b/crates/humanode-peer/src/rpc_url.rs index 048c99b5c..f3516cfad 100644 --- a/crates/humanode-peer/src/rpc_url.rs +++ b/crates/humanode-peer/src/rpc_url.rs @@ -22,8 +22,9 @@ pub enum RpcUrl { DetectFromNgrok { /// The tunnel name to get the public URL from. tunnel_name: String, - /// The scheme to use instead of the one in public URL. - scheme_override: Option, + /// The WebSocket port to match against, and switch protocol to WebSocket if the tunnel + /// address has this port. + ws_rpc_endpoint_port: Option, }, } @@ -59,9 +60,9 @@ impl RpcUrlResolver { } => Ok(format!("{}://localhost:{}", scheme, rpc_endpoint_port).into()), RpcUrl::DetectFromNgrok { tunnel_name, - scheme_override, + ws_rpc_endpoint_port, } => Ok(self - .detect_from_ngrok(&*tunnel_name, scheme_override.as_deref()) + .detect_from_ngrok(&*tunnel_name, *ws_rpc_endpoint_port) .await? .into()), } @@ -73,7 +74,7 @@ impl RpcUrlResolver { async fn detect_from_ngrok( &self, tunnel_name: &str, - scheme_override: Option<&str>, + ws_rpc_endpoint_port: Option, ) -> Result { let mut attempts_left = 10; let res = loop { @@ -95,8 +96,14 @@ impl RpcUrlResolver { } }; let mut public_url = res.public_url; - if let Some(scheme_override) = scheme_override { - public_url = public_url.replacen("https", scheme_override, 1); + if let Some(ws_rpc_endpoint_port) = ws_rpc_endpoint_port { + if res + .config + .addr + .ends_with(&format!(":{}", ws_rpc_endpoint_port)) + { + public_url = public_url.replacen("https", "wss", 1); + } } Ok(public_url) } From b111ff0096ae22589ba23da487574539bee8a630 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Fri, 27 May 2022 01:39:12 +0400 Subject: [PATCH 4/4] Use static str for localhost scheme --- crates/humanode-peer/src/cli/config.rs | 4 ++-- crates/humanode-peer/src/rpc_url.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/humanode-peer/src/cli/config.rs b/crates/humanode-peer/src/cli/config.rs index 812fc536d..54f7deb55 100644 --- a/crates/humanode-peer/src/cli/config.rs +++ b/crates/humanode-peer/src/cli/config.rs @@ -119,14 +119,14 @@ fn rpc_url_from_params( (RpcUrlSchemePreference::Ws | RpcUrlSchemePreference::NoPreference, _, Some(port)) => { RpcUrl::LocalhostWithPort { rpc_endpoint_port: port, - scheme: "ws".into(), + scheme: "ws", } } // Try HTTP second if the user has no preference. (RpcUrlSchemePreference::Http | RpcUrlSchemePreference::NoPreference, Some(port), _) => { RpcUrl::LocalhostWithPort { rpc_endpoint_port: port, - scheme: "http".into(), + scheme: "http", } } // If everything fails - fallback to unset. diff --git a/crates/humanode-peer/src/rpc_url.rs b/crates/humanode-peer/src/rpc_url.rs index f3516cfad..46fdb6020 100644 --- a/crates/humanode-peer/src/rpc_url.rs +++ b/crates/humanode-peer/src/rpc_url.rs @@ -16,7 +16,7 @@ pub enum RpcUrl { /// The port of the RPC endpoint our peer binds the socket to. rpc_endpoint_port: u16, /// The scheme to use for the RPC URL. - scheme: String, + scheme: &'static str, }, /// Detect the RPC URL from ngrok. DetectFromNgrok {