1use std::{
28 borrow::Cow,
29 future::Future,
30 io,
31 pin::{pin, Pin},
32 sync::Arc,
33 task::{Context, Poll},
34 time::Duration,
35};
36
37use crate::{ConnectionState, HttpBody};
38
39use futures_util::{future, ready};
40use hyper::body::{Bytes, Frame};
41use hyper_util::rt::{TokioExecutor, TokioIo};
42use jsonrpsee_core::{middleware::Notification, BoxError, JsonRawValue};
43use jsonrpsee_types::TwoPointZero;
44use pin_project::pin_project;
45use serde::{Deserialize, Deserializer};
46use tokio::{
47 io::{AsyncRead, AsyncWrite, ReadBuf},
48 sync::Notify,
49 time::Sleep,
50};
51
52const BODY_CHUNK_SIZE: usize = 64 * 1024;
54
55pub(crate) const DEFAULT_FIRST_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
57
58const ACCEPT_ERROR_BACKOFF: Duration = Duration::from_secs(1);
60
61pub(crate) const DEFAULT_WRITE_TIMEOUT: Duration = Duration::from_secs(10);
64
65#[derive(Debug, Clone)]
67pub(crate) struct NotifyOnRequest<S> {
68 service: S,
69 request_received: Arc<Notify>,
70}
71
72impl<S> NotifyOnRequest<S> {
73 pub(crate) fn new(service: S, request_received: Arc<Notify>) -> Self {
74 Self { service, request_received }
75 }
76}
77
78impl<S, R> hyper::service::Service<R> for NotifyOnRequest<S>
79where
80 S: hyper::service::Service<R>,
81{
82 type Response = S::Response;
83 type Error = S::Error;
84 type Future = S::Future;
85
86 fn call(&self, request: R) -> Self::Future {
87 self.request_received.notify_one();
88 self.service.call(request)
89 }
90}
91
92pub(crate) async fn no_request_within(
94 request_received: &Notify,
95 timeout: Option<Duration>,
96) -> BoxError {
97 if let Some(timeout) = timeout &&
98 tokio::time::timeout(timeout, request_received.notified()).await.is_err()
99 {
100 return io::Error::new(io::ErrorKind::TimedOut, "no request received in time").into();
101 }
102
103 future::pending().await
104}
105
106pub async fn serve_with_graceful_shutdown<S, B, I>(
111 io: I,
112 service: S,
113 stopped: impl Future<Output = ()>,
114) -> Result<(), BoxError>
115where
116 S: tower::Service<http::Request<hyper::body::Incoming>, Response = http::Response<B>>
117 + Clone
118 + Send
119 + 'static,
120 S::Future: Send,
121 S::Response: Send,
122 S::Error: Into<BoxError>,
123 B: http_body::Body<Data = hyper::body::Bytes> + Send + 'static,
124 B::Error: Into<BoxError>,
125 I: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin + 'static,
126{
127 let request_received = Arc::new(Notify::new());
128 let service = NotifyOnRequest::new(
129 hyper_util::service::TowerToHyperService::new(service),
130 request_received.clone(),
131 );
132 let io = TokioIo::new(io);
133
134 let builder = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new());
135 let conn = builder.serve_connection_with_upgrades(io, service);
136
137 tokio::pin!(stopped, conn);
138
139 tokio::select! {
140 res = &mut conn => res,
142 _ = &mut stopped => {
145 conn.as_mut().graceful_shutdown();
146 conn.await
147 }
148 err = no_request_within(&request_received, Some(DEFAULT_FIRST_REQUEST_TIMEOUT)) => Err(err),
149 }
150}
151
152pub(crate) fn deserialize_notification(data: &[u8]) -> Result<Notification<'_>, serde_json::Error> {
154 let notif = serde_json::from_slice::<NotificationWithoutId<'_>>(data)?;
155 Ok(Notification::new(notif.method, notif.params.map(Cow::Borrowed)))
156}
157
158#[derive(Deserialize)]
159struct NotificationWithoutId<'a> {
160 #[serde(rename = "jsonrpc")]
161 _jsonrpc: TwoPointZero,
162 #[serde(borrow)]
163 method: Cow<'a, str>,
164 #[serde(borrow)]
165 params: Option<&'a JsonRawValue>,
166 #[serde(default, rename = "id", deserialize_with = "reject_notification_id")]
167 _id: (),
168}
169
170fn reject_notification_id<'de, D: Deserializer<'de>>(_: D) -> Result<(), D::Error> {
171 Err(serde::de::Error::custom("notification must not contain an id"))
172}
173
174pub async fn accept_error_backoff<S>(err: &io::Error, stopped: S) -> Option<S>
183where
184 S: Future + Unpin,
185{
186 if matches!(
187 err.kind(),
188 io::ErrorKind::ConnectionRefused |
189 io::ErrorKind::ConnectionAborted |
190 io::ErrorKind::ConnectionReset
191 ) {
192 return Some(stopped);
193 }
194
195 match future::select(pin!(tokio::time::sleep(ACCEPT_ERROR_BACKOFF)), stopped).await {
196 future::Either::Left((_, stopped)) => Some(stopped),
197 future::Either::Right(_) => None,
198 }
199}
200
201#[pin_project]
211pub(crate) struct PermitBody {
212 #[pin]
213 inner: HttpBody,
214 pending: Bytes,
216 _conn: ConnectionState,
217}
218
219impl PermitBody {
220 pub(crate) fn new(inner: HttpBody, conn: ConnectionState) -> Self {
221 Self { inner, pending: Bytes::new(), _conn: conn }
222 }
223}
224
225impl http_body::Body for PermitBody {
226 type Data = Bytes;
227 type Error = BoxError;
228
229 fn poll_frame(
230 self: Pin<&mut Self>,
231 cx: &mut Context<'_>,
232 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
233 let this = self.project();
234
235 if this.pending.is_empty() {
236 match ready!(this.inner.poll_frame(cx)) {
237 Some(Ok(frame)) => match frame.into_data() {
238 Ok(data) => *this.pending = data,
239 Err(frame) => return Poll::Ready(Some(Ok(frame))),
240 },
241 end_or_error => return Poll::Ready(end_or_error),
242 }
243 }
244
245 let chunk = this.pending.split_to(this.pending.len().min(BODY_CHUNK_SIZE));
246 Poll::Ready(Some(Ok(Frame::data(chunk))))
247 }
248
249 fn is_end_stream(&self) -> bool {
250 self.pending.is_empty() && self.inner.is_end_stream()
251 }
252
253 fn size_hint(&self) -> http_body::SizeHint {
254 let pending = self.pending.len() as u64;
255 let mut hint = self.inner.size_hint();
256 hint.set_lower(hint.lower() + pending);
257 if let Some(upper) = hint.upper() {
258 hint.set_upper(upper + pending);
259 }
260 hint
261 }
262}
263
264#[pin_project]
270pub(crate) struct WriteTimeoutIo<T> {
271 #[pin]
272 inner: T,
273 timeout: Duration,
274 stalled: Option<Pin<Box<Sleep>>>,
276}
277
278impl<T> WriteTimeoutIo<T> {
279 pub(crate) fn new(inner: T, timeout: Duration) -> Self {
280 Self { inner, timeout, stalled: None }
281 }
282}
283
284impl<T: AsyncRead> AsyncRead for WriteTimeoutIo<T> {
285 fn poll_read(
286 self: Pin<&mut Self>,
287 cx: &mut Context<'_>,
288 buf: &mut ReadBuf<'_>,
289 ) -> Poll<io::Result<()>> {
290 self.project().inner.poll_read(cx, buf)
291 }
292}
293
294impl<T: AsyncWrite> AsyncWrite for WriteTimeoutIo<T> {
295 fn poll_write(
296 self: Pin<&mut Self>,
297 cx: &mut Context<'_>,
298 buf: &[u8],
299 ) -> Poll<io::Result<usize>> {
300 let this = self.project();
301 let poll = this.inner.poll_write(cx, buf);
302 poll_write_timeout(this.stalled, *this.timeout, cx, poll)
303 }
304
305 fn poll_write_vectored(
306 self: Pin<&mut Self>,
307 cx: &mut Context<'_>,
308 bufs: &[io::IoSlice<'_>],
309 ) -> Poll<io::Result<usize>> {
310 let this = self.project();
311 let poll = this.inner.poll_write_vectored(cx, bufs);
312 poll_write_timeout(this.stalled, *this.timeout, cx, poll)
313 }
314
315 fn is_write_vectored(&self) -> bool {
316 self.inner.is_write_vectored()
317 }
318
319 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
320 self.project().inner.poll_flush(cx)
321 }
322
323 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
324 self.project().inner.poll_shutdown(cx)
325 }
326}
327
328fn poll_write_timeout(
331 stalled: &mut Option<Pin<Box<Sleep>>>,
332 timeout: Duration,
333 cx: &mut Context<'_>,
334 poll: Poll<io::Result<usize>>,
335) -> Poll<io::Result<usize>> {
336 if poll.is_ready() {
337 *stalled = None;
338 return poll;
339 }
340
341 let stalled = stalled.get_or_insert_with(|| Box::pin(tokio::time::sleep(timeout)));
342 match stalled.as_mut().poll(cx) {
343 Poll::Ready(()) => Poll::Ready(Err(io::ErrorKind::TimedOut.into())),
344 Poll::Pending => Poll::Pending,
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351 use tokio::time::Instant;
352
353 #[test]
354 fn notification_id_classification_notification_metadata() {
355 for (raw, expected) in [
356 (
357 r#"{"jsonrpc":"2.0","method":"example","params":[1],"extra":{"id":true}}"#,
358 Some("[1]"),
359 ),
360 (r#"{"jsonrpc":"2.0","method":"example"}"#, None),
361 ] {
362 let notification = deserialize_notification(raw.as_bytes()).unwrap();
363 assert_eq!(notification.method_name(), "example");
364 assert_eq!(notification.params().as_ref().map(|p| p.get()), expected);
365 assert!(!matches!(notification.params(), Some(Cow::Owned(_))));
366 }
367 for id in ["true", "false", "{}", "[]", "null", "0", "\"valid\""] {
368 let raw = format!(r#"{{"jsonrpc":"2.0","method":"example","id":{id}}}"#);
369 assert!(deserialize_notification(raw.as_bytes()).is_err());
370 }
371 }
372
373 #[tokio::test(start_paused = true)]
374 async fn accept_error_backoff_skips_connection_errors() {
375 for kind in [
376 io::ErrorKind::ConnectionRefused,
377 io::ErrorKind::ConnectionAborted,
378 io::ErrorKind::ConnectionReset,
379 ] {
380 let start = Instant::now();
381 let stopped =
382 accept_error_backoff(&io::Error::from(kind), future::pending::<()>()).await;
383 assert!(stopped.is_some());
384 assert_eq!(start.elapsed(), Duration::ZERO);
385 }
386 }
387
388 #[tokio::test(start_paused = true)]
389 async fn accept_error_backoff_sleeps_on_persistent_errors() {
390 let start = Instant::now();
391 let err = io::Error::other("too many open files");
392 assert!(accept_error_backoff(&err, future::pending::<()>()).await.is_some());
393 assert_eq!(start.elapsed(), ACCEPT_ERROR_BACKOFF);
394 }
395
396 #[tokio::test(start_paused = true)]
397 async fn accept_error_backoff_observes_stop() {
398 let start = Instant::now();
399 let err = io::Error::other("out of descriptors");
400 assert!(accept_error_backoff(&err, future::ready(())).await.is_none());
401 assert_eq!(start.elapsed(), Duration::ZERO);
402 }
403}