chore(phoenix-channel): log all errors when connection fails (#8089)

Currently, we are only logging the last error when we fail to connect to
any of the addresses from the portal. This is often not useful because
the last one is likely to be an IPv6 address which may not be supported
on the system so all we learn is "The requested address is not valid in
its context.".
This commit is contained in:
Thomas Eizinger
2025-02-11 05:58:32 +00:00
committed by GitHub
parent 7dcda1dc74
commit 6c93ce76bf
3 changed files with 21 additions and 8 deletions

1
rust/Cargo.lock generated
View File

@@ -4661,6 +4661,7 @@ dependencies = [
"futures",
"hex",
"hostname",
"itertools 0.13.0",
"libc",
"os_info",
"rand_core 0.6.4",

View File

@@ -11,6 +11,7 @@ base64 = { workspace = true }
firezone-logging = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
itertools = { workspace = true }
libc = { workspace = true }
os_info = { workspace = true }
rand_core = { workspace = true }

View File

@@ -19,6 +19,7 @@ use firezone_logging::{err_with_src, telemetry_span};
use futures::future::BoxFuture;
use futures::{FutureExt, SinkExt, StreamExt};
use heartbeat::{Heartbeat, MissedLastHeartbeat};
use itertools::Itertools as _;
use rand_core::{OsRng, RngCore};
use secrecy::{ExposeSecret, Secret};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -109,7 +110,8 @@ async fn connect(
addresses: Vec<SocketAddr>,
socket_factory: &dyn SocketFactory<TcpSocket>,
) -> Result<TcpStream, InternalError> {
let mut last_error = None;
let mut errors = Vec::with_capacity(addresses.len());
for addr in addresses {
let Ok(socket) = socket_factory(&addr) else {
continue;
@@ -118,16 +120,16 @@ async fn connect(
match socket.connect(addr).await {
Ok(socket) => return Ok(socket),
Err(e) => {
last_error = Some(e);
errors.push((addr, e));
}
}
}
let Some(err) = last_error else {
if errors.is_empty() {
return Err(InternalError::InvalidUrl);
};
}
Err(InternalError::SocketConnection(err))
Err(InternalError::SocketConnection(errors))
}
#[derive(Debug, thiserror::Error)]
@@ -162,7 +164,7 @@ enum InternalError {
StreamClosed,
InvalidUrl,
FailedToBuildRequest(tokio_tungstenite::tungstenite::http::Error),
SocketConnection(std::io::Error),
SocketConnection(Vec<(SocketAddr, std::io::Error)>),
Timeout { duration: Duration },
}
@@ -185,7 +187,16 @@ impl fmt::Display for InternalError {
InternalError::CloseMessage => write!(f, "portal closed the websocket connection"),
InternalError::StreamClosed => write!(f, "websocket stream was closed"),
InternalError::InvalidUrl => write!(f, "failed to resolve url"),
InternalError::SocketConnection(_) => write!(f, "failed to connect socket"),
InternalError::SocketConnection(errors) => {
write!(
f,
"failed to connect socket: [{}]",
errors
.iter()
.map(|(addr, e)| format!("{addr}: {e}"))
.join(", ")
)
}
InternalError::Timeout { duration, .. } => {
write!(f, "operation timed out after {duration:?}")
}
@@ -202,8 +213,8 @@ impl std::error::Error for InternalError {
InternalError::WebSocket(tokio_tungstenite::tungstenite::Error::Http(_)) => None,
InternalError::WebSocket(e) => Some(e),
InternalError::Serde(e) => Some(e),
InternalError::SocketConnection(e) => Some(e),
InternalError::FailedToBuildRequest(e) => Some(e),
InternalError::SocketConnection(_) => None,
InternalError::MissedHeartbeat => None,
InternalError::CloseMessage => None,
InternalError::StreamClosed => None,