near_jsonrpc_primitives/
message.rs

1// Copyright 2017 tokio-jsonrpc Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! JSON-RPC 2.0 messages.
9//!
10//! The main entrypoint here is the [Message](enum.Message.html). The others are just building
11//! blocks and you should generally work with `Message` instead.
12use crate::errors::RpcError;
13use serde::de::{Deserializer, Error, Unexpected, Visitor};
14use serde::ser::{SerializeStruct, Serializer};
15use serde_json::{Result as JsonResult, Value};
16use std::fmt::{Formatter, Result as FmtResult};
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19struct Version;
20
21impl serde::Serialize for Version {
22    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
23        serializer.serialize_str("2.0")
24    }
25}
26
27impl<'de> serde::Deserialize<'de> for Version {
28    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
29        struct VersionVisitor;
30        impl<'de> Visitor<'de> for VersionVisitor {
31            type Value = Version;
32
33            fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
34                formatter.write_str("a version string")
35            }
36
37            fn visit_str<E: Error>(self, value: &str) -> Result<Version, E> {
38                match value {
39                    "2.0" => Ok(Version),
40                    _ => Err(E::invalid_value(Unexpected::Str(value), &"value 2.0")),
41                }
42            }
43        }
44        deserializer.deserialize_str(VersionVisitor)
45    }
46}
47
48/// An RPC request.
49#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
50#[serde(deny_unknown_fields)]
51pub struct Request {
52    jsonrpc: Version,
53    pub method: String,
54    #[serde(default, skip_serializing_if = "Value::is_null")]
55    pub params: Value,
56    pub id: Value,
57}
58
59impl Request {
60    /// Answer the request with a (positive) reply.
61    ///
62    /// The ID is taken from the request.
63    pub fn reply(&self, reply: Value) -> Message {
64        Message::Response(Response { jsonrpc: Version, result: Ok(reply), id: self.id.clone() })
65    }
66    /// Answer the request with an error.
67    pub fn error(&self, error: RpcError) -> Message {
68        Message::Response(Response { jsonrpc: Version, result: Err(error), id: self.id.clone() })
69    }
70}
71
72/// A response to an RPC.
73///
74/// It is created by the methods on [Request](struct.Request.html).
75#[derive(Debug, Clone, PartialEq)]
76pub struct Response {
77    jsonrpc: Version,
78    pub result: Result<Value, RpcError>,
79    pub id: Value,
80}
81
82impl serde::Serialize for Response {
83    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
84        let mut sub = serializer.serialize_struct("Response", 3)?;
85        sub.serialize_field("jsonrpc", &self.jsonrpc)?;
86        match self.result {
87            Ok(ref value) => sub.serialize_field("result", value),
88            Err(ref err) => sub.serialize_field("error", err),
89        }?;
90        sub.serialize_field("id", &self.id)?;
91        sub.end()
92    }
93}
94
95/// Deserializer for `Option<Value>` that produces `Some(Value::Null)`.
96///
97/// The usual one produces None in that case. But we need to know the difference between
98/// `{x: null}` and `{}`.
99fn some_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<Value>, D::Error> {
100    serde::Deserialize::deserialize(deserializer).map(Some)
101}
102
103/// A helper trick for deserialization.
104#[derive(serde::Deserialize)]
105#[serde(deny_unknown_fields)]
106struct WireResponse {
107    // It is actually used to eat and sanity check the deserialized text
108    #[allow(dead_code)]
109    jsonrpc: Version,
110    // Make sure we accept null as Some(Value::Null), instead of going to None
111    #[serde(default, deserialize_with = "some_value")]
112    result: Option<Value>,
113    error: Option<RpcError>,
114    id: Value,
115}
116
117// Implementing deserialize is hard. We sidestep the difficulty by deserializing a similar
118// structure that directly corresponds to whatever is on the wire and then convert it to our more
119// convenient representation.
120impl<'de> serde::Deserialize<'de> for Response {
121    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
122        let wr: WireResponse = serde::Deserialize::deserialize(deserializer)?;
123        let result = match (wr.result, wr.error) {
124            (Some(res), None) => Ok(res),
125            (None, Some(err)) => Err(err),
126            _ => {
127                let err = D::Error::custom("Either 'error' or 'result' is expected, but not both");
128                return Err(err);
129            }
130        };
131        Ok(Response { jsonrpc: Version, result, id: wr.id })
132    }
133}
134
135/// A notification (doesn't expect an answer).
136#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
137#[serde(deny_unknown_fields)]
138pub struct Notification {
139    jsonrpc: Version,
140    pub method: String,
141    #[serde(default, skip_serializing_if = "Value::is_null")]
142    pub params: Value,
143}
144
145/// One message of the JSON RPC protocol.
146///
147/// One message, directly mapped from the structures of the protocol. See the
148/// [specification](http://www.jsonrpc.org/specification) for more details.
149///
150/// Since the protocol allows one endpoint to be both client and server at the same time, the
151/// message can decode and encode both directions of the protocol.
152///
153/// The `Batch` variant is supposed to be created directly, without a constructor.
154///
155/// The `UnmatchedSub` variant is used when a request is an array and some of the subrequest
156/// aren't recognized as valid json rpc 2.0 messages. This is never returned as a top-level
157/// element, it is returned as `Err(Broken::Unmatched)`.
158#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
159#[serde(untagged)]
160pub enum Message {
161    /// An RPC request.
162    Request(Request),
163    /// A response to a Request.
164    Response(Response),
165    /// A notification.
166    Notification(Notification),
167    /// A batch of more requests or responses.
168    ///
169    /// The protocol allows bundling multiple requests, notifications or responses to a single
170    /// message.
171    ///
172    /// This variant has no direct constructor and is expected to be constructed manually.
173    Batch(Vec<Message>),
174    /// An unmatched sub entry in a `Batch`.
175    ///
176    /// When there's a `Batch` and an element doesn't conform to the JSONRPC 2.0 format, that one
177    /// is represented by this. This is never produced as a top-level value when parsing, the
178    /// `Err(Broken::Unmatched)` is used instead. It is not possible to serialize.
179    #[serde(skip_serializing)]
180    UnmatchedSub(Value),
181}
182
183impl Message {
184    /// A constructor for a request.
185    ///
186    /// The ID is auto-generated.
187    pub fn request(method: String, params: Value) -> Self {
188        let id = Value::from(near_primitives::utils::generate_random_string(9));
189        Message::Request(Request { jsonrpc: Version, method, params, id })
190    }
191    /// Create a top-level error (without an ID).
192    pub fn error(error: RpcError) -> Self {
193        Message::Response(Response { jsonrpc: Version, result: Err(error), id: Value::Null })
194    }
195    /// A constructor for a notification.
196    pub fn notification(method: String, params: Value) -> Self {
197        Message::Notification(Notification { jsonrpc: Version, method, params })
198    }
199    /// A constructor for a response.
200    pub fn response(id: Value, result: Result<Value, RpcError>) -> Self {
201        Message::Response(Response { jsonrpc: Version, result, id })
202    }
203    /// Returns id or Null if there is no id.
204    pub fn id(&self) -> Value {
205        match self {
206            Message::Request(req) => req.id.clone(),
207            _ => Value::Null,
208        }
209    }
210}
211
212/// A broken message.
213///
214/// Protocol-level errors.
215#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
216#[serde(untagged)]
217pub enum Broken {
218    /// It was valid JSON, but doesn't match the form of a JSONRPC 2.0 message.
219    Unmatched(Value),
220    /// Invalid JSON.
221    #[serde(skip_deserializing)]
222    SyntaxError(String),
223}
224
225impl Broken {
226    /// Generate an appropriate error message.
227    ///
228    /// The error message for these things are specified in the RFC, so this just creates an error
229    /// with the right values.
230    pub fn reply(&self) -> Message {
231        match *self {
232            Broken::Unmatched(_) => Message::error(RpcError::parse_error(
233                "JSON RPC Request format was expected".to_owned(),
234            )),
235            Broken::SyntaxError(ref e) => Message::error(RpcError::parse_error(e.clone())),
236        }
237    }
238}
239
240/// A trick to easily deserialize and detect valid JSON, but invalid Message.
241#[derive(serde::Deserialize)]
242#[serde(untagged)]
243pub enum WireMessage {
244    Message(Message),
245    Broken(Broken),
246}
247
248pub fn decoded_to_parsed(res: JsonResult<WireMessage>) -> Parsed {
249    match res {
250        Ok(WireMessage::Message(Message::UnmatchedSub(value))) => Err(Broken::Unmatched(value)),
251        Ok(WireMessage::Message(m)) => Ok(m),
252        Ok(WireMessage::Broken(b)) => Err(b),
253        Err(e) => Err(Broken::SyntaxError(e.to_string())),
254    }
255}
256
257pub type Parsed = Result<Message, Broken>;
258
259/// Read a [Message](enum.Message.html) from a slice.
260///
261/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html).
262pub fn from_slice(s: &[u8]) -> Parsed {
263    decoded_to_parsed(::serde_json::de::from_slice(s))
264}
265
266/// Read a [Message](enum.Message.html) from a string.
267///
268/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html).
269pub fn from_str(s: &str) -> Parsed {
270    from_slice(s.as_bytes())
271}
272
273impl Into<String> for Message {
274    fn into(self) -> String {
275        ::serde_json::ser::to_string(&self).unwrap()
276    }
277}
278
279impl Into<Vec<u8>> for Message {
280    fn into(self) -> Vec<u8> {
281        ::serde_json::ser::to_vec(&self).unwrap()
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use serde_json::Value;
288    use serde_json::de::from_slice;
289    use serde_json::json;
290    use serde_json::ser::to_vec;
291
292    use super::*;
293
294    /// Test serialization and deserialization of the Message
295    ///
296    /// We first deserialize it from a string. That way we check deserialization works.
297    /// But since serialization doesn't have to produce the exact same result (order, spaces, …),
298    /// we then serialize and deserialize the thing again and check it matches.
299    /// cspell:ignore notif
300    #[test]
301    fn message_serde() {
302        // A helper for running one message test
303        fn one(input: &str, expected: &Message) {
304            let parsed: Message = from_str(input).unwrap();
305            assert_eq!(*expected, parsed);
306            let serialized = to_vec(&parsed).unwrap();
307            let deserialized: Message = from_slice(&serialized).unwrap();
308            assert_eq!(parsed, deserialized);
309        }
310
311        // A request without parameters
312        one(
313            r#"{"jsonrpc": "2.0", "method": "call", "id": 1}"#,
314            &Message::Request(Request {
315                jsonrpc: Version,
316                method: "call".to_owned(),
317                params: Value::Null,
318                id: json!(1),
319            }),
320        );
321        // A request with parameters
322        one(
323            r#"{"jsonrpc": "2.0", "method": "call", "params": [1, 2, 3], "id": 2}"#,
324            &Message::Request(Request {
325                jsonrpc: Version,
326                method: "call".to_owned(),
327                params: json!([1, 2, 3]),
328                id: json!(2),
329            }),
330        );
331        // A notification (with parameters)
332        one(
333            r#"{"jsonrpc": "2.0", "method": "notif", "params": {"x": "y"}}"#,
334            &Message::Notification(Notification {
335                jsonrpc: Version,
336                method: "notif".to_owned(),
337                params: json!({"x": "y"}),
338            }),
339        );
340        // A successful response
341        one(
342            r#"{"jsonrpc": "2.0", "result": 42, "id": 3}"#,
343            &Message::Response(Response { jsonrpc: Version, result: Ok(json!(42)), id: json!(3) }),
344        );
345        // A successful response
346        one(
347            r#"{"jsonrpc": "2.0", "result": null, "id": 3}"#,
348            &Message::Response(Response {
349                jsonrpc: Version,
350                result: Ok(Value::Null),
351                id: json!(3),
352            }),
353        );
354        // An error
355        one(
356            r#"{"jsonrpc": "2.0", "error": {"code": 42, "message": "Wrong!"}, "id": null}"#,
357            &Message::Response(Response {
358                jsonrpc: Version,
359                result: Err(RpcError::new(42, "Wrong!".to_owned(), None)),
360                id: Value::Null,
361            }),
362        );
363        // A batch
364        one(
365            r#"[
366                {"jsonrpc": "2.0", "method": "notif"},
367                {"jsonrpc": "2.0", "method": "call", "id": 42}
368            ]"#,
369            &Message::Batch(vec![
370                Message::Notification(Notification {
371                    jsonrpc: Version,
372                    method: "notif".to_owned(),
373                    params: Value::Null,
374                }),
375                Message::Request(Request {
376                    jsonrpc: Version,
377                    method: "call".to_owned(),
378                    params: Value::Null,
379                    id: json!(42),
380                }),
381            ]),
382        );
383        // Some handling of broken messages inside a batch
384        let parsed = from_str(
385            r#"[
386                {"jsonrpc": "2.0", "method": "notif"},
387                {"jsonrpc": "2.0", "method": "call", "id": 42},
388                true
389            ]"#,
390        )
391        .unwrap();
392        assert_eq!(
393            Message::Batch(vec![
394                Message::Notification(Notification {
395                    jsonrpc: Version,
396                    method: "notif".to_owned(),
397                    params: Value::Null,
398                }),
399                Message::Request(Request {
400                    jsonrpc: Version,
401                    method: "call".to_owned(),
402                    params: Value::Null,
403                    id: json!(42),
404                }),
405                Message::UnmatchedSub(Value::Bool(true)),
406            ]),
407            parsed
408        );
409        to_vec(&Message::UnmatchedSub(Value::Null)).unwrap_err();
410    }
411
412    /// A helper for the `broken` test.
413    ///
414    /// Check that the given JSON string parses, but is not recognized as a valid RPC message.
415    ///
416    /// Test things that are almost but not entirely JSONRPC are rejected
417    ///
418    /// The reject is done by returning it as Unmatched.
419    #[test]
420    fn broken() {
421        // A helper with one test
422        fn one(input: &str) {
423            let msg = from_str(input);
424            match msg {
425                Err(Broken::Unmatched(_)) => (),
426                _ => panic!("{} recognized as an RPC message: {:?}!", input, msg),
427            }
428        }
429
430        // Missing the version
431        one(r#"{"method": "notif"}"#);
432        // Wrong version
433        one(r#"{"jsonrpc": 2.0, "method": "notif"}"#);
434        // A response with both result and error
435        one(r#"{"jsonrpc": "2.0", "result": 42, "error": {"code": 42, "message": "!"}, "id": 1}"#);
436        // A response without an id
437        one(r#"{"jsonrpc": "2.0", "result": 42}"#);
438        // An extra field
439        one(r#"{"jsonrpc": "2.0", "method": "weird", "params": 42, "others": 43, "id": 2}"#);
440        // Something completely different
441        one(r#"{"x": [1, 2, 3]}"#);
442
443        match from_str(r#"{]"#) {
444            Err(Broken::SyntaxError(_)) => (),
445            other => panic!("Something unexpected: {:?}", other),
446        };
447    }
448
449    /// Test some non-trivial aspects of the constructors
450    ///
451    /// This doesn't have a full coverage, because there's not much to actually test there.
452    /// Most of it is related to the ids.
453    #[test]
454    fn constructors() {
455        let msg1 = Message::request("call".to_owned(), json!([1, 2, 3]));
456        let msg2 = Message::request("call".to_owned(), json!([1, 2, 3]));
457        // They differ, even when created with the same parameters
458        assert_ne!(msg1, msg2);
459        // And, specifically, they differ in the ID's
460        let (req1, req2) = if let (Message::Request(req1), Message::Request(req2)) = (msg1, msg2) {
461            assert_ne!(req1.id, req2.id);
462            assert!(req1.id.is_string());
463            assert!(req2.id.is_string());
464            (req1, req2)
465        } else {
466            panic!("Non-request received");
467        };
468        let id1 = req1.id.clone();
469        // When we answer a message, we get the same ID
470        if let Message::Response(ref resp) = req1.reply(json!([1, 2, 3])) {
471            assert_eq!(*resp, Response { jsonrpc: Version, result: Ok(json!([1, 2, 3])), id: id1 });
472        } else {
473            panic!("Not a response");
474        }
475        let id2 = req2.id.clone();
476        // The same with an error
477        if let Message::Response(ref resp) =
478            req2.error(RpcError::new(42, "Wrong!".to_owned(), None))
479        {
480            assert_eq!(
481                *resp,
482                Response {
483                    jsonrpc: Version,
484                    result: Err(RpcError::new(42, "Wrong!".to_owned(), None)),
485                    id: id2,
486                }
487            );
488        } else {
489            panic!("Not a response");
490        }
491        // When we have unmatched, we generate a top-level error with Null id.
492        if let Message::Response(ref resp) =
493            Message::error(RpcError::new(43, "Also wrong!".to_owned(), None))
494        {
495            assert_eq!(
496                *resp,
497                Response {
498                    jsonrpc: Version,
499                    result: Err(RpcError::new(43, "Also wrong!".to_owned(), None)),
500                    id: Value::Null,
501                }
502            );
503        } else {
504            panic!("Not a response");
505        }
506    }
507}