Skip to main content

karyon_jsonrpc/server/
quic.rs

1//! QUIC transport: one request per stream, with separate
2//! notification streaming for pubsub.
3
4use std::sync::Arc;
5
6use log::{debug, error};
7
8use karyon_core::async_util::{select, Either, TaskResult};
9
10use karyon_net::{
11    framed,
12    quic::{QuicConn, QuicIncoming},
13    Endpoint, FramedConn, StreamMux,
14};
15
16use crate::{
17    codec::JsonCodec,
18    error::Result,
19    message,
20    server::{
21        channel::Channel,
22        dispatch::{sanity_check, Handler, NewRequest, SanityCheckResult},
23        Server, CHANNEL_SUBSCRIPTION_BUFFER_SIZE,
24    },
25};
26
27impl Server {
28    /// Run the handshake off the accept loop, then serve the connection.
29    pub(super) fn handle_quic_incoming(self: &Arc<Self>, incoming: QuicIncoming) {
30        let peer = incoming.peer_endpoint();
31        let server = self.clone();
32        self.task_group.spawn(async move {
33            // Bounded by the QUIC idle timeout (QuicConfig::idle_timeout).
34            match incoming.handshake().await {
35                Ok(quic_conn) => server.handle_quic_conn(quic_conn),
36                Err(err) => debug!("QUIC handshake with {peer} failed: {err}"),
37            }
38        });
39    }
40
41    /// Serve a QUIC connection; each incoming stream is handled
42    /// as an independent request.
43    fn handle_quic_conn(self: &Arc<Self>, quic_conn: QuicConn) {
44        let peer = quic_conn.peer_endpoint().ok();
45        debug!("Handle QUIC connection {peer:?}");
46
47        self.task_group.spawn_then(
48            quic_accept_streams_task(self.clone(), Arc::new(quic_conn)),
49            move |result: TaskResult<Result<()>>| async move {
50                if let TaskResult::Completed(Err(err)) = result {
51                    debug!("QUIC conn {peer:?} dropped: {err}");
52                } else {
53                    debug!("QUIC conn {peer:?} dropped");
54                }
55            },
56        );
57    }
58}
59
60async fn quic_accept_streams_task(server: Arc<Server>, quic_conn: Arc<QuicConn>) -> Result<()> {
61    let codec = JsonCodec::default();
62    let peer = quic_conn.peer_endpoint().ok();
63    loop {
64        let stream = quic_conn.accept_stream().await?;
65        let conn = framed(stream, codec.clone());
66        server
67            .task_group
68            .spawn(quic_handle_stream_task(server.clone(), conn, peer.clone()));
69    }
70}
71
72async fn quic_handle_stream_task(
73    server: Arc<Server>,
74    conn: FramedConn<JsonCodec>,
75    peer: Option<Endpoint>,
76) -> Result<()> {
77    if let Err(err) = handle_quic_stream(server, conn, peer).await {
78        error!("Handle QUIC stream: {err}");
79    }
80    Ok(())
81}
82
83/// Handle a single QUIC stream: one request, one response.
84/// Upgrades to pubsub notification streaming if the method matches.
85async fn handle_quic_stream(
86    server: Arc<Server>,
87    mut conn: FramedConn<JsonCodec>,
88    peer: Option<Endpoint>,
89) -> Result<()> {
90    let msg = conn.recv_msg().await?;
91
92    let req = match sanity_check(msg) {
93        SanityCheckResult::NewReq(req) => req,
94        SanityCheckResult::ErrRes(res) => {
95            conn.send_msg(serde_json::json!(res)).await?;
96            return Ok(());
97        }
98    };
99
100    // Pubsub handlers need a dedicated stream for streaming notifications.
101    // Everything else (regular RPC, method not found) can reuse the
102    // shared dispatch path.
103    // TODO: match on the handler and pass it on. Now it is resolved three
104    // times (here, `handle_quic_subscription`, `handle_request`).
105    let is_pubsub = matches!(
106        server.resolve_handler(&req.srvc_name, &req.method_name, true),
107        Handler::Pubsub(_)
108    );
109
110    if is_pubsub {
111        return handle_quic_subscription(server, conn, req, peer).await;
112    }
113
114    let msg = serde_json::to_value(&req.msg).expect("serializable request");
115    let response = server.handle_request(None, msg).await;
116    debug!("--> {response}");
117    conn.send_msg(serde_json::json!(response)).await?;
118    Ok(())
119}
120
121/// Pubsub over QUIC: split the stream so notifications stream
122/// from the writer while the reader waits for an unsubscribe.
123async fn handle_quic_subscription(
124    server: Arc<Server>,
125    conn: FramedConn<JsonCodec>,
126    req: NewRequest,
127    peer: Option<Endpoint>,
128) -> Result<()> {
129    let (ch_tx, ch_rx) = async_channel::bounded(CHANNEL_SUBSCRIPTION_BUFFER_SIZE);
130    let channel = Channel::new(ch_tx, peer);
131
132    let method = match server.resolve_handler(&req.srvc_name, &req.method_name, true) {
133        Handler::Pubsub(m) => m,
134        _ => unreachable!("pubsub method presence checked by caller"),
135    };
136
137    let params = req.msg.params.unwrap_or(serde_json::json!(()));
138    let result = method(channel.clone(), req.msg.method, params).await;
139
140    let response = match result {
141        Ok(res) => message::Response {
142            result: Some(res),
143            id: Some(req.msg.id),
144            ..Default::default()
145        },
146        Err(err) => {
147            let mut conn = conn;
148            let response = err.to_response(Some(req.msg.id), None);
149            conn.send_msg(serde_json::json!(response)).await?;
150            return Ok(());
151        }
152    };
153
154    debug!("--> {response}");
155
156    let (mut reader, mut writer) = conn.split();
157    writer.send_msg(serde_json::json!(response)).await?;
158
159    let notification_encoder = server.config.notification_encoder;
160
161    loop {
162        match select(ch_rx.recv(), reader.recv_msg()).await {
163            Either::Left(nt) => {
164                let nt = nt?;
165                let notification = notification_encoder(nt);
166                debug!("--> {notification}");
167                writer.send_msg(serde_json::json!(notification)).await?;
168            }
169            Either::Right(msg) => match msg {
170                Ok(msg) => {
171                    let response = server.handle_request(Some(channel.clone()), msg).await;
172                    debug!("--> {response}");
173                    writer.send_msg(serde_json::json!(response)).await?;
174                    channel.close();
175                    return Ok(());
176                }
177                Err(_) => {
178                    channel.close();
179                    return Ok(());
180                }
181            },
182        }
183    }
184}