Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions crates/humanode-peer/src/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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(),
Expand Down Expand Up @@ -86,22 +87,49 @@ impl<T: sc_cli::CliConfiguration> 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<u16>) -> RpcUrl {
fn rpc_url_from_params(
params: &BioauthFlowParams,
rpc_http_port: Option<u16>,
rpc_ws_port: Option<u16>,
) -> RpcUrl {
if let Some(val) = &params.rpc_url {
return RpcUrl::Set(val.clone());
}
if params.rpc_url_unset {
return RpcUrl::Unset;
}
if params.rpc_url_ngrok_detect {
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(),
ws_rpc_endpoint_port,
};
}

if let Some(rpc_endpoint_port) = rpc_port {
return RpcUrl::LocalhostWithPort { rpc_endpoint_port };
match (
&params.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",
}
}
// Try HTTP second if the user has no preference.
(RpcUrlSchemePreference::Http | RpcUrlSchemePreference::NoPreference, Some(port), _) => {
RpcUrl::LocalhostWithPort {
rpc_endpoint_port: port,
scheme: "http",
}
}
// If everything fails - fallback to unset.
_ => RpcUrl::Unset,
}

RpcUrl::Unset
}
25 changes: 23 additions & 2 deletions crates/humanode-peer/src/cli/params.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -10,15 +21,25 @@ 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-preference", "rpc-url-ngrok-detect", "rpc-url-unset"])]
pub rpc_url: Option<String>,

/// 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-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-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.
Expand Down
42 changes: 33 additions & 9 deletions crates/humanode-peer/src/rpc_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ 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: &'static str,
},
/// Detect the RPC URL from ngrok.
DetectFromNgrok {
/// The tunnel name to get the public URL from.
tunnel_name: String,
/// The WebSocket port to match against, and switch protocol to WebSocket if the tunnel
/// address has this port.
ws_rpc_endpoint_port: Option<u16>,
},
}

Expand Down Expand Up @@ -49,19 +54,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,
ws_rpc_endpoint_port,
} => Ok(self
.detect_from_ngrok(&*tunnel_name, *ws_rpc_endpoint_port)
.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<String, String> {
async fn detect_from_ngrok(
&self,
tunnel_name: &str,
ws_rpc_endpoint_port: Option<u16>,
) -> Result<String, String> {
let mut attempts_left = 10;
let res = loop {
let tunnel_name = std::borrow::Cow::Owned(tunnel_name.to_owned());
Expand All @@ -81,6 +95,16 @@ impl RpcUrlResolver {
Err(err) => return Err(err.to_string()),
}
};
Ok(res.public_url)
let mut public_url = res.public_url;
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)
}
}