1use super::ProxyDB;
2use arc_swap::ArcSwap;
3use rama_core::error::BoxErrorExt as _;
4use rama_core::error::{BoxError, ErrorContext};
5use rama_utils::collections::NonEmptyVec;
6use std::{fmt, num::NonZeroUsize, ops::Deref, sync::Arc};
7
8pub fn proxy_db_updater<T>() -> (LiveUpdateProxyDB<T>, LiveUpdateProxyDBSetter<T>)
31where
32 T: ProxyDB<Error: Into<BoxError>>,
33{
34 let data = Arc::new(ArcSwap::from_pointee(None));
35 let reader = LiveUpdateProxyDB(data.clone());
36 let writer = LiveUpdateProxyDBSetter(data);
37 (reader, writer)
38}
39
40pub struct LiveUpdateProxyDB<T>(Arc<ArcSwap<Option<T>>>);
45
46impl<T: fmt::Debug> fmt::Debug for LiveUpdateProxyDB<T> {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 f.debug_tuple("LiveUpdateProxyDB").field(&self.0).finish()
49 }
50}
51
52impl<T> Clone for LiveUpdateProxyDB<T> {
53 fn clone(&self) -> Self {
54 Self(self.0.clone())
55 }
56}
57
58impl<T> ProxyDB for LiveUpdateProxyDB<T>
59where
60 T: ProxyDB<Error: Into<BoxError>>,
61{
62 type Error = BoxError;
63
64 async fn get_proxies_if(
65 &self,
66 ctx: super::ProxyContext,
67 filter: super::ProxyFilter,
68 predicate: impl super::ProxyQueryPredicate,
69 limit: Option<NonZeroUsize>,
70 ) -> Result<NonEmptyVec<super::Proxy>, Self::Error> {
71 match self.0.load().deref().deref() {
72 Some(db) => db
73 .get_proxies_if(ctx, filter, predicate, limit)
74 .await
75 .into_box_error(),
76 None => Err(BoxError::from_static_str(
77 "live proxy db: proxy db is None: get_proxies_if unable to proceed",
78 )),
79 }
80 }
81
82 async fn get_proxies(
83 &self,
84 ctx: super::ProxyContext,
85 filter: super::ProxyFilter,
86 limit: Option<NonZeroUsize>,
87 ) -> Result<NonEmptyVec<super::Proxy>, Self::Error> {
88 match self.0.load().deref().deref() {
89 Some(db) => db.get_proxies(ctx, filter, limit).await.into_box_error(),
90 None => Err(BoxError::from_static_str(
91 "live proxy db: proxy db is None: get_proxies unable to proceed",
92 )),
93 }
94 }
95
96 async fn get_proxy_if(
97 &self,
98 ctx: super::ProxyContext,
99 filter: super::ProxyFilter,
100 predicate: impl super::ProxyQueryPredicate,
101 ) -> Result<super::Proxy, Self::Error> {
102 match self.0.load().deref().deref() {
103 Some(db) => db
104 .get_proxy_if(ctx, filter, predicate)
105 .await
106 .into_box_error(),
107 None => Err(BoxError::from_static_str(
108 "live proxy db: proxy db is None: get_proxy_if unable to proceed",
109 )),
110 }
111 }
112
113 async fn get_proxy(
114 &self,
115 ctx: super::ProxyContext,
116 filter: super::ProxyFilter,
117 ) -> Result<super::Proxy, Self::Error> {
118 match self.0.load().deref().deref() {
119 Some(db) => db.get_proxy(ctx, filter).await.into_box_error(),
120 None => Err(BoxError::from_static_str(
121 "live proxy db: proxy db is None: get_proxy unable to proceed",
122 )),
123 }
124 }
125}
126
127pub struct LiveUpdateProxyDBSetter<T>(Arc<ArcSwap<Option<T>>>);
134
135impl<T> LiveUpdateProxyDBSetter<T> {
136 pub fn set(&self, db: T) {
139 self.0.store(Arc::new(Some(db)))
140 }
141}
142
143impl<T: fmt::Debug> fmt::Debug for LiveUpdateProxyDBSetter<T> {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 f.debug_tuple("LiveUpdateProxyDBSetter")
146 .field(&self.0)
147 .finish()
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::{Proxy, ProxyFilter, proxydb::ProxyContext};
154 use rama_net::{asn::Asn, transport::TransportProtocol};
155 use rama_utils::str::non_empty_str;
156
157 use super::*;
158
159 #[tokio::test]
160 async fn test_empty_live_update_db() {
161 let (reader, _) = proxy_db_updater::<Proxy>();
162 reader
163 .get_proxies(
164 ProxyContext {
165 protocol: TransportProtocol::Tcp,
166 },
167 ProxyFilter::default(),
168 None,
169 )
170 .await
171 .unwrap_err();
172 reader
173 .get_proxy(
174 ProxyContext {
175 protocol: TransportProtocol::Tcp,
176 },
177 ProxyFilter::default(),
178 )
179 .await
180 .unwrap_err();
181 }
182
183 #[tokio::test]
184 async fn test_live_update_db_updated() {
185 let (reader, writer) = proxy_db_updater();
186
187 reader
188 .get_proxy(
189 ProxyContext {
190 protocol: TransportProtocol::Tcp,
191 },
192 ProxyFilter::default(),
193 )
194 .await
195 .unwrap_err();
196
197 writer.set(Proxy {
198 id: non_empty_str!("id"),
199 address: "authority:80".parse().unwrap(),
200 tcp: true,
201 udp: false,
202 http: false,
203 https: true,
204 socks5: false,
205 socks5h: false,
206 datacenter: true,
207 residential: false,
208 mobile: true,
209 pool_id: Some("pool_id".into()),
210 continent: Some("continent".into()),
211 country: Some("country".into()),
212 state: Some("state".into()),
213 city: Some("city".into()),
214 carrier: Some("carrier".into()),
215 asn: Some(Asn::from_static(1)),
216 });
217
218 assert_eq!(
219 "id",
220 reader
221 .get_proxy(
222 ProxyContext {
223 protocol: TransportProtocol::Tcp,
224 },
225 ProxyFilter::default(),
226 )
227 .await
228 .unwrap()
229 .id
230 );
231
232 let proxies = reader
233 .get_proxies(
234 ProxyContext {
235 protocol: TransportProtocol::Tcp,
236 },
237 ProxyFilter::default(),
238 None,
239 )
240 .await
241 .unwrap();
242 assert_eq!(proxies.len(), 1);
243 assert_eq!(proxies.head.id, "id");
244
245 reader
246 .get_proxy(
247 ProxyContext {
248 protocol: TransportProtocol::Udp,
249 },
250 ProxyFilter::default(),
251 )
252 .await
253 .unwrap_err();
254
255 assert_eq!(
256 "id",
257 reader
258 .get_proxy(
259 ProxyContext {
260 protocol: TransportProtocol::Tcp,
261 },
262 ProxyFilter::default(),
263 )
264 .await
265 .unwrap()
266 .id
267 );
268 }
269}