Skip to main content

jsonrpsee_types/
request.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27//! Types to handle JSON-RPC requests according to the [spec](https://www.jsonrpc.org/specification#request-object).
28//! Some types come with a "*Ser" variant that implements [`serde::Serialize`]; these are used in
29//! the client.
30
31use std::borrow::Cow;
32
33use crate::{
34    params::{Id, TwoPointZero},
35    Params,
36};
37use serde::{Deserialize, Deserializer, Serialize};
38use serde_json::value::RawValue;
39
40/// JSON-RPC request object as defined in the [spec](https://www.jsonrpc.org/specification#request-object).
41#[derive(Serialize, Deserialize, Debug, Clone)]
42pub struct Request<'a> {
43    /// JSON-RPC version.
44    pub jsonrpc: TwoPointZero,
45    /// Request ID
46    #[serde(borrow)]
47    pub id: Id<'a>,
48    /// Name of the method to be invoked.
49    #[serde(borrow)]
50    pub method: Cow<'a, str>,
51    /// Parameter values of the request.
52    ///
53    /// Decoding borrows them from the input, so a request with params only decodes from borrowed
54    /// input such as [`serde_json::from_str`] or [`serde_json::from_slice`].
55    #[serde(borrow, default, deserialize_with = "deserialize_borrowed_params")]
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub params: Option<Cow<'a, RawValue>>,
58}
59
60impl<'a> Request<'a> {
61    /// Create new borrowed [`Request`].
62    pub fn borrowed(method: &'a str, params: Option<&'a RawValue>, id: Id<'a>) -> Self {
63        Self {
64            jsonrpc: TwoPointZero,
65            id,
66            method: Cow::Borrowed(method),
67            params: params.map(Cow::Borrowed),
68        }
69    }
70
71    /// Create new owned [`Request`].
72    pub fn owned(method: String, params: Option<Box<RawValue>>, id: Id<'a>) -> Self {
73        Self {
74            jsonrpc: TwoPointZero,
75            id,
76            method: Cow::Owned(method),
77            params: params.map(Cow::Owned),
78        }
79    }
80
81    /// Get the ID of the request.
82    pub fn id(&self) -> Id<'a> {
83        self.id.clone()
84    }
85
86    /// Get the method name of the request.
87    pub fn method_name(&self) -> &str {
88        &self.method
89    }
90
91    /// Get the params of the request.
92    pub fn params(&self) -> Params<'_> {
93        Params::new(self.params.as_ref().map(|p| RawValue::get(p)))
94    }
95}
96
97/// JSON-RPC Invalid request as defined in the [spec](https://www.jsonrpc.org/specification#request-object).
98#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
99pub struct InvalidRequest<'a> {
100    /// Request ID
101    #[serde(borrow)]
102    pub id: Id<'a>,
103}
104
105/// JSON-RPC notification (a request object without a request ID) as defined in the
106/// [spec](https://www.jsonrpc.org/specification#request-object).
107#[derive(Serialize, Deserialize, Debug, Clone)]
108pub struct Notification<'a, T> {
109    /// JSON-RPC version.
110    pub jsonrpc: TwoPointZero,
111    /// Name of the method to be invoked.
112    #[serde(borrow)]
113    pub method: Cow<'a, str>,
114    /// Parameter values of the request.
115    pub params: T,
116}
117
118impl<'a, T> Notification<'a, T> {
119    /// Create a new [`Notification`].
120    pub fn new(method: Cow<'a, str>, params: T) -> Self {
121        Self { jsonrpc: TwoPointZero, method, params }
122    }
123
124    /// Get the method name of the request.
125    pub fn method_name(&self) -> &str {
126        &self.method
127    }
128
129    /// Get the params of the request.
130    pub fn params(&self) -> &T {
131        &self.params
132    }
133}
134
135/// Borrows `params` from the input, which `#[serde(borrow)]` alone does only for `Cow<str>` and
136/// `Cow<[u8]>`: a `Cow<RawValue>` would be decoded as an owned copy.
137fn deserialize_borrowed_params<'de, D: Deserializer<'de>>(
138    deserializer: D,
139) -> Result<Option<Cow<'de, RawValue>>, D::Error> {
140    Option::<&'de RawValue>::deserialize(deserializer).map(|params| params.map(Cow::Borrowed))
141}
142
143#[cfg(test)]
144mod test {
145    use super::{Cow, Id, InvalidRequest, Notification, Request, TwoPointZero};
146    use serde_json::value::RawValue;
147
148    fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) {
149        assert_eq!(request.jsonrpc, TwoPointZero);
150        assert_eq!(request.id, id);
151        assert_eq!(request.method, method);
152        assert_eq!(request.params.as_ref().map(|p| RawValue::get(p)), params);
153        assert!(!matches!(request.params, Some(Cow::Owned(_))));
154    }
155
156    /// Checks that we can deserialize the object with or without non-mandatory fields.
157    #[test]
158    fn deserialize_call() {
159        let method = "subtract";
160        let params = "[42, 23]";
161
162        let test_vector = vec![
163            // With all fields set.
164            (
165                r#"{"jsonrpc":"2.0", "method":"subtract", "params":[42, 23], "id":1}"#,
166                Id::Number(1),
167                Some(params),
168                method,
169            ),
170            // Without params field
171            (r#"{"jsonrpc":"2.0", "method":"subtract", "id":null}"#, Id::Null, None, method),
172            // Escaped method name.
173            (r#"{"jsonrpc":"2.0", "method":"\"m", "id":null}"#, Id::Null, None, "\"m"),
174        ];
175
176        for (ser, id, params, method) in test_vector.into_iter() {
177            let request = serde_json::from_str(ser).unwrap();
178            assert_request(request, id, method, params);
179        }
180    }
181
182    #[test]
183    fn deserialize_call_escaped_method_name() {
184        let ser = r#"{"jsonrpc":"2.0","id":1,"method":"\"m\""}"#;
185        let req: Request = serde_json::from_str(ser).unwrap();
186        assert_request(req, Id::Number(1), "\"m\"", None);
187    }
188
189    #[test]
190    fn deserialize_valid_notif_works() {
191        let ser = r#"{"jsonrpc":"2.0","method":"say_hello","params":[]}"#;
192        let dsr: Notification<&RawValue> = serde_json::from_str(ser).unwrap();
193        assert_eq!(dsr.method, "say_hello");
194        assert_eq!(dsr.jsonrpc, TwoPointZero);
195    }
196
197    #[test]
198    fn deserialize_valid_notif_escaped_method() {
199        let ser = r#"{"jsonrpc":"2.0","method":"\"m\"","params":[]}"#;
200        let dsr: Notification<&RawValue> = serde_json::from_str(ser).unwrap();
201        assert_eq!(dsr.method, "\"m\"");
202        assert_eq!(dsr.jsonrpc, TwoPointZero);
203    }
204
205    #[test]
206    fn deserialize_call_bad_id_should_fail() {
207        let ser = r#"{"jsonrpc":"2.0","method":"say_hello","params":[],"id":{}}"#;
208        assert!(serde_json::from_str::<Request>(ser).is_err());
209    }
210
211    #[test]
212    fn deserialize_invalid_request() {
213        let s = r#"{"id":120,"method":"my_method","params":["foo", "bar"],"extra_field":[]}"#;
214        let deserialized: InvalidRequest = serde_json::from_str(s).unwrap();
215        assert_eq!(deserialized, InvalidRequest { id: Id::Number(120) });
216    }
217
218    #[test]
219    fn deserialize_invalid_request_bad_id_should_fail() {
220        let s = r#"{"id":[0,0],"method":"my_method","params":[]}"#;
221        let err = serde_json::from_str::<InvalidRequest>(s).unwrap_err();
222        assert_eq!(
223            err.to_string(),
224            "invalid type: sequence, expected null, an unsigned integer or a string at line 1 column 7"
225        );
226    }
227
228    /// Checks that we can serialize the object with or without non-mandatory fields.
229    #[test]
230    fn serialize_call() {
231        let method = "subtract";
232        // It's enough to check one variant, since the type itself also has tests.
233        let id = Id::Number(1);
234        let params = Some(serde_json::value::to_raw_value(&[42, 23]).unwrap());
235
236        let test_vector: &[(&'static str, Option<_>, Option<_>, &'static str)] = &[
237            // With all fields set.
238            (
239                r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#,
240                Some(id.clone()),
241                params.clone(),
242                method,
243            ),
244            // Escaped method name.
245            (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(id.clone()), None, "\"m"),
246            // Without ID field.
247            (
248                r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#,
249                None,
250                params,
251                method,
252            ),
253            // Without params field
254            (r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, Some(id), None, method),
255            // Without params and ID.
256            (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, None, method),
257        ];
258
259        for (ser, id, params, method) in test_vector.iter().cloned() {
260            let request = serde_json::to_string(&Request::borrowed(
261                method,
262                params.as_deref(),
263                id.unwrap_or(Id::Null),
264            ))
265            .unwrap();
266
267            assert_eq!(&request, ser);
268        }
269    }
270}