use derive_deftly::Deftly;
use dyn_clone::DynClone;
use futures::{SinkExt as _, StreamExt as _};
use serde::{Deserialize, Serialize};
use std::{net::IpAddr, sync::Arc};
use tor_proto::client::stream::DataStream;
use tor_rpcbase as rpc;
use tor_rtcompat::Runtime;
use crate::{StreamPrefs, TorAddr, TorClient};
impl<R: Runtime> TorClient<R> {
pub fn rpc_methods() -> Vec<rpc::dispatch::InvokerEnt> {
rpc::invoker_ent_list![
get_client_status::<R>,
watch_client_status::<R>,
isolated_client::<R>,
@special client_connect_with_prefs::<R>,
@special client_resolve_with_prefs::<R>,
@special client_resolve_ptr_with_prefs::<R>,
]
}
}
#[derive(Deftly, Debug, Serialize, Deserialize)]
#[derive_deftly(rpc::DynMethod)]
#[deftly(rpc(method_name = "arti:get_client_status"))]
struct GetClientStatus {}
impl rpc::RpcMethod for GetClientStatus {
type Output = ClientStatusInfo;
type Update = rpc::NoUpdates;
}
#[derive(Deftly, Debug, Serialize, Deserialize)]
#[derive_deftly(rpc::DynMethod)]
#[deftly(rpc(method_name = "arti:watch_client_status"))]
struct WatchClientStatus {}
impl rpc::RpcMethod for WatchClientStatus {
type Output = rpc::Nil; type Update = ClientStatusInfo;
}
#[derive(Serialize, Deserialize)]
struct ClientStatusInfo {
ready: bool,
fraction: f32,
blocked: Option<String>,
}
impl From<crate::status::BootstrapStatus> for ClientStatusInfo {
fn from(s: crate::status::BootstrapStatus) -> Self {
let ready = s.ready_for_traffic();
let fraction = s.as_frac();
let blocked = s.blocked().map(|b| b.to_string());
Self {
ready,
fraction,
blocked,
}
}
}
async fn get_client_status<R: Runtime>(
client: Arc<TorClient<R>>,
_method: Box<GetClientStatus>,
_ctx: Arc<dyn rpc::Context>,
) -> Result<ClientStatusInfo, rpc::RpcError> {
Ok(client.bootstrap_status().into())
}
async fn watch_client_status<R: Runtime>(
client: Arc<TorClient<R>>,
_method: Box<WatchClientStatus>,
_ctx: Arc<dyn rpc::Context>,
mut updates: rpc::UpdateSink<ClientStatusInfo>,
) -> Result<rpc::Nil, rpc::RpcError> {
let mut events = client.bootstrap_events();
updates.send(client.bootstrap_status().into()).await?;
while let Some(status) = events.next().await {
updates.send(status.into()).await?;
}
Ok(rpc::NIL)
}
#[derive(Deftly, Debug, Serialize, Deserialize)]
#[derive_deftly(rpc::DynMethod)]
#[deftly(rpc(method_name = "arti:new_isolated_client"))]
#[non_exhaustive]
pub struct IsolatedClient {}
impl rpc::RpcMethod for IsolatedClient {
type Output = rpc::SingleIdResponse;
type Update = rpc::NoUpdates;
}
async fn isolated_client<R: Runtime>(
client: Arc<TorClient<R>>,
_method: Box<IsolatedClient>,
ctx: Arc<dyn rpc::Context>,
) -> Result<rpc::SingleIdResponse, rpc::RpcError> {
let new_client = Arc::new(client.isolated_client());
let client_id = ctx.register_owned(new_client);
Ok(rpc::SingleIdResponse::from(client_id))
}
pub trait ClientConnectionError:
std::error::Error + tor_error::HasKind + DynClone + Send + Sync + seal::Sealed
{
}
impl<E> seal::Sealed for E where E: std::error::Error + tor_error::HasKind + DynClone + Send + Sync {}
impl<E> ClientConnectionError for E where
E: std::error::Error + tor_error::HasKind + DynClone + Send + Sync + seal::Sealed
{
}
impl std::error::Error for Box<dyn ClientConnectionError> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.as_ref().source()
}
}
impl tor_error::HasKind for Box<dyn ClientConnectionError> {
fn kind(&self) -> tor_error::ErrorKind {
self.as_ref().kind()
}
}
dyn_clone::clone_trait_object!(ClientConnectionError);
mod seal {
#[allow(unreachable_pub)]
pub trait Sealed {}
}
pub type ClientConnectionResult<T> = Result<T, Box<dyn ClientConnectionError>>;
#[derive(Deftly, Debug)]
#[derive_deftly(rpc::DynMethod)]
#[deftly(rpc(no_method_name))]
#[allow(clippy::exhaustive_structs)]
pub struct ConnectWithPrefs {
pub target: TorAddr,
pub prefs: StreamPrefs,
}
impl rpc::Method for ConnectWithPrefs {
type Output = Result<DataStream, Box<dyn ClientConnectionError>>;
type Update = rpc::NoUpdates;
}
#[derive(Deftly, Debug)]
#[derive_deftly(rpc::DynMethod)]
#[deftly(rpc(no_method_name))]
#[allow(clippy::exhaustive_structs)]
pub struct ResolveWithPrefs {
pub hostname: String,
pub prefs: StreamPrefs,
}
impl rpc::Method for ResolveWithPrefs {
type Output = Result<Vec<IpAddr>, Box<dyn ClientConnectionError>>;
type Update = rpc::NoUpdates;
}
#[derive(Deftly, Debug)]
#[derive_deftly(rpc::DynMethod)]
#[deftly(rpc(no_method_name))]
#[allow(clippy::exhaustive_structs)]
pub struct ResolvePtrWithPrefs {
pub addr: IpAddr,
pub prefs: StreamPrefs,
}
impl rpc::Method for ResolvePtrWithPrefs {
type Output = Result<Vec<String>, Box<dyn ClientConnectionError>>;
type Update = rpc::NoUpdates;
}
async fn client_connect_with_prefs<R: Runtime>(
client: Arc<TorClient<R>>,
method: Box<ConnectWithPrefs>,
_ctx: Arc<dyn rpc::Context>,
) -> Result<DataStream, Box<dyn ClientConnectionError>> {
TorClient::connect_with_prefs(client.as_ref(), &method.target, &method.prefs)
.await
.map_err(|e| Box::new(e) as _)
}
async fn client_resolve_with_prefs<R: Runtime>(
client: Arc<TorClient<R>>,
method: Box<ResolveWithPrefs>,
_ctx: Arc<dyn rpc::Context>,
) -> Result<Vec<IpAddr>, Box<dyn ClientConnectionError>> {
TorClient::resolve_with_prefs(client.as_ref(), &method.hostname, &method.prefs)
.await
.map_err(|e| Box::new(e) as _)
}
async fn client_resolve_ptr_with_prefs<R: Runtime>(
client: Arc<TorClient<R>>,
method: Box<ResolvePtrWithPrefs>,
_ctx: Arc<dyn rpc::Context>,
) -> Result<Vec<String>, Box<dyn ClientConnectionError>> {
TorClient::resolve_ptr_with_prefs(client.as_ref(), method.addr, &method.prefs)
.await
.map_err(|e| Box::new(e) as _)
}