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::{CfgMgr, 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 cfg_mgr: &Arc<CfgMgr<R>>,
86 listener_info: &RpcConnInfo,
87 ) -> Arc<Self> {
88 let _ = auth; let client = client_root.isolated_client();
90 let session = arti_rpcserver::RpcSession::new_with_client(client);
91 if listener_info.allow_superuser == SuperuserPermission::Allowed {
92 session.provide_superuser_permission(Arc::new(RpcSuperuser::new(
93 client_root.clone(),
94 launchable_client.clone(),
95 cfg_mgr.clone(),
96 )) as _);
97 }
98 Arc::new(ArtiRpcSession {
99 session,
100 arti_state: arti_state.clone(),
101 })
102 }
103}
104
105#[derive(Debug, Clone)]
107enum ProxyInfoState {
108 Unset,
110 Set(Arc<ProxyInfo>),
112 Eof,
114}
115
116impl DropNotifyEofSignallable for ProxyInfoState {
117 fn eof() -> Self {
118 Self::Eof
119 }
120}
121
122impl RpcVisibleArtiState {
123 pub(crate) fn new() -> (Arc<Self>, RpcStateSender) {
125 let (proxy_info_sender, proxy_info) = postage::watch::channel_with(ProxyInfoState::Unset);
126 let proxy_info_sender = DropNotifyWatchSender::new(proxy_info_sender);
127 (
128 Arc::new(Self { proxy_info }),
129 RpcStateSender { proxy_info_sender },
130 )
131 }
132
133 pub(super) async fn get_proxy_info(&self) -> Result<Arc<ProxyInfo>, ()> {
137 let mut proxy_info = self.proxy_info.clone();
138 while let Some(v) = proxy_info.next().await {
139 match v {
140 ProxyInfoState::Unset => {
141 }
143 ProxyInfoState::Set(proxyinfo) => return Ok(Arc::clone(&proxyinfo)),
144 ProxyInfoState::Eof => return Err(()),
145 }
146 }
147 Err(())
148 }
149}
150
151impl RpcStateSender {
152 pub(crate) fn set_stream_listeners(&mut self, ports: &[port_info::Port]) {
156 let info = ProxyInfo {
157 proxies: ports
158 .iter()
159 .filter_map(|port| {
160 Some(proxyinfo::Proxy {
161 listener: proxyinfo::ProxyListener::try_from_portinfo(port)?,
162 })
163 })
164 .collect(),
165 };
166 *self.proxy_info_sender.borrow_mut() = ProxyInfoState::Set(Arc::new(info));
167 }
168}
169
170#[cfg(test)]
171mod test {
172 #![allow(clippy::bool_assert_comparison)]
174 #![allow(clippy::clone_on_copy)]
175 #![allow(clippy::dbg_macro)]
176 #![allow(clippy::mixed_attributes_style)]
177 #![allow(clippy::print_stderr)]
178 #![allow(clippy::print_stdout)]
179 #![allow(clippy::single_char_pattern)]
180 #![allow(clippy::unwrap_used)]
181 #![allow(clippy::unchecked_time_subtraction)]
182 #![allow(clippy::useless_vec)]
183 #![allow(clippy::needless_pass_by_value)]
184 #![allow(clippy::string_slice)] use tor_rtcompat::SpawnExt as _;
188 use tor_rtmock::MockRuntime;
189
190 use super::*;
191
192 #[test]
193 fn set_proxy_info() {
194 MockRuntime::test_with_various(|rt| async move {
195 let (state, mut sender) = RpcVisibleArtiState::new();
196 let _task = rt.clone().spawn_with_handle(async move {
197 sender.set_stream_listeners(&[port_info::Port {
198 protocol: port_info::SupportedProtocol::Socks,
199 address: "8.8.8.8:40".parse().unwrap(),
200 }]);
201 sender });
203
204 let value = state.get_proxy_info().await;
205
206 let value_again = state.get_proxy_info().await;
209 assert_eq!(value.unwrap(), value_again.unwrap());
210 });
211 }
212}