diff --git a/crates/tor-chanmgr/src/lib.rs b/crates/tor-chanmgr/src/lib.rs index 92a3ef500462b3dd101a99db184aea45651e2b03..12f3645fd1b22ccd647f8009146003ae2a19570d 100644 --- a/crates/tor-chanmgr/src/lib.rs +++ b/crates/tor-chanmgr/src/lib.rs @@ -82,6 +82,17 @@ pub struct ChanMgr<R: Runtime> { bootstrap_status: event::ConnStatusEvents, } +/// Description of how we got a channel. +#[non_exhaustive] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ChanProvenance { + /// This channel was newly launched, or was in progress and finished while + /// we were waiting. + NewlyCreated, + /// This channel already existed when we asked for it. + Preexisting, +} + impl<R: Runtime> ChanMgr<R> { /// Construct a new channel manager. /// @@ -118,16 +129,19 @@ impl<R: Runtime> ChanMgr<R> { /// If there is already a channel launch attempt in progress, this /// function will wait until that launch is complete, and succeed /// or fail depending on its outcome. - pub async fn get_or_launch<T: ChanTarget + ?Sized>(&self, target: &T) -> Result<Channel> { + pub async fn get_or_launch<T: ChanTarget + ?Sized>( + &self, + target: &T, + ) -> Result<(Channel, ChanProvenance)> { let ed_identity = target.ed_identity(); let targetinfo = OwnedChanTarget::from_chan_target(target); - let chan = self.mgr.get_or_launch(*ed_identity, targetinfo).await?; + let (chan, provenance) = self.mgr.get_or_launch(*ed_identity, targetinfo).await?; // Double-check the match to make sure that the RSA identity is // what we wanted too. chan.check_match(target) .map_err(Error::from_proto_no_skew)?; - Ok(chan) + Ok((chan, provenance)) } /// Return a stream of [`ConnStatus`] events to tell us about changes diff --git a/crates/tor-chanmgr/src/mgr.rs b/crates/tor-chanmgr/src/mgr.rs index 89bccc450a580689fe722355a672ccbecd0a22fe..4fe39f1ccdf838b733fc2fce9d6dc5fde44a987b 100644 --- a/crates/tor-chanmgr/src/mgr.rs +++ b/crates/tor-chanmgr/src/mgr.rs @@ -1,7 +1,7 @@ //! Abstract implementation of a channel manager use crate::mgr::map::OpenEntry; -use crate::{Error, Result}; +use crate::{ChanProvenance, Error, Result}; use async_trait::async_trait; use futures::channel::oneshot; @@ -110,7 +110,7 @@ impl<CF: ChannelFactory> AbstractChanMgr<CF> { &self, ident: <<CF as ChannelFactory>::Channel as AbstractChannel>::Ident, target: CF::BuildSpec, - ) -> Result<CF::Channel> { + ) -> Result<(CF::Channel, ChanProvenance)> { use map::ChannelState::*; /// Possible actions that we'll decide to take based on the @@ -123,7 +123,7 @@ impl<CF: ChannelFactory> AbstractChanMgr<CF> { /// We're going to wait for it to finish. Wait(Pending<C>), /// We found a usable channel. We're going to return it. - Return(Result<C>), + Return(Result<(C, ChanProvenance)>), } /// How many times do we try? const N_ATTEMPTS: usize = 2; @@ -140,7 +140,10 @@ impl<CF: ChannelFactory> AbstractChanMgr<CF> { Some(Open(ref ent)) => { if ent.channel.is_usable() { // Good channel. Return it. - let action = Action::Return(Ok(ent.channel.clone())); + let action = Action::Return(Ok(( + ent.channel.clone(), + ChanProvenance::Preexisting, + ))); (oldstate, action) } else { // Unusable channel. Move to the Building @@ -181,7 +184,7 @@ impl<CF: ChannelFactory> AbstractChanMgr<CF> { } // There's an in-progress channel. Wait for it. Action::Wait(pend) => match pend.await { - Ok(Ok(chan)) => return Ok(chan), + Ok(Ok(chan)) => return Ok((chan, ChanProvenance::NewlyCreated)), Ok(Err(e)) => { last_err = Some(e); } @@ -207,7 +210,7 @@ impl<CF: ChannelFactory> AbstractChanMgr<CF> { // It's okay if all the receivers went away: // that means that nobody was waiting for this channel. let _ignore_err = send.send(Ok(chan.clone())); - return Ok(chan); + return Ok((chan, ChanProvenance::NewlyCreated)); } Err(e) => { // The channel failed. Make it non-pending, tell the @@ -340,8 +343,8 @@ mod test { let cf = FakeChannelFactory::new(runtime); let mgr = AbstractChanMgr::new(cf); let target = (413, '!'); - let chan1 = mgr.get_or_launch(413, target).await.unwrap(); - let chan2 = mgr.get_or_launch(413, target).await.unwrap(); + let chan1 = mgr.get_or_launch(413, target).await.unwrap().0; + let chan2 = mgr.get_or_launch(413, target).await.unwrap().0; assert_eq!(chan1, chan2); @@ -411,14 +414,14 @@ mod test { mgr.get_or_launch(5, (5, 'a')), ); - let ch3 = ch3.unwrap(); + let ch3 = ch3.unwrap().0; let _ch4 = ch4.unwrap(); - let ch5 = ch5.unwrap(); + let ch5 = ch5.unwrap().0; ch3.start_closing(); ch5.start_closing(); - let ch3_new = mgr.get_or_launch(3, (3, 'b')).await.unwrap(); + let ch3_new = mgr.get_or_launch(3, (3, 'b')).await.unwrap().0; assert_ne!(ch3, ch3_new); assert_eq!(ch3_new.mood, 'b'); diff --git a/crates/tor-circmgr/src/build.rs b/crates/tor-circmgr/src/build.rs index c0e30ff364c8c3df07e257b609377311b1712c55..0d1269f20e366568120c7a40f3402e7f8c99ac95 100644 --- a/crates/tor-circmgr/src/build.rs +++ b/crates/tor-circmgr/src/build.rs @@ -73,13 +73,14 @@ async fn create_common<RT: Runtime, CT: ChanTarget>( rt: &RT, target: &CT, ) -> Result<PendingClientCirc> { - let chan = chanmgr - .get_or_launch(target) - .await - .map_err(|cause| Error::Channel { - peer: OwnedChanTarget::from_chan_target(target), - cause, - })?; + let (chan, _provenance) = + chanmgr + .get_or_launch(target) + .await + .map_err(|cause| Error::Channel { + peer: OwnedChanTarget::from_chan_target(target), + cause, + })?; let (pending_circ, reactor) = chan.new_circ().await.map_err(|error| Error::Protocol { error, peer: None, // we don't blame the peer, because new_circ() does no networking. diff --git a/doc/semver_status.md b/doc/semver_status.md index d5bdfa75bedd97bd04a3d2112c77264621d34f35..93848310e15e4b40be8c9968a24e6c16106d6541 100644 --- a/doc/semver_status.md +++ b/doc/semver_status.md @@ -25,6 +25,7 @@ MODIFIED: Added `reset()` method to RetrySchedule. ### tor-chanmgr BREAKING: Added members to `Error::Proto` +BREAKING: Added `ChanProvenance` to `ChanMgr::get_or_launch`. ### tor-circmgr