casper_json_rpc/
request_handlers.rs

1use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc};
2
3use futures::FutureExt;
4use serde::Serialize;
5use serde_json::Value;
6use tracing::{debug, error};
7
8use crate::{
9    error::{Error, ReservedErrorCode},
10    request::{Params, Request},
11    response::Response,
12};
13
14/// A boxed future of `Result<Value, Error>`; the return type of a request-handling closure.
15type HandleRequestFuture = Pin<Box<dyn Future<Output = Result<Value, Error>> + Send>>;
16/// A request-handling closure.
17type RequestHandler = Arc<dyn Fn(Option<Params>) -> HandleRequestFuture + Send + Sync>;
18
19/// A collection of request-handlers, indexed by the JSON-RPC "method" applicable to each.
20///
21/// There needs to be a unique handler for each JSON-RPC request "method" to be handled.  Handlers
22/// are added via a [`RequestHandlersBuilder`].
23#[derive(Clone)]
24pub struct RequestHandlers(Arc<HashMap<&'static str, RequestHandler>>);
25
26impl RequestHandlers {
27    /// Finds the relevant handler for the given request's "method" field, and invokes it with the
28    /// given "params" value.
29    ///
30    /// If a handler cannot be found, a MethodNotFound error is created.  In this case, or if
31    /// invoking the handler yields an [`Error`], the error is converted into a
32    /// [`Response::Failure`].
33    ///
34    /// Otherwise a [`Response::Success`] is returned.
35    pub(crate) async fn handle_request(&self, request: Request) -> Response {
36        let handler = match self.0.get(request.method.as_str()) {
37            Some(handler) => Arc::clone(handler),
38            None => {
39                debug!(requested_method = %request.method.as_str(), "failed to get handler");
40                let error = Error::new(
41                    ReservedErrorCode::MethodNotFound,
42                    format!(
43                        "'{}' is not a supported json-rpc method on this server",
44                        request.method.as_str()
45                    ),
46                );
47                return Response::new_failure(request.id, error);
48            }
49        };
50
51        match handler(request.params).await {
52            Ok(result) => Response::new_success(request.id, result),
53            Err(error) => Response::new_failure(request.id, error),
54        }
55    }
56}
57
58/// A builder for [`RequestHandlers`].
59//
60// This builder exists so the internal `HashMap` can be populated before it is made immutable behind
61// the `Arc` in the `RequestHandlers`.
62#[derive(Default)]
63pub struct RequestHandlersBuilder(HashMap<&'static str, RequestHandler>);
64
65impl RequestHandlersBuilder {
66    /// Returns a new builder.
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    /// Adds a new request-handler which will be called to handle all JSON-RPC requests with the
72    /// given "method" field.
73    ///
74    /// The handler should be an async closure or function with a signature like:
75    /// ```ignore
76    /// async fn handle_it(params: Option<Params>) -> Result<T, Error>
77    /// ```
78    /// where `T` implements `Serialize` and will be used as the JSON-RPC response's "result" field.
79    pub fn register_handler<Func, Fut, T>(&mut self, method: &'static str, handler: Arc<Func>)
80    where
81        Func: Fn(Option<Params>) -> Fut + Send + Sync + 'static,
82        Fut: Future<Output = Result<T, Error>> + Send,
83        T: Serialize + 'static,
84    {
85        let handler = Arc::clone(&handler);
86        // The provided handler returns a future with output of `Result<T, Error>`. We need to
87        // convert that to a boxed future with output `Result<Value, Error>` to store it in a
88        // homogenous collection.
89        let wrapped_handler = move |maybe_params| {
90            let handler = Arc::clone(&handler);
91            async move {
92                let success = Arc::clone(&handler)(maybe_params).await?;
93                serde_json::to_value(success).map_err(|error| {
94                    error!(%error, "failed to encode json-rpc response value");
95                    Error::new(
96                        ReservedErrorCode::InternalError,
97                        format!("failed to encode json-rpc response value: {}", error),
98                    )
99                })
100            }
101            .boxed()
102        };
103        if self.0.insert(method, Arc::new(wrapped_handler)).is_some() {
104            error!(
105                method,
106                "already registered a handler for this json-rpc request method"
107            );
108        }
109    }
110
111    /// Finalize building by converting `self` to a [`RequestHandlers`].
112    pub fn build(self) -> RequestHandlers {
113        RequestHandlers(Arc::new(self.0))
114    }
115}