1use crate::server::connection::{IpcConn, JsonRpcStream};
4use futures::StreamExt;
5use futures_util::future::Either;
6use interprocess::local_socket::{
7 tokio::prelude::{LocalSocketListener, LocalSocketStream},
8 traits::tokio::{Listener, Stream},
9 GenericFilePath, ListenerOptions, ToFsName,
10};
11use jsonrpsee::{
12 core::{
13 middleware::layer::{Either as RpcEither, RpcLoggerLayer},
14 JsonRawValue, TEN_MB_SIZE_BYTES,
15 },
16 server::{
17 middleware::rpc::RpcServiceT, stop_channel, ConnectionGuard, ConnectionPermit, IdProvider,
18 RandomIntegerIdProvider, ServerHandle, StopHandle,
19 },
20 BoundedSubscriptions, MethodResponse, MethodSink, Methods,
21};
22use std::{
23 future::Future,
24 io,
25 pin::{pin, Pin},
26 sync::Arc,
27 task::{Context, Poll},
28};
29use tokio::{
30 io::{AsyncRead, AsyncWrite, AsyncWriteExt},
31 sync::oneshot,
32};
33use tower::{layer::util::Identity, Layer, Service};
34use tracing::{debug, instrument, trace, warn, Instrument};
35use crate::{
37 server::{connection::IpcConnDriver, rpc_service::RpcServiceCfg},
38 stream_codec::StreamCodec,
39};
40use tokio::sync::mpsc;
41use tokio_stream::wrappers::ReceiverStream;
42use tokio_util::task::AbortOnDropHandle;
43use tower::layer::{util::Stack, LayerFn};
44
45mod connection;
46mod ipc;
47mod rpc_service;
48
49pub use rpc_service::RpcService;
50
51pub struct IpcServer<HttpMiddleware = Identity, RpcMiddleware = Identity> {
55 endpoint: String,
57 id_provider: Arc<dyn IdProvider>,
58 cfg: Settings,
59 rpc_middleware: RpcServiceBuilder<RpcMiddleware>,
60 http_middleware: tower::ServiceBuilder<HttpMiddleware>,
61}
62
63impl<HttpMiddleware, RpcMiddleware> IpcServer<HttpMiddleware, RpcMiddleware> {
64 pub fn endpoint(&self) -> String {
66 self.endpoint.clone()
67 }
68}
69
70impl<HttpMiddleware, RpcMiddleware> IpcServer<HttpMiddleware, RpcMiddleware>
71where
72 RpcMiddleware: Layer<RpcService, Service: RpcServiceT> + Clone + Send + 'static,
73 HttpMiddleware: Layer<
74 TowerServiceNoHttp<RpcMiddleware>,
75 Service: Service<
76 String,
77 Response = Option<String>,
78 Error = Box<dyn core::error::Error + Send + Sync + 'static>,
79 Future: Send + Unpin,
80 > + Send,
81 > + Send
82 + 'static,
83{
84 pub async fn start(
106 mut self,
107 methods: impl Into<Methods>,
108 ) -> Result<ServerHandle, IpcServerStartError> {
109 let methods = methods.into();
110
111 let (stop_handle, server_handle) = stop_channel();
112
113 let (tx, rx) = oneshot::channel();
115
116 match self.cfg.tokio_runtime.take() {
117 Some(rt) => rt.spawn(self.start_inner(methods, stop_handle, tx)),
118 None => tokio::spawn(self.start_inner(methods, stop_handle, tx)),
119 };
120 rx.await.expect("channel is open")?;
121
122 Ok(server_handle)
123 }
124
125 async fn start_inner(
126 self,
127 methods: Methods,
128 stop_handle: StopHandle,
129 on_ready: oneshot::Sender<Result<(), IpcServerStartError>>,
130 ) {
131 trace!(endpoint = ?self.endpoint, "starting ipc server");
132
133 if cfg!(unix) {
134 if std::fs::remove_file(&self.endpoint).is_ok() {
136 debug!(endpoint = ?self.endpoint, "removed existing IPC endpoint file");
137 }
138 }
139
140 let listener = match self
141 .endpoint
142 .as_str()
143 .to_fs_name::<GenericFilePath>()
144 .and_then(|name| ListenerOptions::new().name(name).create_tokio())
145 {
146 Ok(listener) => {
147 #[cfg(unix)]
148 {
149 use std::os::unix::fs::PermissionsExt;
151 if let Some(perms_str) = &self.cfg.ipc_socket_permissions &&
152 let Ok(mode) = u32::from_str_radix(&perms_str.replace("0o", ""), 8)
153 {
154 let perms = std::fs::Permissions::from_mode(mode);
155 let _ = std::fs::set_permissions(&self.endpoint, perms);
156 }
157 }
158 listener
159 }
160 Err(err) => {
161 on_ready
162 .send(Err(IpcServerStartError { endpoint: self.endpoint.clone(), source: err }))
163 .ok();
164 return;
165 }
166 };
167
168 on_ready.send(Ok(())).ok();
170
171 let mut id: u32 = 0;
172 let connection_guard = ConnectionGuard::new(self.cfg.max_connections as usize);
173
174 let stopped = stop_handle.clone().shutdown();
175 let mut stopped = pin!(stopped);
176
177 let (drop_on_completion, mut process_connection_awaiter) = mpsc::channel::<()>(1);
178
179 trace!("accepting ipc connections");
180 loop {
181 match try_accept_conn(&listener, stopped).await {
182 AcceptConnection::Established { local_socket_stream, stop } => {
183 let Some(conn_permit) = connection_guard.try_acquire() else {
184 let (_reader, mut writer) = local_socket_stream.split();
185 let _ = writer
186 .write_all(b"Too many connections. Please try again later.")
187 .await;
188 stopped = stop;
189 continue;
190 };
191
192 let max_conns = connection_guard.max_connections();
193 let curr_conns = max_conns - connection_guard.available_connections();
194 trace!("Accepting new connection {}/{}", curr_conns, max_conns);
195
196 let conn_permit = Arc::new(conn_permit);
197
198 process_connection(ProcessConnection {
199 http_middleware: &self.http_middleware,
200 rpc_middleware: self.rpc_middleware.clone(),
201 conn_permit,
202 conn_id: id,
203 server_cfg: self.cfg.clone(),
204 stop_handle: stop_handle.clone(),
205 drop_on_completion: drop_on_completion.clone(),
206 methods: methods.clone(),
207 id_provider: self.id_provider.clone(),
208 local_socket_stream,
209 });
210
211 id = id.wrapping_add(1);
212 stopped = stop;
213 }
214 AcceptConnection::Shutdown => {
215 break;
216 }
217 AcceptConnection::Err((err, stop)) => {
218 tracing::error!(%err, "Failed accepting a new IPC connection");
219 stopped = stop;
220 }
221 }
222 }
223
224 drop(drop_on_completion);
226
227 while process_connection_awaiter.recv().await.is_some() {
230 }
233 }
234}
235
236enum AcceptConnection<S> {
237 Shutdown,
238 Established { local_socket_stream: LocalSocketStream, stop: S },
239 Err((io::Error, S)),
240}
241
242async fn try_accept_conn<S>(listener: &LocalSocketListener, stopped: S) -> AcceptConnection<S>
243where
244 S: Future + Unpin,
245{
246 match futures_util::future::select(pin!(listener.accept()), stopped).await {
247 Either::Left((res, stop)) => match res {
248 Ok(local_socket_stream) => AcceptConnection::Established { local_socket_stream, stop },
249 Err(e) => AcceptConnection::Err((e, stop)),
250 },
251 Either::Right(_) => AcceptConnection::Shutdown,
252 }
253}
254
255impl<HttpMiddleware, RpcMiddleware> std::fmt::Debug for IpcServer<HttpMiddleware, RpcMiddleware> {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 f.debug_struct("IpcServer")
258 .field("endpoint", &self.endpoint)
259 .field("cfg", &self.cfg)
260 .field("id_provider", &self.id_provider)
261 .finish()
262 }
263}
264
265#[derive(Debug, thiserror::Error)]
267#[error("failed to listen on ipc endpoint `{endpoint}`: {source}")]
268pub struct IpcServerStartError {
269 endpoint: String,
270 #[source]
271 source: io::Error,
272}
273
274#[derive(Debug, Clone)]
276#[allow(dead_code)]
277pub(crate) struct ServiceData {
278 pub(crate) methods: Methods,
280 pub(crate) id_provider: Arc<dyn IdProvider>,
282 pub(crate) stop_handle: StopHandle,
284 pub(crate) conn_id: u32,
286 pub(crate) conn_permit: Arc<ConnectionPermit>,
288 pub(crate) bounded_subscriptions: BoundedSubscriptions,
290 pub(crate) method_sink: MethodSink,
294 pub(crate) server_cfg: Settings,
296}
297
298#[derive(Debug, Clone)]
301pub struct RpcServiceBuilder<L>(tower::ServiceBuilder<L>);
302
303impl Default for RpcServiceBuilder<Identity> {
304 fn default() -> Self {
305 Self(tower::ServiceBuilder::new())
306 }
307}
308
309impl RpcServiceBuilder<Identity> {
310 pub const fn new() -> Self {
312 Self(tower::ServiceBuilder::new())
313 }
314}
315
316impl<L> RpcServiceBuilder<L> {
317 pub fn option_layer<T>(
321 self,
322 layer: Option<T>,
323 ) -> RpcServiceBuilder<Stack<RpcEither<T, Identity>, L>> {
324 let layer = if let Some(layer) = layer {
325 RpcEither::Left(layer)
326 } else {
327 RpcEither::Right(Identity::new())
328 };
329 self.layer(layer)
330 }
331
332 pub fn layer<T>(self, layer: T) -> RpcServiceBuilder<Stack<T, L>> {
336 RpcServiceBuilder(self.0.layer(layer))
337 }
338
339 pub fn layer_fn<F>(self, f: F) -> RpcServiceBuilder<Stack<LayerFn<F>, L>> {
344 RpcServiceBuilder(self.0.layer_fn(f))
345 }
346
347 pub fn rpc_logger(self, max_log_len: u32) -> RpcServiceBuilder<Stack<RpcLoggerLayer, L>> {
351 RpcServiceBuilder(self.0.layer(RpcLoggerLayer::new(max_log_len)))
352 }
353
354 pub(crate) fn service<S>(&self, service: S) -> L::Service
356 where
357 L: tower::Layer<S>,
358 {
359 self.0.service(service)
360 }
361}
362
363#[derive(Debug, Clone)]
368pub struct TowerServiceNoHttp<L> {
369 inner: ServiceData,
370 rpc_middleware: RpcServiceBuilder<L>,
371}
372
373impl<RpcMiddleware> Service<String> for TowerServiceNoHttp<RpcMiddleware>
374where
375 RpcMiddleware: Layer<RpcService>,
376 <RpcMiddleware as Layer<RpcService>>::Service:
377 Send + Sync + 'static + RpcServiceT<MethodResponse = MethodResponse>,
378{
379 type Response = Option<String>;
385
386 type Error = Box<dyn core::error::Error + Send + Sync + 'static>;
387
388 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
389
390 fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
392 Poll::Ready(Ok(()))
393 }
394
395 fn call(&mut self, request: String) -> Self::Future {
396 trace!("{:?}", request);
397
398 let cfg = RpcServiceCfg {
399 bounded_subscriptions: self.inner.bounded_subscriptions.clone(),
400 id_provider: self.inner.id_provider.clone(),
401 sink: self.inner.method_sink.clone(),
402 };
403
404 let max_response_body_size = self.inner.server_cfg.max_response_body_size as usize;
405 let max_request_body_size = self.inner.server_cfg.max_request_body_size as usize;
406 let conn = self.inner.conn_permit.clone();
407 let rpc_service = self.rpc_middleware.service(RpcService::new(
408 self.inner.methods.clone(),
409 max_response_body_size,
410 self.inner.conn_id.into(),
411 cfg,
412 ));
413 let f = AbortOnDropHandle::new(tokio::task::spawn(async move {
423 ipc::call_with_service(
424 request,
425 rpc_service,
426 max_response_body_size,
427 max_request_body_size,
428 conn,
429 )
430 .await
431 }));
432
433 Box::pin(async move {
434 Ok(f.await.unwrap_or_else(|err| {
438 warn!(%err, "IPC call task failed");
439 None
440 }))
441 })
442 }
443}
444
445struct ProcessConnection<'a, HttpMiddleware, RpcMiddleware> {
446 http_middleware: &'a tower::ServiceBuilder<HttpMiddleware>,
447 rpc_middleware: RpcServiceBuilder<RpcMiddleware>,
448 conn_permit: Arc<ConnectionPermit>,
449 conn_id: u32,
450 server_cfg: Settings,
451 stop_handle: StopHandle,
452 drop_on_completion: mpsc::Sender<()>,
453 methods: Methods,
454 id_provider: Arc<dyn IdProvider>,
455 local_socket_stream: LocalSocketStream,
456}
457
458#[instrument(name = "connection", skip_all, fields(conn_id = %params.conn_id))]
460fn process_connection<RpcMiddleware, HttpMiddleware>(
461 params: ProcessConnection<'_, HttpMiddleware, RpcMiddleware>,
462) where
463 RpcMiddleware: Layer<RpcService> + Clone + Send + 'static,
464 for<'a> <RpcMiddleware as Layer<RpcService>>::Service: RpcServiceT,
465 HttpMiddleware: Layer<TowerServiceNoHttp<RpcMiddleware>> + Send + 'static,
466 <HttpMiddleware as Layer<TowerServiceNoHttp<RpcMiddleware>>>::Service: Send
467 + Service<
468 String,
469 Response = Option<String>,
470 Error = Box<dyn core::error::Error + Send + Sync + 'static>,
471 >,
472 <<HttpMiddleware as Layer<TowerServiceNoHttp<RpcMiddleware>>>::Service as Service<String>>::Future:
473 Send + Unpin,
474{
475 let ProcessConnection {
476 http_middleware,
477 rpc_middleware,
478 conn_permit,
479 conn_id,
480 server_cfg,
481 stop_handle,
482 drop_on_completion,
483 id_provider,
484 methods,
485 local_socket_stream,
486 } = params;
487
488 let ipc = IpcConn(tokio_util::codec::Decoder::framed(
489 StreamCodec::stream_incoming(),
490 local_socket_stream,
491 ));
492
493 let (tx, rx) = mpsc::channel::<Box<JsonRawValue>>(server_cfg.message_buffer_capacity as usize);
494 let method_sink = MethodSink::new_with_limit(tx, server_cfg.max_response_body_size);
495 let tower_service = TowerServiceNoHttp {
496 inner: ServiceData {
497 methods,
498 id_provider,
499 stop_handle: stop_handle.clone(),
500 server_cfg: server_cfg.clone(),
501 conn_id,
502 conn_permit,
503 bounded_subscriptions: BoundedSubscriptions::new(
504 server_cfg.max_subscriptions_per_connection,
505 ),
506 method_sink,
507 },
508 rpc_middleware,
509 };
510
511 let service = http_middleware.service(tower_service);
512 tokio::spawn(async {
513 to_ipc_service(ipc, service, stop_handle, rx).in_current_span().await;
514 drop(drop_on_completion)
515 });
516}
517
518async fn to_ipc_service<S, T>(
519 ipc: IpcConn<JsonRpcStream<T>>,
520 service: S,
521 stop_handle: StopHandle,
522 rx: mpsc::Receiver<Box<JsonRawValue>>,
523) where
524 S: Service<String, Response = Option<String>> + Send + 'static,
525 S::Error: Into<Box<dyn core::error::Error + Send + Sync>>,
526 S::Future: Send + Unpin,
527 T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
528{
529 let rx_item = ReceiverStream::new(rx);
530 let conn = IpcConnDriver {
531 conn: ipc,
532 service,
533 pending_calls: Default::default(),
534 items: Default::default(),
535 };
536 let stopped = stop_handle.shutdown();
537
538 let mut conn = pin!(conn);
539 let mut rx_item = pin!(rx_item);
540 let mut stopped = pin!(stopped);
541
542 loop {
543 tokio::select! {
544 _ = &mut conn => {
545 break
546 }
547 item = rx_item.next() => {
548 let Some(item) = item else { break };
549 conn.push_back(String::from(Box::<str>::from(item)));
550 }
551 _ = &mut stopped => {
552 break
554 }
555 }
556 }
557}
558
559#[derive(Debug, Clone)]
561pub struct Settings {
562 max_request_body_size: u32,
564 max_response_body_size: u32,
566 max_log_length: u32,
570 max_connections: u32,
572 max_subscriptions_per_connection: u32,
574 message_buffer_capacity: u32,
576 tokio_runtime: Option<tokio::runtime::Handle>,
578 ipc_socket_permissions: Option<String>,
580}
581
582impl Default for Settings {
583 fn default() -> Self {
584 Self {
585 max_request_body_size: TEN_MB_SIZE_BYTES,
586 max_response_body_size: TEN_MB_SIZE_BYTES,
587 max_log_length: 4096,
588 max_connections: 100,
589 max_subscriptions_per_connection: 1024,
590 message_buffer_capacity: 1024,
591 tokio_runtime: None,
592 ipc_socket_permissions: None,
593 }
594 }
595}
596
597#[derive(Debug)]
599pub struct Builder<HttpMiddleware, RpcMiddleware> {
600 settings: Settings,
601 id_provider: Arc<dyn IdProvider>,
603 rpc_middleware: RpcServiceBuilder<RpcMiddleware>,
604 http_middleware: tower::ServiceBuilder<HttpMiddleware>,
605}
606
607impl Default for Builder<Identity, Identity> {
608 fn default() -> Self {
609 Self {
610 settings: Settings::default(),
611 id_provider: Arc::new(RandomIntegerIdProvider),
612 rpc_middleware: RpcServiceBuilder::new(),
613 http_middleware: tower::ServiceBuilder::new(),
614 }
615 }
616}
617
618impl<HttpMiddleware, RpcMiddleware> Builder<HttpMiddleware, RpcMiddleware> {
619 pub const fn max_request_body_size(mut self, size: u32) -> Self {
621 self.settings.max_request_body_size = size;
622 self
623 }
624
625 pub const fn max_response_body_size(mut self, size: u32) -> Self {
627 self.settings.max_response_body_size = size;
628 self
629 }
630
631 pub const fn max_log_length(mut self, size: u32) -> Self {
633 self.settings.max_log_length = size;
634 self
635 }
636
637 pub const fn max_connections(mut self, max: u32) -> Self {
639 self.settings.max_connections = max;
640 self
641 }
642
643 pub const fn max_subscriptions_per_connection(mut self, max: u32) -> Self {
645 self.settings.max_subscriptions_per_connection = max;
646 self
647 }
648
649 pub const fn set_message_buffer_capacity(mut self, c: u32) -> Self {
667 self.settings.message_buffer_capacity = c;
668 self
669 }
670
671 pub fn custom_tokio_runtime(mut self, rt: tokio::runtime::Handle) -> Self {
675 self.settings.tokio_runtime = Some(rt);
676 self
677 }
678
679 pub fn set_ipc_socket_permissions(mut self, permissions: Option<String>) -> Self {
681 self.settings.ipc_socket_permissions = permissions;
682 self
683 }
684
685 pub fn set_id_provider<I: IdProvider + 'static>(mut self, id_provider: I) -> Self {
706 self.id_provider = Arc::new(id_provider);
707 self
708 }
709
710 pub fn set_http_middleware<T>(
727 self,
728 service_builder: tower::ServiceBuilder<T>,
729 ) -> Builder<T, RpcMiddleware> {
730 Builder {
731 settings: self.settings,
732 id_provider: self.id_provider,
733 http_middleware: service_builder,
734 rpc_middleware: self.rpc_middleware,
735 }
736 }
737
738 pub fn set_rpc_middleware<T>(
750 self,
751 rpc_middleware: RpcServiceBuilder<T>,
752 ) -> Builder<HttpMiddleware, T> {
753 Builder {
754 settings: self.settings,
755 id_provider: self.id_provider,
756 rpc_middleware,
757 http_middleware: self.http_middleware,
758 }
759 }
760
761 pub fn build(self, endpoint: String) -> IpcServer<HttpMiddleware, RpcMiddleware> {
763 IpcServer {
764 endpoint,
765 cfg: self.settings,
766 id_provider: self.id_provider,
767 http_middleware: self.http_middleware,
768 rpc_middleware: self.rpc_middleware,
769 }
770 }
771}
772
773#[cfg(test)]
774#[expect(missing_docs)]
775pub fn dummy_name() -> String {
776 use rand::Rng;
777 let num: u64 = rand::rng().random();
778 format!(r"/tmp/my-uds-{num}")
779}
780
781#[cfg(test)]
782mod tests {
783 use super::*;
784 use crate::client::IpcClientBuilder;
785 use futures::future::select;
786 use jsonrpsee::{
787 core::{
788 client::{self, ClientT, Error, Subscription, SubscriptionClientT},
789 middleware::{Batch, BatchEntry, Notification},
790 params::BatchRequestBuilder,
791 },
792 rpc_params,
793 types::{error::TOO_MANY_SUBSCRIPTIONS_CODE, ErrorCode, Request},
794 PendingSubscriptionSink, RpcModule, SubscriptionMessage,
795 };
796 use reth_tracing::init_test_tracing;
797 use std::pin::pin;
798 use tokio::sync::broadcast;
799 use tokio_stream::wrappers::BroadcastStream;
800
801 #[tokio::test]
802 #[cfg(unix)]
803 async fn test_ipc_socket_permissions() {
804 use std::os::unix::fs::PermissionsExt;
805 let endpoint = &dummy_name();
806 let perms = "0777";
807 let server = Builder::default()
808 .set_ipc_socket_permissions(Some(perms.to_string()))
809 .build(endpoint.clone());
810 let module = RpcModule::new(());
811 let handle = server.start(module).await.unwrap();
812 tokio::spawn(handle.stopped());
813
814 let meta = std::fs::metadata(endpoint).unwrap();
815 let perms = meta.permissions();
816 assert_eq!(perms.mode() & 0o777, 0o777);
817 }
818
819 async fn pipe_from_stream_with_bounded_buffer(
820 pending: PendingSubscriptionSink,
821 stream: BroadcastStream<usize>,
822 ) -> Result<(), Box<dyn core::error::Error + Send + Sync>> {
823 let sink = pending.accept().await.unwrap();
824 let closed = sink.closed();
825
826 let mut closed = pin!(closed);
827 let mut stream = pin!(stream);
828
829 loop {
830 match select(closed, stream.next()).await {
831 Either::Left((_, _)) | Either::Right((None, _)) => break Ok(()),
833
834 Either::Right((Some(Ok(item)), c)) => {
836 let raw_value = serde_json::value::to_raw_value(&item)?;
837 let notif = SubscriptionMessage::from(raw_value);
838
839 if sink.send(notif).await.is_err() {
843 break Ok(());
844 }
845
846 closed = c;
847 }
848
849 Either::Right((Some(Err(e)), _)) => break Err(e.into()),
851 }
852 }
853 }
854
855 fn produce_items(tx: broadcast::Sender<usize>) {
857 for c in 1..=100 {
858 std::thread::sleep(std::time::Duration::from_millis(1));
859 let _ = tx.send(c);
860 }
861 }
862
863 #[tokio::test]
864 async fn can_set_the_max_response_body_size() {
865 let endpoint = &dummy_name();
867 let server = Builder::default().max_response_body_size(100).build(endpoint.clone());
868 let mut module = RpcModule::new(());
869 module.register_method("anything", |_, _, _| "a".repeat(101)).unwrap();
870 let handle = server.start(module).await.unwrap();
871 tokio::spawn(handle.stopped());
872
873 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
874 let response: Result<String, Error> = client.request("anything", rpc_params![]).await;
875 assert!(response.unwrap_err().to_string().contains("Exceeded max limit of"));
876 }
877
878 #[tokio::test]
879 async fn can_set_the_max_request_body_size() {
880 init_test_tracing();
881 let endpoint = &dummy_name();
882 let server = Builder::default().max_request_body_size(100).build(endpoint.clone());
883 let mut module = RpcModule::new(());
884 module.register_method("anything", |_, _, _| "succeed").unwrap();
885 let handle = server.start(module).await.unwrap();
886 tokio::spawn(handle.stopped());
887
888 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
889 let response: Result<String, Error> =
890 client.request("anything", rpc_params!["a".repeat(101)]).await;
891 assert!(response.is_err());
892 let mut batch_request_builder = BatchRequestBuilder::new();
893 let _ = batch_request_builder.insert("anything", rpc_params![]);
894 let _ = batch_request_builder.insert("anything", rpc_params![]);
895 let _ = batch_request_builder.insert("anything", rpc_params![]);
896 let response: Result<client::BatchResponse<'_, String>, Error> =
901 client.batch_request(batch_request_builder).await;
902 assert!(response.is_err());
903 }
904
905 #[tokio::test]
906 async fn can_set_max_connections() {
907 init_test_tracing();
908
909 let endpoint = &dummy_name();
910 let server = Builder::default().max_connections(2).build(endpoint.clone());
911 let mut module = RpcModule::new(());
912 module.register_method("anything", |_, _, _| "succeed").unwrap();
913 let handle = server.start(module).await.unwrap();
914 tokio::spawn(handle.stopped());
915
916 let client1 = IpcClientBuilder::default().build(endpoint).await.unwrap();
917 let client2 = IpcClientBuilder::default().build(endpoint).await.unwrap();
918 let client3 = IpcClientBuilder::default().build(endpoint).await.unwrap();
919
920 let response1: Result<String, Error> = client1.request("anything", rpc_params![]).await;
921 let response2: Result<String, Error> = client2.request("anything", rpc_params![]).await;
922 let response3: Result<String, Error> = client3.request("anything", rpc_params![]).await;
923
924 assert!(response1.is_ok());
925 assert!(response2.is_ok());
926 assert!(response3.is_err());
928
929 drop(client2);
931 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
932
933 let client4 = IpcClientBuilder::default().build(endpoint).await.unwrap();
935 let response4: Result<String, Error> = client4.request("anything", rpc_params![]).await;
936 assert!(response4.is_ok());
937 }
938
939 #[tokio::test(flavor = "multi_thread")]
940 async fn test_pending_call_aborted_on_disconnect() {
941 init_test_tracing();
942
943 let endpoint = &dummy_name();
944 let server = Builder::default().build(endpoint.clone());
945 let (started_tx, started_rx) = oneshot::channel::<()>();
946 let (dropped_tx, dropped_rx) = oneshot::channel::<()>();
947 let mut module = RpcModule::new(std::sync::Mutex::new(Some((started_tx, dropped_tx))));
948 module
949 .register_async_method("hang", |_, ctx, _| async move {
950 let (started_tx, _dropped_tx) = ctx.lock().unwrap().take().unwrap();
952 let _ = started_tx.send(());
953 std::future::pending::<()>().await;
954 "unreachable"
955 })
956 .unwrap();
957 let handle = server.start(module).await.unwrap();
958 tokio::spawn(handle.stopped());
959
960 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
961 tokio::select! {
962 _ = client.request::<String, _>("hang", rpc_params![]) => panic!("call completed"),
963 _ = started_rx => {}
964 }
965 drop(client);
966
967 let dropped = tokio::time::timeout(std::time::Duration::from_secs(5), dropped_rx).await;
968 assert!(dropped.is_ok(), "call kept running after the connection closed");
969 }
970
971 #[tokio::test]
972 async fn test_panicking_call_returns_internal_error() {
973 init_test_tracing();
974
975 let endpoint = &dummy_name();
976 let server = Builder::default().build(endpoint.clone());
977 let mut module = RpcModule::new(());
978 module
979 .register_async_method("maybe_panic", |params, _, _| async move {
980 assert!(!params.one::<bool>().unwrap(), "requested panic");
981 "ok"
982 })
983 .unwrap();
984 let handle = server.start(module).await.unwrap();
985 tokio::spawn(handle.stopped());
986
987 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
988 let err = client.request::<String, _>("maybe_panic", rpc_params![true]).await.unwrap_err();
989 assert!(
990 matches!(&err, Error::Call(err) if err.code() == ErrorCode::InternalError.code()),
991 "{err:?}"
992 );
993
994 let mut batch_request_builder = BatchRequestBuilder::new();
995 let _ = batch_request_builder.insert("maybe_panic", rpc_params![true]);
996 let _ = batch_request_builder.insert("maybe_panic", rpc_params![false]);
997 let responses = client
998 .batch_request::<String>(batch_request_builder)
999 .await
1000 .unwrap()
1001 .into_iter()
1002 .collect::<Vec<_>>();
1003 assert!(matches!(&responses[0], Err(err) if err.code() == ErrorCode::InternalError.code()));
1004 assert_eq!(responses[1].as_deref(), Ok("ok"));
1005 }
1006
1007 #[tokio::test]
1008 async fn test_rpc_request() {
1009 init_test_tracing();
1010 let endpoint = &dummy_name();
1011 let server = Builder::default().build(endpoint.clone());
1012 let mut module = RpcModule::new(());
1013 let msg = r#"{"jsonrpc":"2.0","id":83,"result":"0x7a69"}"#;
1014 module.register_method("eth_chainId", move |_, _, _| msg).unwrap();
1015 let handle = server.start(module).await.unwrap();
1016 tokio::spawn(handle.stopped());
1017
1018 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
1019 let response: String = client.request("eth_chainId", rpc_params![]).await.unwrap();
1020 assert_eq!(response, msg);
1021 }
1022
1023 #[tokio::test]
1024 async fn test_batch_request() {
1025 let endpoint = &dummy_name();
1026 let server = Builder::default().build(endpoint.clone());
1027 let mut module = RpcModule::new(());
1028 module.register_method("anything", |_, _, _| "ok").unwrap();
1029 let handle = server.start(module).await.unwrap();
1030 tokio::spawn(handle.stopped());
1031
1032 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
1033 let mut batch_request_builder = BatchRequestBuilder::new();
1034 let _ = batch_request_builder.insert("anything", rpc_params![]);
1035 let _ = batch_request_builder.insert("anything", rpc_params![]);
1036 let _ = batch_request_builder.insert("anything", rpc_params![]);
1037 let result = client
1038 .batch_request(batch_request_builder)
1039 .await
1040 .unwrap()
1041 .into_ok()
1042 .unwrap()
1043 .collect::<Vec<String>>();
1044 assert_eq!(result, vec!["ok", "ok", "ok"]);
1045 }
1046
1047 #[tokio::test]
1048 async fn test_ipc_modules() {
1049 reth_tracing::init_test_tracing();
1050 let endpoint = &dummy_name();
1051 let server = Builder::default().build(endpoint.clone());
1052 let mut module = RpcModule::new(());
1053 let msg = r#"{"admin":"1.0","debug":"1.0","engine":"1.0","eth":"1.0","ethash":"1.0","miner":"1.0","net":"1.0","rpc":"1.0","txpool":"1.0","web3":"1.0"}"#;
1054 module.register_method("rpc_modules", move |_, _, _| msg).unwrap();
1055 let handle = server.start(module).await.unwrap();
1056 tokio::spawn(handle.stopped());
1057
1058 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
1059 let response: String = client.request("rpc_modules", rpc_params![]).await.unwrap();
1060 assert_eq!(response, msg);
1061 }
1062
1063 #[tokio::test(flavor = "multi_thread")]
1064 async fn test_rpc_subscription() {
1065 let endpoint = &dummy_name();
1066 let server = Builder::default().build(endpoint.clone());
1067 let (tx, _rx) = broadcast::channel::<usize>(16);
1068
1069 let mut module = RpcModule::new(tx.clone());
1070 std::thread::spawn(move || produce_items(tx));
1071
1072 module
1073 .register_subscription(
1074 "subscribe_hello",
1075 "s_hello",
1076 "unsubscribe_hello",
1077 |_, pending, tx, _| async move {
1078 let rx = tx.subscribe();
1079 let stream = BroadcastStream::new(rx);
1080 pipe_from_stream_with_bounded_buffer(pending, stream).await?;
1081 Ok(())
1082 },
1083 )
1084 .unwrap();
1085
1086 let handle = server.start(module).await.unwrap();
1087 tokio::spawn(handle.stopped());
1088
1089 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
1090 let sub: Subscription<usize> =
1091 client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap();
1092
1093 let items = sub.take(16).collect::<Vec<_>>().await;
1094 assert_eq!(items.len(), 16);
1095 }
1096
1097 #[tokio::test(flavor = "multi_thread")]
1098 async fn test_max_subscriptions_per_connection() {
1099 let endpoint = &dummy_name();
1100 let server = Builder::default().max_subscriptions_per_connection(1).build(endpoint.clone());
1101 let mut module = RpcModule::new(());
1102 module
1103 .register_subscription(
1104 "subscribe_hello",
1105 "s_hello",
1106 "unsubscribe_hello",
1107 |_, pending, _, _| async move {
1108 let Ok(sink) = pending.accept().await else { return };
1109 sink.closed().await;
1110 },
1111 )
1112 .unwrap();
1113 let handle = server.start(module).await.unwrap();
1114 tokio::spawn(handle.stopped());
1115
1116 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
1117 let _sub: Subscription<usize> =
1118 client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap();
1119 let err = client
1120 .subscribe::<usize, _>("subscribe_hello", rpc_params![], "unsubscribe_hello")
1121 .await
1122 .unwrap_err();
1123 assert!(
1124 matches!(&err, Error::Call(err) if err.code() == TOO_MANY_SUBSCRIPTIONS_CODE),
1125 "{err}"
1126 );
1127 }
1128
1129 #[tokio::test]
1130 async fn test_rpc_middleware() {
1131 #[derive(Clone)]
1132 struct ModifyRequestIf<S>(S);
1133
1134 impl<S> RpcServiceT for ModifyRequestIf<S>
1135 where
1136 S: Send + Sync + RpcServiceT,
1137 {
1138 type MethodResponse = S::MethodResponse;
1139 type NotificationResponse = S::NotificationResponse;
1140 type BatchResponse = S::BatchResponse;
1141
1142 fn call<'a>(
1143 &self,
1144 mut req: Request<'a>,
1145 ) -> impl Future<Output = Self::MethodResponse> + Send + 'a {
1146 if req.method == "say_hello" {
1148 req.method = "say_goodbye".into();
1149 } else if req.method == "say_goodbye" {
1150 req.method = "say_hello".into();
1151 }
1152
1153 self.0.call(req)
1154 }
1155
1156 fn batch<'a>(
1157 &self,
1158 mut batch: Batch<'a>,
1159 ) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
1160 for call in batch.iter_mut() {
1161 match call {
1162 Ok(BatchEntry::Call(req)) => {
1163 if req.method == "say_hello" {
1164 req.method = "say_goodbye".into();
1165 } else if req.method == "say_goodbye" {
1166 req.method = "say_hello".into();
1167 }
1168 }
1169 Ok(BatchEntry::Notification(n)) => {
1170 if n.method == "say_hello" {
1171 n.method = "say_goodbye".into();
1172 } else if n.method == "say_goodbye" {
1173 n.method = "say_hello".into();
1174 }
1175 }
1176 Err(_err) => {}
1178 }
1179 }
1180
1181 self.0.batch(batch)
1182 }
1183
1184 fn notification<'a>(
1185 &self,
1186 mut n: Notification<'a>,
1187 ) -> impl Future<Output = Self::NotificationResponse> + Send + 'a {
1188 if n.method == "say_hello" {
1189 n.method = "say_goodbye".into();
1190 } else if n.method == "say_goodbye" {
1191 n.method = "say_hello".into();
1192 }
1193 self.0.notification(n)
1194 }
1195 }
1196
1197 reth_tracing::init_test_tracing();
1198 let endpoint = &dummy_name();
1199
1200 let rpc_middleware = RpcServiceBuilder::new().layer_fn(ModifyRequestIf);
1201 let server = Builder::default().set_rpc_middleware(rpc_middleware).build(endpoint.clone());
1202
1203 let mut module = RpcModule::new(());
1204 let goodbye_msg = r#"{"jsonrpc":"2.0","id":1,"result":"goodbye"}"#;
1205 let hello_msg = r#"{"jsonrpc":"2.0","id":2,"result":"hello"}"#;
1206 module.register_method("say_hello", move |_, _, _| hello_msg).unwrap();
1207 module.register_method("say_goodbye", move |_, _, _| goodbye_msg).unwrap();
1208 let handle = server.start(module).await.unwrap();
1209 tokio::spawn(handle.stopped());
1210
1211 let client = IpcClientBuilder::default().build(endpoint).await.unwrap();
1212 let say_hello_response: String = client.request("say_hello", rpc_params![]).await.unwrap();
1213 let say_goodbye_response: String =
1214 client.request("say_goodbye", rpc_params![]).await.unwrap();
1215
1216 assert_eq!(say_hello_response, goodbye_msg);
1217 assert_eq!(say_goodbye_response, hello_msg);
1218 }
1219}