1use super::{helpers::MethodSink, MethodResponse, ResponsePayload};
30use crate::{
31 error::SubscriptionError,
32 server::{
33 error::{DisconnectError, PendingSubscriptionAcceptError, SendTimeoutError, TrySendError},
34 rpc_module::ConnectionId,
35 },
36 traits::IdProvider,
37};
38use jsonrpsee_types::{
39 response::SubscriptionPayloadError, ErrorCode, ErrorObjectOwned, Id, SubscriptionId,
40 SubscriptionPayload, SubscriptionResponse,
41};
42use parking_lot::Mutex;
43use rustc_hash::FxHashMap;
44use serde::Serialize;
45use serde_json::value::RawValue;
46use std::{collections::hash_map::Entry, sync::Arc, time::Duration};
47use tokio::sync::{mpsc, oneshot, OwnedSemaphorePermit, Semaphore};
48
49pub type Subscribers = Arc<Mutex<FxHashMap<SubscriptionKey, (MethodSink, mpsc::Receiver<()>)>>>;
51pub type SubscriptionPermit = OwnedSemaphorePermit;
53
54pub trait IntoSubscriptionCloseResponse {
57 fn into_response(self) -> SubscriptionCloseResponse;
59}
60
61#[derive(Debug)]
63pub enum SubscriptionCloseResponse {
64 None,
66 Notif(SubscriptionMessage),
82 NotifErr(SubscriptionError),
98}
99
100impl IntoSubscriptionCloseResponse for Result<(), SubscriptionError> {
101 fn into_response(self) -> SubscriptionCloseResponse {
102 match self {
103 Ok(()) => SubscriptionCloseResponse::None,
104 Err(e) => SubscriptionCloseResponse::NotifErr(e),
105 }
106 }
107}
108
109impl IntoSubscriptionCloseResponse for () {
110 fn into_response(self) -> SubscriptionCloseResponse {
111 SubscriptionCloseResponse::None
112 }
113}
114
115impl IntoSubscriptionCloseResponse for SubscriptionCloseResponse {
116 fn into_response(self) -> Self {
117 self
118 }
119}
120
121#[derive(Debug, Clone)]
123pub enum SubscriptionMessageInner {
124 Complete(String),
126 NeedsData(Box<RawValue>),
128}
129
130#[derive(Debug, Clone)]
132pub struct SubscriptionMessage(pub(crate) SubscriptionMessageInner);
133
134impl From<Box<RawValue>> for SubscriptionMessage {
135 fn from(json: Box<RawValue>) -> Self {
136 Self(SubscriptionMessageInner::NeedsData(json))
137 }
138}
139
140impl SubscriptionMessage {
141 pub fn new(
146 method: &str,
147 subscription: SubscriptionId,
148 result: &impl Serialize,
149 ) -> Result<Self, serde_json::Error> {
150 let json = serde_json::to_string(&SubscriptionResponse::new(
151 method.into(),
152 SubscriptionPayload { subscription, result },
153 ))?;
154 Ok(Self::from_complete_message(json))
155 }
156
157 pub(crate) fn from_complete_message(msg: String) -> Self {
158 SubscriptionMessage(SubscriptionMessageInner::Complete(msg))
159 }
160}
161
162#[derive(Clone, Debug, PartialEq, Eq, Hash)]
164pub struct SubscriptionKey {
165 pub(crate) conn_id: ConnectionId,
166 pub(crate) sub_id: SubscriptionId<'static>,
167}
168
169#[derive(Debug, Clone)]
173pub struct IsUnsubscribed(mpsc::Sender<()>);
174
175impl IsUnsubscribed {
176 pub fn is_unsubscribed(&self) -> bool {
182 self.0.is_closed()
183 }
184
185 pub async fn unsubscribed(&self) {
192 self.0.closed().await;
193 }
194}
195
196#[derive(Debug)]
204#[must_use = "PendingSubscriptionSink does nothing unless `accept` or `reject` is called"]
205pub struct PendingSubscriptionSink {
206 pub(crate) inner: MethodSink,
208 pub(crate) method: &'static str,
210 pub(crate) subscribers: Subscribers,
212 pub(crate) uniq_sub: SubscriptionKey,
214 pub(crate) id: Id<'static>,
217 pub(crate) subscribe: oneshot::Sender<MethodResponse>,
219 pub(crate) permit: OwnedSemaphorePermit,
221}
222
223impl PendingSubscriptionSink {
224 pub async fn reject(self, err: impl Into<ErrorObjectOwned>) {
233 let err = MethodResponse::subscription_error(self.id, err.into());
234 _ = self.inner.send(err.to_json()).await;
235 _ = self.subscribe.send(err);
236 }
237
238 pub async fn accept(self) -> Result<SubscriptionSink, PendingSubscriptionAcceptError> {
246 let (tx, rx) = mpsc::channel(1);
247
248 let reserved = match self.subscribers.lock().entry(self.uniq_sub.clone()) {
251 Entry::Vacant(entry) => {
252 entry.insert((self.inner.clone(), rx));
253 true
254 }
255 Entry::Occupied(_) => false,
256 };
257
258 if !reserved {
259 self.reject(ErrorCode::InternalError).await;
260 return Err(PendingSubscriptionAcceptError);
261 }
262
263 let sink = SubscriptionSink {
265 inner: self.inner,
266 method: self.method,
267 subscribers: self.subscribers,
268 uniq_sub: self.uniq_sub,
269 unsubscribe: IsUnsubscribed(tx),
270 _permit: Some(Arc::new(self.permit)),
271 };
272
273 let response = MethodResponse::subscription_response(
274 self.id,
275 ResponsePayload::success_borrowed(&sink.uniq_sub.sub_id),
276 sink.inner.max_response_size() as usize,
277 );
278 let success = response.is_success();
279
280 sink.inner.send(response.to_json()).await.map_err(|_| PendingSubscriptionAcceptError)?;
287 self.subscribe.send(response).map_err(|_| PendingSubscriptionAcceptError)?;
288
289 if success {
290 Ok(sink)
291 } else {
292 Err(PendingSubscriptionAcceptError)
294 }
295 }
296
297 pub fn subscription_id(&self) -> SubscriptionId<'static> {
299 self.uniq_sub.sub_id.clone()
300 }
301
302 pub fn connection_id(&self) -> ConnectionId {
304 self.uniq_sub.conn_id
305 }
306
307 pub fn capacity(&self) -> usize {
309 self.inner.capacity()
310 }
311
312 pub fn max_capacity(&self) -> usize {
314 self.inner.max_capacity()
315 }
316
317 pub fn method_name(&self) -> &str {
319 self.method
320 }
321}
322
323#[derive(Debug, Clone)]
328pub struct SubscriptionSink {
329 inner: MethodSink,
331 method: &'static str,
333 subscribers: Subscribers,
335 uniq_sub: SubscriptionKey,
337 unsubscribe: IsUnsubscribed,
339 _permit: Option<Arc<SubscriptionPermit>>,
341}
342
343impl SubscriptionSink {
344 pub fn subscription_id(&self) -> SubscriptionId<'static> {
346 self.uniq_sub.sub_id.clone()
347 }
348
349 pub fn method_name(&self) -> &str {
351 self.method
352 }
353
354 pub fn connection_id(&self) -> ConnectionId {
356 self.uniq_sub.conn_id
357 }
358
359 pub async fn send(&self, msg: impl Into<SubscriptionMessage>) -> Result<(), DisconnectError> {
370 let msg = msg.into();
371
372 if self.is_closed() {
374 return Err(DisconnectError(msg));
375 }
376
377 let json = sub_message_to_json(msg, &self.uniq_sub.sub_id, self.method);
378 self.inner.send(json).await
379 }
380
381 pub async fn send_timeout(
383 &self,
384 msg: impl Into<SubscriptionMessage>,
385 timeout: Duration,
386 ) -> Result<(), SendTimeoutError> {
387 let msg = msg.into();
388
389 if self.is_closed() {
391 return Err(SendTimeoutError::Closed(msg));
392 }
393
394 let json = sub_message_to_json(msg, &self.uniq_sub.sub_id, self.method);
395 self.inner.send_timeout(json, timeout).await
396 }
397
398 pub fn try_send(&mut self, msg: impl Into<SubscriptionMessage>) -> Result<(), TrySendError> {
405 let msg = msg.into();
406
407 if self.is_closed() {
409 return Err(TrySendError::Closed(msg));
410 }
411
412 let json = sub_message_to_json(msg, &self.uniq_sub.sub_id, self.method);
413 self.inner.try_send(json)
414 }
415
416 pub fn is_closed(&self) -> bool {
418 self.inner.is_closed() || !self.is_active_subscription()
419 }
420
421 pub async fn closed(&self) {
423 tokio::select! {
425 _ = self.inner.closed() => (),
426 _ = self.unsubscribe.unsubscribed() => (),
427 }
428 }
429
430 pub fn capacity(&self) -> usize {
432 self.inner.capacity()
433 }
434
435 pub fn max_capacity(&self) -> usize {
437 self.inner.max_capacity()
438 }
439
440 fn is_active_subscription(&self) -> bool {
441 !self.unsubscribe.is_unsubscribed()
442 }
443}
444
445impl Drop for SubscriptionSink {
446 fn drop(&mut self) {
447 let mut subs = self.subscribers.lock();
450 if self.is_active_subscription() &&
453 self._permit.take().is_some_and(|p| Arc::try_unwrap(p).is_ok())
454 {
455 subs.remove(&self.uniq_sub);
456 }
457 }
458}
459
460#[derive(Debug, Clone)]
463pub struct BoundedSubscriptions {
464 guard: Arc<Semaphore>,
465 max: u32,
466}
467
468impl BoundedSubscriptions {
469 pub fn new(max_subscriptions: u32) -> Self {
471 Self { guard: Arc::new(Semaphore::new(max_subscriptions as usize)), max: max_subscriptions }
472 }
473
474 pub fn acquire(&self) -> Option<SubscriptionPermit> {
478 Arc::clone(&self.guard).try_acquire_owned().ok()
479 }
480
481 pub const fn max(&self) -> u32 {
483 self.max
484 }
485}
486
487#[derive(Debug)]
488pub struct SubscriptionState<'a> {
490 pub conn_id: ConnectionId,
492 pub id_provider: &'a dyn IdProvider,
494 pub subscription_permit: SubscriptionPermit,
496}
497
498pub(crate) fn sub_message_to_json(
499 msg: SubscriptionMessage,
500 sub_id: &SubscriptionId,
501 method: &str,
502) -> String {
503 match msg.0 {
504 SubscriptionMessageInner::Complete(msg) => msg,
505 SubscriptionMessageInner::NeedsData(result) => {
506 serde_json::to_string(&SubscriptionResponse::new(
507 method.into(),
508 SubscriptionPayload { subscription: sub_id.clone(), result },
509 ))
510 .expect("Serialize infallible; qed")
511 }
512 }
513}
514
515pub(crate) fn sub_err_to_json(
516 error: SubscriptionError,
517 sub_id: SubscriptionId,
518 method: &str,
519) -> String {
520 serde_json::to_string(&jsonrpsee_types::response::SubscriptionError::new(
521 method.into(),
522 SubscriptionPayloadError { subscription: sub_id, error },
523 ))
524 .expect("Serialize infallible; qed")
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use crate::server::{MethodCallback, Methods, RpcModule};
531 use futures_util::FutureExt;
532 use jsonrpsee_types::Params;
533 use serde_json::value::to_raw_value;
534 use std::{
535 sync::atomic::{AtomicBool, Ordering},
536 time::Instant,
537 };
538
539 const SUB_ID: &str = "0xdeadbeef";
540
541 #[derive(Debug)]
543 struct ConstantId;
544
545 impl IdProvider for ConstantId {
546 fn next_id(&self) -> SubscriptionId<'static> {
547 SUB_ID.to_string().into()
548 }
549 }
550
551 struct Harness {
553 methods: Methods,
554 pending: mpsc::UnboundedReceiver<PendingSubscriptionSink>,
555 permits: BoundedSubscriptions,
556 }
557
558 impl Harness {
559 fn new() -> Self {
560 let (pending_tx, pending) = mpsc::unbounded_channel();
561 let mut module = RpcModule::new(pending_tx);
562 module
563 .register_subscription_raw("sub", "notif", "unsub", |_, pending, ctx| {
564 ctx.send(pending).unwrap();
565 })
566 .unwrap();
567
568 Self {
569 methods: Methods::clone(&module),
570 pending,
571 permits: BoundedSubscriptions::new(16),
572 }
573 }
574
575 fn subscribe(
578 &mut self,
579 id: u64,
580 conn_id: ConnectionId,
581 sink: &mpsc::Sender<String>,
582 ) -> (PendingSubscriptionSink, futures_util::future::BoxFuture<'static, MethodResponse>)
583 {
584 let state = SubscriptionState {
585 conn_id,
586 id_provider: &ConstantId,
587 subscription_permit: self.permits.acquire().unwrap(),
588 };
589 let Some(MethodCallback::Subscription(subscribe)) = self.methods.method("sub") else {
590 panic!("`sub` is a subscription");
591 };
592 let response =
593 subscribe(Id::Number(id), Params::new(None), MethodSink::new(sink.clone()), state);
594 (self.pending.try_recv().unwrap(), response)
595 }
596
597 fn unsubscribe(&self, id: u64, conn_id: ConnectionId) -> String {
599 call_unsubscribe(&self.methods, SUB_ID, id, conn_id)
600 }
601 }
602
603 struct StopOnDrop<'a>(&'a AtomicBool);
604
605 impl Drop for StopOnDrop<'_> {
606 fn drop(&mut self) {
607 self.0.store(true, Ordering::Relaxed);
608 }
609 }
610
611 fn call_unsubscribe(methods: &Methods, sub_id: &str, id: u64, conn_id: ConnectionId) -> String {
612 let params = format!(r#"["{sub_id}"]"#);
613 let Some(MethodCallback::Unsubscription(unsubscribe)) = methods.method("unsub") else {
614 panic!("`unsub` is an unsubscription");
615 };
616 unsubscribe(Id::Number(id), Params::new(Some(¶ms)), conn_id, usize::MAX).into_json()
617 }
618
619 #[tokio::test]
620 async fn colliding_subscription_id_keeps_existing_subscription() {
621 let mut harness = Harness::new();
622 let conn_id = ConnectionId(0);
623 let (tx, mut rx) = mpsc::channel(8);
624
625 let (pending, response) = harness.subscribe(1, conn_id, &tx);
626 let first = pending.accept().await.unwrap();
627 let ack = r#"{"jsonrpc":"2.0","id":1,"result":"0xdeadbeef"}"#;
628 assert_eq!(response.await.into_json(), ack);
629 assert_eq!(rx.recv().await.unwrap(), ack);
630
631 let (pending, response) = harness.subscribe(2, conn_id, &tx);
633 assert!(matches!(pending.accept().await, Err(PendingSubscriptionAcceptError)));
634 let error =
635 r#"{"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"Internal error"}}"#;
636 assert_eq!(response.await.into_json(), error);
637 assert_eq!(rx.recv().await.unwrap(), error);
638 assert!(rx.try_recv().is_err());
639
640 assert!(!first.is_closed());
642 first.send(to_raw_value(&"item").unwrap()).await.unwrap();
643 assert_eq!(
644 rx.recv().await.unwrap(),
645 r#"{"jsonrpc":"2.0","method":"notif","params":{"subscription":"0xdeadbeef","result":"item"}}"#
646 );
647
648 assert_eq!(harness.unsubscribe(3, conn_id), r#"{"jsonrpc":"2.0","id":3,"result":true}"#);
650 assert!(first.is_closed());
651 assert_eq!(harness.unsubscribe(4, conn_id), r#"{"jsonrpc":"2.0","id":4,"result":false}"#);
652 }
653
654 #[tokio::test]
655 async fn same_subscription_id_on_different_connections() {
656 let mut harness = Harness::new();
657 let (tx, _rx) = mpsc::channel(8);
658
659 let (pending, _first_response) = harness.subscribe(1, ConnectionId(0), &tx);
660 let first = pending.accept().await.unwrap();
661 let (pending, _second_response) = harness.subscribe(1, ConnectionId(1), &tx);
662 let second = pending.accept().await.unwrap();
663
664 assert_eq!(
665 harness.unsubscribe(2, ConnectionId(0)),
666 r#"{"jsonrpc":"2.0","id":2,"result":true}"#
667 );
668 assert!(first.is_closed());
669 assert!(!second.is_closed());
670 assert_eq!(
671 harness.unsubscribe(3, ConnectionId(1)),
672 r#"{"jsonrpc":"2.0","id":3,"result":true}"#
673 );
674 assert!(second.is_closed());
675 }
676
677 #[tokio::test]
678 async fn failed_acknowledgement_releases_subscription_id() {
679 let mut harness = Harness::new();
680 let conn_id = ConnectionId(0);
681
682 let (tx, rx) = mpsc::channel(8);
684 drop(rx);
685 let (pending, _) = harness.subscribe(1, conn_id, &tx);
686 assert!(matches!(pending.accept().await, Err(PendingSubscriptionAcceptError)));
687 assert_eq!(harness.unsubscribe(2, conn_id), r#"{"jsonrpc":"2.0","id":2,"result":false}"#);
688
689 let (tx, _rx) = mpsc::channel(8);
691 let (pending, _response) = harness.subscribe(3, conn_id, &tx);
692 let sink = pending.accept().await.unwrap();
693 assert_eq!(harness.unsubscribe(4, conn_id), r#"{"jsonrpc":"2.0","id":4,"result":true}"#);
694 assert!(sink.is_closed());
695 }
696
697 #[tokio::test]
698 async fn dropping_unsubscribed_sink_keeps_replacement_subscription() {
699 let mut harness = Harness::new();
700 let conn_id = ConnectionId(0);
701 let (tx, _rx) = mpsc::channel(8);
702
703 let (pending, _first_response) = harness.subscribe(1, conn_id, &tx);
704 let first = pending.accept().await.unwrap();
705 assert_eq!(harness.unsubscribe(2, conn_id), r#"{"jsonrpc":"2.0","id":2,"result":true}"#);
706
707 let (pending, _second_response) = harness.subscribe(3, conn_id, &tx);
709 let second = pending.accept().await.unwrap();
710
711 drop(first);
712 assert!(!second.is_closed());
713 assert_eq!(harness.unsubscribe(4, conn_id), r#"{"jsonrpc":"2.0","id":4,"result":true}"#);
714 assert!(second.is_closed());
715 }
716
717 #[test]
720 fn dropping_sink_races_with_unsubscribe_and_resubscribe() {
721 let mut harness = Harness::new();
722 let methods = harness.methods.clone();
723 let conn_id = ConnectionId(0);
724 let (tx, mut rx) = mpsc::channel(8);
725 let deadline = Instant::now() + Duration::from_secs(1);
726 let stop = AtomicBool::new(false);
727 let mut removed_replacements = 0;
728
729 std::thread::scope(|scope| {
730 let _stop = StopOnDrop(&stop);
732
733 for i in 0..4 {
735 let (methods, stop) = (&methods, &stop);
736 scope.spawn(move || {
737 while !stop.load(Ordering::Relaxed) {
738 call_unsubscribe(methods, "other", 0, ConnectionId(1000 + i));
739 }
740 });
741 }
742
743 while Instant::now() < deadline {
744 while rx.try_recv().is_ok() {}
745
746 let (pending, _first_response) = harness.subscribe(1, conn_id, &tx);
747 let first = pending.accept().now_or_never().unwrap().unwrap();
748 let (pending, _second_response) = harness.subscribe(2, conn_id, &tx);
749
750 let go = AtomicBool::new(false);
751 let second = std::thread::scope(|race| {
752 let (methods, go) = (&methods, &go);
753 let dropper = race.spawn(move || {
754 while !go.load(Ordering::Acquire) {
755 std::hint::spin_loop();
756 }
757 drop(first);
758 });
759 let replacer = race.spawn(move || {
760 while !go.load(Ordering::Acquire) {
761 std::hint::spin_loop();
762 }
763 call_unsubscribe(methods, SUB_ID, 3, conn_id);
764 pending.accept().now_or_never().unwrap().unwrap()
765 });
766 go.store(true, Ordering::Release);
767 dropper.join().unwrap();
768 replacer.join().unwrap()
769 });
770
771 if second.is_closed() {
772 removed_replacements += 1;
773 }
774 }
775 });
776
777 assert_eq!(removed_replacements, 0);
778 }
779}