ChanMgr: Return provenance information from get_or_launch

We need this since we want to report certain conditions only when
they happen on a new channel, not if we observe them on a
preexisting channel.
This commit is contained in:
Nick Mathewson 2022-04-07 10:43:04 -04:00
parent 9160b55c57
commit 0050045867
4 changed files with 40 additions and 21 deletions

View File

@ -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

View File

@ -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');

View File

@ -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.

View File

@ -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