pb_mapper_client/sdk/
handle.rs1use std::sync::Mutex;
8use std::time::Duration;
9
10use tokio::sync::watch;
11use tokio::task::JoinHandle;
12use tokio_util::sync::CancellationToken;
13
14use super::{Error, Result, TunnelStatus};
15
16pub(crate) struct LiveTunnel {
17 shutdown: CancellationToken,
18 join: Mutex<Option<JoinHandle<()>>>,
19 status: watch::Receiver<TunnelStatus>,
20}
21
22impl LiveTunnel {
23 pub(crate) fn new(
24 shutdown: CancellationToken,
25 join: JoinHandle<()>,
26 status: watch::Receiver<TunnelStatus>,
27 ) -> Self {
28 Self {
29 shutdown,
30 join: Mutex::new(Some(join)),
31 status,
32 }
33 }
34
35 pub(crate) fn status(&self) -> TunnelStatus {
36 self.status.borrow().clone()
37 }
38
39 pub(crate) fn subscribe(&self) -> watch::Receiver<TunnelStatus> {
40 self.status.clone()
41 }
42
43 pub(crate) async fn wait_ready(&self) -> Result<()> {
44 wait_for_connected(&mut self.status.clone()).await
45 }
46
47 pub(crate) async fn wait_ready_timeout(&self, timeout: Duration) -> Result<()> {
48 match tokio::time::timeout(timeout, self.wait_ready()).await {
49 Ok(result) => result,
50 Err(_) => Err(Error::ReadyTimeout { timeout }),
51 }
52 }
53
54 pub(crate) async fn stop(&self) -> Result<()> {
55 self.shutdown.cancel();
56 let handle = self
57 .join
58 .lock()
59 .unwrap_or_else(|poisoned| poisoned.into_inner())
60 .take();
61 if let Some(mut handle) = handle {
62 match tokio::time::timeout(Duration::from_secs(5), &mut handle).await {
63 Ok(Ok(())) => {}
64 Ok(Err(join_error)) if join_error.is_cancelled() => {}
65 Ok(Err(join_error)) => {
66 return Err(Error::protocol(format!("tunnel task failed: {join_error}")));
67 }
68 Err(_) => {
69 handle.abort();
70 let _ = handle.await;
71 }
72 }
73 }
74 Ok(())
75 }
76}
77
78impl Drop for LiveTunnel {
79 fn drop(&mut self) {
80 self.shutdown.cancel();
81 if let Some(handle) = self
82 .join
83 .lock()
84 .unwrap_or_else(|poisoned| poisoned.into_inner())
85 .take()
86 {
87 handle.abort();
88 }
89 }
90}
91
92async fn wait_for_connected(status: &mut watch::Receiver<TunnelStatus>) -> Result<()> {
93 loop {
94 match status.borrow().clone() {
95 TunnelStatus::Connected => return Ok(()),
96 TunnelStatus::Failed(reason) => return Err(Error::TunnelFailed { reason }),
97 TunnelStatus::Stopped => return Err(Error::Stopped),
98 TunnelStatus::Starting | TunnelStatus::Retrying => {}
99 }
100 status.changed().await.map_err(|_| Error::Stopped)?;
101 }
102}
103
104macro_rules! tunnel_handle {
111 ($(#[$doc:meta])* $name:ident) => {
112 $(#[$doc])*
113 pub struct $name {
114 inner: LiveTunnel,
115 key: String,
116 }
117
118 impl $name {
119 pub(crate) fn new(inner: LiveTunnel, key: String) -> Self {
120 Self { inner, key }
121 }
122
123 pub fn key(&self) -> &str {
125 &self.key
126 }
127
128 pub fn status(&self) -> TunnelStatus {
130 self.inner.status()
131 }
132
133 pub fn subscribe(&self) -> watch::Receiver<TunnelStatus> {
135 self.inner.subscribe()
136 }
137
138 pub async fn wait_ready(&self) -> Result<()> {
140 self.inner.wait_ready().await
141 }
142
143 pub async fn wait_ready_timeout(&self, timeout: Duration) -> Result<()> {
145 self.inner.wait_ready_timeout(timeout).await
146 }
147
148 pub async fn stop(&self) -> Result<()> {
150 self.inner.stop().await
151 }
152 }
153 };
154}
155
156tunnel_handle!(
157 Registration
159);
160
161tunnel_handle!(
162 Connection
164);