1use std::future::Future;
17use std::pin::Pin;
18use std::sync::{Arc, Mutex, Weak};
19use std::time::Duration;
20
21use tokio::sync::watch;
22
23use crate::cbor::{self, Value};
24use crate::frame::{self, StreamMode, VerifiedRequest};
25use crate::record::{
26 self, Authorization, ProcedureAdvertisementOptions, Reason, Record, RecordError,
27 TombstoneOptions, Trust,
28};
29
30use super::admission::Verdict;
31use super::framing::MAX_FRAME_BYTES;
32use super::stream::StreamHandler;
33use super::{now_ms, Inner, Link, LinkError};
34
35const CODE_HANDLER_ERROR: &str = "handler_error";
39const CODE_HANDLER_CRASHED: &str = "temporary_relay_failure";
40const CODE_UNKNOWN_PROCEDURE: &str = "unknown_next_peer";
41pub(super) const CODE_REQUEST_COPY: &str = "request_copy";
42const CODE_PAYLOAD_TOO_LARGE: &str = "payload_too_large";
43const CODE_UNSENDABLE: &str = "unknown_error";
44
45const MAX_DETAIL_BYTES: usize = 256;
47
48const MAX_ADVERTISEMENT_TTL: Duration = Duration::from_secs(5 * 60);
50const REFRESH_RETRY: Duration = Duration::from_secs(10);
53
54pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
56
57pub type Handler = Arc<dyn Fn(Request) -> BoxFuture<Result<Value, String>> + Send + Sync>;
61
62pub fn handler<F, Fut>(f: F) -> Handler
64where
65 F: Fn(Request) -> Fut + Send + Sync + 'static,
66 Fut: Future<Output = Result<Value, String>> + Send + 'static,
67{
68 Arc::new(move |r| Box::pin(f(r)))
69}
70
71#[derive(Debug, Clone, PartialEq)]
75pub struct Request {
76 pub caller: [u8; 32],
77 pub realm: [u8; 32],
78 pub procedure: String,
79 pub payload: Value,
80 pub token: Option<Vec<u8>>,
81 pub proofs: Option<Vec<Vec<u8>>>,
82 pub deadline_ms: u64,
83}
84
85#[derive(Clone)]
90pub struct Offer {
91 pub realm: [u8; 32],
92 pub procedure: String,
93 pub handler: Option<Handler>,
94 pub stream: Option<StreamOffer>,
95 pub realm_key: Option<Vec<u8>>,
96}
97
98impl Offer {
99 pub fn unary(realm: [u8; 32], procedure: &str, handler: Handler) -> Offer {
101 Offer {
102 realm,
103 procedure: procedure.to_string(),
104 handler: Some(handler),
105 stream: None,
106 realm_key: None,
107 }
108 }
109
110 pub fn stream(
112 realm: [u8; 32],
113 procedure: &str,
114 mode: StreamMode,
115 handler: StreamHandler,
116 ) -> Offer {
117 Offer {
118 realm,
119 procedure: procedure.to_string(),
120 handler: None,
121 stream: Some(StreamOffer { mode, handler }),
122 realm_key: None,
123 }
124 }
125}
126
127#[derive(Clone)]
131pub struct StreamOffer {
132 pub mode: StreamMode,
133 pub handler: StreamHandler,
134}
135
136pub(super) type ServedEntry = Arc<ServedInner>;
138
139pub(super) struct ServedInner {
140 link: Weak<Inner>,
141 key: ([u8; 32], String),
142 pub(super) offer: Offer,
143 latest: Mutex<Record>,
144 err: Mutex<Option<LinkError>>,
145 done_tx: watch::Sender<bool>,
146}
147
148#[derive(Clone)]
152pub struct Served {
153 inner: Arc<ServedInner>,
154}
155
156impl Link {
157 pub async fn serve(&self, o: Offer) -> Result<Served, LinkError> {
168 let own = record::in_own_namespace(&o.procedure);
169 if o.handler.is_some() == o.stream.is_some() || (!own && o.realm_key.is_none()) {
170 return Err(LinkError::InvalidOffer);
171 }
172 if !matches!(record::procedure_org(&o.procedure), Ok(Some(_))) {
173 return Err(LinkError::NoOrg);
174 }
175 let (advertisement, wire) = self.advertisement(&o, MAX_ADVERTISEMENT_TTL).await?;
176 let key = (o.realm, o.procedure.clone());
177 let (done_tx, _) = watch::channel(false);
178 let served = Arc::new(ServedInner {
179 link: Arc::downgrade(&self.inner),
180 key: key.clone(),
181 offer: o,
182 latest: Mutex::new(advertisement),
183 err: Mutex::new(None),
184 done_tx,
185 });
186 {
187 let mut state = self.inner.lock();
188 if let Some(e) = &state.ended {
189 return Err(e.clone());
190 }
191 if state.served.contains_key(&key) {
192 return Err(LinkError::AlreadyServed);
193 }
194 state.served.insert(key, served.clone());
195 }
196 if let Err(e) = self.announce(&wire).await {
197 served.end(e.clone());
198 return Err(e);
199 }
200 tokio::spawn(renew(served.clone()));
201 Ok(Served { inner: served })
202 }
203
204 async fn advertisement(
208 &self,
209 o: &Offer,
210 max_ttl: Duration,
211 ) -> Result<(Record, Vec<u8>), LinkError> {
212 let inner = &self.inner;
213 let max_ttl_ms = max_ttl.as_millis() as u64;
214 let opts = if record::in_own_namespace(&o.procedure) {
217 ProcedureAdvertisementOptions {
218 authorization: Authorization::None,
219 kem_key: None,
220 ttl_ms: max_ttl_ms,
221 }
222 } else {
223 let org = record::procedure_org(&o.procedure)?.ok_or(LinkError::NoOrg)?;
224 let directory = self
225 .find_record(&record::org_directory_key(&o.realm, org))
226 .await?;
227 let named = record::read_org_directory(directory.record())?;
228 let delegation = self
229 .find_record(&record::procedure_delegation_key(
230 &named.org_key,
231 &inner.self_id,
232 ))
233 .await?;
234 let now = now_ms();
235 let ttl = (max_ttl_ms as i64)
236 .min(directory.record().expires_at as i64 - now)
237 .min(delegation.record().expires_at as i64 - now);
238 if ttl <= 0 {
239 return Err(RecordError::AuthorizationOutlived.into());
240 }
241 ProcedureAdvertisementOptions {
242 authorization: Authorization::Delegation {
243 org_directory: record::encode(directory.record())?,
244 procedure_delegation: record::encode(delegation.record())?,
245 },
246 kem_key: None,
247 ttl_ms: ttl as u64,
248 }
249 };
250 let unsigned = record::new_procedure_advertisement(
251 &inner.self_id,
252 &o.realm,
253 &o.procedure,
254 &inner.station.node_id,
255 &opts,
256 )?;
257 let signed = record::sign(&unsigned, &inner.key)?;
260 let wire = record::encode(&signed)?;
261 let now = now_ms();
262 let verified = record::verify(&wire, inner.profile, now)?;
263 record::verify_authorization(
264 &verified,
265 &Trust {
266 profile: inner.profile,
267 realm_key: o.realm_key.clone(),
268 },
269 now,
270 )?;
271 Ok((signed, wire))
272 }
273
274 async fn announce(&self, wire: &[u8]) -> Result<(), LinkError> {
278 self.inner
279 .send_control(&frame::advertise_frame(wire))
280 .await?;
281 self.put_record(wire).await
282 }
283}
284
285impl Served {
286 pub async fn stop(&self) -> Result<(), LinkError> {
291 if self.inner.is_done() {
292 return Ok(());
293 }
294 self.inner.end(LinkError::Stopped);
295 let Some(inner) = self.inner.link.upgrade() else {
296 return Ok(());
297 };
298 let latest = self
299 .inner
300 .latest
301 .lock()
302 .unwrap_or_else(|p| p.into_inner())
303 .clone();
304 let tombstone =
305 record::new_tombstone(&latest, Reason::Shutdown, &TombstoneOptions::default())?;
306 let wire = record::encode(&record::sign(&tombstone, &inner.key)?)?;
307 inner.send_control(&frame::unadvertise_frame(&wire)).await?;
308 Link { inner }.put_record(&wire).await
309 }
310
311 pub async fn done(&self) -> LinkError {
313 let mut done = self.inner.done_tx.subscribe();
314 let _ = done.wait_for(|ended| *ended).await;
315 self.error().unwrap_or(LinkError::Stopped)
316 }
317
318 pub fn error(&self) -> Option<LinkError> {
322 self.inner
323 .err
324 .lock()
325 .unwrap_or_else(|p| p.into_inner())
326 .clone()
327 }
328}
329
330impl ServedInner {
331 fn is_done(&self) -> bool {
332 *self.done_tx.borrow()
333 }
334
335 pub(super) fn end(self: &Arc<Self>, err: LinkError) {
338 {
339 let mut held = self.err.lock().unwrap_or_else(|p| p.into_inner());
340 if held.is_some() {
341 return;
342 }
343 *held = Some(err);
344 }
345 if let Some(inner) = self.link.upgrade() {
346 let mut state = inner.lock();
347 if state
348 .served
349 .get(&self.key)
350 .is_some_and(|s| Arc::ptr_eq(s, self))
351 {
352 state.served.remove(&self.key);
353 }
354 }
355 let _ = self.done_tx.send_replace(true);
356 }
357}
358
359async fn renew(served: Arc<ServedInner>) {
364 let mut current = served
365 .latest
366 .lock()
367 .unwrap_or_else(|p| p.into_inner())
368 .clone();
369 let mut wait = half_life(¤t);
370 let mut last_err: Option<LinkError> = None;
371 let mut stopped = served.done_tx.subscribe();
372 loop {
373 let Some(mut link_done) = served.link.upgrade().map(|l| l.done_rx.clone()) else {
374 return;
375 };
376 tokio::select! {
377 _ = stopped.wait_for(|ended| *ended) => return,
378 _ = link_done.wait_for(|ended| *ended) => {
379 let err = served.link.upgrade().and_then(|l| l.lock().ended.clone()).unwrap_or(LinkError::Closed);
380 served.end(err);
381 return;
382 }
383 _ = tokio::time::sleep(wait) => {}
384 }
385 if now_ms() >= current.expires_at as i64 {
386 served.end(last_err.unwrap_or(LinkError::Stopped));
387 return;
388 }
389 let Some(inner) = served.link.upgrade() else {
390 return;
391 };
392 let link = Link { inner };
393 let renewed = async {
394 let (advertisement, wire) = link
395 .advertisement(&served.offer, MAX_ADVERTISEMENT_TTL)
396 .await?;
397 link.announce(&wire).await?;
398 Ok::<_, LinkError>(advertisement)
399 }
400 .await;
401 match renewed {
402 Ok(advertisement) => {
403 *served.latest.lock().unwrap_or_else(|p| p.into_inner()) = advertisement.clone();
404 wait = half_life(&advertisement);
405 current = advertisement;
406 }
407 Err(e) => {
408 last_err = Some(e);
409 let left = (current.expires_at as i64 - now_ms()).max(0) as u64;
410 wait = REFRESH_RETRY.min(Duration::from_millis(left));
411 }
412 }
413 }
414}
415
416fn half_life(r: &Record) -> Duration {
417 Duration::from_millis(r.expires_at.saturating_sub(r.created_at) / 2)
418}
419
420pub(super) fn called(inner: &Arc<Inner>, v: &Value) {
425 let Ok(request) = frame::verify_request(v, inner.profile) else {
426 inner.count("unverified_call");
427 return;
428 };
429 if request.target != inner.self_id {
430 inner.count("call_for_another_node");
431 return;
432 }
433 let inner = inner.clone();
434 match inner.admission.admit(&request, &inner.share, now_ms()) {
435 Verdict::Refused(code) => {
436 let reply = provider_error(&inner, &request, code, None);
437 tokio::spawn(async move { send_reply(&inner, reply).await });
438 }
439 Verdict::Copy(None) => {
440 let reply = provider_error(&inner, &request, CODE_REQUEST_COPY, None);
441 tokio::spawn(async move { send_reply(&inner, reply).await });
442 }
443 Verdict::Copy(Some(stored)) => {
444 tokio::spawn(async move {
445 let _ = inner.control.write(&stored, MAX_FRAME_BYTES).await;
446 });
447 }
448 Verdict::New => {
449 tokio::spawn(answer(inner, request));
450 }
451 }
452}
453
454async fn answer(inner: Arc<Inner>, request: VerifiedRequest) {
457 let handler = inner
458 .lock()
459 .served
460 .get(&(request.realm, request.procedure.clone()))
461 .and_then(|s| s.offer.handler.clone());
462 let reply = match handler {
463 None => provider_error(&inner, &request, CODE_UNKNOWN_PROCEDURE, None),
464 Some(handler) => handled(&inner, handler, &request).await,
465 };
466 let Ok(encoded) = cbor::encode(&reply) else {
467 inner.count("unencodable_reply");
468 return;
469 };
470 inner.admission.store(&request, encoded.clone());
471 let _ = inner.control.write(&encoded, MAX_FRAME_BYTES).await;
472}
473
474async fn handled(inner: &Inner, handler: Handler, request: &VerifiedRequest) -> Value {
478 let running = tokio::spawn(handler(Request {
479 caller: request.caller,
480 realm: request.realm,
481 procedure: request.procedure.clone(),
482 payload: request.payload.clone(),
483 token: request.token.clone(),
484 proofs: request.proofs.clone(),
485 deadline_ms: request.deadline,
486 }));
487 let abort = running.abort_handle();
488 let left = (request.deadline as i64 - now_ms()).max(0) as u64;
489 let outcome = tokio::time::timeout(Duration::from_millis(left), running).await;
490 match outcome {
491 Err(_) => {
492 abort.abort();
493 provider_error(
494 inner,
495 request,
496 CODE_HANDLER_ERROR,
497 Some("the request's deadline passed"),
498 )
499 }
500 Ok(Err(_panicked)) => provider_error(inner, request, CODE_HANDLER_CRASHED, None),
501 Ok(Ok(Err(refusal))) => provider_error(
502 inner,
503 request,
504 CODE_HANDLER_ERROR,
505 Some(bounded_detail(&refusal)),
506 ),
507 Ok(Ok(Ok(payload))) => match frame::sign_result(request, &payload, None, &inner.key) {
508 Ok(signed) => signed,
509 Err(_) if cbor::encode(&payload).is_ok_and(|e| e.len() > frame::MAX_FRAME_BYTES) => {
510 provider_error(inner, request, CODE_PAYLOAD_TOO_LARGE, None)
511 }
512 Err(_) => provider_error(inner, request, CODE_UNSENDABLE, None),
513 },
514 }
515}
516
517fn provider_error(
521 inner: &Inner,
522 request: &VerifiedRequest,
523 code: &str,
524 detail: Option<&str>,
525) -> Value {
526 frame::sign_provider_error(request, code, detail, None, &inner.key)
527 .unwrap_or_else(|e| panic!("station_link: a provider error that does not sign: {e}"))
528}
529
530async fn send_reply(inner: &Inner, reply: Value) {
531 let _ = inner.write_control(&reply).await;
532}
533
534pub(super) fn bounded_detail(text: &str) -> &str {
536 if text.len() <= MAX_DETAIL_BYTES {
537 return text;
538 }
539 let mut cut = MAX_DETAIL_BYTES;
540 while !text.is_char_boundary(cut) {
541 cut -= 1;
542 }
543 &text[..cut]
544}