diff --git a/client/src/_network_view.rs b/client/src/_network_view.rs index 5da906ba..d50832a2 100644 --- a/client/src/_network_view.rs +++ b/client/src/_network_view.rs @@ -304,6 +304,16 @@ impl NodeView { Ok(()) } + pub async fn restart_initialization(&self) -> Result<(), TransitionError> { + { + let mut target = self.state.write().await; + target.state.transition(SessionState::RestartConnect)?; + } + + self.notify_change(SessionState::RestartConnect); + Ok(()) + } + pub async fn public_addresses(&self) -> Vec { let target = self.state.read().await; target.addresses.clone() diff --git a/client/src/_session_layer.rs b/client/src/_session_layer.rs index 67107302..373ac2cf 100644 --- a/client/src/_session_layer.rs +++ b/client/src/_session_layer.rs @@ -700,6 +700,7 @@ impl SessionLayer { // We can still try other methods. Err(SessionError::NotApplicable(e)) => { log::debug!("Can't establish direct p2p session with [{node_id}]. {e}"); + permit.registry.restart_initialization().await?; } // In case of other errors we probably shouldn't even try something else. Err(e) => { @@ -722,6 +723,7 @@ impl SessionLayer { // We can still try other methods. Err(SessionError::NotApplicable(e)) => { log::debug!("Can't establish reverse p2p session with [{node_id}]. {e}"); + permit.registry.restart_initialization().await?; } // In case of other errors we probably shouldn't even try something else. Err(e) => { @@ -1631,7 +1633,7 @@ mod tests { layer2.capturer.captures.reverse_connection.drop_all(); - // Function should return in timeout and return error. + // Function should finish in timeout and return error. assert!( timeout(Duration::from_millis(4500), layer1.layer.session(layer2.id)) .await @@ -1653,7 +1655,7 @@ mod tests { layer2.layer.server_session().await.unwrap(); layer2.capturer.captures.session_request.drop_all(); - // Function should return in timeout and return error. + // Function should finish in timeout and return error. assert!( timeout(Duration::from_millis(3500), layer1.layer.session(layer2.id)) .await diff --git a/client/src/_session_state.rs b/client/src/_session_state.rs index 8c0b8fcd..360a51ed 100644 --- a/client/src/_session_state.rs +++ b/client/src/_session_state.rs @@ -26,6 +26,10 @@ pub enum SessionState { /// Session was closed gracefully. (Still the reason /// for closing could be some kind of failure) Closed, + /// Current initialization method failed, but we could try other methods. + /// This state is equivalent of `ConnectIntent`, but moving to this state doesn't + /// give you `SessionPermit`. + RestartConnect, } #[derive(Clone, PartialEq, Display, Debug)] @@ -166,13 +170,18 @@ impl SessionState { SessionState::ReverseConnection(ReverseState::Awaiting), ) => true, ( - SessionState::Outgoing(InitState::ConnectIntent), - SessionState::Relayed(RelayedState::Initializing), + SessionState::RestartConnect, + SessionState::ReverseConnection(ReverseState::Awaiting), ) => true, + // We can establish Relayed connection if we made connect intent or we tried other methods + // that failed and we are in restating state. ( - SessionState::ReverseConnection(ReverseState::InProgress(InitState::ConnectIntent)), + SessionState::Outgoing(InitState::ConnectIntent), SessionState::Relayed(RelayedState::Initializing), ) => true, + (SessionState::RestartConnect, SessionState::Relayed(RelayedState::Initializing)) => { + true + } // We can start new session if it was closed, or if we failed to establish it previously. (SessionState::Closed, SessionState::Outgoing(InitState::ConnectIntent)) => true, (SessionState::Closed, SessionState::Incoming(InitState::ConnectIntent)) => true, @@ -184,12 +193,21 @@ impl SessionState { SessionState::FailedEstablish(_), SessionState::Incoming(InitState::ConnectIntent), ) => true, + // If session is established, we don't want to block this transition to avoid unexpected + // behaviors. On the other side when in `Established` state, getting transition request + // to `Established` is already unexpected. (SessionState::Established(_), SessionState::Established(_)) => true, + // In case of every initialization method we can retry using other method. + (SessionState::Relayed(_), SessionState::RestartConnect) => true, + (SessionState::Incoming(_), SessionState::RestartConnect) => true, + (SessionState::Outgoing(_), SessionState::RestartConnect) => true, + (SessionState::ReverseConnection(_), SessionState::RestartConnect) => true, + // We can attempt to make p2p connection, when previous initialization method failed. + (SessionState::RestartConnect, SessionState::Outgoing(InitState::Initializing)) => true, // We can start closing only Established session. In other cases we want to make // transition to `FailedEstablish` state. (SessionState::Established(_), SessionState::Closing) => true, (SessionState::Closing, SessionState::Closed) => true, - (SessionState::Incoming(prev), SessionState::Incoming(next)) => prev.allowed(next), (SessionState::Outgoing(prev), SessionState::Outgoing(next)) => prev.allowed(next), (SessionState::Relayed(prev), SessionState::Relayed(next)) => prev.allowed(next),