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::Executor,
20    async_util::{select, timeout, AsyncQueue, Either, TaskGroup, TaskResult},
21};
22
23use karyon_net::{Endpoint, MessageRx, MessageTx};
24
25#[cfg(feature = "ws")]
26use karyon_net::layers::ws::WsLayer;
27#[cfg(feature = "tcp")]
28use karyon_net::tcp::{TcpConfig, TcpListener};
29#[cfg(feature = "tls")]
30use karyon_net::tls::TlsLayer;
31#[cfg(all(feature = "unix", target_family = "unix"))]
32use karyon_net::unix::UnixListener;
33
34use crate::{
35    codec::JsonRpcCodec,
36    error::{Error, Result},
37    message,
38    server::{
39        acceptor::{AsyncAcceptor, StreamAcceptor},
40        channel::NewNotification,
41    },
42};
43
44#[cfg(feature = "ws")]
45use crate::{codec::JsonRpcWsCodec, server::acceptor::WsAcceptor};
46
47pub use builder::ServerBuilder;
48pub use channel::Channel;
49pub use pubsub_service::{PubSubRPCMethod, PubSubRPCService};
50pub use service::{RPCMethod, RPCService};
51
52pub const INVALID_REQUEST_ERROR_MSG: &str = "Invalid request";
53pub const FAILED_TO_PARSE_ERROR_MSG: &str = "Failed to parse";
54pub const METHOD_NOT_FOUND_ERROR_MSG: &str = "Method not found";
55pub const UNSUPPORTED_JSONRPC_VERSION: &str = "Unsupported jsonrpc version";
56
57const CHANNEL_SUBSCRIPTION_BUFFER_SIZE: usize = 100;
58
59/// Bound on the per-connection outbound response queue.
60const RESPONSE_QUEUE_SIZE: usize = 256;
61
62pub(crate) struct ServerConfig {
63    pub endpoint: Endpoint,
64    #[cfg(feature = "tcp")]
65    pub tcp_config: TcpConfig,
66    #[cfg(feature = "tls")]
67    pub tls_config: Option<karyon_net::tls::ServerTlsConfig>,
68    #[cfg(feature = "quic")]
69    pub quic_config: Option<karyon_net::quic::ServerQuicConfig>,
70    pub services: HashMap<String, Arc<dyn RPCService + 'static>>,
71    pub pubsub_services: HashMap<String, Arc<dyn PubSubRPCService + 'static>>,
72    /// `None` (the default) means never. Set via
73    /// `ServerBuilder::read_timeout`.
74    pub read_timeout: Option<Duration>,
75    /// Set via `ServerBuilder::handshake_timeout`.
76    #[cfg(any(feature = "tls", feature = "ws"))]
77    pub handshake_timeout: Duration,
78    /// User-customizable notification wire format.
79    /// Set via `ServerBuilder::with_notification_encoder`.
80    pub notification_encoder: fn(NewNotification) -> message::Notification,
81}
82
83/// One variant per backend family. Stream-based transports share
84/// a single `AsyncAcceptor`; QUIC and HTTP have their own loops.
85enum ServerBackend {
86    StreamAcceptor(Arc<dyn AsyncAcceptor>),
87    #[cfg(feature = "quic")]
88    QuicEndpoint(karyon_net::quic::QuicEndpoint),
89    #[cfg(feature = "http")]
90    Http(http::HttpServer),
91}
92
93/// A JSON-RPC 2.0 server.
94pub struct Server {
95    backend: ServerBackend,
96    pub(crate) task_group: Arc<TaskGroup>,
97    pub(crate) config: ServerConfig,
98}
99
100impl Server {
101    pub fn start(self: Arc<Self>) {
102        self.task_group.spawn(self.clone().start_block());
103    }
104
105    pub async fn start_block(self: Arc<Self>) -> Result<()> {
106        if let Err(err) = self.accept_loop().await {
107            error!("Main accept loop stopped: {err}");
108            self.shutdown().await;
109        };
110        Ok(())
111    }
112
113    async fn accept_loop(self: &Arc<Self>) -> Result<()> {
114        match &self.backend {
115            // Only the kernel accept runs here; the handshake runs in a
116            // separate task, so a slow client cannot stall the loop.
117            ServerBackend::StreamAcceptor(acceptor) => loop {
118                match acceptor.accept().await {
119                    Ok(stream) => {
120                        let acceptor = acceptor.clone();
121                        let server = self.clone();
122                        self.task_group.spawn(async move {
123                            if let Err(err) = acceptor.handle(stream, &server).await {
124                                error!("Handle connection: {err}");
125                            }
126                        });
127                    }
128                    Err(err) => error!("Accept connection: {err}"),
129                }
130            },
131            // As above: the QUIC handshake runs in its own task.
132            #[cfg(feature = "quic")]
133            ServerBackend::QuicEndpoint(endpoint) => loop {
134                match endpoint.accept_incoming().await {
135                    Ok(incoming) => self.handle_quic_incoming(incoming),
136                    Err(err) => {
137                        error!("Accept QUIC conn: {err}")
138                    }
139                }
140            },
141            #[cfg(feature = "http")]
142            ServerBackend::Http(http_server) => {
143                http::accept_loop(self.clone(), http_server).await?;
144                Ok(())
145            }
146        }
147    }
148
149    pub fn local_endpoint(&self) -> Result<Endpoint> {
150        match &self.backend {
151            ServerBackend::StreamAcceptor(acceptor) => acceptor.local_endpoint(),
152            #[cfg(feature = "quic")]
153            ServerBackend::QuicEndpoint(endpoint) => endpoint.local_endpoint().map_err(Error::from),
154            #[cfg(feature = "http")]
155            ServerBackend::Http(http_server) => http_server.local_endpoint(),
156        }
157    }
158
159    pub async fn shutdown(&self) {
160        self.task_group.cancel().await;
161    }
162
163    /// Handle a split message connection (framed byte stream or
164    /// WebSocket). Spawns a reader and writer task; the writer drains
165    /// both responses and notifications.
166    pub(crate) fn handle_message_conn<R, W>(
167        self: &Arc<Self>,
168        reader: R,
169        writer: W,
170        peer: Option<Endpoint>,
171    ) where
172        R: MessageRx<Message = serde_json::Value> + Send + 'static,
173        W: MessageTx<Message = serde_json::Value> + Send + 'static,
174    {
175        debug!("Handle connection {peer:?}");
176
177        let (ch_tx, ch_rx) = async_channel::bounded(CHANNEL_SUBSCRIPTION_BUFFER_SIZE);
178        let channel = Channel::new(ch_tx);
179        let queue = AsyncQueue::new(RESPONSE_QUEUE_SIZE);
180
181        let writer_chan = channel.clone();
182        self.task_group.spawn_then(
183            stream_writer_task(
184                writer,
185                queue.clone(),
186                ch_rx,
187                self.config.notification_encoder,
188            ),
189            |result: TaskResult<Result<()>>| async move {
190                if let TaskResult::Completed(Err(err)) = result {
191                    debug!("Writer stopped: {err}");
192                }
193                writer_chan.close();
194            },
195        );
196
197        let reader_chan = channel.clone();
198        let read_timeout = self.config.read_timeout;
199        self.task_group.spawn_then(
200            stream_reader_task(self.clone(), reader, queue, channel, read_timeout),
201            |result: TaskResult<Result<()>>| async move {
202                if let TaskResult::Completed(Err(err)) = result {
203                    debug!("Connection {peer:?} dropped: {err}");
204                } else {
205                    debug!("Connection {peer:?} dropped");
206                }
207                reader_chan.close();
208            },
209        );
210    }
211
212    async fn new_request(
213        self: &Arc<Self>,
214        queue: Arc<AsyncQueue<serde_json::Value>>,
215        channel: Arc<Channel>,
216        msg: serde_json::Value,
217    ) {
218        self.task_group.spawn_then(
219            request_task(self.clone(), queue, channel, msg),
220            |result: TaskResult<Result<()>>| async move {
221                if let TaskResult::Completed(Err(err)) = result {
222                    error!("Handle request: {err}");
223                }
224            },
225        );
226    }
227
228    pub(super) async fn init<B, W>(
229        config: ServerConfig,
230        ex: Option<Executor>,
231        byte_codec: B,
232        ws_codec: W,
233    ) -> Result<Arc<Self>>
234    where
235        B: JsonRpcCodec,
236        W: WsCodec,
237    {
238        let task_group = Arc::new(match ex {
239            Some(ex) => TaskGroup::with_executor(ex),
240            None => TaskGroup::new(),
241        });
242
243        let backend = create_backend(&config, byte_codec, ws_codec).await?;
244        info!("RPC server listens to the endpoint: {}", config.endpoint);
245
246        Ok(Arc::new(Server {
247            backend,
248            task_group,
249            config,
250        }))
251    }
252}
253
254/// Bound on the WebSocket codec generic. With the `ws` feature it
255/// requires `JsonRpcWsCodec`; otherwise it accepts any clonable type
256/// (the codec is unused) so callers can pass `JsonCodec` unchanged.
257#[cfg(feature = "ws")]
258pub trait WsCodec: JsonRpcWsCodec {}
259#[cfg(feature = "ws")]
260impl<T: JsonRpcWsCodec> WsCodec for T {}
261
262#[cfg(not(feature = "ws"))]
263pub trait WsCodec: Clone + Send + Sync + 'static {}
264#[cfg(not(feature = "ws"))]
265impl<T: Clone + Send + Sync + 'static> WsCodec for T {}
266
267async fn create_backend<B, W>(
268    config: &ServerConfig,
269    byte_codec: B,
270    ws_codec: W,
271) -> Result<ServerBackend>
272where
273    B: JsonRpcCodec,
274    W: WsCodec,
275{
276    let endpoint = config.endpoint.clone();
277    match endpoint {
278        #[cfg(feature = "http")]
279        Endpoint::Http(..) => {
280            #[cfg(feature = "http3")]
281            let http_server = match config.quic_config.clone() {
282                Some(quic_cfg) => http::HttpServer::new_h3(&endpoint, quic_cfg).await?,
283                None => http::HttpServer::new(&endpoint).await?,
284            };
285            #[cfg(not(feature = "http3"))]
286            let http_server = http::HttpServer::new(&endpoint).await?;
287            Ok(ServerBackend::Http(http_server))
288        }
289        #[cfg(feature = "quic")]
290        Endpoint::Quic(..) => match &config.quic_config {
291            Some(conf) => {
292                let quic_endpoint =
293                    karyon_net::quic::QuicEndpoint::listen(&endpoint, conf.clone()).await?;
294                Ok(ServerBackend::QuicEndpoint(quic_endpoint))
295            }
296            None => Err(Error::QUICConfigRequired),
297        },
298        #[cfg(feature = "tcp")]
299        Endpoint::Tcp(..) => {
300            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
301            Ok(ServerBackend::StreamAcceptor(Arc::new(StreamAcceptor {
302                listener: Box::new(listener),
303                codec: byte_codec,
304                #[cfg(feature = "tls")]
305                tls: None,
306                #[cfg(feature = "tls")]
307                handshake_timeout: config.handshake_timeout,
308            })))
309        }
310        #[cfg(feature = "tls")]
311        Endpoint::Tls(..) => {
312            let tls_config = config.tls_config.as_ref().ok_or(Error::TLSConfigRequired)?;
313            // Plain TCP listener; `StreamAcceptor::handle` runs the TLS
314            // handshake.
315            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
316            Ok(ServerBackend::StreamAcceptor(Arc::new(StreamAcceptor {
317                listener: Box::new(listener),
318                codec: byte_codec,
319                tls: Some(TlsLayer::server(tls_config.clone())),
320                handshake_timeout: config.handshake_timeout,
321            })))
322        }
323        #[cfg(feature = "ws")]
324        Endpoint::Ws(..) => {
325            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
326            let layer = Arc::new(WsLayer::server(ws_codec));
327            Ok(ServerBackend::StreamAcceptor(Arc::new(WsAcceptor {
328                listener: Box::new(listener),
329                layer,
330                #[cfg(feature = "tls")]
331                tls: None,
332                handshake_timeout: config.handshake_timeout,
333            })))
334        }
335        #[cfg(all(feature = "ws", feature = "tls"))]
336        Endpoint::Wss(..) => {
337            let tls_config = config.tls_config.as_ref().ok_or(Error::TLSConfigRequired)?;
338            let listener = TcpListener::bind(&endpoint, config.tcp_config.clone()).await?;
339            let layer = Arc::new(WsLayer::server(ws_codec));
340            Ok(ServerBackend::StreamAcceptor(Arc::new(WsAcceptor {
341                listener: Box::new(listener),
342                layer,
343                tls: Some(TlsLayer::server(tls_config.clone())),
344                handshake_timeout: config.handshake_timeout,
345            })))
346        }
347        #[cfg(all(feature = "unix", target_family = "unix"))]
348        Endpoint::Unix(..) => {
349            let listener = UnixListener::bind(&endpoint)?;
350            Ok(ServerBackend::StreamAcceptor(Arc::new(StreamAcceptor {
351                listener: Box::new(listener),
352                codec: byte_codec,
353                #[cfg(feature = "tls")]
354                tls: None,
355                #[cfg(feature = "tls")]
356                handshake_timeout: config.handshake_timeout,
357            })))
358        }
359        _ => Err(Error::UnsupportedProtocol(endpoint.to_string())),
360    }
361}
362
363async fn stream_writer_task<W>(
364    mut writer: W,
365    queue: Arc<AsyncQueue<serde_json::Value>>,
366    ch_rx: async_channel::Receiver<NewNotification>,
367    notification_encoder: fn(NewNotification) -> message::Notification,
368) -> Result<()>
369where
370    W: MessageTx<Message = serde_json::Value> + Send,
371{
372    loop {
373        match select(queue.recv(), ch_rx.recv()).await {
374            Either::Left(res) => {
375                writer.send_msg(res).await?;
376            }
377            Either::Right(notification) => {
378                let nt = notification?;
379                let notification = notification_encoder(nt);
380                debug!("--> {notification}");
381                writer.send_msg(serde_json::json!(notification)).await?;
382            }
383        }
384    }
385}
386
387async fn stream_reader_task<R>(
388    server: Arc<Server>,
389    mut reader: R,
390    queue: Arc<AsyncQueue<serde_json::Value>>,
391    channel: Arc<Channel>,
392    read_timeout: Option<Duration>,
393) -> Result<()>
394where
395    R: MessageRx<Message = serde_json::Value> + Send,
396{
397    loop {
398        // Once set, an idle client is dropped instead of holding the
399        // socket open forever.
400        let msg = match read_timeout {
401            Some(t) => timeout(t, reader.recv_msg()).await??,
402            None => reader.recv_msg().await?,
403        };
404        server
405            .new_request(queue.clone(), channel.clone(), msg)
406            .await;
407    }
408}
409
410async fn request_task(
411    server: Arc<Server>,
412    queue: Arc<AsyncQueue<serde_json::Value>>,
413    channel: Arc<Channel>,
414    msg: serde_json::Value,
415) -> Result<()> {
416    let response = server.handle_request(Some(channel), msg).await;
417    debug!("--> {response}");
418    queue.push(serde_json::json!(response)).await;
419    Ok(())
420}