Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
fetch: surface underlying I/O error
Merged fintohaps opened 4 months ago

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.

3 files changed +39 -11 75b665ff 3168107d
modified crates/radicle-cli/examples/rad-init-private-clone.md
@@ -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
```

modified crates/radicle-fetch/src/lib.rs
@@ -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}");
-
    }
+
    handle
+
        .transport
+
        .handshake()
+
        .map_err(handle_handshake_err)
+
        .map_err(Error::from)
+
}

-
    Ok(result?)
+
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
}
modified crates/radicle-fetch/src/transport.rs
@@ -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.