1use crate::deadline::instant_from_unix_ms;
2use crate::error::{ErrorKind, RpcError};
3use crate::frame::{AsyncFrameReader, write_frame};
4use crate::jsonrpc::{Envelope, Notification, Request, RequestId, Response};
5use crate::protocol::{CancelParams, InitializeParams, InitializeResult, Limits, rpc};
6use crate::{ABSOLUTE_MAX_FRAME_BYTES, Error, Result};
7use serde::Serialize;
8use serde::de::DeserializeOwned;
9use serde_json::{Value, json};
10use std::collections::{HashMap, HashSet};
11use std::sync::atomic::{AtomicU8, AtomicU64, AtomicUsize, Ordering};
12use std::sync::{Arc, Mutex as StdMutex, Weak};
13use std::time::Duration;
14use tokio::io::{AsyncRead, AsyncWrite};
15use tokio::sync::{Mutex, Notify, RwLock, Semaphore, mpsc, oneshot};
16use tokio::task::JoinHandle;
17use tokio::time::Instant;
18use tokio_util::sync::CancellationToken;
19use zeroize::{Zeroize, Zeroizing};
20
21const INITIALIZING: u8 = 0;
22const READY: u8 = 1;
23const CLOSING: u8 = 2;
24const CLOSED: u8 = 3;
25const MAX_ABANDONED_REQUESTS: usize = crate::MAX_IN_FLIGHT * 4;
26
27enum WriterCommand {
28 Frame {
29 payload: Zeroizing<Vec<u8>>,
30 limit: usize,
31 },
32 Close,
33}
34
35#[async_trait::async_trait]
43pub trait CallbackHandler: Send + Sync + 'static {
44 async fn call(&self, method: &str, params: Value) -> std::result::Result<Value, RpcError>;
45}
46
47struct Inner {
48 writer: mpsc::Sender<WriterCommand>,
49 callbacks: Option<Arc<dyn CallbackHandler>>,
50 advertised: HashSet<String>,
53 inbound: StdMutex<InboundCallbacks>,
54 pending: StdMutex<HashMap<RequestId, PendingRequest>>,
55 abandoned: StdMutex<HashSet<RequestId>>,
56 request_order: Mutex<()>,
58 next_id: AtomicU64,
59 max_frame_bytes: AtomicUsize,
60 max_in_flight: AtomicUsize,
61 limits: RwLock<Limits>,
62 semaphore: RwLock<Arc<Semaphore>>,
63 capabilities: RwLock<HashSet<String>>,
64 state: AtomicU8,
65 closed: Notify,
66 reader_task: Mutex<Option<JoinHandle<()>>>,
67 writer_task: Mutex<Option<JoinHandle<()>>>,
68}
69
70impl Drop for Inner {
71 fn drop(&mut self) {
72 if let Some(task) = self.reader_task.get_mut().take() {
76 task.abort();
77 }
78 if let Some(task) = self.writer_task.get_mut().take() {
79 task.abort();
80 }
81 }
82}
83
84struct PendingRequest {
85 sender: oneshot::Sender<Response>,
86 deadline_unix_ms: u64,
87 cancellation: CancellationToken,
88}
89
90#[derive(Default)]
91struct InboundCallbacks {
92 active: usize,
93 last_seen_id: Option<RequestId>,
94}
95
96#[derive(Clone)]
98pub struct Client {
99 inner: Arc<Inner>,
100}
101
102impl Client {
103 pub async fn connect<R, W, A, B>(
105 reader: R,
106 writer: W,
107 initialize: InitializeParams<A>,
108 startup_deadline_unix_ms: u64,
109 ) -> Result<(Self, InitializeResult<B>)>
110 where
111 R: AsyncRead + Unpin + Send + 'static,
112 W: AsyncWrite + Unpin + Send + 'static,
113 A: Serialize,
114 B: DeserializeOwned,
115 {
116 Self::connect_with_callbacks(reader, writer, initialize, startup_deadline_unix_ms, None)
117 .await
118 }
119
120 pub async fn connect_with_callbacks<R, W, A, B>(
127 reader: R,
128 mut writer: W,
129 initialize: InitializeParams<A>,
130 startup_deadline_unix_ms: u64,
131 callbacks: Option<Arc<dyn CallbackHandler>>,
132 ) -> Result<(Self, InitializeResult<B>)>
133 where
134 R: AsyncRead + Unpin + Send + 'static,
135 W: AsyncWrite + Unpin + Send + 'static,
136 A: Serialize,
137 B: DeserializeOwned,
138 {
139 if callbacks.is_none() && !initialize.client_methods.is_empty() {
140 return Err(Error::Protocol(
141 "client advertised a callback with no handler to answer it",
142 ));
143 }
144 let offered_protocol = initialize.protocol.clone();
145 let offered_versions = initialize.versions.clone();
146 let offered_limits = initialize.limits;
147 let advertised: HashSet<String> = initialize.client_methods.iter().cloned().collect();
148 initialize.validate_common(&offered_protocol)?;
153 let initialize_id = RequestId::new(1)?;
154 let initialize_params = serde_json::to_value(initialize)
155 .map_err(|_| Error::Protocol("failed to serialize initialization"))?;
156 let initialize_request = Request::new(
157 initialize_id,
158 rpc::INITIALIZE,
159 startup_deadline_unix_ms,
160 initialize_params,
161 )?;
162
163 let (writer_tx, mut writer_rx) =
164 mpsc::channel::<WriterCommand>(offered_limits.max_in_flight.saturating_add(2));
165 let mut reader = AsyncFrameReader::new(reader);
166 let inner = Arc::new(Inner {
167 writer: writer_tx,
168 callbacks,
169 advertised,
170 inbound: StdMutex::new(InboundCallbacks::default()),
171 pending: StdMutex::new(HashMap::new()),
172 abandoned: StdMutex::new(HashSet::new()),
173 request_order: Mutex::new(()),
174 next_id: AtomicU64::new(2),
175 max_frame_bytes: AtomicUsize::new(ABSOLUTE_MAX_FRAME_BYTES),
176 max_in_flight: AtomicUsize::new(1),
177 limits: RwLock::new(Limits::PRE_NEGOTIATION),
178 semaphore: RwLock::new(Arc::new(Semaphore::new(1))),
179 capabilities: RwLock::new(HashSet::new()),
180 state: AtomicU8::new(INITIALIZING),
181 closed: Notify::new(),
182 reader_task: Mutex::new(None),
183 writer_task: Mutex::new(None),
184 });
185
186 let writer_inner = Arc::downgrade(&inner);
187 let writer_task = tokio::spawn(async move {
188 while let Some(command) = writer_rx.recv().await {
189 match command {
190 WriterCommand::Frame { payload, limit } => {
191 if write_frame(&mut writer, &payload, limit).await.is_err() {
192 fail_session_weak(&writer_inner);
193 break;
194 }
195 }
196 WriterCommand::Close => break,
197 }
198 }
199 use tokio::io::AsyncWriteExt;
200 let _ = writer.shutdown().await;
201 });
202 *inner.writer_task.lock().await = Some(writer_task);
203
204 let reader_inner = Arc::downgrade(&inner);
205 let reader_task = tokio::spawn(async move {
206 loop {
207 let Some(inner) = reader_inner.upgrade() else {
208 break;
209 };
210 let limit = inner.max_frame_bytes.load(Ordering::Acquire);
211 drop(inner);
212 let frame = match reader.read_frame(limit).await {
213 Ok(Some(frame)) => frame,
214 Ok(None) | Err(_) => {
215 fail_session_weak(&reader_inner);
216 break;
217 }
218 };
219 let response = match Envelope::parse(&frame) {
220 Ok(Envelope::Response(response)) => response,
221 Ok(Envelope::Request(request)) => {
227 let Some(inner) = reader_inner.upgrade() else {
228 break;
229 };
230 let Some(handler) = inner.callbacks.clone() else {
231 fail_session(&inner);
232 break;
233 };
234 if !serve_callback(&inner, handler, request) {
235 fail_session(&inner);
236 break;
237 }
238 continue;
239 }
240 Ok(Envelope::Notification(_)) => continue,
244 _ => {
245 fail_session_weak(&reader_inner);
246 break;
247 }
248 };
249 let Some(id) = response.id() else {
250 fail_session_weak(&reader_inner);
251 break;
252 };
253 let Some(inner) = reader_inner.upgrade() else {
254 break;
255 };
256 let pending = lock_unpoisoned(&inner.pending).remove(&id);
257 if let Some(pending) = pending {
258 lock_unpoisoned(&inner.abandoned).remove(&id);
261 pending.cancellation.cancel();
262 let _ = pending.sender.send(response);
263 continue;
264 }
265 if lock_unpoisoned(&inner.abandoned).remove(&id) {
266 continue;
267 }
268 fail_session(&inner);
270 break;
271 }
272 });
273 *inner.reader_task.lock().await = Some(reader_task);
274
275 let client = Self { inner };
276 let response = match client
277 .request_internal(initialize_request, ABSOLUTE_MAX_FRAME_BYTES)
278 .await
279 {
280 Ok(response) => response,
281 Err(error) => {
282 client.abort_transport().await;
283 return Err(error);
284 }
285 };
286 let initialized = response_value(response)
287 .and_then(|value| {
288 serde_json::from_value::<InitializeResult<B>>(value)
289 .map_err(|error| Error::ProtocolOwned(error.to_string()))
290 })
291 .and_then(|initialized| {
292 initialized.validate_common(
293 &offered_protocol,
294 &offered_versions,
295 offered_limits,
296 )?;
297 Ok(initialized)
298 });
299 let initialized = match initialized {
300 Ok(initialized) => initialized,
301 Err(error) => {
302 client.abort_transport().await;
303 return Err(error);
304 }
305 };
306 client
307 .inner
308 .max_frame_bytes
309 .store(initialized.limits.max_frame_bytes, Ordering::Release);
310 client
311 .inner
312 .max_in_flight
313 .store(initialized.limits.max_in_flight, Ordering::Release);
314 *client.inner.limits.write().await = initialized.limits;
315 *client.inner.semaphore.write().await =
316 Arc::new(Semaphore::new(initialized.limits.max_in_flight));
317 *client.inner.capabilities.write().await = initialized.methods.iter().cloned().collect();
318 client.inner.state.store(READY, Ordering::Release);
319 Ok((client, initialized))
320 }
321
322 pub async fn start<T: Serialize>(
323 &self,
324 method: &str,
325 params: &T,
326 deadline_unix_ms: u64,
327 ) -> Result<Call> {
328 if self.inner.state.load(Ordering::Acquire) != READY {
329 return Err(Error::Closed);
330 }
331 if !self.inner.capabilities.read().await.contains(method) {
332 return Err(Error::Protocol("method was not advertised"));
333 }
334 let params = serde_json::to_value(params)
335 .map_err(|_| Error::Protocol("failed to serialize call params"))?;
336 let deadline_unix_ms = crate::deadline::clamp_unix_ms(deadline_unix_ms);
339 let deadline = instant_from_unix_ms(deadline_unix_ms);
340 if deadline <= Instant::now() {
341 return Err(Error::DeadlineExceeded);
342 }
343 let semaphore = self.inner.semaphore.read().await.clone();
344 let permit = semaphore
345 .try_acquire_owned()
346 .map_err(|_| Error::Unavailable)?;
347 let _order = tokio::time::timeout_at(deadline, self.inner.request_order.lock())
348 .await
349 .map_err(|_| Error::DeadlineExceeded)?;
350 if self.inner.state.load(Ordering::Acquire) != READY {
351 return Err(Error::Closed);
352 }
353 let id = self.next_id()?;
354 let request = Request::new(id, method, deadline_unix_ms, params)?;
355 let (sender, receiver) = oneshot::channel();
356 lock_unpoisoned(&self.inner.pending).insert(
357 id,
358 PendingRequest {
359 sender,
360 deadline_unix_ms,
361 cancellation: CancellationToken::new(),
362 },
363 );
364 let registration = PendingRegistration::new(&self.inner, id);
365 self.queue_request(request, deadline).await?;
366 registration.queued();
367 Ok(Call {
368 id,
369 deadline,
370 receiver: Some(receiver),
371 client: Arc::downgrade(&self.inner),
372 _permit: Some(permit),
373 terminal: false,
374 })
375 }
376
377 pub async fn call<P: Serialize, R: DeserializeOwned>(
378 &self,
379 method: &str,
380 params: &P,
381 deadline_unix_ms: u64,
382 ) -> Result<R> {
383 let mut call = self.start(method, params, deadline_unix_ms).await?;
384 let value = call.wait().await?;
385 serde_json::from_value(value).map_err(|error| Error::ProtocolOwned(error.to_string()))
386 }
387
388 pub async fn limits(&self) -> Limits {
389 *self.inner.limits.read().await
390 }
391
392 pub async fn capabilities(&self) -> HashSet<String> {
393 self.inner.capabilities.read().await.clone()
394 }
395
396 pub fn is_closed(&self) -> bool {
397 self.inner.state.load(Ordering::Acquire) == CLOSED
398 }
399
400 pub async fn close(&self, deadline_unix_ms: u64) -> Result<()> {
403 let _order = tokio::time::timeout_at(
404 instant_from_unix_ms(deadline_unix_ms),
405 self.inner.request_order.lock(),
406 )
407 .await
408 .map_err(|_| Error::DeadlineExceeded)?;
409 let state =
410 self.inner
411 .state
412 .compare_exchange(READY, CLOSING, Ordering::AcqRel, Ordering::Acquire);
413 let outcome = match state {
414 Ok(_) => {
415 let id = self.next_id()?;
416 let request = Request::new(id, rpc::SHUTDOWN, deadline_unix_ms, json!({}))?;
417 self.request_internal(request, self.inner.max_frame_bytes.load(Ordering::Acquire))
418 .await
419 .and_then(|response| {
420 let value = response_value(response)?;
421 if value == json!({}) {
422 Ok(())
423 } else {
424 Err(Error::Protocol("shutdown result is not empty"))
425 }
426 })
427 }
428 Err(CLOSED) => Ok(()),
429 Err(_) => return Err(Error::Closed),
430 };
431
432 let _ = self.inner.writer.try_send(WriterCommand::Close);
433 let deadline = instant_from_unix_ms(deadline_unix_ms);
434 if let Some(mut task) = self.inner.writer_task.lock().await.take()
435 && tokio::time::timeout_at(deadline, &mut task).await.is_err()
436 {
437 task.abort();
438 let _ = task.await;
439 }
440 if let Some(mut task) = self.inner.reader_task.lock().await.take()
441 && tokio::time::timeout_at(deadline, &mut task).await.is_err()
442 {
443 task.abort();
444 let _ = task.await;
445 }
446 fail_session(&self.inner);
447 outcome
448 }
449
450 pub async fn abandon(&self) {
453 fail_session(&self.inner);
454 }
455
456 pub(crate) async fn abandon_after_process_exit(&self, grace: Duration) {
461 if self.is_closed() {
462 return;
463 }
464 let closed = self.inner.closed.notified();
465 tokio::pin!(closed);
466 closed.as_mut().enable();
467 if self.is_closed() {
468 return;
469 }
470 if tokio::time::timeout(grace, closed).await.is_err() && !self.is_closed() {
471 fail_session(&self.inner);
472 }
473 }
474
475 async fn abort_transport(&self) {
476 fail_session(&self.inner);
477 let _ = self.inner.writer.try_send(WriterCommand::Close);
478 if let Some(task) = self.inner.writer_task.lock().await.take() {
479 task.abort();
480 let _ = task.await;
481 }
482 if let Some(task) = self.inner.reader_task.lock().await.take() {
483 task.abort();
484 let _ = task.await;
485 }
486 }
487
488 fn next_id(&self) -> Result<RequestId> {
489 let id = self.inner.next_id.fetch_add(1, Ordering::Relaxed);
490 RequestId::new(id)
491 }
492
493 async fn queue_request(&self, request: Request, deadline: Instant) -> Result<()> {
494 let envelope = Envelope::Request(request);
495 let payload = Zeroizing::new(envelope.to_vec()?);
496 let limit = self.inner.max_frame_bytes.load(Ordering::Acquire);
497 if payload.len() > limit {
498 return Err(Error::Protocol(
499 "request exceeds the negotiated frame limit",
500 ));
501 }
502 match tokio::time::timeout_at(
503 deadline,
504 self.inner
505 .writer
506 .send(WriterCommand::Frame { payload, limit }),
507 )
508 .await
509 {
510 Ok(Ok(())) => Ok(()),
511 Ok(Err(_)) => Err(Error::Closed),
512 Err(_) => Err(Error::DeadlineExceeded),
513 }
514 }
515
516 async fn request_internal(&self, request: Request, limit: usize) -> Result<Response> {
517 let id = request.id;
518 let deadline_unix_ms = request.deadline_unix_ms();
519 let deadline = instant_from_unix_ms(deadline_unix_ms);
520 let (sender, receiver) = oneshot::channel();
521 lock_unpoisoned(&self.inner.pending).insert(
522 id,
523 PendingRequest {
524 sender,
525 deadline_unix_ms,
526 cancellation: CancellationToken::new(),
527 },
528 );
529 let registration = PendingRegistration::new(&self.inner, id);
530 let payload = Zeroizing::new(Envelope::Request(request).to_vec()?);
531 if payload.len() > limit {
532 return Err(Error::Protocol("request exceeds the active frame limit"));
533 }
534 match tokio::time::timeout_at(
535 deadline,
536 self.inner
537 .writer
538 .send(WriterCommand::Frame { payload, limit }),
539 )
540 .await
541 {
542 Ok(Ok(())) => registration.queued(),
543 Ok(Err(_)) => return Err(Error::Closed),
544 Err(_) => return Err(Error::DeadlineExceeded),
545 }
546 match tokio::time::timeout_at(deadline, receiver).await {
547 Ok(Ok(response)) => Ok(response),
548 Ok(Err(_)) => Err(Error::Closed),
549 Err(_) => {
550 abandon_request(&self.inner, id);
551 Err(Error::DeadlineExceeded)
552 }
553 }
554 }
555}
556
557pub struct Call {
560 id: RequestId,
561 deadline: Instant,
562 receiver: Option<oneshot::Receiver<Response>>,
563 client: Weak<Inner>,
564 _permit: Option<tokio::sync::OwnedSemaphorePermit>,
565 terminal: bool,
566}
567
568impl Call {
569 pub const fn id(&self) -> RequestId {
570 self.id
571 }
572
573 pub async fn cancel(&self) -> Result<()> {
574 let Some(client) = self.client.upgrade() else {
575 return Err(Error::Closed);
576 };
577 cancel_parent_callbacks(&client, self.id);
578 match tokio::time::timeout_at(self.deadline, queue_cancel(&client, self.id)).await {
579 Ok(result) => result,
580 Err(_) => Err(Error::DeadlineExceeded),
581 }
582 }
583
584 pub async fn wait(&mut self) -> Result<Value> {
585 let receiver = self
586 .receiver
587 .take()
588 .ok_or(Error::Protocol("call has already been waited"))?;
589 let response = match tokio::time::timeout_at(self.deadline, receiver).await {
590 Ok(Ok(response)) => response,
591 Ok(Err(_)) => {
592 self.terminal = true;
593 self._permit.take();
594 return Err(Error::Closed);
595 }
596 Err(_) => {
597 if let Some(client) = self.client.upgrade() {
598 abandon_request(&client, self.id);
599 try_queue_cancel(&client, self.id);
600 }
601 self.terminal = true;
602 self._permit.take();
603 return Err(Error::DeadlineExceeded);
604 }
605 };
606 self.terminal = true;
607 self._permit.take();
608 response_value(response)
609 }
610}
611
612impl Drop for Call {
613 fn drop(&mut self) {
614 if self.terminal {
615 return;
616 }
617 if let Some(client) = self.client.upgrade() {
618 abandon_request(&client, self.id);
619 try_queue_cancel(&client, self.id);
620 }
621 }
622}
623
624async fn queue_cancel(inner: &Arc<Inner>, id: RequestId) -> Result<()> {
625 let notification = Notification::new(
626 rpc::CANCEL,
627 serde_json::to_value(CancelParams { id })
628 .map_err(|_| Error::Protocol("failed to serialize cancellation"))?,
629 )?;
630 let payload = Zeroizing::new(Envelope::Notification(notification).to_vec()?);
631 let limit = inner.max_frame_bytes.load(Ordering::Acquire);
632 inner
633 .writer
634 .send(WriterCommand::Frame { payload, limit })
635 .await
636 .map_err(|_| Error::Closed)
637}
638
639fn try_queue_cancel(inner: &Arc<Inner>, id: RequestId) {
640 let notification = serde_json::to_value(CancelParams { id })
641 .ok()
642 .and_then(|params| Notification::new(rpc::CANCEL, params).ok())
643 .and_then(|notification| Envelope::Notification(notification).to_vec().ok())
644 .map(Zeroizing::new);
645 if let Some(payload) = notification {
646 let limit = inner.max_frame_bytes.load(Ordering::Acquire);
647 let _ = inner
648 .writer
649 .try_send(WriterCommand::Frame { payload, limit });
650 }
651}
652
653fn serve_callback(inner: &Arc<Inner>, handler: Arc<dyn CallbackHandler>, request: Request) -> bool {
665 if !inner.advertised.contains(&request.method) {
666 return false;
667 }
668 let Some(parent_id) = request.meta.parent_request_id else {
669 return false;
670 };
671 let parent_cancellation = {
672 let pending = lock_unpoisoned(&inner.pending);
676 let Some(parent) = pending.get(&parent_id) else {
677 return false;
678 };
679 if request.deadline_unix_ms() > parent.deadline_unix_ms {
680 return false;
681 }
682 let mut inbound = lock_unpoisoned(&inner.inbound);
683 let active_limit = inner.max_in_flight.load(Ordering::Acquire);
684 if inbound.active >= active_limit
685 || inbound.last_seen_id.is_some_and(|last| request.id <= last)
686 {
687 return false;
688 }
689 inbound.last_seen_id = Some(request.id);
690 inbound.active += 1;
691 parent.cancellation.clone()
692 };
693 let deadline = instant_from_unix_ms(request.deadline_unix_ms());
694 let task_inner = Arc::downgrade(inner);
695 tokio::spawn(async move {
696 let mut outcome = tokio::select! {
697 biased;
698 _ = parent_cancellation.cancelled() => Err(RpcError::new(ErrorKind::Cancelled)),
699 outcome = tokio::time::timeout_at(
700 deadline,
701 handler.call(&request.method, request.params),
702 ) => outcome.unwrap_or_else(|_| Err(RpcError::new(ErrorKind::DeadlineExceeded))),
703 };
704 if parent_cancellation.is_cancelled() {
707 if let Ok(value) = &mut outcome {
708 zeroize_json(value);
709 }
710 outcome = Err(RpcError::new(ErrorKind::Cancelled));
711 }
712 let mut response = match outcome {
713 Ok(result) => Response::success(request.id, result),
714 Err(error) => Response::error(Some(request.id), error),
715 };
716 let Some(inner) = task_inner.upgrade() else {
717 zeroize_response(&mut response);
718 return;
719 };
720 {
721 let mut inbound = lock_unpoisoned(&inner.inbound);
722 inbound.active = inbound.active.saturating_sub(1);
723 }
724 let limit = inner.max_frame_bytes.load(Ordering::Acquire);
725 let encoded = serde_json::to_vec(&response).map(Zeroizing::new);
726 zeroize_response(&mut response);
727 let payload = match encoded {
728 Ok(payload) if payload.len() <= limit => payload,
729 _ => {
732 let replacement =
733 Response::error(Some(request.id), RpcError::new(ErrorKind::MessageTooLarge));
734 match Envelope::Response(replacement).to_vec().map(Zeroizing::new) {
735 Ok(payload) => payload,
736 Err(_) => return,
737 }
738 }
739 };
740 let send = inner.writer.send(WriterCommand::Frame { payload, limit });
741 tokio::select! {
742 biased;
743 _ = parent_cancellation.cancelled() => {
744 let replacement =
748 Response::error(Some(request.id), RpcError::new(ErrorKind::Cancelled));
749 if let Ok(payload) = Envelope::Response(replacement).to_vec().map(Zeroizing::new) {
750 let _ = inner
751 .writer
752 .send(WriterCommand::Frame { payload, limit })
753 .await;
754 }
755 }
756 _ = send => {}
757 }
758 });
759 true
760}
761
762fn response_value(response: Response) -> Result<Value> {
763 match response {
764 Response::Success(response) => Ok(response.result),
765 Response::Error(response) => match response.error.data.kind {
766 ErrorKind::Cancelled => Err(Error::Cancelled),
767 ErrorKind::DeadlineExceeded => Err(Error::DeadlineExceeded),
768 ErrorKind::Unavailable => Err(Error::Unavailable),
769 _ => Err(Error::Remote(response.error)),
770 },
771 }
772}
773
774fn fail_session_weak(inner: &Weak<Inner>) {
775 if let Some(inner) = inner.upgrade() {
776 fail_session(&inner);
777 }
778}
779
780fn fail_session(inner: &Arc<Inner>) {
781 inner.state.store(CLOSED, Ordering::Release);
782 for (_, pending) in lock_unpoisoned(&inner.pending).drain() {
783 pending.cancellation.cancel();
784 }
785 lock_unpoisoned(&inner.abandoned).clear();
786 inner.closed.notify_waiters();
787}
788
789fn abandon_request(inner: &Arc<Inner>, id: RequestId) {
790 let overflow = {
796 let mut pending = lock_unpoisoned(&inner.pending);
797 let Some(entry) = pending.remove(&id) else {
798 return;
799 };
800 entry.cancellation.cancel();
801 let mut abandoned = lock_unpoisoned(&inner.abandoned);
802 if abandoned.len() >= MAX_ABANDONED_REQUESTS {
803 true
804 } else {
805 abandoned.insert(id);
806 false
807 }
808 };
809 if overflow {
810 fail_session(inner);
813 }
814}
815
816fn cancel_parent_callbacks(inner: &Arc<Inner>, id: RequestId) {
817 if let Some(pending) = lock_unpoisoned(&inner.pending).get(&id) {
818 pending.cancellation.cancel();
819 }
820}
821
822struct PendingRegistration<'a> {
827 inner: &'a Arc<Inner>,
828 id: Option<RequestId>,
829}
830
831impl<'a> PendingRegistration<'a> {
832 fn new(inner: &'a Arc<Inner>, id: RequestId) -> Self {
833 Self {
834 inner,
835 id: Some(id),
836 }
837 }
838
839 fn queued(mut self) {
841 self.id = None;
842 }
843}
844
845impl Drop for PendingRegistration<'_> {
846 fn drop(&mut self) {
847 if let Some(id) = self.id {
848 remove_pending(self.inner, id);
849 }
850 }
851}
852
853fn remove_pending(inner: &Arc<Inner>, id: RequestId) {
854 if let Some(pending) = lock_unpoisoned(&inner.pending).remove(&id) {
855 pending.cancellation.cancel();
856 }
857}
858
859fn zeroize_response(response: &mut Response) {
860 if let Response::Success(response) = response {
861 zeroize_json(&mut response.result);
862 }
863}
864
865fn zeroize_json(value: &mut Value) {
866 match value {
867 Value::String(value) => value.zeroize(),
868 Value::Array(values) => values.iter_mut().for_each(zeroize_json),
869 Value::Object(values) => values.values_mut().for_each(zeroize_json),
870 _ => {}
871 }
872}
873
874fn lock_unpoisoned<T>(mutex: &StdMutex<T>) -> std::sync::MutexGuard<'_, T> {
875 mutex
876 .lock()
877 .unwrap_or_else(|poisoned| poisoned.into_inner())
878}
879
880#[cfg(test)]
881mod tests {
882 use super::*;
883 use crate::frame::read_frame;
884 use crate::protocol::Product;
885
886 async fn stalled_session() -> (Client, JoinHandle<()>) {
889 let (client_io, mut peer_io) = tokio::io::duplex(64);
890 let peer = tokio::spawn(async move {
891 let request = read_frame(&mut peer_io, ABSOLUTE_MAX_FRAME_BYTES)
892 .await
893 .unwrap()
894 .unwrap();
895 let id = serde_json::from_slice::<Value>(&request).unwrap()["id"].clone();
896 let response = serde_json::to_vec(&json!({
897 "jsonrpc": "2.0",
898 "id": id,
899 "result": {
900 "protocol": "secretspec.resolver",
901 "version": 1,
902 "server": {"name": "stalled", "version": "1"},
903 "methods": ["resolver.get"],
904 "capabilities": {},
905 "limits": {"max_frame_bytes": 4096, "max_in_flight": 1},
906 "application": {}
907 }
908 }))
909 .unwrap();
910 write_frame(&mut peer_io, &response, ABSOLUTE_MAX_FRAME_BYTES)
911 .await
912 .unwrap();
913 std::future::pending::<()>().await;
914 });
915 let (client_read, client_write) = tokio::io::split(client_io);
916 let initialize = InitializeParams {
917 protocol: "secretspec.resolver".into(),
918 versions: vec![1],
919 client: Product {
920 name: "test".into(),
921 version: "1".into(),
922 },
923 limits: Limits {
924 max_frame_bytes: 4096,
925 max_in_flight: 1,
926 },
927 client_methods: Vec::new(),
928 application: json!({}),
929 };
930 let (client, _) = Client::connect::<_, _, _, Value>(
931 client_read,
932 client_write,
933 initialize,
934 crate::deadline_unix_ms_after(Duration::from_secs(300)),
935 )
936 .await
937 .unwrap();
938 (client, peer)
939 }
940
941 #[tokio::test]
942 async fn a_start_dropped_under_backpressure_leaves_no_pending_request() {
943 let (client, peer) = stalled_session().await;
944 let deadline = crate::deadline_unix_ms_after(Duration::from_secs(300));
945 let params = json!({"padding": "x".repeat(3000)});
946 for _ in 0..2 {
949 let call = client
950 .start("resolver.get", ¶ms, deadline)
951 .await
952 .unwrap();
953 drop(call);
954 }
955 let blocked = client.start("resolver.get", ¶ms, deadline);
956 tokio::select! {
957 biased;
958 _ = blocked => panic!("the writer queue should be full"),
959 _ = std::future::ready(()) => {}
960 }
961 assert!(lock_unpoisoned(&client.inner.pending).is_empty());
962 peer.abort();
963 }
964
965 #[tokio::test]
966 async fn abandoning_an_answered_request_leaves_no_marker() {
967 let (client, peer) = stalled_session().await;
968 abandon_request(&client.inner, RequestId::new(99).unwrap());
971 assert!(lock_unpoisoned(&client.inner.abandoned).is_empty());
972 peer.abort();
973 }
974}