Skip to main content

karyon_jsonrpc/server/
mod.rs

1mod acceptor;
2pub mod builder;
3pub mod channel;
4mod dispatch;
5pub mod pubsub_service;
6pub mod service;
7
8#[cfg(feature = "quic")]
9mod quic;
10
11#[cfg(feature = "http")]
12mod http;
13
14use std::{collections::HashMap, sync::Arc, time::Duration};
15
16use log::{debug, error, info};
17
18use karyon_core::{
19    async_runtime::{
20        lock::{Semaphore, SemaphoreGuard},
21        Executor,
22    },
23    async_util::{select, timeout, AsyncQueue, Backoff, CondWait, Either, TaskGroup, TaskResult},
24};
25
26use karyon_net::{Endpoint, MessageRx, MessageTx};
27
28#[cfg(feature = "ws")]
29use karyon_net::layers::ws::WsLayer;
30#[cfg(feature = "tcp")]
31use karyon_net::tcp::{TcpConfig, TcpListener};
32#[cfg(feature = "tls")]
33use karyon_net::tls::TlsLayer;
34#[cfg(all(feature = "unix", target_family = "unix"))]
35use karyon_net::unix::UnixListener;
36
37use crate::{
38    codec::JsonRpcCodec,
39    error::{Error, Result},
40    message,
41    server::{
42        acceptor::{AsyncAcceptor, StreamAcceptor},
43        channel::NewNotification,
44    },
45};
46
47#[cfg(feature = "http")]
48use crate::server::http::{HttpAcceptor, HttpServer};
49#[cfg(feature = "ws")]
50use crate::{codec::JsonRpcWsCodec, server::acceptor::WsAcceptor};
51
52pub use builder::ServerBuilder;
53pub use channel::Channel;
54#[cfg(feature = "http")]
55pub use http::HttpConfig;
56pub use pubsub_service::{PubSubRPCMethod, PubSubRPCService};
57pub use service::{RPCMethod, RPCService};
58
59pub const INVALID_REQUEST_ERROR_MSG: &str = "Invalid request";
60pub const FAILED_TO_PARSE_ERROR_MSG: &str = "Failed to parse";
61pub const METHOD_NOT_FOUND_ERROR_MSG: &str = "Method not found";
62pub const UNSUPPORTED_JSONRPC_VERSION: &str = "Unsupported jsonrpc version";
63
64const CHANNEL_SUBSCRIPTION_BUFFER_SIZE: usize = 100;
65
66/// Max requests handled at once per message connection. The reader waits
67/// for a free slot before reading the next request.
68// TODO: make this configurable.
69const MAX_CONCURRENT_REQUESTS: usize = 64;
70
71/// First retry delay after a failed accept, e.g. when out of file
72/// descriptors, in milliseconds.
73// TODO: make this configurable.
74const ACCEPT_BACKOFF_BASE: u64 = 10;
75/// Max retry delay after a failed accept, in milliseconds.
76// TODO: make this configurable.
77const ACCEPT_BACKOFF_MAX: u64 = 1000;
78
79/// Bound on the per-connection outbound response queue.
80const RESPONSE_QUEUE_SIZE: usize = 256;
81
82pub(crate) struct ServerConfig {
83    pub endpoint: Endpoint,
84    #[cfg(feature = "tcp")]
85    pub tcp_config: TcpConfig,
86    #[cfg(feature = "tls")]
87    pub tls_config: Option<karyon_net::tls::ServerTlsConfig>,
88    #[cfg(feature = "quic")]
89    pub quic_config: Option<karyon_net::quic::ServerQuicConfig>,
90    pub services: HashMap<String, Arc<dyn RPCService + 'static>>,
91    pub pubsub_services: HashMap<String, Arc<dyn PubSubRPCService + 'static>>,
92    /// `None` (the default) means never. Set via
93    /// `ServerBuilder::read_timeout`.
94    pub read_timeout: Option<Duration>,
95    /// Set via `ServerBuilder::handshake_timeout`.
96    #[cfg(any(feature = "tls", feature = "ws"))]
97    pub handshake_timeout: Duration,
98    /// User-customizable notification wire format.
99    /// Set via `ServerBuilder::with_notification_encoder`.
100    pub notification_encoder: fn(NewNotification) -> message::Notification,
101    /// Set via `ServerBuilder::http_config`.
102    #[cfg(feature = "http")]
103    pub http_config: HttpConfig,
104}
105
106/// One variant per backend family. Stream-based transports share
107/// a single `AsyncAcceptor`; QUIC and HTTP have their own loops.
108enum ServerBackend {
109    StreamAcceptor(Arc<dyn AsyncAcceptor>),
110    #[cfg(feature = "quic")]
111    QuicEndpoint(karyon_net::quic::QuicEndpoint),
112    #[cfg(feature = "http")]
113    Http(Arc<dyn HttpAcceptor>),
114}
115
116/// A JSON-RPC 2.0 server.
117pub struct Server {
118    backend: ServerBackend,
119    pub(crate) task_group: Arc<TaskGroup>,
120    pub(crate) config: ServerConfig,
121}
122
123impl Server {
124    pub fn start(self: Arc<Self>) {
125        self.task_group.spawn(self.clone().start_block());
126    }
127
128    pub async fn start_block(self: Arc<Self>) -> Result<()> {
129        if let Err(err) = self.accept_loop().await {
130            error!("Main accept loop stopped: {err}");
131            self.shutdown().await;
132        };
133        Ok(())
134    }
135
136    async fn accept_loop(self: &Arc<Self>) -> Result<()> {
137        // Back off on accept errors, so the loop does not spin.
138        let backoff = accept_backoff();
139        match &self.backend {
140            ServerBackend::StreamAcceptor(acceptor) => loop {
141                match acceptor.accept().await {
142                    Ok(stream) => {
143                        backoff.reset();
144                        let acceptor = acceptor.clone();
145                        let server = self.clone();
146                        self.task_group.spawn(async move {
147                            if let Err(err) = acceptor.handle(stream, &server).await {
148                                error!("Handle connection: {err}");
149                            }
150                        });
151                    }
152                    Err(err) => {
153                        error!("Accept connection: {err}");
154                        backoff.sleep().await;
155                    }
156                }
157            },
158            #[cfg(feature = "quic")]
159            ServerBackend::QuicEndpoint(endpoint) => loop {
160                match endpoint.accept_incoming().await {
161                    Ok(incoming) => {
162                        backoff.reset();
163                        self.handle_quic_incoming(incoming);
164                    }
165                    Err(err) => {
166                        error!("Accept QUIC conn: {err}");
167                        backoff.sleep().await;
168                    }
169                }
170            },
171            #[cfg(feature = "http")]
172            ServerBackend::Http(http_server) => {
173                http_server.accept_loop(self.clone()).await?;
174                Ok(())
175            }
176        }
177    }
178
179    pub fn local_endpoint(&self) -> Result<Endpoint> {
180        match &self.backend {
181            ServerBackend::StreamAcceptor(acceptor) => acceptor.local_endpoint(),
182            #[cfg(feature = "quic")]
183            ServerBackend::QuicEndpoint(endpoint) => endpoint.local_endpoint().map_err(Error::from),
184            #[cfg(feature = "http")]
185            ServerBackend::Http(http_server) => http_server.local_endpoint(),
186        }
187    }
188
189    pub async fn shutdown(&self) {
190        self.task_group.cancel().await;
191    }
192
193    /// Handle a split message connection (framed byte stream or
194    /// WebSocket). Spawns a reader and writer task; the writer drains
195    /// both responses and notifications.
196    pub(crate) fn handle_message_conn<R, W>(
197        self: &Arc<Self>,
198        reader: R,
199        writer: W,
200        peer: Option<Endpoint>,
201    ) where
202        R: MessageRx<Message = serde_json::Value> + Send + 'static,
203        W: MessageTx<Message = serde_json::Value> + Send + 'static,
204    {
205        debug!("Handle connection {peer:?}");
206
207        let (ch_tx, ch_rx) = async_channel::bounded(CHANNEL_SUBSCRIPTION_BUFFER_SIZE);
208        let channel = Channel::new(ch_tx, peer.clone());
209        let queue = AsyncQueue::new(RESPONSE_QUEUE_SIZE);
210        // Set when the writer stops. The reader and the request tasks
211        // stop too, instead of waiting forever on a full queue.
212        let writer_done = Arc::new(CondWait::new());
213
214        let writer_chan = channel.clone();
215        let writer_stopped = writer_done.clone();
216        self.task_group.spawn_then(
217            stream_writer_task(
218                writer,
219                queue.clone(),
220                ch_rx,
221                self.config.notification_encoder,
222            ),
223            |result: TaskResult<Result<()>>| async move {
224                if let TaskResult::Completed(Err(err)) = result {
225                    debug!("Writer stopped: {err}");
226                }
227                writer_chan.close();
228                writer_stopped.broadcast().await;
229            },
230        );
231
232        let reader_chan = channel.clone();
233        let read_timeout = self.config.read_timeout;
234        let conn = ConnState {
235            queue,
236            channel,
237            writer_done,
238        };
239        self.task_group.spawn_then(
240            stream_reader_task(self.clone(), reader, conn, read_timeout),
241            |result: TaskResult<Result<()>>| async move {
242                if let TaskResult::Completed(Err(err)) = result {
243                    debug!("Connection {peer:?} dropped: {err}");
244                } else {
245                    debug!("Connection {peer:?} dropped");
246                }
247                reader_chan.close();
248            },
249        );
250    }
251
252    fn new_request(
253        self: &Arc<Self>,
254        conn: ConnState,
255        permit: SemaphoreGuard,
256        msg: serde_json::Value,
257    ) {
258        self.task_group.spawn_then(
259            request_task(self.clone(), conn, permit, msg),
260            |result: TaskResult<Result<()>>| async move {
261                if let TaskResult::Completed(Err(err)) = result {
262                    error!("Handle request: {err}");
263                }
264            },
265        );
266    }
267
268    pub(super) async fn init<B, W>(
269        config: ServerConfig,
270        ex: Option<Executor>,
271        byte_codec: B,
272        ws_codec: W,
273    ) -> Result<Arc<Self>>
274    where
275        B: JsonRpcCodec,
276        W: WsCodec,
277    {
278        let task_group = Arc::new(match ex {
279            Some(ex) => TaskGroup::with_executor(ex),
280            None => TaskGroup::new(),
281        });
282
283        let backend = create_backend(&config, byte_codec, ws_codec).await?;
284        info!("RPC server listens to the endpoint: {}", config.endpoint);
285
286        Ok(Arc::new(Server {
287            backend,
288            task_group,
289            config,
290        }))
291    }
292}
293
294/// Bound on the WebSocket codec generic. With the `ws` feature it
295/// requires `JsonRpcWsCodec`; otherwise it accepts any clonable type
296/// (the codec is unused) so callers can pass `JsonCodec` unchanged.
297#[cfg(feature = "ws")]
298pub trait WsCodec: JsonRpcWsCodec {}
299#[cfg(feature = "ws")]
300impl<T: JsonRpcWsCodec> WsCodec for T {}
301
302#[cfg(not(feature = "ws"))]
303pub trait WsCodec: Clone + Send + Sync + 'static {}
304#[cfg(not(feature = "ws"))]
305impl<T: Clone + Send + Sync + 'static> WsCodec for T {}
306
307async fn create_backend<B, W>(
308    config: &ServerConfig,
309    byte_codec: B,
310    ws_codec: W,
311) -> Result<ServerBackend>
312where
313    B: JsonRpcCodec,
314    W: WsCodec,
315{
316    let endpoint = config.endpoint.clone();
317    match endpoint {
318        #[cfg(feature = "http")]
319        Endpoint::Http(..) => {
320            #[cfg(feature = "http3")]
321            let http_server = match config.quic_config.clone() {
322                Some(quic_cfg) => HttpServer::new_h3(&endpoint, quic_cfg, byte_codec).await?,
323                None => HttpServer::new(&endpoint, byte_codec).await?,
324            };
325            #[cfg(not(feature = "http3"))]
326            let http_server = HttpServer::new(&endpoint, byte_codec).await?;
327            Ok(ServerBackend::Http(Arc::new(http_server)))
328        }
329        #[cfg(feature = "quic")]
330        Endpoint::Quic(..) => match &config.quic_config {
331            Some(conf) => {
332                let quic_endpoint =
333                    karyon_net::quic::QuicEndpoint::listen(&endpoint, conf.clone()).await?;
334                Ok(ServerBackend::QuicEndpoint(quic_endpoint))
335            }
336            None => Err(Error::QUICConfigRequired),
337        },
338        #[cfg(feature = "tcp")]
339        Endpoint::Tcp(..) => {
340            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
341            Ok(ServerBackend::StreamAcceptor(Arc::new(StreamAcceptor {
342                listener: Box::new(listener),
343                codec: byte_codec,
344                #[cfg(feature = "tls")]
345                tls: None,
346                #[cfg(feature = "tls")]
347                handshake_timeout: config.handshake_timeout,
348            })))
349        }
350        #[cfg(feature = "tls")]
351        Endpoint::Tls(..) => {
352            let tls_config = config.tls_config.as_ref().ok_or(Error::TLSConfigRequired)?;
353            // Plain TCP listener; `StreamAcceptor::handle` runs the TLS
354            // handshake.
355            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
356            Ok(ServerBackend::StreamAcceptor(Arc::new(StreamAcceptor {
357                listener: Box::new(listener),
358                codec: byte_codec,
359                tls: Some(TlsLayer::server(tls_config.clone())),
360                handshake_timeout: config.handshake_timeout,
361            })))
362        }
363        #[cfg(feature = "ws")]
364        Endpoint::Ws(..) => {
365            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
366            let layer = Arc::new(WsLayer::server(ws_codec));
367            Ok(ServerBackend::StreamAcceptor(Arc::new(WsAcceptor {
368                listener: Box::new(listener),
369                layer,
370                #[cfg(feature = "tls")]
371                tls: None,
372                handshake_timeout: config.handshake_timeout,
373            })))
374        }
375        #[cfg(all(feature = "ws", feature = "tls"))]
376        Endpoint::Wss(..) => {
377            let tls_config = config.tls_config.as_ref().ok_or(Error::TLSConfigRequired)?;
378            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
379            let layer = Arc::new(WsLayer::server(ws_codec));
380            Ok(ServerBackend::StreamAcceptor(Arc::new(WsAcceptor {
381                listener: Box::new(listener),
382                layer,
383                tls: Some(TlsLayer::server(tls_config.clone())),
384                handshake_timeout: config.handshake_timeout,
385            })))
386        }
387        #[cfg(all(feature = "unix", target_family = "unix"))]
388        Endpoint::Unix(..) => {
389            let listener = UnixListener::bind(&endpoint)?;
390            Ok(ServerBackend::StreamAcceptor(Arc::new(StreamAcceptor {
391                listener: Box::new(listener),
392                codec: byte_codec,
393                #[cfg(feature = "tls")]
394                tls: None,
395                #[cfg(feature = "tls")]
396                handshake_timeout: config.handshake_timeout,
397            })))
398        }
399        _ => Err(Error::UnsupportedProtocol(endpoint.to_string())),
400    }
401}
402
403async fn stream_writer_task<W>(
404    mut writer: W,
405    queue: Arc<AsyncQueue<serde_json::Value>>,
406    ch_rx: async_channel::Receiver<NewNotification>,
407    notification_encoder: fn(NewNotification) -> message::Notification,
408) -> Result<()>
409where
410    W: MessageTx<Message = serde_json::Value> + Send,
411{
412    loop {
413        match select(queue.recv(), ch_rx.recv()).await {
414            Either::Left(res) => {
415                writer.send_msg(res).await?;
416            }
417            Either::Right(notification) => {
418                let nt = notification?;
419                let notification = notification_encoder(nt);
420                debug!("--> {notification}");
421                writer.send_msg(serde_json::json!(notification)).await?;
422            }
423        }
424    }
425}
426
427/// Shared state of one message connection, cloned into each request.
428#[derive(Clone)]
429struct ConnState {
430    queue: Arc<AsyncQueue<serde_json::Value>>,
431    channel: Arc<Channel>,
432    writer_done: Arc<CondWait>,
433}
434
435async fn stream_reader_task<R>(
436    server: Arc<Server>,
437    mut reader: R,
438    conn: ConnState,
439    read_timeout: Option<Duration>,
440) -> Result<()>
441where
442    R: MessageRx<Message = serde_json::Value> + Send,
443{
444    let permits = Semaphore::new(MAX_CONCURRENT_REQUESTS);
445    loop {
446        // Wait for a free slot, so one client cannot start an unbounded
447        // number of request tasks.
448        let permit = permits.acquire().await?;
449
450        // Once set, an idle client is dropped instead of holding the
451        // socket open forever.
452        let recv = async {
453            match read_timeout {
454                Some(t) => timeout(t, reader.recv_msg()).await?,
455                None => reader.recv_msg().await,
456            }
457        };
458        let msg = match select(conn.writer_done.wait(), recv).await {
459            // Nobody sends the responses anymore.
460            Either::Left(_) => return Ok(()),
461            Either::Right(msg) => msg?,
462        };
463        server.new_request(conn.clone(), permit, msg);
464    }
465}
466
467async fn request_task(
468    server: Arc<Server>,
469    conn: ConnState,
470    _permit: SemaphoreGuard,
471    msg: serde_json::Value,
472) -> Result<()> {
473    let response = server.handle_request(Some(conn.channel), msg).await;
474    debug!("--> {response}");
475    // Drop the response if the writer is gone.
476    select(
477        conn.queue.push(serde_json::json!(response)),
478        conn.writer_done.wait(),
479    )
480    .await;
481    Ok(())
482}
483
484pub(super) fn accept_backoff() -> Backoff {
485    Backoff::new(ACCEPT_BACKOFF_BASE, ACCEPT_BACKOFF_MAX)
486}