microsandbox_control_client/
connection.rs1use std::path::Path;
4use std::sync::Arc;
5
6use microsandbox_protocol::control::DEFAULT_REQUEST_TIMEOUT;
7use microsandbox_protocol_client::{
8 ClientError, ConnectOptions, Connector, Delivery, ErrorKind, LocalConnector, Message, Protocol,
9 RequestOptions,
10};
11use tokio::time::timeout_at;
12use tokio_util::sync::CancellationToken;
13
14use crate::{
15 CompatibleControlRequest, ControlClient, ControlClientError, ControlClientResult,
16 ControlProtocol, IntoControlMessage, JsonControlClient, JsonReply, VerifiedControlConnector,
17 dialer::Dialer,
18 json_client::{check_deadline, deadline},
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ControlMode {
28 Framed,
30 Json,
32}
33
34#[derive(Debug)]
36pub enum ControlReply {
37 Framed(Message),
39 Json(JsonReply),
41}
42
43#[derive(Clone)]
46pub struct ControlConnection {
47 inner: Arc<Inner>,
48}
49
50struct Inner {
51 selected: Selected,
52 dialer: Dialer,
53 options: ConnectOptions,
54 closed: CancellationToken,
55 capabilities: crate::Capabilities,
56 runtime_capabilities: crate::RuntimeCapabilities,
57}
58
59enum Selected {
60 Framed(ControlClient),
61 Json(JsonControlClient),
62}
63
64impl ControlConnection {
69 pub async fn connect(path: impl AsRef<Path>) -> ControlClientResult<Self> {
72 Self::connect_with(path, |options| options).await
73 }
74
75 pub async fn connect_with(
77 path: impl AsRef<Path>,
78 configure: impl FnOnce(ConnectOptions) -> ConnectOptions,
79 ) -> ControlClientResult<Self> {
80 Self::connect_connector_with(Arc::new(LocalConnector::new(path)), configure).await
81 }
82
83 pub async fn connect_connector(connector: Arc<dyn Connector>) -> ControlClientResult<Self> {
85 Self::connect_connector_with(connector, |options| options).await
86 }
87
88 pub async fn connect_connector_with(
90 connector: Arc<dyn Connector>,
91 configure: impl FnOnce(ConnectOptions) -> ConnectOptions,
92 ) -> ControlClientResult<Self> {
93 Self::establish(
94 Dialer::Unverified(connector),
95 configure(ConnectOptions::default()),
96 )
97 .await
98 }
99
100 pub async fn connect_verified_connector(
103 connector: Arc<dyn VerifiedControlConnector>,
104 ) -> ControlClientResult<Self> {
105 Self::connect_verified_connector_with(connector, |options| options).await
106 }
107
108 pub async fn connect_verified_connector_with(
110 connector: Arc<dyn VerifiedControlConnector>,
111 configure: impl FnOnce(ConnectOptions) -> ConnectOptions,
112 ) -> ControlClientResult<Self> {
113 Self::establish(
114 Dialer::Verified(connector),
115 configure(ConnectOptions::default()),
116 )
117 .await
118 }
119
120 async fn establish(dialer: Dialer, options: ConnectOptions) -> ControlClientResult<Self> {
121 options.limits.validate()?;
122 let until = deadline(options.setup_timeout)?;
123 timeout_at(until, async {
124 let json = JsonControlClient::configured(
125 dialer.clone(),
126 options.clone(),
127 Some(ControlMode::Json),
128 );
129 let (mode, runtime_capabilities) = json.discover(until).await?;
130 let capabilities = runtime_capabilities.generation_one();
131 let selected = match mode {
132 ControlMode::Json => Selected::Json(json),
133 ControlMode::Framed => {
134 let transport = dialer.connect(until).await?;
138 dialer.verify(until).await?;
139 check_deadline(until)?;
140 let established =
141 ControlProtocol::establish(transport, options.clone()).await?;
142 Selected::Framed(ControlClient::from_established(established).await?)
143 }
144 };
145 Ok(Self {
146 inner: Arc::new(Inner {
147 selected,
148 dialer,
149 options,
150 closed: CancellationToken::new(),
151 capabilities,
152 runtime_capabilities,
153 }),
154 })
155 })
156 .await
157 .unwrap_or_else(|_| Err(ClientError::new(ErrorKind::Timeout).into()))
158 .map_err(not_sent)
159 }
160
161 pub fn mode(&self) -> ControlMode {
164 match self.inner.selected {
165 Selected::Framed(_) => ControlMode::Framed,
166 Selected::Json(_) => ControlMode::Json,
167 }
168 }
169
170 pub fn capabilities(&self) -> &crate::Capabilities {
173 &self.inner.capabilities
174 }
175
176 pub fn runtime_capabilities(&self) -> &crate::RuntimeCapabilities {
178 &self.inner.runtime_capabilities
179 }
180
181 pub fn is_closed(&self) -> bool {
183 match &self.inner.selected {
184 Selected::Framed(client) => client.is_closed(),
185 Selected::Json(client) => client.is_closed(),
186 }
187 }
188
189 pub async fn closed(&self) {
191 match &self.inner.selected {
192 Selected::Framed(client) => client.closed().await,
193 Selected::Json(client) => client.closed().await,
194 }
195 }
196
197 pub fn framed(&self) -> ControlClientResult<&ControlClient> {
200 match &self.inner.selected {
201 Selected::Framed(client) => Ok(client),
202 Selected::Json(_) => Err(ControlClientError::UnsupportedMode),
203 }
204 }
205
206 pub async fn close(&self) {
208 self.inner.closed.cancel();
211 match &self.inner.selected {
212 Selected::Framed(client) => client.close().await,
213 Selected::Json(client) => client.close().await,
214 }
215 }
216
217 pub async fn request(
219 &self,
220 message: impl IntoControlMessage,
221 ) -> ControlClientResult<ControlReply> {
222 self.request_with(message, |options| options).await
223 }
224
225 pub async fn request_with(
227 &self,
228 message: impl IntoControlMessage,
229 configure: impl FnOnce(RequestOptions) -> RequestOptions,
230 ) -> ControlClientResult<ControlReply> {
231 let options = configure(RequestOptions::default());
232 match &self.inner.selected {
233 Selected::Json(client) => Ok(ControlReply::Json(
234 client.operation(message.into_json()?, options).await?,
235 )),
236 Selected::Framed(client) => {
237 let options = self.verify_before_request(options).await?;
238 Ok(ControlReply::Framed(
239 client.request_with(message, |_| options).await?,
240 ))
241 }
242 }
243 }
244
245 pub async fn request_typed<R: CompatibleControlRequest>(
247 &self,
248 request: &R,
249 ) -> ControlClientResult<R::Response> {
250 self.request_typed_with(request, |options| options).await
251 }
252
253 pub async fn request_typed_with<R: CompatibleControlRequest>(
255 &self,
256 request: &R,
257 configure: impl FnOnce(RequestOptions) -> RequestOptions,
258 ) -> ControlClientResult<R::Response> {
259 let options = configure(RequestOptions::default());
260 match &self.inner.selected {
261 Selected::Json(client) => client.request_compatible(request, options).await,
262 Selected::Framed(client) => {
263 if request.min_generation() > client.ready().welcome.generation {
264 return self.inner_json(request, options).await;
265 }
266 let options = self.verify_before_request(options).await?;
267 client.request_typed_with(request, |_| options).await
268 }
269 }
270 }
271
272 async fn inner_json<R: CompatibleControlRequest>(
273 &self,
274 request: &R,
275 options: RequestOptions,
276 ) -> ControlClientResult<R::Response> {
277 let json = JsonControlClient::configured(
280 self.inner.dialer.clone(),
281 self.inner.options.clone(),
282 Some(ControlMode::Framed),
283 );
284 json.request_compatible(request, options).await
285 }
286
287 async fn verify_before_request(
288 &self,
289 options: RequestOptions,
290 ) -> ControlClientResult<RequestOptions> {
291 if self.is_closed() {
292 return Err(ClientError::new(ErrorKind::Closed).into());
293 }
294 let until = deadline(
295 options
296 .request_timeout
297 .or(self.inner.options.limits.request_timeout)
298 .unwrap_or(DEFAULT_REQUEST_TIMEOUT),
299 )?;
300 check_deadline(until)?;
301 let result = tokio::select! {
302 biased;
303 _ = self.inner.closed.cancelled() => Err(ClientError::new(ErrorKind::Closed).into()),
304 result = timeout_at(until, self.inner.dialer.verify(until)) => result
305 .unwrap_or_else(|_| Err(ClientError::new(ErrorKind::Timeout).into())),
306 };
307 if let Err(error) = result {
308 self.close().await;
309 return Err(not_sent(error));
310 }
311 check_deadline(until)?;
312 Ok(RequestOptions::default()
313 .request_timeout(until.saturating_duration_since(tokio::time::Instant::now())))
314 }
315}
316
317pub(crate) fn not_sent(error: ControlClientError) -> ControlClientError {
322 match error {
323 ControlClientError::Client(error) => error.with_delivery(Delivery::NotSent).into(),
324 error => error,
325 }
326}