json_rpc2/
lib.rs

1#![deny(missing_docs)]
2#![allow(clippy::borrowed_box)]
3//! Simple, robust and pragmatic facade for JSON-RPC2 services that is transport agnostic.
4//!
5//! ```
6//! use json_rpc2::*;
7//! use serde_json::Value;
8//!
9//! struct ServiceHandler;
10//! impl Service for ServiceHandler {
11//!    type Data = ();
12//!    fn handle(&self, request: &Request, _ctx: &Self::Data) -> Result<Option<Response>> {
13//!        let response = match request.method() {
14//!          "hello" => {
15//!            let params: String = request.deserialize()?;
16//!            let message = format!("Hello, {}!", params);
17//!            Some((request, Value::String(message)).into())
18//!          }
19//!          _ => None
20//!        };
21//!        Ok(response)
22//!    }
23//! }
24//!
25//! fn main() -> Result<()> {
26//!    let service: Box<dyn Service<Data = ()>> = Box::new(ServiceHandler {});
27//!    let request = Request::new_reply(
28//!        "hello", Some(Value::String("world".to_string())));
29//!    let server = Server::new(vec![&service]);
30//!    let response = server.serve(&request, &());
31//!    assert_eq!(
32//!        Some(Value::String("Hello, world!".to_string())),
33//!        response.unwrap().into());
34//!    Ok(())
35//! }
36//! ```
37//!
38//! ## Parsing
39//!
40//! When converting from incoming payloads use the `from_*` functions
41//! to convert JSON to a [Request](Request) so that errors are mapped correctly.
42//!
43//! ## Context
44//!
45//! For most applications user data can be assigned to the struct that implements
46//! the `Service` trait but sometimes you may need to serve requests from a callback
47//! function that passes useful information you want to expose to the service
48//! methods. Use `Data = T` with a custom type to expose user data to your handlers
49//! that is not available when the services are created.
50//!
51//! ## Async
52//!
53//! For nonblocking support enable the `async` feature and use the `Service`
54//! trait from the `futures` module. You will also need to depend upon the
55//! [async-trait](https://docs.rs/async-trait/0.1.42/async_trait/) crate and
56//! use the `#[async_trait]` attribute macro on your service implementation.
57//!
58//! See the `async` example for usage.
59//!
60
61#[cfg(any(test, feature = "async"))]
62pub mod futures;
63
64use rand::Rng;
65use serde::{de::DeserializeOwned, Deserialize, Serialize};
66use serde_json::{Number, Value};
67
68const VERSION: &str = "2.0";
69const INVALID_REQUEST: isize = -32600;
70const METHOD_NOT_FOUND: isize = -32601;
71const INVALID_PARAMS: isize = -32602;
72const INTERNAL_ERROR: isize = -32603;
73const PARSE_ERROR: isize = -32700;
74
75/// Result type for service handler functions and internal library errors.
76pub type Result<T> = std::result::Result<T, Error>;
77
78/// Enumeration of errors.
79#[derive(Debug, thiserror::Error)]
80pub enum Error {
81    /// Error generated when a JSON payload cannot be parsed.
82    #[error("Parsing failed, invalid JSON data")]
83    Parse {
84        /// The underlying JSON error message.
85        data: String,
86    },
87    /// Error generated when the contents of a JSON payload do not
88    /// match the request type semantics.
89    #[error("Invalid JSON-RPC request")]
90    InvalidRequest {
91        /// The underlying JSON error message.
92        data: String,
93    },
94
95    /// Error generated when the request method name did not
96    /// match any services.
97    #[error("Service method not found: {name}")]
98    MethodNotFound {
99        /// The id of the request message.
100        id: Option<Value>,
101        /// The name of the request method.
102        name: String,
103    },
104
105    /// Error generated when request parameters cannot be converted
106    /// to the expected type.
107    #[error("Message parameters are invalid")]
108    InvalidParams {
109        /// The id of the request message.
110        id: Option<Value>,
111        /// The underlying JSON error message.
112        data: String,
113    },
114
115    /// Generic error type converted to an internal error response.
116    #[error(transparent)]
117    Boxed(#[from] Box<dyn std::error::Error + Send + Sync>),
118}
119
120impl<'a> From<&'a Error> for (isize, Option<String>) {
121    fn from(error: &'a Error) -> Self {
122        match error {
123            Error::MethodNotFound { .. } => (METHOD_NOT_FOUND, None),
124            Error::InvalidParams { data, .. } => {
125                (INVALID_PARAMS, Some(data.to_string()))
126            }
127            Error::Parse { data } => (PARSE_ERROR, Some(data.to_string())),
128            Error::InvalidRequest { data } => {
129                (INVALID_REQUEST, Some(data.to_string()))
130            }
131            _ => (INTERNAL_ERROR, None),
132        }
133    }
134}
135
136impl<'a> From<(&'a mut Request, &'a str)> for Error {
137    fn from(value: (&'a mut Request, &'a str)) -> Error {
138        Error::from((value.0, value.1.to_string()))
139    }
140}
141
142impl<'a> From<(&'a mut Request, String)> for Error {
143    fn from(value: (&'a mut Request, String)) -> Error {
144        Error::InvalidParams {
145            id: value.0.id().clone(),
146            data: value.1,
147        }
148    }
149}
150
151/// Error information for response messages.
152#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
153pub struct RpcError {
154    /// The error code.
155    pub code: isize,
156    /// The error message.
157    pub message: String,
158    /// Additional data for the error, typically an underlying
159    /// cause for the error.
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub data: Option<String>,
162}
163
164impl RpcError {
165    /// Create a new JSON-RPC internal error.
166    pub fn new(message: String, data: Option<String>) -> Self {
167        Self {
168            code: INTERNAL_ERROR,
169            message,
170            data,
171        }
172    }
173}
174
175/// Trait for services that maybe handle a request.
176pub trait Service: Send + Sync {
177    /// Type of the user data for this service.
178    type Data;
179
180    /// Service implementations are invoked with a request
181    /// and should reply with a response if the method name
182    /// is one handled by the service.
183    ///
184    /// If the method name for the request is not handled by the service
185    /// it should return `None`.
186    fn handle(
187        &self,
188        request: &Request,
189        ctx: &Self::Data,
190    ) -> Result<Option<Response>>;
191}
192
193/// Serve requests.
194///
195/// Requests are passed to each service in turn and the first service
196/// that returns a response wins.
197pub struct Server<'a, T> {
198    /// Services that the server should invoke for every request.
199    services: Vec<&'a Box<dyn Service<Data = T>>>,
200}
201
202impl<'a, T> Server<'a, T> {
203    /// Create a new server.
204    pub fn new(services: Vec<&'a Box<dyn Service<Data = T>>>) -> Self {
205        Self { services }
206    }
207
208    /// Call services in order and return the first response message.
209    ///
210    /// If no services match the incoming request this will
211    /// return `Error::MethodNotFound`.
212    pub(crate) fn handle(
213        &self,
214        request: &Request,
215        ctx: &T,
216    ) -> Result<Response> {
217        for service in self.services.iter() {
218            if let Some(result) = service.handle(request, ctx)? {
219                return Ok(result);
220            }
221        }
222
223        let err = Error::MethodNotFound {
224            name: request.method().to_string(),
225            id: request.id.clone(),
226        };
227
228        Ok((request, err).into())
229    }
230
231    /// Infallible service handler, errors are automatically converted to responses.
232    pub fn serve(&self, request: &Request, ctx: &T) -> Option<Response> {
233        match self.handle(request, ctx) {
234            Ok(response) => {
235                if response.error().is_some() || response.id().is_some() {
236                    Some(response)
237                } else {
238                    None
239                }
240            }
241            Err(e) => Some((request, e).into()),
242        }
243    }
244}
245
246/// Parse a JSON payload from a string slice into a request.
247pub fn from_str(payload: &str) -> Result<Request> {
248    serde_json::from_str::<Request>(payload).map_err(map_json_error)
249}
250
251/// Parse a JSON payload from a [Value](serde_json::Value) into a request.
252pub fn from_value(payload: Value) -> Result<Request> {
253    serde_json::from_value::<Request>(payload).map_err(map_json_error)
254}
255
256/// Parse a JSON payload from a byte slice into a request.
257pub fn from_slice(payload: &[u8]) -> Result<Request> {
258    serde_json::from_slice::<Request>(payload).map_err(map_json_error)
259}
260
261/// Parse a JSON payload from an IO reader into a request.
262pub fn from_reader<R: std::io::Read>(payload: R) -> Result<Request> {
263    serde_json::from_reader::<R, Request>(payload).map_err(map_json_error)
264}
265
266/// JSON-RPC request.
267#[derive(Serialize, Deserialize, Debug, Clone)]
268pub struct Request {
269    jsonrpc: String,
270    method: String,
271    #[serde(skip_serializing_if = "Option::is_none")]
272    id: Option<Value>,
273    #[serde(skip_serializing_if = "Option::is_none")]
274    params: Option<Value>,
275}
276
277impl Request {
278    /// Create a new request.
279    pub fn new(
280        id: Option<Value>,
281        method: String,
282        params: Option<Value>,
283    ) -> Self {
284        Self {
285            jsonrpc: VERSION.to_string(),
286            id,
287            method,
288            params,
289        }
290    }
291
292    /// Create a new request that expects a reply.
293    ///
294    /// A random number is generated for the message id.
295    pub fn new_reply(method: &str, params: Option<Value>) -> Self {
296        Self {
297            jsonrpc: VERSION.to_string(),
298            method: method.to_string(),
299            params,
300            id: Some(Value::Number(Number::from(
301                rand::thread_rng().gen_range(1..std::u32::MAX),
302            ))),
303        }
304    }
305
306    /// Create a new notification.
307    ///
308    /// The id field is `None`.
309    pub fn new_notification(method: &str, params: Option<Value>) -> Self {
310        Self {
311            jsonrpc: VERSION.to_string(),
312            method: method.to_string(),
313            params,
314            id: None,
315        }
316    }
317
318    /// The id for the request.
319    pub fn id(&self) -> &Option<Value> {
320        &self.id
321    }
322
323    /// The mutable id for the request if you need to take it
324    /// to assign to response.
325    pub fn id_mut(&mut self) -> &mut Option<Value> {
326        &mut self.id
327    }
328
329    /// The request service method name.
330    pub fn method(&self) -> &str {
331        &self.method
332    }
333
334    /// The request parameters.
335    pub fn params(&self) -> &Option<Value> {
336        &self.params
337    }
338
339    #[deprecated(note = "Use match expression on method() instead")]
340    /// Determine if the given name matches the request method.
341    pub fn matches(&self, name: &str) -> bool {
342        *name == self.method
343    }
344
345    /// Deserialize and consume the message parameters into type `T`.
346    ///
347    /// If this request message has no parameters or the `params`
348    /// payload cannot be converted to `T` this will return
349    /// `Error::InvalidParams`.
350    pub fn deserialize<T: DeserializeOwned>(&self) -> Result<T> {
351        if let Some(params) = &self.params {
352            Ok(serde_json::from_value::<T>(params.clone()).map_err(|e| {
353                Error::InvalidParams {
354                    id: self.id.clone(),
355                    data: e.to_string(),
356                }
357            })?)
358        } else {
359            Err(Error::InvalidParams {
360                id: self.id.clone(),
361                data: "No parameters given".to_string(),
362            })
363        }
364    }
365}
366
367fn map_json_error(e: serde_json::Error) -> Error {
368    if e.is_data() {
369        Error::InvalidRequest {
370            data: e.to_string(),
371        }
372    } else {
373        Error::Parse {
374            data: e.to_string(),
375        }
376    }
377}
378
379/// JSON-RPC response.
380#[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
381pub struct Response {
382    jsonrpc: String,
383    #[serde(skip_serializing_if = "Option::is_none")]
384    id: Option<Value>,
385    #[serde(skip_serializing_if = "Option::is_none")]
386    result: Option<Value>,
387    #[serde(skip_serializing_if = "Option::is_none")]
388    error: Option<RpcError>,
389}
390
391impl Response {
392    /// The id for the response.
393    pub fn id(&self) -> &Option<Value> {
394        &self.id
395    }
396
397    /// The result for the response.
398    pub fn result(&self) -> &Option<Value> {
399        &self.result
400    }
401
402    /// The error for the response.
403    pub fn error(&self) -> &Option<RpcError> {
404        &self.error
405    }
406}
407
408impl From<Response> for (Option<Value>, Option<RpcError>, Option<Value>) {
409    fn from(response: Response) -> Self {
410        (response.id, response.error, response.result)
411    }
412}
413
414impl From<Response> for Option<Value> {
415    fn from(response: Response) -> Self {
416        response.result
417    }
418}
419
420impl From<Response> for Option<RpcError> {
421    fn from(response: Response) -> Self {
422        response.error
423    }
424}
425
426impl From<Error> for Response {
427    fn from(error: Error) -> Self {
428        let (code, data): (isize, Option<String>) = (&error).into();
429        Response {
430            jsonrpc: VERSION.to_string(),
431            id: Some(Value::Null),
432            result: None,
433            error: Some(RpcError {
434                code,
435                message: error.to_string(),
436                data,
437            }),
438        }
439    }
440}
441
442impl<'a> From<(&'a Request, Error)> for Response {
443    fn from(result: (&'a Request, Error)) -> Self {
444        let (code, data): (isize, Option<String>) = (&result.1).into();
445        Response {
446            jsonrpc: VERSION.to_string(),
447            id: result.0.id.clone(),
448            result: None,
449            error: Some(RpcError {
450                code,
451                message: result.1.to_string(),
452                data,
453            }),
454        }
455    }
456}
457
458impl<'a> From<(&'a Request, RpcError)> for Response {
459    fn from(result: (&'a Request, RpcError)) -> Self {
460        Response {
461            jsonrpc: VERSION.to_string(),
462            id: result.0.id.clone(),
463            result: None,
464            error: Some(result.1),
465        }
466    }
467}
468
469impl<'a> From<(&'a Request, Value)> for Response {
470    fn from(req: (&'a Request, Value)) -> Self {
471        Self {
472            jsonrpc: VERSION.to_string(),
473            id: req.0.id.clone(),
474            result: Some(req.1),
475            error: None,
476        }
477    }
478}
479
480impl<'a> From<&'a Request> for Response {
481    fn from(req: &'a Request) -> Self {
482        Self {
483            jsonrpc: VERSION.to_string(),
484            result: None,
485            error: None,
486            id: req.id.clone(),
487        }
488    }
489}
490
491impl From<Value> for Response {
492    fn from(result: Value) -> Self {
493        Self {
494            jsonrpc: VERSION.to_string(),
495            result: Some(result),
496            error: None,
497            id: Some(Value::from(Number::from(0))),
498        }
499    }
500}
501
502mod test {
503    use super::*;
504
505    #[derive(Debug, thiserror::Error)]
506    enum MockError {
507        #[error("{0}")]
508        Internal(String),
509    }
510
511    struct HelloServiceHandler;
512    impl Service for HelloServiceHandler {
513        type Data = ();
514        fn handle(
515            &self,
516            request: &Request,
517            _context: &Self::Data,
518        ) -> Result<Option<Response>> {
519            let response = match request.method() {
520                "hello" => {
521                    let params: String = request.deserialize()?;
522                    let message = format!("Hello, {}!", params);
523                    Some((request, Value::String(message)).into())
524                }
525                _ => None,
526            };
527            Ok(response)
528        }
529    }
530
531    struct InternalErrorService;
532    impl Service for InternalErrorService {
533        type Data = ();
534        fn handle(
535            &self,
536            _request: &Request,
537            _context: &Self::Data,
538        ) -> Result<Option<Response>> {
539            // Must Box the error as it is foreign.
540            Err(Error::from(Box::from(MockError::Internal(
541                "Mock error".to_string(),
542            ))))
543        }
544    }
545
546    struct InternalRpcErrorService;
547    impl Service for InternalRpcErrorService {
548        type Data = ();
549        fn handle(
550            &self,
551            request: &Request,
552            _context: &Self::Data,
553        ) -> Result<Option<Response>> {
554            let err = RpcError::new("Mock RPC error".to_string(), Some("close-connection".to_string()));
555            let res = Some((request, err).into());
556            Ok(res)
557        }
558    }
559
560    #[test]
561    fn jsonrpc_service_ok() -> Result<()> {
562        let service: Box<dyn Service<Data = ()>> =
563            Box::new(HelloServiceHandler {});
564        let mut request = Request::new_reply(
565            "hello",
566            Some(Value::String("world".to_string())),
567        );
568        let server = Server::new(vec![&service]);
569        let response = server.serve(&mut request, &());
570        assert_eq!(
571            Some(Value::String("Hello, world!".to_string())),
572            response.unwrap().into()
573        );
574        Ok(())
575    }
576
577    #[test]
578    fn jsonrpc_service_notification() -> Result<()> {
579        let service: Box<dyn Service<Data = ()>> =
580            Box::new(HelloServiceHandler {});
581        let mut request = Request::new_notification(
582            "hello",
583            Some(Value::String("world".to_string())),
584        );
585        let server = Server::new(vec![&service]);
586        let response = server.serve(&mut request, &());
587        assert_eq!(None, response);
588        Ok(())
589    }
590
591    #[test]
592    fn jsonrpc_invalid_request_error() -> Result<()> {
593        let bad_json = "{}";
594        let response: Response = match from_str(bad_json) {
595            Ok(request) => (&request).into(),
596            Err(e) => e.into(),
597        };
598        assert_eq!(
599            Some(RpcError {
600                code: -32600,
601                message: "Invalid JSON-RPC request".to_string(),
602                data: Some(
603                    "missing field `jsonrpc` at line 1 column 2".to_string()
604                )
605            }),
606            response.into()
607        );
608        Ok(())
609    }
610
611    #[test]
612    fn jsonrpc_service_method_not_found() -> Result<()> {
613        let service: Box<dyn Service<Data = ()>> =
614            Box::new(HelloServiceHandler {});
615        let mut request = Request::new_reply("non-existent", None);
616        let server = Server::new(vec![&service]);
617        let response = server.serve(&mut request, &());
618        assert_eq!(
619            Some(RpcError {
620                code: -32601,
621                message: "Service method not found: non-existent".to_string(),
622                data: None
623            }),
624            response.unwrap().into()
625        );
626        Ok(())
627    }
628
629    #[test]
630    fn jsonrpc_invalid_params() -> Result<()> {
631        let service: Box<dyn Service<Data = ()>> =
632            Box::new(HelloServiceHandler {});
633        let mut request = Request::new_reply("hello", Some(Value::Bool(true)));
634        let server = Server::new(vec![&service]);
635        let response = server.serve(&mut request, &());
636        assert_eq!(
637            Some(RpcError {
638                code: -32602,
639                message: "Message parameters are invalid".to_string(),
640                data: Some(
641                    "invalid type: boolean `true`, expected a string"
642                        .to_string()
643                )
644            }),
645            response.unwrap().into()
646        );
647        Ok(())
648    }
649
650    #[test]
651    fn jsonrpc_internal_error() -> Result<()> {
652        let service: Box<dyn Service<Data = ()>> =
653            Box::new(InternalErrorService {});
654        let request = Request::new_reply("foo", None);
655        let server = Server::new(vec![&service]);
656        let response = server.serve(&request, &());
657        assert_eq!(
658            Some(RpcError {
659                code: -32603,
660                message: "Mock error".to_string(),
661                data: None
662            }),
663            response.unwrap().into()
664        );
665        Ok(())
666    }
667
668    #[test]
669    fn jsonrpc_parse_error() -> Result<()> {
670        let bad_json = r#"{"jsonrpc": "oops}"#;
671        let response: Response = match from_str(bad_json) {
672            Ok(request) => (&request).into(),
673            Err(e) => e.into(),
674        };
675        assert_eq!(
676            Some(RpcError {
677                code: -32700,
678                message: "Parsing failed, invalid JSON data".to_string(),
679                data: Some(
680                    "EOF while parsing a string at line 1 column 18"
681                        .to_string()
682                )
683            }),
684            response.into()
685        );
686        Ok(())
687    }
688
689    #[test]
690    fn jsonrpc_internal_rpc_error() -> Result<()> {
691    
692        let service: Box<dyn Service<Data = ()>> =
693            Box::new(InternalRpcErrorService {});
694        let request = Request::new_reply("foo", None);
695        let server = Server::new(vec![&service]);
696        let response = server.serve(&request, &());
697        assert_eq!(
698            Some(RpcError {
699                code: -32603,
700                message: "Mock RPC error".to_string(),
701                data: Some("close-connection".to_string())
702            }),
703            response.unwrap().into()
704        );
705        Ok(())
706    }
707}