fetch: surface underlying I/O error

The message that is returned by `gix-transport` for I/O errors can be
unhelpful, since it does not provide the reason for what happened.

Instead, surface the error so that it provides more detail for logging.
This commit is contained in:
Fintan Halpenny 2025-12-19 10:16:55 +00:00 committed by Lorenz Leutgeb
parent 75b665ff3a
commit 3168107df9
3 changed files with 40 additions and 12 deletions

View File

@ -10,7 +10,7 @@ $ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --seed z6MknSLrJoTcukLrE435hVNQT4J
Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from the network, found 1 potential seed(s).
✗ Target not met: could not fetch from [z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi], and required 1 more seed(s)
! Warning: Failed to fetch from 1 seed(s).
! Warning: z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi: failed to perform fetch handshake: [..]
! Warning: z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi: an I/O error occurred during the fetch handshake (connection reset)
✗ Error: no seeds found for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu
```

View File

@ -9,6 +9,7 @@ mod refs;
mod stage;
mod state;
use std::io;
use std::time::Instant;
use gix_protocol::handshake;
@ -28,8 +29,8 @@ use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("failed to perform fetch handshake: {0}")]
Handshake(#[from] Box<handshake::Error>),
#[error(transparent)]
Handshake(Box<HandshakeError>),
#[error("failed to load `rad/id`")]
Identity {
#[source]
@ -43,6 +44,20 @@ pub enum Error {
ReplicateSelf,
}
impl From<HandshakeError> for Error {
fn from(err: HandshakeError) -> Self {
Self::Handshake(Box::new(err))
}
}
#[derive(Debug, Error)]
pub enum HandshakeError {
#[error("failed to perform fetch handshake: {0}")]
Gix(handshake::Error),
#[error("an I/O error occurred during the fetch handshake ({0})")]
Io(io::Error),
}
/// Pull changes from the `remote`.
///
/// It is expected that the local peer has a copy of the repository
@ -127,11 +142,24 @@ fn perform_handshake<R, S>(handle: &mut Handle<R, S>) -> Result<handshake::Outco
where
S: transport::ConnectionStream,
{
let result = handle.transport.handshake();
if let Err(err) = &result {
log::warn!(target: "fetch", "Failed to perform handshake: {err}");
}
Ok(result?)
handle
.transport
.handshake()
.map_err(handle_handshake_err)
.map_err(Error::from)
}
fn handle_handshake_err(err: handshake::Error) -> HandshakeError {
let err = match err {
handshake::Error::Transport(error) => match error {
gix_transport::client::Error::Io(error) => HandshakeError::Io(error),
err => HandshakeError::Gix(handshake::Error::Transport(err)),
},
err => {
log::warn!(target: "fetch", "Failed to perform handshake: {err}");
HandshakeError::Gix(err)
}
};
log::warn!(target: "fetch", "{err}");
err
}

View File

@ -89,7 +89,8 @@ where
}
/// Perform the handshake with the server side.
pub(crate) fn handshake(&mut self) -> Result<handshake::Outcome, Box<handshake::Error>> {
#[allow(clippy::result_large_err)]
pub(crate) fn handshake(&mut self) -> Result<handshake::Outcome, handshake::Error> {
log::trace!(target: "fetch", "Performing handshake for {}", self.repo);
let (read, write) = self.stream.open();
gix_protocol::fetch::handshake(
@ -98,7 +99,6 @@ where
vec![],
&mut progress::Discard,
)
.map_err(Box::new)
}
/// Perform ls-refs with the server side.