1use arti_client::TorClient;
4use arti_rpcserver::RpcAuthentication;
5use derive_deftly::Deftly;
6use futures::stream::StreamExt as _;
7use std::sync::Arc;
8use tor_async_utils::{DropNotifyEofSignallable, DropNotifyWatchSender};
9use tor_rpc_connect::SuperuserPermission;
10use tor_rpcbase::{self as rpc};
11use tor_rtcompat::Runtime;
12
13use crate::{
14 proxy::port_info,
15 reload_cfg::LaunchableTorClient,
16 rpc::{listener::RpcConnInfo, superuser::RpcSuperuser},
17};
18
19use super::proxyinfo::{self, ProxyInfo};
20
21#[derive(Deftly)]
36#[derive_deftly(rpc::Object)]
37#[deftly(rpc(
38 delegate_with = "|this: &Self| Some(this.session.clone())",
39 delegate_type = "arti_rpcserver::RpcSession"
40))]
41#[deftly(rpc(expose_outside_of_session))]
42pub(super) struct ArtiRpcSession {
43 pub(super) arti_state: Arc<RpcVisibleArtiState>,
45 session: Arc<arti_rpcserver::RpcSession>,
47}
48
49pub(crate) struct RpcVisibleArtiState {
60 proxy_info: postage::watch::Receiver<ProxyInfoState>,
64}
65
66#[derive(Debug)]
68pub(crate) struct RpcStateSender {
69 proxy_info_sender: DropNotifyWatchSender<ProxyInfoState>,
71}
72
73impl ArtiRpcSession {
74 pub(super) fn new<R: Runtime>(
81 auth: &RpcAuthentication,
82 client_root: &Arc<TorClient<R>>,
83 launchable_client: &Arc<LaunchableTorClient<R>>,
84 arti_state: &Arc<RpcVisibleArtiState>,
85 listener_info: &RpcConnInfo,
86 ) -> Arc<Self> {
87 let _ = auth; let client = client_root.isolated_client();
89 let session = arti_rpcserver::RpcSession::new_with_client(client);
90 if listener_info.allow_superuser == SuperuserPermission::Allowed {
91 session.provide_superuser_permission(Arc::new(RpcSuperuser::new(
92 client_root.clone(),
93 launchable_client.clone(),
94 )) as _);
95 }
96 Arc::new(ArtiRpcSession {
97 session,
98 arti_state: arti_state.clone(),
99 })
100 }
101}
102
103#[derive(Debug, Clone)]
105enum ProxyInfoState {
106 Unset,
108 Set(Arc<ProxyInfo>),
110 Eof,
112}
113
114impl DropNotifyEofSignallable for ProxyInfoState {
115 fn eof() -> Self {
116 Self::Eof
117 }
118}
119
120impl RpcVisibleArtiState {
121 pub(crate) fn new() -> (Arc<Self>, RpcStateSender) {
123 let (proxy_info_sender, proxy_info) = postage::watch::channel_with(ProxyInfoState::Unset);
124 let proxy_info_sender = DropNotifyWatchSender::new(proxy_info_sender);
125 (
126 Arc::new(Self { proxy_info }),
127 RpcStateSender { proxy_info_sender },
128 )
129 }
130
131 pub(super) async fn get_proxy_info(&self) -> Result<Arc<ProxyInfo>, ()> {
135 let mut proxy_info = self.proxy_info.clone();
136 while let Some(v) = proxy_info.next().await {
137 match v {
138 ProxyInfoState::Unset => {
139 }
141 ProxyInfoState::Set(proxyinfo) => return Ok(Arc::clone(&proxyinfo)),
142 ProxyInfoState::Eof => return Err(()),
143 }
144 }
145 Err(())
146 }
147}
148
149impl RpcStateSender {
150 pub(crate) fn set_stream_listeners(&mut self, ports: &[port_info::Port]) {
154 let info = ProxyInfo {
155 proxies: ports
156 .iter()
157 .filter_map(|port| {
158 Some(proxyinfo::Proxy {
159 listener: proxyinfo::ProxyListener::try_from_portinfo(port)?,
160 })
161 })
162 .collect(),
163 };
164 *self.proxy_info_sender.borrow_mut() = ProxyInfoState::Set(Arc::new(info));
165 }
166}
167
168#[cfg(test)]
169mod test {
170 #![allow(clippy::bool_assert_comparison)]
172 #![allow(clippy::clone_on_copy)]
173 #![allow(clippy::dbg_macro)]
174 #![allow(clippy::mixed_attributes_style)]
175 #![allow(clippy::print_stderr)]
176 #![allow(clippy::print_stdout)]
177 #![allow(clippy::single_char_pattern)]
178 #![allow(clippy::unwrap_used)]
179 #![allow(clippy::unchecked_time_subtraction)]
180 #![allow(clippy::useless_vec)]
181 #![allow(clippy::needless_pass_by_value)]
182 #![allow(clippy::string_slice)] use tor_rtcompat::SpawnExt as _;
186 use tor_rtmock::MockRuntime;
187
188 use super::*;
189
190 #[test]
191 fn set_proxy_info() {
192 MockRuntime::test_with_various(|rt| async move {
193 let (state, mut sender) = RpcVisibleArtiState::new();
194 let _task = rt.clone().spawn_with_handle(async move {
195 sender.set_stream_listeners(&[port_info::Port {
196 protocol: port_info::SupportedProtocol::Socks,
197 address: "8.8.8.8:40".parse().unwrap(),
198 }]);
199 sender });
201
202 let value = state.get_proxy_info().await;
203
204 let value_again = state.get_proxy_info().await;
207 assert_eq!(value.unwrap(), value_again.unwrap());
208 });
209 }
210}