jsonrpsee_server/transport/
ws.rs1use std::{sync::Arc, time::Duration};
2
3use crate::{
4 server::{handle_rpc_call, ConnectionState, ServerConfig},
5 LOG_TARGET,
6};
7
8use futures_util::{
9 future::{self, Either},
10 io::{BufReader, BufWriter},
11 Future, StreamExt, TryStreamExt,
12};
13use hyper::upgrade::Upgraded;
14use hyper_util::rt::TokioIo;
15use jsonrpsee_core::{classify_message, middleware::RpcServiceT, server::MethodSink};
16use jsonrpsee_types::{
17 error::{reject_too_big_request, ErrorCode},
18 Id,
19};
20use soketto::connection::Error as SokettoError;
21use tokio::{
22 sync::{mpsc, oneshot, Semaphore},
23 time::timeout,
24};
25use tokio_stream::wrappers::ReceiverStream;
26use tokio_util::compat::Compat;
27
28pub(crate) type Sender = soketto::Sender<BufReader<BufWriter<Compat<TokioIo<Upgraded>>>>>;
29pub(crate) type Receiver = soketto::Receiver<BufReader<BufWriter<Compat<TokioIo<Upgraded>>>>>;
30
31pub use soketto::handshake::http::is_upgrade_request;
32
33const MAX_MESSAGES_PER_FLUSH: usize = 64;
36
37pub(crate) struct BackgroundTaskParams<S> {
38 pub(crate) server_cfg: ServerConfig,
39 pub(crate) conn: ConnectionState,
40 pub(crate) ws_sender: Sender,
41 pub(crate) ws_receiver: Receiver,
42 pub(crate) rpc_service: S,
43 pub(crate) sink: MethodSink,
44 pub(crate) rx: mpsc::Receiver<String>,
45 pub(crate) pending_calls: mpsc::Sender<()>,
47 pub(crate) pending_calls_completed: mpsc::Receiver<()>,
48}
49
50pub(crate) async fn background_task<S>(params: BackgroundTaskParams<S>)
51where
52 S: RpcServiceT + Send + Sync + 'static,
53{
54 let BackgroundTaskParams {
55 server_cfg,
56 conn,
57 ws_sender,
58 ws_receiver,
59 rpc_service,
60 sink,
61 rx,
62 pending_calls,
63 pending_calls_completed,
64 } = params;
65 let ServerConfig {
66 batch_requests_config,
67 max_request_body_size,
68 write_timeout,
69 message_buffer_capacity,
70 ..
71 } = server_cfg;
72
73 let (conn_tx, conn_rx) = oneshot::channel();
74
75 let send_task_handle = tokio::spawn(send_task(rx, ws_sender, write_timeout, conn_rx));
77
78 let stopped = conn.stop_handle.clone().shutdown();
79 let rpc_service = Arc::new(rpc_service);
80
81 tokio::pin!(stopped);
82
83 let ws_stream = futures_util::stream::unfold(ws_receiver, |mut receiver| async {
84 let mut data = Vec::new();
85 loop {
86 match receiver.receive(&mut data).await {
87 Ok(soketto::Incoming::Data(_)) => return Some((Ok(data), receiver)),
88 Ok(soketto::Incoming::Pong(_)) => data.clear(),
90 Ok(soketto::Incoming::Closed(_)) | Err(SokettoError::Closed) => return None,
91 Err(e) => return Some((Err(e), receiver)),
95 }
96 }
97 })
98 .fuse();
99
100 tokio::pin!(ws_stream);
101
102 let call_permits = Arc::new(Semaphore::new(message_buffer_capacity as usize));
103
104 let result = loop {
105 let receive = tokio::select! {
106 receive = try_recv(&mut ws_stream, stopped) => receive,
107 _ = sink.closed() => break Ok(Shutdown::ConnectionClosed),
110 };
111 let data = match receive {
112 Receive::ConnectionClosed => break Ok(Shutdown::ConnectionClosed),
113 Receive::Stopped => break Ok(Shutdown::Stopped),
114 Receive::Ok(data, stop) => {
115 stopped = stop;
116 data
117 }
118 Receive::Err(err, stop) => {
119 stopped = stop;
120
121 match err {
122 SokettoError::Closed => {
123 break Ok(Shutdown::ConnectionClosed);
124 }
125 SokettoError::MessageTooLarge { current, maximum } => {
126 tracing::debug!(
127 target: LOG_TARGET,
128 "WS recv error: message too large current={}/max={}",
129 current,
130 maximum
131 );
132 if sink
133 .send_error(Id::Null, reject_too_big_request(max_request_body_size))
134 .await
135 .is_err()
136 {
137 break Ok(Shutdown::ConnectionClosed);
138 }
139
140 continue;
141 }
142 err => {
143 tracing::debug!(target: LOG_TARGET, "WS error: {}; terminate connection: {}", err, conn.conn_id);
144 break Err(err);
145 }
146 };
147 }
148 };
149
150 let permit = tokio::select! {
157 permit = call_permits.clone().acquire_owned() => permit,
158 _ = &mut stopped => break Ok(Shutdown::Stopped),
159 };
160 let Ok(permit) = permit else { break Ok(Shutdown::ConnectionClosed) };
162
163 let rpc_service = rpc_service.clone();
164 let sink = sink.clone();
165 let pending_calls = pending_calls.clone();
166
167 tokio::spawn(async move {
168 let _pending_calls = pending_calls;
169 let _permit = permit;
170 let Some((data, is_single)) = classify_message(&data) else {
171 _ = sink.send_error(Id::Null, ErrorCode::ParseError.into()).await;
172 return;
173 };
174
175 let rp = tokio::select! {
178 biased;
181 rp = handle_rpc_call(data, is_single, batch_requests_config, &*rpc_service) => rp,
182 _ = sink.closed() => return,
183 };
184
185 if rp.is_method_call() || rp.is_batch() {
188 let _ = sink.send(rp.into_json()).await;
190 }
191 });
192 };
193
194 drop(rpc_service);
198 drop(pending_calls);
199 graceful_shutdown(result, pending_calls_completed, ws_stream, conn_tx, send_task_handle).await;
200
201 drop(conn);
202}
203
204async fn send_task(
206 mut rx: mpsc::Receiver<String>,
207 mut ws_sender: Sender,
208 write_timeout: Duration,
209 mut stop: oneshot::Receiver<()>,
210) {
211 let mut write_timed_out = false;
212
213 loop {
214 let response = tokio::select! {
215 biased;
219 _ = &mut stop, if !rx.is_closed() => {
220 rx.close();
224 continue;
225 }
226 response = rx.recv() => response,
227 };
228 let Some(response) = response else { break };
230
231 let mut result = timeout(write_timeout, ws_sender.send_text_owned(response)).await;
232 let mut written = 1;
234 while matches!(result, Ok(Ok(()))) && written < MAX_MESSAGES_PER_FLUSH {
235 let Ok(response) = rx.try_recv() else { break };
236 result = timeout(write_timeout, ws_sender.send_text_owned(response)).await;
237 written += 1;
238 }
239 if matches!(result, Ok(Ok(()))) {
240 result = timeout(write_timeout, ws_sender.flush()).await;
241 }
242
243 match result {
245 Ok(Ok(())) => {}
246 Ok(Err(err)) => {
247 tracing::debug!(target: LOG_TARGET, "WS send error: {}", err);
248 break;
249 }
250 Err(_) => {
251 tracing::debug!(target: LOG_TARGET, "WS send timed out after {write_timeout:?}");
252 write_timed_out = true;
253 break;
254 }
255 }
256 }
257
258 rx.close();
261
262 if !write_timed_out {
266 let _ = timeout(write_timeout, ws_sender.close()).await;
267 }
268}
269
270enum Receive<S> {
271 ConnectionClosed,
272 Stopped,
273 Err(SokettoError, S),
274 Ok(Vec<u8>, S),
275}
276
277async fn try_recv<T, S>(ws_stream: &mut T, stopped: S) -> Receive<S>
279where
280 S: Future<Output = ()> + Unpin,
281 T: StreamExt<Item = Result<Vec<u8>, SokettoError>> + Unpin,
282{
283 match future::select(stopped, ws_stream.next()).await {
287 Either::Left(_) => Receive::Stopped,
289 Either::Right((None, _)) => Receive::ConnectionClosed,
291 Either::Right((Some(Ok(data)), stopped)) => Receive::Ok(data, stopped),
293 Either::Right((Some(Err(e)), stopped)) => Receive::Err(e, stopped),
295 }
296}
297
298#[derive(Debug, Copy, Clone)]
299pub(crate) enum Shutdown {
300 Stopped,
301 ConnectionClosed,
302}
303
304async fn graceful_shutdown<S>(
309 result: Result<Shutdown, SokettoError>,
310 pending_calls: mpsc::Receiver<()>,
311 ws_stream: S,
312 mut conn_tx: oneshot::Sender<()>,
313 send_task_handle: tokio::task::JoinHandle<()>,
314) where
315 S: StreamExt<Item = Result<Vec<u8>, SokettoError>> + Unpin,
316{
317 let pending_calls = ReceiverStream::new(pending_calls);
318
319 if let Ok(Shutdown::Stopped) = result {
320 let graceful_shutdown = pending_calls.for_each(|_| async {});
321 let disconnect = ws_stream.try_for_each(|_| async { Ok(()) });
322
323 tokio::select! {
324 _ = graceful_shutdown => {}
325 res = disconnect => {
326 if let Err(err) = res {
327 tracing::warn!(target: LOG_TARGET, "Graceful shutdown terminated because of error: `{err}`");
328 }
329 }
330 _ = conn_tx.closed() => {}
331 }
332 }
333
334 _ = conn_tx.send(());
336 _ = send_task_handle.await;
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use futures_util::stream;
344
345 #[tokio::test]
346 async fn try_recv_observes_stop_while_input_stays_ready() {
347 let mut always_ready = stream::repeat_with(|| Ok::<_, SokettoError>(b"{}".to_vec()));
349
350 let received = try_recv(&mut always_ready, future::ready(())).await;
351 assert!(matches!(received, Receive::Stopped));
352 }
353}