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 the client.
29
30use std::borrow::Cow;
31
32use crate::{
33	Params,
34	params::{Id, TwoPointZero},
35};
36use http::Extensions;
37use serde::{Deserialize, 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	#[serde(borrow)]
53	#[serde(skip_serializing_if = "Option::is_none")]
54	pub params: Option<Cow<'a, RawValue>>,
55	/// The request's extensions.
56	#[serde(skip)]
57	pub extensions: Extensions,
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			extensions: Extensions::new(),
69		}
70	}
71
72	/// Create new owned [`Request`].
73	pub fn owned(method: String, params: Option<Box<RawValue>>, id: Id<'a>) -> Self {
74		Self {
75			jsonrpc: TwoPointZero,
76			id,
77			method: Cow::Owned(method),
78			params: params.map(Cow::Owned),
79			extensions: Extensions::new(),
80		}
81	}
82
83	/// Get the ID of the request.
84	pub fn id(&self) -> Id<'a> {
85		self.id.clone()
86	}
87
88	/// Get the method name of the request.
89	pub fn method_name(&self) -> &str {
90		&self.method
91	}
92
93	/// Get the params of the request.
94	pub fn params(&self) -> Params<'_> {
95		Params::new(self.params.as_ref().map(|p| RawValue::get(p)))
96	}
97
98	/// Returns a reference to the associated extensions.
99	pub fn extensions(&self) -> &Extensions {
100		&self.extensions
101	}
102
103	/// Returns a reference to the associated extensions.
104	pub fn extensions_mut(&mut self) -> &mut Extensions {
105		&mut self.extensions
106	}
107}
108
109/// JSON-RPC Invalid request as defined in the [spec](https://www.jsonrpc.org/specification#request-object).
110#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
111pub struct InvalidRequest<'a> {
112	/// Request ID
113	#[serde(borrow)]
114	pub id: Id<'a>,
115}
116
117/// JSON-RPC notification (a request object without a request ID) as defined in the
118/// [spec](https://www.jsonrpc.org/specification#request-object).
119#[derive(Serialize, Deserialize, Debug, Clone)]
120pub struct Notification<'a, T> {
121	/// JSON-RPC version.
122	pub jsonrpc: TwoPointZero,
123	/// Name of the method to be invoked.
124	#[serde(borrow)]
125	pub method: Cow<'a, str>,
126	/// Parameter values of the request.
127	pub params: T,
128	/// Extensions of the notification.
129	#[serde(skip)]
130	pub extensions: Extensions,
131}
132
133impl<'a, T> Notification<'a, T> {
134	/// Create a new [`Notification`].
135	pub fn new(method: Cow<'a, str>, params: T) -> Self {
136		Self { jsonrpc: TwoPointZero, method, params, extensions: Extensions::new() }
137	}
138
139	/// Get the method name of the request.
140	pub fn method_name(&self) -> &str {
141		&self.method
142	}
143
144	/// Returns a reference to the associated extensions.
145	pub fn extensions(&self) -> &Extensions {
146		&self.extensions
147	}
148
149	/// Get the params of the request.
150	pub fn params(&self) -> &T {
151		&self.params
152	}
153
154	/// Returns a reference to the associated extensions.
155	pub fn extensions_mut(&mut self) -> &mut Extensions {
156		&mut self.extensions
157	}
158}
159
160#[cfg(test)]
161mod test {
162	use super::{Id, InvalidRequest, Notification, Request, TwoPointZero};
163	use serde_json::value::RawValue;
164
165	fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) {
166		assert_eq!(request.jsonrpc, TwoPointZero);
167		assert_eq!(request.id, id);
168		assert_eq!(request.method, method);
169		assert_eq!(request.params.as_ref().map(|p| RawValue::get(p)), params);
170	}
171
172	/// Checks that we can deserialize the object with or without non-mandatory fields.
173	#[test]
174	fn deserialize_call() {
175		let method = "subtract";
176		let params = "[42, 23]";
177
178		let test_vector = vec![
179			// With all fields set.
180			(
181				r#"{"jsonrpc":"2.0", "method":"subtract", "params":[42, 23], "id":1}"#,
182				Id::Number(1),
183				Some(params),
184				method,
185			),
186			// Without params field
187			(r#"{"jsonrpc":"2.0", "method":"subtract", "id":null}"#, Id::Null, None, method),
188			// Escaped method name.
189			(r#"{"jsonrpc":"2.0", "method":"\"m", "id":null}"#, Id::Null, None, "\"m"),
190		];
191
192		for (ser, id, params, method) in test_vector.into_iter() {
193			let request = serde_json::from_str(ser).unwrap();
194			assert_request(request, id, method, params);
195		}
196	}
197
198	#[test]
199	fn deserialize_call_escaped_method_name() {
200		let ser = r#"{"jsonrpc":"2.0","id":1,"method":"\"m\""}"#;
201		let req: Request = serde_json::from_str(ser).unwrap();
202		assert_request(req, Id::Number(1), "\"m\"", None);
203	}
204
205	#[test]
206	fn deserialize_valid_notif_works() {
207		let ser = r#"{"jsonrpc":"2.0","method":"say_hello","params":[]}"#;
208		let dsr: Notification<&RawValue> = serde_json::from_str(ser).unwrap();
209		assert_eq!(dsr.method, "say_hello");
210		assert_eq!(dsr.jsonrpc, TwoPointZero);
211	}
212
213	#[test]
214	fn deserialize_valid_notif_escaped_method() {
215		let ser = r#"{"jsonrpc":"2.0","method":"\"m\"","params":[]}"#;
216		let dsr: Notification<&RawValue> = serde_json::from_str(ser).unwrap();
217		assert_eq!(dsr.method, "\"m\"");
218		assert_eq!(dsr.jsonrpc, TwoPointZero);
219	}
220
221	#[test]
222	fn deserialize_call_bad_id_should_fail() {
223		let ser = r#"{"jsonrpc":"2.0","method":"say_hello","params":[],"id":{}}"#;
224		assert!(serde_json::from_str::<Request>(ser).is_err());
225	}
226
227	#[test]
228	fn deserialize_invalid_request() {
229		let s = r#"{"id":120,"method":"my_method","params":["foo", "bar"],"extra_field":[]}"#;
230		let deserialized: InvalidRequest = serde_json::from_str(s).unwrap();
231		assert_eq!(deserialized, InvalidRequest { id: Id::Number(120) });
232	}
233
234	/// Checks that we can serialize the object with or without non-mandatory fields.
235	#[test]
236	fn serialize_call() {
237		let method = "subtract";
238		let id = Id::Number(1); // It's enough to check one variant, since the type itself also has tests.
239		let params = Some(serde_json::value::to_raw_value(&[42, 23]).unwrap());
240
241		let test_vector: &[(&'static str, Option<_>, Option<_>, &'static str)] = &[
242			// With all fields set.
243			(
244				r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#,
245				Some(id.clone()),
246				params.clone(),
247				method,
248			),
249			// Escaped method name.
250			(r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(id.clone()), None, "\"m"),
251			// Without ID field.
252			(r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, None, params, method),
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 =
261				serde_json::to_string(&Request::borrowed(method, params.as_deref(), id.unwrap_or(Id::Null))).unwrap();
262
263			assert_eq!(&request, ser);
264		}
265	}
266}