onlyne_client/session/dispatch/
outbound.rs1use super::*;
2
3use super::env::{AGENT, REQUEST_TIMEOUT};
4use super::projection::with_cluster;
5use super::state::{DispatchInner, DispatchState};
6
7pub(super) fn store_ack(inner: &DispatchInner, mut ack: AckArgs) {
9 if ack.op_id.is_none() {
10 ack.op_id = Some(onlyne_proto::new_op_id());
11 }
12 let Some(op_id) = ack.op_id.clone() else {
13 return;
14 };
15 match serde_json::to_value(ClientOp::Ack(ack)) {
16 Ok(value) => {
17 if let Err(error) = inner.store.enqueue_intent(&op_id, &value) {
18 tracing::warn!(error = %error, "settled ack was not stored");
19 }
20 }
21 Err(error) => tracing::warn!(error = %error, "settled ack did not serialize"),
22 }
23}
24
25pub(super) fn queue_outbound_locked(
28 inner: &mut DispatchInner,
29 envelope: &Envelope,
30) -> Result<String> {
31 let mut stamped = envelope.clone();
32 let op_id = stamp_op_id(&mut stamped);
33 stamped
34 .validate()
35 .map_err(|error| anyhow!(error.to_string()))?;
36 inner
37 .store
38 .enqueue_intent(&op_id, &serde_json::to_value(&stamped)?)?;
39 Ok(op_id)
40}
41
42pub(super) async fn transport_envelope(state: &DispatchState, envelope: &Envelope) -> Result<()> {
44 let op = ClientOp::Send(Box::new(envelope.clone()));
45 let outbox = { state.inner.lock().outbox.clone() };
46 match outbox {
47 Some(outbox) => match outbox.send(op).await {
48 Ok(()) => Ok(()),
49 Err(error) => {
50 tracing::warn!(error = %error, "completion fell back to the intent queue");
51 state.enqueue_outbound(envelope).map(|_| ())
52 }
53 },
54 None => state.enqueue_outbound(envelope).map(|_| ()),
55 }
56}
57
58#[derive(Clone)]
64pub struct ClientLink {
65 handle: ClientConn,
66 welcome: Arc<Welcome>,
67 hello: HandshakeArgs,
68}
69
70impl ClientLink {
71 pub async fn connect(init: &ClientInit, live_tasks: Vec<String>) -> Result<Self, NetError> {
74 let keypair = KeyPair::load(&init.key_path)?;
75 let settings = ConnSettings {
76 agent: AGENT.to_string(),
77 version: env!("CARGO_PKG_VERSION").to_string(),
78 ..ConnSettings::new(PROTOCOL_VERSION)
79 };
80 let handle: ClientConn =
81 dial(&init.server, &keypair, &init.cert_pin, &init.role, settings).await?;
82 let hello = HandshakeArgs {
83 protocol: PROTOCOL_VERSION,
84 role: init.role.clone(),
85 key: keypair.public_str(),
86 signature: String::new(),
87 agent: AGENT.to_string(),
88 version: env!("CARGO_PKG_VERSION").to_string(),
89 aggregate: false,
90 live_tasks: Vec::new(),
91 };
92 let body = handle
93 .request(
94 Frame::req(
95 String::new(),
96 ClientOp::Hello(hello_with_live_tasks(&hello, live_tasks)),
97 ),
98 REQUEST_TIMEOUT,
99 )
100 .await?;
101 if !body.ok {
102 let error = body.error.clone().unwrap_or(onlyne_proto::ErrorPayload {
103 code: onlyne_proto::ErrorCode::Internal,
104 message: "hello refused".to_string(),
105 field: None,
106 });
107 return Err(NetError::Rejected {
108 code: wire_code(error.code),
109 message: error.message,
110 });
111 }
112 let data = body.data().cloned().ok_or(NetError::BadFrame)?;
113 let welcome: Welcome = serde_json::from_value(data).map_err(|_| NetError::BadFrame)?;
114 Ok(Self {
115 handle,
116 welcome: Arc::new(welcome),
117 hello,
118 })
119 }
120
121 pub async fn authenticate(&self, live_tasks: Vec<String>) -> Result<(), NetError> {
127 let body = self
128 .handle
129 .request(
130 Frame::req(
131 String::new(),
132 ClientOp::Hello(hello_with_live_tasks(&self.hello, live_tasks)),
133 ),
134 REQUEST_TIMEOUT,
135 )
136 .await?;
137 if !body.ok {
138 let error = body.error.clone().unwrap_or(onlyne_proto::ErrorPayload {
139 code: onlyne_proto::ErrorCode::Internal,
140 message: "hello refused".to_string(),
141 field: None,
142 });
143 return Err(NetError::Rejected {
144 code: wire_code(error.code),
145 message: error.message,
146 });
147 }
148 Ok(())
149 }
150
151 pub fn welcome(&self) -> &Welcome {
153 &self.welcome
154 }
155
156 pub async fn request(&self, op: ClientOp) -> Result<ResBody, NetError> {
160 self.handle
161 .request(Frame::req(String::new(), op), REQUEST_TIMEOUT)
162 .await
163 }
164
165 pub fn events(&self) -> broadcast::Receiver<Frame<ClientOp>> {
167 self.handle.events()
168 }
169
170 pub async fn close(&self) -> Result<(), NetError> {
172 self.handle.close().await
173 }
174
175 pub fn readiness(&self) -> ConnReadiness {
177 self.handle.readiness()
178 }
179 pub async fn failure(&self) -> Option<NetError> {
181 self.handle.failure().await
182 }
183}
184
185pub(crate) fn hello_with_live_tasks(
191 hello: &HandshakeArgs,
192 live_tasks: Vec<String>,
193) -> HandshakeArgs {
194 let mut hello = hello.clone();
195 hello.live_tasks = live_tasks;
196 hello
197}
198
199fn wire_code(code: onlyne_proto::ErrorCode) -> String {
201 serde_json::to_value(code)
202 .ok()
203 .and_then(|value| value.as_str().map(str::to_string))
204 .unwrap_or_else(|| "internal".to_string())
205}
206
207pub trait Outbox: Send + Sync {
212 fn send(&self, op: ClientOp)
213 -> Pin<Box<dyn Future<Output = Result<(), NetError>> + Send + '_>>;
214
215 fn request(
218 &self,
219 op: ClientOp,
220 ) -> Pin<Box<dyn Future<Output = Result<ResBody, NetError>> + Send + '_>>;
221}
222
223impl Outbox for ClientLink {
224 fn send(
225 &self,
226 op: ClientOp,
227 ) -> Pin<Box<dyn Future<Output = Result<(), NetError>> + Send + '_>> {
228 Box::pin(async move { self.request(op).await.map(|_| ()) })
229 }
230
231 fn request(
232 &self,
233 op: ClientOp,
234 ) -> Pin<Box<dyn Future<Output = Result<ResBody, NetError>> + Send + '_>> {
235 Box::pin(async move { ClientLink::request(self, op).await })
236 }
237}
238
239pub async fn send_frame(state: &DispatchState, op: ClientOp) -> Result<()> {
255 let op = match op {
256 ClientOp::Report(report) => ClientOp::Report(with_cluster(state, report)),
257 other => other,
258 };
259 if let Some(outbox) = state.outbox() {
260 if outbox.send(op.clone()).await.is_ok() {
261 return Ok(());
262 }
263 }
264 state.enqueue_op(&op)?;
265 Ok(())
266}
267
268impl DispatchState {
269 pub fn enqueue_outbound(&self, envelope: &Envelope) -> Result<String> {
277 queue_outbound_locked(&mut self.inner.lock(), envelope)
278 }
279
280 pub fn accept_new(&self) -> Arc<AtomicBool> {
282 self.inner.lock().accept_new.clone()
283 }
284
285 pub fn link_up(&self) -> bool {
287 self.inner.lock().link_up.load(Ordering::SeqCst)
288 }
289
290 pub fn set_link_up(&self, up: bool) {
292 self.inner.lock().link_up.store(up, Ordering::SeqCst);
293 }
294
295 pub fn cluster_ref(&self) -> String {
297 self.inner.lock().cluster_ref.clone()
298 }
299
300 pub fn set_topology(&self, cluster: &str) {
307 self.inner.lock().topology = cluster.trim().to_string();
308 }
309
310 pub fn topology(&self) -> String {
312 self.inner.lock().topology.clone()
313 }
314
315 pub fn set_cluster_ref(&self, aggregate: impl Into<String>) {
318 self.inner.lock().cluster_ref = aggregate.into();
319 }
320
321 pub async fn request(&self, op: ClientOp) -> Result<ResBody, NetError> {
326 let outbox = { self.inner.lock().outbox.clone() };
327 let Some(outbox) = outbox else {
328 return Err(NetError::NotReady);
329 };
330 outbox.request(op).await
331 }
332
333 pub fn attach_outbox(&self, outbox: Arc<dyn Outbox>) {
335 self.inner.lock().outbox = Some(outbox);
336 }
337
338 pub fn detach_outbox(&self) {
340 self.inner.lock().outbox = None;
341 }
342
343 fn outbox(&self) -> Option<Arc<dyn Outbox>> {
344 self.inner.lock().outbox.clone()
345 }
346
347 pub fn enqueue_op(&self, op: &ClientOp) -> Result<String> {
349 let op_id = onlyne_proto::new_id();
350 self.inner
351 .lock()
352 .store
353 .enqueue_intent(&op_id, &serde_json::to_value(op)?)?;
354 Ok(op_id)
355 }
356}