1use super::*;
2use buntrail_protocol::{
3 frame::{Decoder, Frame},
4 types::{Context, Header, Message},
5};
6use std::{
7 collections::{HashMap, VecDeque},
8 io::{Read, Write},
9 os::unix::net::UnixStream,
10 sync::{
11 Condvar, Mutex, Weak,
12 mpsc::{self, SyncSender},
13 },
14 task::{Poll, Waker},
15 time::Duration,
16};
17struct Inbox {
18 frames: VecDeque<Frame>,
19 bytes: usize,
20 error: Option<CoreError>,
21 waker: Option<Waker>,
22}
23struct Mailbox {
24 cancelled: AtomicBool,
25 finished: AtomicBool,
26 unsent_refusal: AtomicBool,
27 state: Mutex<Inbox>,
28 ready: Condvar,
29}
30impl Mailbox {
31 fn new() -> Arc<Self> {
32 Arc::new(Self {
33 cancelled: AtomicBool::new(false),
34 finished: AtomicBool::new(false),
35 unsent_refusal: AtomicBool::new(false),
36 state: Mutex::new(Inbox {
37 frames: VecDeque::new(),
38 bytes: 0,
39 error: None,
40 waker: None,
41 }),
42 ready: Condvar::new(),
43 })
44 }
45 fn fail(&self, e: CoreError) {
46 let mut s = self.state.lock().unwrap();
47 s.error = Some(e);
48 if let Some(w) = s.waker.take() {
49 w.wake()
50 }
51 self.ready.notify_all();
52 }
53 fn refuse_unsent(&self, error: CoreError) {
54 self.unsent_refusal.store(true, Ordering::SeqCst);
57 self.fail(error);
58 }
59 fn push(&self, f: Frame) -> Result<(), CoreError> {
60 let mut s = self.state.lock().unwrap();
61 if s.bytes + f.payload.len() > 1024 * 1024 || s.frames.len() >= 10000 {
62 return Err(CoreError::new(
63 ErrorCode::Busy,
64 "Remote receive window exceeded",
65 ));
66 }
67 s.bytes += f.payload.len();
68 s.frames.push_back(f);
69 if let Some(w) = s.waker.take() {
70 w.wake()
71 }
72 self.ready.notify_all();
73 Ok(())
74 }
75 fn take(s: &mut Inbox) -> Option<Result<Frame, CoreError>> {
76 if let Some(f) = s.frames.pop_front() {
77 s.bytes -= f.payload.len();
78 Some(Ok(f))
79 } else {
80 s.error.clone().map(Err)
81 }
82 }
83}
84type Credits = Arc<Mutex<HashMap<u64, (Header, usize, Arc<Scope>)>>>;
85struct SendFrame {
86 frame: Frame,
87 mailbox: Option<Arc<Mailbox>>,
88 scope: Arc<Scope>,
89}
90#[derive(Clone)]
91pub(super) struct Transport {
92 sender: SyncSender<SendFrame>,
93 credits: Credits,
94 control: SyncSender<SendFrame>,
95 pub context: Context,
96 scope: Arc<Scope>,
97 scopes: Arc<Mutex<Vec<Weak<Scope>>>>,
98 registrations: Arc<Mutex<()>>,
99 pub capabilities: Arc<Vec<String>>,
100 retired: Arc<Retirement>,
101 pub runtime: RuntimeGeneration,
102 pub connected: Arc<AtomicBool>,
103 ids: Arc<std::sync::atomic::AtomicU64>,
104 pub(super) authentication_ready: Arc<AtomicBool>,
105 pub(super) physical: Option<Arc<crate::environment::PhysicalOwner>>,
106 pub(super) connection_permit: Option<Arc<crate::environment::ConnectionPermit>>,
107 requests: Arc<std::sync::atomic::AtomicUsize>,
108}
109struct Scope {
110 context: Context,
111 lease: WorkspaceLease,
112 active: AtomicBool,
113 release_confirmed: Mutex<Option<buntrail_protocol::types::ReleaseIntent>>,
114}
115#[derive(Default)]
116struct Retirement {
117 done: AtomicBool,
118 wake: Mutex<Option<Waker>>,
119}
120struct Retire(Arc<Retirement>, Arc<AtomicBool>);
121impl Drop for Retire {
122 fn drop(&mut self) {
123 self.1.store(false, Ordering::SeqCst);
124 self.0.done.store(true, Ordering::SeqCst);
125 if let Some(wake) = self.0.wake.lock().unwrap().take() {
126 wake.wake();
127 }
128 }
129}
130pub struct Shutdown(Arc<Retirement>);
131impl std::future::Future for Shutdown {
132 type Output = ();
133 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<()> {
134 let mut wake = self.0.wake.lock().unwrap();
135 if self.0.done.load(Ordering::SeqCst) {
136 Poll::Ready(())
137 } else {
138 *wake = Some(cx.waker().clone());
139 Poll::Pending
140 }
141 }
142}
143pub(super) struct Request {
144 pub header: Header,
145 mailbox: Arc<Mailbox>,
146 transport: Transport,
147 finished: bool,
148}
149impl Drop for Request {
150 fn drop(&mut self) {
151 self.transport.requests.fetch_sub(1, Ordering::SeqCst);
152 if !self.finished {
153 self.mailbox.cancelled.store(true, Ordering::SeqCst);
154 }
155 }
156}
157impl Request {
158 pub fn send_control(&self, message: Message, payload: Vec<u8>) -> Result<(), CoreError> {
159 let mut header = self.header.clone();
160 header.message = message;
161 self.transport
162 .control
163 .try_send(SendFrame {
164 frame: Frame { header, payload },
165 mailbox: None,
166 scope: self.transport.scope.clone(),
167 })
168 .map_err(|_| {
169 CoreError::new(
170 ErrorCode::Unknown,
171 "original transfer control queue unavailable; inspect without replay",
172 )
173 })
174 }
175
176 pub fn send(&self, message: Message, payload: Vec<u8>) -> Result<(), CoreError> {
177 let mut header = self.header.clone();
178 header.message = message;
179 self.transport
180 .sender
181 .send(SendFrame {
182 frame: Frame { header, payload },
183 mailbox: None,
184 scope: self.transport.scope.clone(),
185 })
186 .map_err(|_| closed())
187 }
188 pub fn credit(&self, stream: &str, bytes: usize) -> Result<(), CoreError> {
189 let mut credits = self.transport.credits.lock().unwrap();
190 if credits.len() >= 64 && !credits.contains_key(&self.header.request_id) {
191 return Err(CoreError::new(
192 ErrorCode::Busy,
193 "Remote credit owner capacity reached",
194 ));
195 }
196 let mut header = self.header.clone();
197 header.stream_id = Some(stream.into());
198 let entry =
199 credits
200 .entry(header.request_id)
201 .or_insert((header, 0, self.transport.scope.clone()));
202 if entry.0.stream_id.as_deref() != Some(stream) || entry.1 + bytes > 524288 {
203 return Err(w::invalid());
204 }
205 entry.1 += bytes;
206 Ok(())
207 }
208 pub fn next(&mut self) -> Result<Frame, CoreError> {
209 self.next_with_timeout(Duration::from_secs(120))
210 }
211 fn next_with_timeout(&mut self, timeout: Duration) -> Result<Frame, CoreError> {
212 let mut s = self.mailbox.state.lock().unwrap();
213 loop {
214 if let Some(f) = Mailbox::take(&mut s) {
215 return f;
216 }
217 let (state, timeout) = self.mailbox.ready.wait_timeout(s, timeout).unwrap();
218 s = state;
219 if timeout.timed_out() {
220 return Err(CoreError::new(
221 ErrorCode::Unknown,
222 "Remote request deadline exceeded; inspect original operation without replay",
223 ));
224 }
225 }
226 }
227 pub fn poll(&mut self, cx: &mut std::task::Context<'_>) -> Poll<Result<Frame, CoreError>> {
228 let mut s = self.mailbox.state.lock().unwrap();
229 if let Some(f) = Mailbox::take(&mut s) {
230 Poll::Ready(f)
231 } else {
232 s.waker = Some(cx.waker().clone());
233 Poll::Pending
234 }
235 }
236 pub fn finish(&mut self) {
237 self.finished = true;
238 self.mailbox.finished.store(true, Ordering::SeqCst);
239 }
240 pub fn connection_lost(&self) -> bool {
241 !self.transport.connected.load(Ordering::SeqCst)
242 }
243 pub fn refused_before_send(&self) -> bool {
244 self.mailbox.unsent_refusal.load(Ordering::SeqCst)
245 }
246}
247fn closed() -> CoreError {
248 CoreError::new(
249 ErrorCode::Closed,
250 "Remote connection lost; inspect accepted operations, never replay",
251 )
252}
253impl Transport {
254 pub fn connect(
255 mut socket: UnixStream,
256 target: &TargetIdentity,
257 lease: &WorkspaceLease,
258 ) -> Result<Self, CoreError> {
259 socket
260 .set_read_timeout(Some(Duration::from_secs(5)))
261 .map_err(io_error)?;
262 socket
263 .set_write_timeout(Some(Duration::from_secs(5)))
264 .map_err(io_error)?;
265 let mut h = Header {
266 major: 1,
267 minor: 0,
268 request_id: 1,
269 context: None,
270 operation_id: None,
271 stream_id: None,
272 sequence: None,
273 message: Message::Hello {
274 capabilities: buntrail_protocol::negotiation::CAPABILITIES
275 .iter()
276 .map(|s| s.to_string())
277 .collect(),
278 max_header: 65536,
279 max_payload: 1048576,
280 },
281 };
282 write_frame(
283 &mut socket,
284 &Frame {
285 header: h.clone(),
286 payload: vec![],
287 },
288 )?;
289 let welcome = read_frame(&mut socket, None)?;
290 if welcome.header.request_id != 1 {
291 return Err(w::invalid());
292 }
293 let Message::Welcome {
294 target_binding,
295 runtime_generation,
296 container_start_base64,
297 capabilities,
298 ..
299 } = welcome.header.message
300 else {
301 return Err(w::invalid());
302 };
303 if !capabilities.iter().any(|s| s == "services") {
304 return Err(CoreError::new(
305 ErrorCode::Unavailable,
306 "Remote service capability unavailable",
307 ));
308 }
309 let expected = w::hex(&sha2::Sha256::digest(
310 serde_json::to_vec(target).map_err(io_error)?,
311 ));
312 if target_binding != expected {
313 return Err(CoreError::new(
314 ErrorCode::TargetChanged,
315 "Helper target binding changed",
316 ));
317 }
318 let runtime = RuntimeGeneration {
319 supervisor_boot: w::id(&runtime_generation)?,
320 container_start: buntrail_protocol::types::decode_container_start(
321 container_start_base64.as_deref(),
322 )
323 .map_err(|_| w::invalid())?,
324 };
325 if target.container.is_some() && runtime.container_start.is_none() {
326 return Err(CoreError::new(
327 ErrorCode::TargetChanged,
328 "Container helper Welcome is missing verified start evidence; no owner was registered",
329 ));
330 }
331 if runtime.container_start != lease.runtime.container_start {
332 return Err(CoreError::new(
333 ErrorCode::TargetChanged,
334 "Helper container start generation changed; preserve the original runtime and reconcile explicitly",
335 ));
336 }
337 if lease.runtime.supervisor_boot != [0; 16] && lease.runtime != runtime {
338 return Err(CoreError::new(
339 ErrorCode::TargetChanged,
340 "Helper runtime changed",
341 ));
342 }
343 let context = Context {
344 workspace_id: w::uuid(lease.workspace.0),
345 client_id: w::uuid(lease.client.0),
346 target_binding,
347 runtime_generation,
348 };
349 h.request_id = 2;
350 h.message = Message::Register {
351 lease_id: w::uuid(lease.lease.0),
352 client_id: context.client_id.clone(),
353 workspace_id: context.workspace_id.clone(),
354 target_binding: context.target_binding.clone(),
355 runtime_generation: context.runtime_generation.clone(),
356 };
357 write_frame(
358 &mut socket,
359 &Frame {
360 header: h,
361 payload: vec![],
362 },
363 )?;
364 let ack = read_frame(&mut socket, Some(&context))?;
365 if ack.header.request_id != 2 || !matches!(ack.header.message, Message::Ack {}) {
366 return Err(w::invalid());
367 }
368 socket.set_nonblocking(true).map_err(io_error)?;
369 let (tx, rx) = mpsc::sync_channel::<SendFrame>(32);
370 let (control, controls) = mpsc::sync_channel::<SendFrame>(32);
371 let connected = Arc::new(AtomicBool::new(true));
372 let live = connected.clone();
373 let scope = Arc::new(Scope {
374 context: context.clone(),
375 lease: WorkspaceLease {
376 runtime: runtime.clone(),
377 ..lease.clone()
378 },
379 active: AtomicBool::new(true),
380 release_confirmed: Mutex::new(None),
381 });
382 let scopes = Arc::new(Mutex::new(vec![Arc::downgrade(&scope)]));
383 let retired = Arc::new(Retirement::default());
384 let completion = Retire(retired.clone(), connected.clone());
385 let credits: Credits = Arc::new(Mutex::new(HashMap::new()));
386 let pending_credit = credits.clone();
387 let ids = Arc::new(std::sync::atomic::AtomicU64::new(3));
388 let worker_ids = ids.clone();
389 std::thread::Builder::new()
390 .name("buntrail-remote-wire".into())
391 .spawn(move || {
392 let _completion = completion;
393 worker(socket, rx, controls, pending_credit, worker_ids, live)
394 })
395 .map_err(io_error)?;
396 Ok(Self {
397 sender: tx,
398 credits,
399 control,
400 context,
401 scope,
402 scopes,
403 registrations: Arc::new(Mutex::new(())),
404 capabilities: Arc::new(capabilities),
405 retired,
406 runtime,
407 connected,
408 ids,
409 authentication_ready: Arc::new(AtomicBool::new(false)),
410 physical: None,
411 connection_permit: None,
412 requests: Arc::new(std::sync::atomic::AtomicUsize::new(0)),
413 })
414 }
415 pub fn register(&self, lease: WorkspaceLease) -> Result<Self, CoreError> {
416 let _registration = self.registrations.lock().map_err(io_error)?;
417 if !self.connected.load(Ordering::SeqCst) {
418 return Err(closed());
419 }
420 if lease.client != self.scope.lease.client || lease.runtime != self.runtime {
421 return Err(CoreError::new(
422 ErrorCode::TargetChanged,
423 "workspace client or runtime changed",
424 ));
425 }
426 let mut context = self.context.clone();
427 context.workspace_id = w::uuid(lease.workspace.0);
428 let scope = {
429 let mut scopes = self.scopes.lock().map_err(io_error)?;
430 scopes.retain(|s| s.strong_count() > 0);
431 if scopes
432 .iter()
433 .filter_map(Weak::upgrade)
434 .any(|s| s.lease.lease == lease.lease && s.lease != lease)
435 {
436 return Err(CoreError::new(
437 ErrorCode::InvalidArgument,
438 "lease already belongs to another workspace",
439 ));
440 }
441 if let Some(existing) = scopes
442 .iter()
443 .filter_map(Weak::upgrade)
444 .find(|s| s.lease == lease && s.active.load(Ordering::SeqCst))
445 {
446 existing
447 } else {
448 if scopes.len() >= 256 {
449 return Err(CoreError::new(
450 ErrorCode::Busy,
451 "workspace view capacity reached; retire prior views",
452 ));
453 }
454 let scope = Arc::new(Scope {
455 context: context.clone(),
456 lease: lease.clone(),
457 active: AtomicBool::new(false),
458 release_confirmed: Mutex::new(None),
459 });
460 scopes.push(Arc::downgrade(&scope));
461 scope
462 }
463 };
464 let view = Self {
465 context,
466 scope,
467 ..self.clone()
468 };
469 if view.scope.active.load(Ordering::SeqCst) {
470 return Ok(view);
471 }
472 let c = &view.context;
473 let mut request = view.start(
474 Message::Register {
475 lease_id: w::uuid(lease.lease.0),
476 client_id: c.client_id.clone(),
477 workspace_id: c.workspace_id.clone(),
478 target_binding: c.target_binding.clone(),
479 runtime_generation: c.runtime_generation.clone(),
480 },
481 None,
482 None,
483 )?;
484 if !matches!(request.next()?.header.message, Message::Ack {}) {
485 return Err(w::invalid());
486 }
487 request.finish();
488 view.scope.active.store(true, Ordering::SeqCst);
489 Ok(view)
490 }
491 pub fn release(
492 &self,
493 intent: buntrail_protocol::types::ReleaseIntent,
494 ) -> Result<(), CoreError> {
495 self.release_wait(intent, Duration::from_secs(120), false)
496 }
497 pub(super) fn detach(&self) -> Result<(), CoreError> {
498 self.release_wait(
499 buntrail_protocol::types::ReleaseIntent::ViewDetachment,
500 Duration::from_millis(500),
501 true,
502 )
503 }
504 fn release_wait(
505 &self,
506 intent: buntrail_protocol::types::ReleaseIntent,
507 timeout: Duration,
508 nonblocking: bool,
509 ) -> Result<(), CoreError> {
510 let _registration = if nonblocking {
511 self.registrations.try_lock().map_err(|_| {
512 self.scope.active.store(false, Ordering::SeqCst);
513 CoreError::new(
514 ErrorCode::Unknown,
515 "Local view detached; remote lease acknowledgement is unconfirmed",
516 )
517 })?
518 } else {
519 self.registrations.lock().map_err(io_error)?
520 };
521 if !self.scope.active.swap(false, Ordering::SeqCst) {
522 return if self
523 .scope
524 .release_confirmed
525 .lock()
526 .map_err(io_error)?
527 .as_ref()
528 == Some(&intent)
529 {
530 Ok(())
531 } else {
532 Err(CoreError::new(
533 ErrorCode::Closed,
534 "workspace release is unconfirmed; automatic replay is disabled",
535 ))
536 };
537 }
538 let mut request = self.start(
539 Message::Release {
540 lease_id: w::uuid(self.scope.lease.lease.0),
541 intent: intent.clone(),
542 },
543 None,
544 None,
545 )?;
546 if !matches!(
547 request.next_with_timeout(timeout)?.header.message,
548 Message::Ack {}
549 ) {
550 return Err(w::invalid());
551 }
552 request.finish();
553 *self.scope.release_confirmed.lock().map_err(io_error)? = Some(intent);
554 Ok(())
555 }
556 pub(super) fn owns_registration(&self, lease: &WorkspaceLease) -> bool {
557 self.registered() && &self.scope.lease == lease && lease.runtime == self.runtime
558 }
559 pub(super) fn registered(&self) -> bool {
560 self.scope.active.load(Ordering::SeqCst) && self.connected.load(Ordering::SeqCst)
561 }
562 pub(super) fn lifetime(&self) -> Lifetime {
563 Lifetime {
564 registrations: self.registrations.clone(),
565 connected: self.connected.clone(),
566 retired: self.retired.clone(),
567 scopes: self.scopes.clone(),
568 requests: self.requests.clone(),
569 }
570 }
571 pub fn close(&self) -> Shutdown {
572 self.connected.store(false, Ordering::SeqCst);
573 Shutdown(self.retired.clone())
574 }
575 fn frame(
576 &self,
577 message: Message,
578 operation: Option<OperationID>,
579 stream: Option<String>,
580 ) -> Frame {
581 Frame {
582 header: Header {
583 major: 1,
584 minor: 0,
585 request_id: self.ids.fetch_add(1, Ordering::SeqCst),
586 context: if matches!(message, Message::Register { .. }) {
587 None
588 } else {
589 Some(self.context.clone())
590 },
591 operation_id: operation.map(|o| w::uuid(o.0)),
592 stream_id: stream,
593 sequence: None,
594 message,
595 },
596 payload: vec![],
597 }
598 }
599 pub fn start(
600 &self,
601 message: Message,
602 operation: Option<OperationID>,
603 stream: Option<String>,
604 ) -> Result<Request, CoreError> {
605 self.start_payload(message, operation, stream, Vec::new())
606 }
607 pub fn start_payload(
608 &self,
609 message: Message,
610 operation: Option<OperationID>,
611 stream: Option<String>,
612 payload: Vec<u8>,
613 ) -> Result<Request, CoreError> {
614 if !self.connected.load(Ordering::SeqCst)
615 || (!self.scope.active.load(Ordering::SeqCst)
616 && !matches!(message, Message::Register { .. } | Message::Release { .. }))
617 {
618 return Err(closed());
619 }
620 let mut frame = self.frame(message, operation, stream);
621 frame.payload = payload;
622 if matches!(
624 &frame.header.message,
625 Message::ServiceCall {
626 call: w::Call::GitExisting { .. }
627 }
628 ) {
629 struct HeaderBound(usize);
630 impl std::io::Write for HeaderBound {
631 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
632 if bytes.len() > 65536 - self.0 {
633 return Err(std::io::Error::other(
634 "legacy original exceeds header limit",
635 ));
636 }
637 self.0 += bytes.len();
638 Ok(bytes.len())
639 }
640 fn flush(&mut self) -> std::io::Result<()> {
641 Ok(())
642 }
643 }
644 serde_json::to_writer(HeaderBound(0), &frame.header).map_err(|_| CoreError::new(ErrorCode::Unavailable, "Active helper cannot stream this complete original request; preserve its operation and supervisor"))?;
645 }
646 let header = frame.header.clone();
647 let mailbox = Mailbox::new();
648 let sender = if matches!(&frame.header.message, Message::ServiceCall { call } if call.unprepared_worktree_mutation())
649 || matches!(
650 frame.header.message,
651 Message::ProofLookupBegin { .. }
652 | Message::ProofCancel { .. }
653 | Message::ProofRelease { .. }
654 | Message::ServiceCall {
655 call: w::Call::Inspect
656 | w::Call::GitExisting { .. }
657 | w::Call::LeaseObserve { .. }
658 | w::Call::TerminalEnd { .. }
659 | w::Call::TerminalInput { .. }
660 | w::Call::TerminalResize { .. }
661 }
662 ) {
663 &self.control
664 } else {
665 &self.sender
666 };
667 sender
668 .try_send(SendFrame {
669 frame,
670 mailbox: Some(mailbox.clone()),
671 scope: self.scope.clone(),
672 })
673 .map_err(|_| CoreError::new(ErrorCode::Busy, "Remote request queue full"))?;
674 self.requests.fetch_add(1, Ordering::SeqCst);
675 Ok(Request {
676 header,
677 mailbox,
678 transport: self.clone(),
679 finished: false,
680 })
681 }
682}
683fn write_frame(s: &mut UnixStream, f: &Frame) -> Result<(), CoreError> {
684 s.write_all(&f.encode().map_err(|_| w::invalid())?)
685 .map_err(io_error)
686}
687fn read_frame(s: &mut UnixStream, c: Option<&Context>) -> Result<Frame, CoreError> {
688 let mut prefix = [0; 8];
689 s.read_exact(&mut prefix).map_err(io_error)?;
690 let h = u32::from_be_bytes(prefix[..4].try_into().unwrap()) as usize;
691 let p = u32::from_be_bytes(prefix[4..].try_into().unwrap()) as usize;
692 if h > 65536 || p > 1048576 {
693 return Err(w::invalid());
694 }
695 let mut bytes = vec![0; h + p + 8];
696 bytes[..8].copy_from_slice(&prefix);
697 s.read_exact(&mut bytes[8..]).map_err(io_error)?;
698 Decoder::default()
699 .push(&bytes, c)
700 .map_err(|_| w::invalid())?
701 .pop()
702 .ok_or_else(w::invalid)
703}
704
705fn worker(
706 mut socket: UnixStream,
707 rx: mpsc::Receiver<SendFrame>,
708 controls: mpsc::Receiver<SendFrame>,
709 pending_credit: Credits,
710 worker_ids: Arc<std::sync::atomic::AtomicU64>,
711 live: Arc<AtomicBool>,
712) {
713 let mut routes: HashMap<u64, (Arc<Mailbox>, Header, Arc<Scope>)> = HashMap::new();
714 let mut decoder = Decoder::default();
715 let mut output = Vec::new();
716 let mut written = 0;
717 let mut _writing_scope: Option<Arc<Scope>> = None;
718 let mut input = [0; 65536];
719 let outcome = (|| -> Result<(), CoreError> {
720 loop {
721 if !live.load(Ordering::SeqCst) {
722 return Ok(());
723 }
724 if output.is_empty() {
725 {
728 let credits = pending_credit.lock().unwrap();
729 routes.retain(|id, (mailbox, _, _)| {
730 !mailbox.finished.load(Ordering::SeqCst) || credits.contains_key(id)
731 });
732 }
733 let cancelled = routes
734 .iter()
735 .find(|(_, (m, _, _))| m.cancelled.load(Ordering::SeqCst))
736 .map(|(id, _)| *id);
737
738 let next = if let Some(id) = cancelled {
739 let (_, mut header, scope) = routes.remove(&id).unwrap();
740
741 pending_credit.lock().unwrap().remove(&id);
742
743 header.request_id = worker_ids.fetch_add(1, Ordering::SeqCst);
744
745 header.message = Message::Cancel { request_id: id };
746
747 header.operation_id = None;
748 header.stream_id = None;
749 header.sequence = None;
750
751 Some(SendFrame {
752 frame: Frame {
753 header,
754 payload: vec![],
755 },
756 mailbox: None,
757 scope,
758 })
759 } else if let Ok(control) = controls.try_recv() {
760 Some(control)
761 } else {
762 let credit = {
763 let mut credits = pending_credit.lock().unwrap();
764
765 let id = credits.keys().next().copied();
766
767 id.and_then(|id| credits.remove(&id))
768 };
769
770 if let Some((mut header, bytes, scope)) = credit {
771 header.message = Message::Credit {
772 bytes: bytes as u32,
773 };
774
775 Some(SendFrame {
776 frame: Frame {
777 header,
778 payload: vec![],
779 },
780 mailbox: None,
781 scope,
782 })
783 } else {
784 match rx.try_recv() {
785 Ok(v) => Some(v),
786 Err(mpsc::TryRecvError::Empty) => None,
787 Err(mpsc::TryRecvError::Disconnected) => return Ok(()),
788 }
789 }
790 };
791
792 if let Some(v) = next {
793 if let Some(mailbox) = v.mailbox {
794 if mailbox.cancelled.load(Ordering::SeqCst) {
795 continue;
796 }
797 if routes.len() >= 32 {
798 mailbox.refuse_unsent(CoreError::new(
799 ErrorCode::Busy,
800 "Remote request capacity reached",
801 ));
802
803 continue;
804 }
805 routes.insert(
806 v.frame.header.request_id,
807 (mailbox, v.frame.header.clone(), v.scope.clone()),
808 );
809 } else if !matches!(
810 v.frame.header.message,
811 Message::Credit { .. } | Message::Cancel { .. }
812 ) && !routes.contains_key(&v.frame.header.request_id)
813 {
814 continue;
815 }
816 output = v.frame.encode().map_err(|_| w::invalid())?;
817 _writing_scope = Some(v.scope);
818 written = 0;
819 }
820 }
821 if !output.is_empty() {
822 match socket.write(&output[written..]) {
823 Ok(0) => return Err(closed()),
824 Ok(n) => {
825 written += n;
826 if written == output.len() {
827 output.clear();
828 _writing_scope = None;
829 }
830 }
831 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
832 Err(e) => return Err(io_error(e)),
833 }
834 }
835 match socket.read(&mut input) {
836 Ok(0) => return Err(closed()),
837 Ok(n) => {
838 for f in decoder
839 .push_replies(&input[..n], |id| {
840 routes.get(&id).map(|(_, header, scope)| {
841 (scope.context.clone(), header.operation_id.clone())
842 })
843 })
844 .map_err(|_| w::invalid())?
845 {
846 let id = f.header.request_id;
847
848 if let Some((route, request_header, expected)) = routes.get(&id) {
849 if f.header.operation_id != request_header.operation_id
850 || f.header.context.as_ref() != Some(&expected.context)
851 {
852 return Err(w::invalid());
853 }
854 route.push(f)?;
855 }
856 }
857 }
858 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
859 Err(e) => return Err(io_error(e)),
860 }
861 std::thread::sleep(Duration::from_millis(1));
862 }
863 })();
864
865 live.store(false, Ordering::SeqCst);
866
867 let error = outcome.err().unwrap_or_else(closed);
868
869 for (route, _, _) in routes.values() {
870 route.fail(error.clone());
871 }
872 for receiver in [rx, controls] {
873 while let Ok(v) = receiver.try_recv() {
874 if let Some(m) = v.mailbox {
875 m.refuse_unsent(error.clone());
876 }
877 }
878 }
879}
880
881#[cfg(test)]
882#[path = "transport_tests.rs"]
883mod tests;
884
885pub(crate) struct Lifetime {
886 registrations: Arc<Mutex<()>>,
887 pub connected: Arc<AtomicBool>,
888 retired: Arc<Retirement>,
889 scopes: Arc<Mutex<Vec<Weak<Scope>>>>,
890 requests: Arc<std::sync::atomic::AtomicUsize>,
891}
892impl Lifetime {
893 pub fn has_active_views(&self) -> bool {
894 self.scopes
895 .lock()
896 .unwrap()
897 .iter()
898 .filter_map(Weak::upgrade)
899 .any(|s| s.active.load(Ordering::SeqCst))
900 }
901 pub fn idle(&self) -> bool {
902 let Ok(_transition) = self.registrations.try_lock() else {
904 return false;
905 };
906 self.requests.load(Ordering::SeqCst) == 0 && !self.has_active_views()
907 }
908 pub fn shutdown(&self) -> Shutdown {
909 self.connected.store(false, Ordering::SeqCst);
910 Shutdown(self.retired.clone())
911 }
912}