Skip to main content

jsonrpsee_core/middleware/
mod.rs

1//! Middleware for the RPC service.
2
3pub mod layer;
4
5use std::{
6    borrow::Cow,
7    future::Future,
8    pin::Pin,
9    task::{Context, Poll},
10};
11
12use crate::server::MethodResponse;
13use futures_util::future::Either;
14use jsonrpsee_types::{ErrorObject, Id};
15use pin_project::pin_project;
16use serde_json::value::RawValue;
17use tower::layer::{
18    util::{Identity, Stack},
19    LayerFn,
20};
21
22/// Re-export types from `jsonrpsee_types` crate for convenience.
23pub type Notification<'a> = jsonrpsee_types::Notification<'a, Option<Cow<'a, RawValue>>>;
24/// Re-export types from `jsonrpsee_types` crate for convenience.
25pub use jsonrpsee_types::Request;
26
27/// Error response that can used to indicate an error in JSON-RPC request batch request.
28/// This is used in the [`Batch`] type to indicate an error in the batch entry.
29#[derive(Debug)]
30pub struct BatchEntryErr<'a> {
31    id: Id<'a>,
32    err: ErrorObject<'a>,
33}
34
35impl<'a> BatchEntryErr<'a> {
36    /// Create a new error response.
37    pub const fn new(id: Id<'a>, err: ErrorObject<'a>) -> Self {
38        Self { id, err }
39    }
40
41    /// Get the parts of the error response.
42    pub fn into_parts(self) -> (ErrorObject<'a>, Id<'a>) {
43        (self.err, self.id)
44    }
45}
46
47/// A batch of JSON-RPC requests.
48#[derive(Debug)]
49pub struct Batch<'a> {
50    inner: Vec<Result<BatchEntry<'a>, BatchEntryErr<'a>>>,
51}
52
53impl<'a> IntoIterator for Batch<'a> {
54    type Item = Result<BatchEntry<'a>, BatchEntryErr<'a>>;
55    type IntoIter = std::vec::IntoIter<Self::Item>;
56
57    fn into_iter(self) -> Self::IntoIter {
58        self.inner.into_iter()
59    }
60}
61
62impl<'a> Batch<'a> {
63    /// Create a new batch from a vector of batch entries.
64    pub fn from(entries: Vec<Result<BatchEntry<'a>, BatchEntryErr<'a>>>) -> Self {
65        Self { inner: entries }
66    }
67
68    /// Get the length of the batch.
69    pub fn len(&self) -> usize {
70        self.inner.len()
71    }
72
73    /// Returns whether the batch is empty.
74    pub fn is_empty(&self) -> bool {
75        self.inner.is_empty()
76    }
77
78    /// Get an iterator over the batch.
79    pub fn iter(&self) -> impl Iterator<Item = &Result<BatchEntry<'a>, BatchEntryErr<'a>>> {
80        self.inner.iter()
81    }
82
83    /// Get a mutable iterator over the batch.
84    pub fn iter_mut(
85        &mut self,
86    ) -> impl Iterator<Item = &mut Result<BatchEntry<'a>, BatchEntryErr<'a>>> {
87        self.inner.iter_mut()
88    }
89}
90
91/// A batch entry specific for the [`RpcServiceT::batch`] method to support both
92/// method calls and notifications.
93#[derive(Debug, Clone)]
94pub enum BatchEntry<'a> {
95    /// A regular JSON-RPC call.
96    Call(Request<'a>),
97    /// A JSON-RPC notification.
98    Notification(Notification<'a>),
99}
100
101impl<'a> BatchEntry<'a> {
102    /// Get the method name of the batch entry.
103    pub fn method_name(&self) -> &str {
104        match self {
105            BatchEntry::Call(req) => req.method_name(),
106            BatchEntry::Notification(n) => n.method_name(),
107        }
108    }
109
110    /// Get the params of the batch entry (may be empty).
111    pub fn params(&self) -> Option<&Cow<'a, RawValue>> {
112        match self {
113            BatchEntry::Call(req) => req.params.as_ref(),
114            BatchEntry::Notification(n) => n.params.as_ref(),
115        }
116    }
117}
118
119/// Represent a JSON-RPC service that can process JSON-RPC calls, notifications, and batch requests.
120///
121/// This trait is similar to [`tower::Service`] but it's specialized for JSON-RPC operations.
122pub trait RpcServiceT {
123    /// Processes a single JSON-RPC call, which may be a subscription or regular call.
124    fn call<'a>(&self, request: Request<'a>) -> impl Future<Output = MethodResponse> + Send + 'a;
125
126    /// Processes multiple JSON-RPC calls at once, similar to `RpcServiceT::call`.
127    ///
128    /// This method wraps `RpcServiceT::call` and `RpcServiceT::notification`,
129    /// but the root RPC service does not inherently recognize custom implementations
130    /// of these methods.
131    ///
132    /// As a result, if you have custom logic for individual calls or notifications,
133    /// you must duplicate that implementation in this method or no middleware will be applied
134    /// for calls inside the batch.
135    fn batch<'a>(&self, requests: Batch<'a>) -> impl Future<Output = MethodResponse> + Send + 'a;
136
137    /// Similar to `RpcServiceT::call` but processes a JSON-RPC notification.
138    fn notification<'a>(
139        &self,
140        n: Notification<'a>,
141    ) -> impl Future<Output = MethodResponse> + Send + 'a;
142}
143
144/// Similar to [`tower::ServiceBuilder`] but doesn't
145/// support any tower middleware implementations.
146#[derive(Debug, Clone)]
147pub struct RpcServiceBuilder<L>(tower::ServiceBuilder<L>);
148
149impl Default for RpcServiceBuilder<Identity> {
150    fn default() -> Self {
151        RpcServiceBuilder(tower::ServiceBuilder::new())
152    }
153}
154
155impl RpcServiceBuilder<Identity> {
156    /// Create a new [`RpcServiceBuilder`].
157    pub fn new() -> Self {
158        Self(tower::ServiceBuilder::new())
159    }
160}
161
162impl<L> RpcServiceBuilder<L> {
163    /// Optionally add a new layer `T` to the [`RpcServiceBuilder`].
164    ///
165    /// See the documentation for [`tower::ServiceBuilder::option_layer`] for more details.
166    pub fn option_layer<T>(
167        self,
168        layer: Option<T>,
169    ) -> RpcServiceBuilder<Stack<layer::Either<T, Identity>, L>> {
170        let layer = if let Some(layer) = layer {
171            layer::Either::Left(layer)
172        } else {
173            layer::Either::Right(Identity::new())
174        };
175        RpcServiceBuilder(self.0.layer(layer))
176    }
177
178    /// Add a new layer `T` to the [`RpcServiceBuilder`].
179    ///
180    /// See the documentation for [`tower::ServiceBuilder::layer`] for more details.
181    pub fn layer<T>(self, layer: T) -> RpcServiceBuilder<Stack<T, L>> {
182        RpcServiceBuilder(self.0.layer(layer))
183    }
184
185    /// Add a [`tower::Layer`] built from a function that accepts a service and returns another
186    /// service.
187    ///
188    /// See the documentation for [`tower::ServiceBuilder::layer_fn`] for more details.
189    pub fn layer_fn<F>(self, f: F) -> RpcServiceBuilder<Stack<LayerFn<F>, L>> {
190        RpcServiceBuilder(self.0.layer_fn(f))
191    }
192
193    /// Wrap the service `S` with the middleware.
194    pub fn service<S>(&self, service: S) -> L::Service
195    where
196        L: tower::Layer<S>,
197    {
198        self.0.service(service)
199    }
200}
201
202/// Response which may be ready or a future.
203#[derive(Debug)]
204#[pin_project]
205pub struct ResponseFuture<F, R>(#[pin] futures_util::future::Either<F, std::future::Ready<R>>);
206
207impl<F, R> ResponseFuture<F, R> {
208    /// Returns a future that resolves to a response.
209    pub fn future(f: F) -> ResponseFuture<F, R> {
210        ResponseFuture(Either::Left(f))
211    }
212
213    /// Return a response which is already computed.
214    pub fn ready(response: R) -> ResponseFuture<F, R> {
215        ResponseFuture(Either::Right(std::future::ready(response)))
216    }
217}
218
219impl<F, R> Future for ResponseFuture<F, R>
220where
221    F: Future<Output = R>,
222{
223    type Output = F::Output;
224
225    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
226        match self.project().0.poll(cx) {
227            Poll::Ready(rp) => Poll::Ready(rp),
228            Poll::Pending => Poll::Pending,
229        }
230    }
231}