diff --git a/nym-api/src/node_describe_cache/mod.rs b/nym-api/src/node_describe_cache/mod.rs index 7948fbf2ed..fc1c697cac 100644 --- a/nym-api/src/node_describe_cache/mod.rs +++ b/nym-api/src/node_describe_cache/mod.rs @@ -36,6 +36,9 @@ pub enum NodeDescribeCacheError { source: NymNodeApiClientError, }, + #[error("gateway '{gateway}' with host '{host}' doesn't seem to expose any of the standard API ports, i.e.: 80, 443 or {}", DEFAULT_NYM_NODE_HTTP_PORT)] + NoHttpPortsAvailable { host: String, gateway: IdentityKey }, + #[error("failed to query gateway '{gateway}': {source}")] ApiFailure { gateway: IdentityKey, @@ -72,22 +75,49 @@ impl NodeDescriptionProvider { } } +async fn try_get_client( + gateway: &Gateway, +) -> Result { + let gateway_host = &gateway.host; + + // first try the standard port in case the operator didn't put the node behind the proxy, + // then default https (443) + // finally default http (80) + let addresses_to_try = vec![ + format!("http://{gateway_host}:{DEFAULT_NYM_NODE_HTTP_PORT}"), + format!("https://{gateway_host}"), + format!("http://{gateway_host}"), + ]; + + for address in addresses_to_try { + // if provided host was malformed, no point in continuing + let client = match nym_node_requests::api::Client::new_url(address, None) { + Ok(client) => client, + Err(err) => { + return Err(NodeDescribeCacheError::MalformedHost { + host: gateway_host.clone(), + gateway: gateway.identity_key.clone(), + source: err, + }); + } + }; + if let Ok(health) = client.get_health().await { + if health.status.is_up() { + return Ok(client); + } + } + } + + Err(NodeDescribeCacheError::NoHttpPortsAvailable { + host: gateway_host.clone(), + gateway: gateway.identity_key.clone(), + }) +} + async fn get_gateway_description( gateway: Gateway, ) -> Result<(IdentityKey, NymNodeDescription), NodeDescribeCacheError> { - let client = match nym_node_requests::api::Client::new_url( - format!("{}:{}", gateway.host, DEFAULT_NYM_NODE_HTTP_PORT), - None, - ) { - Ok(client) => client, - Err(err) => { - return Err(NodeDescribeCacheError::MalformedHost { - host: gateway.host, - gateway: gateway.identity_key, - source: err, - }); - } - }; + let client = try_get_client(&gateway).await?; let host_info = client diff --git a/nym-node/nym-node-requests/src/api/client.rs b/nym-node/nym-node-requests/src/api/client.rs index 63eb87e009..1967b6963e 100644 --- a/nym-node/nym-node-requests/src/api/client.rs +++ b/nym-node/nym-node-requests/src/api/client.rs @@ -9,6 +9,7 @@ use async_trait::async_trait; use http_api_client::{ApiClient, HttpClientError}; use nym_bin_common::build_information::BinaryBuildInformationOwned; +use crate::api::v1::health::models::NodeHealth; pub use http_api_client::Client; pub type NymNodeApiClientError = HttpClientError; @@ -16,6 +17,10 @@ pub type NymNodeApiClientError = HttpClientError; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait NymNodeApiClientExt: ApiClient { + async fn get_health(&self) -> Result { + self.get_json_from(routes::api::v1::health_absolute()).await + } + async fn get_host_information(&self) -> Result { self.get_json_from(routes::api::v1::host_info_absolute()) .await diff --git a/nym-node/nym-node-requests/src/api/v1/health/models.rs b/nym-node/nym-node-requests/src/api/v1/health/models.rs index 94ec606711..08aa4afe4c 100644 --- a/nym-node/nym-node-requests/src/api/v1/health/models.rs +++ b/nym-node/nym-node-requests/src/api/v1/health/models.rs @@ -26,3 +26,9 @@ impl NodeHealth { pub enum NodeStatus { Up, } + +impl NodeStatus { + pub fn is_up(&self) -> bool { + matches!(self, NodeStatus::Up) + } +}