use crate::mgr::{self, AbstractSpec, MockablePlan};
use crate::path::OwnedPath;
use crate::usage::{SupportedCircUsage, TargetCircUsage};
use crate::{DirInfo, Error, Result};
use async_trait::async_trait;
use educe::Educe;
use futures::future::OptionFuture;
use std::sync::Arc;
use tor_basic_utils::skip_fmt;
use tor_error::internal;
use tor_proto::circuit::{CircParameters, ClientCirc};
use tor_rtcompat::Runtime;
impl mgr::AbstractCirc for tor_proto::circuit::ClientCirc {
type Id = tor_proto::circuit::UniqId;
fn id(&self) -> Self::Id {
self.unique_id()
}
fn usable(&self) -> bool {
!self.is_closing()
}
}
#[derive(Educe)]
#[educe(Debug)]
pub(crate) struct Plan {
final_spec: SupportedCircUsage,
path: OwnedPath,
params: CircParameters,
guard_status: Option<tor_guardmgr::GuardMonitor>,
#[educe(Debug(method = "skip_fmt"))]
guard_usable: Option<tor_guardmgr::GuardUsable>,
}
impl MockablePlan for Plan {}
#[async_trait]
impl<R: Runtime> crate::mgr::AbstractCircBuilder for crate::build::CircuitBuilder<R> {
type Circ = ClientCirc;
type Spec = SupportedCircUsage;
type Plan = Plan;
fn plan_circuit(
&self,
usage: &TargetCircUsage,
dir: DirInfo<'_>,
) -> Result<(Plan, SupportedCircUsage)> {
let mut rng = rand::thread_rng();
let (path, final_spec, guard_status, guard_usable) = usage.build_path(
&mut rng,
dir,
Some(self.guardmgr()),
self.path_config().as_ref(),
self.runtime().wallclock(),
)?;
let plan = Plan {
final_spec: final_spec.clone(),
path: (&path).try_into()?,
params: dir.circ_params(),
guard_status,
guard_usable,
};
Ok((plan, final_spec))
}
async fn build_circuit(&self, plan: Plan) -> Result<(SupportedCircUsage, ClientCirc)> {
use crate::build::GuardStatusHandle;
use tor_guardmgr::GuardStatus;
let Plan {
final_spec,
path,
params,
guard_status,
guard_usable,
} = plan;
let guard_usable: OptionFuture<_> = guard_usable.into();
let guard_status: Arc<GuardStatusHandle> = Arc::new(guard_status.into());
guard_status.pending(GuardStatus::AttemptAbandoned);
match self
.build_owned(
path,
¶ms,
Arc::clone(&guard_status),
final_spec.channel_usage(),
)
.await
{
Ok(circuit) => {
guard_status.report(GuardStatus::Success);
match guard_usable.await {
Some(Ok(true)) | None => (),
Some(Ok(false)) => return Err(Error::GuardNotUsable),
Some(Err(_)) => {
return Err(internal!("Guard usability status cancelled").into());
}
}
Ok((final_spec, circuit))
}
Err(e) => {
guard_status.commit();
Err(e)
}
}
}
fn launch_parallelism(&self, spec: &TargetCircUsage) -> usize {
match spec {
TargetCircUsage::Dir => 3,
_ => 1,
}
}
fn select_parallelism(&self, spec: &TargetCircUsage) -> usize {
self.launch_parallelism(spec)
}
fn learning_timeouts(&self) -> bool {
crate::build::CircuitBuilder::learning_timeouts(self)
}
}