rpc_router/request/
request.rs

1use crate::RequestParsingError;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5/// The raw JSON-RPC request object, serving as the foundation for RPC routing.
6#[derive(Deserialize, Serialize, Clone)]
7pub struct Request {
8	pub id: Value,
9	pub method: String,
10	pub params: Option<Value>,
11}
12
13impl Request {
14	/// Will perform the `jsonrpc: "2.0"` validation.
15	/// If this is not desired, using the standard `serde_json::from_value` would do the parsing
16	/// and ignore `jsonrpc` property.
17	pub fn from_value(mut value: Value) -> Result<Request, RequestParsingError> {
18		// TODO: When capturing the Value, we might implement a safeguard to prevent capturing Value Object or arrays
19		//       as they can be indefinitely large. One technical solution would be to replace the value with a String,
20		//       using something like `"[object/array redacted, 'id' should be of type number, string or null]"` as the string.
21
22		// -- validate the version
23		// assert if present
24		let Some(version) = value.get_mut("jsonrpc").map(Value::take) else {
25			let (id, method) = take_rpc_ref(value);
26			return Err(RequestParsingError::VersionMissing { id, method });
27		};
28		// assert if equal "2.0"
29		if version.as_str().unwrap_or_default() != "2.0" {
30			let (id, method) = take_rpc_ref(value);
31			return Err(RequestParsingError::VersionInvalid { id, method, version });
32		}
33
34		// assert if id is present
35		if value.get("id").is_none() {
36			return Err(RequestParsingError::IdMissing {
37				method: get_method(value),
38			});
39		}
40
41		// assert that method is present
42		let Some(method) = value.get("method") else {
43			let id = value.get_mut("id").map(Value::take);
44			return Err(RequestParsingError::MethodMissing { id });
45		};
46
47		// assert the method is the correct type
48		if method.as_str().is_none() {
49			let id = value.get_mut("id").map(Value::take);
50			// Note: here the unwrap_or_default() should never run as we know method is present.
51			//       however, we never want to call `unwrap()`, so, acceptable fall back.
52			let method = value.get_mut("method").map(Value::take).unwrap_or_default();
53			return Err(RequestParsingError::MethodInvalidType { id, method });
54		}
55
56		// -- serde json parse
57		let res = serde_json::from_value(value).map_err(RequestParsingError::Parse)?;
58
59		Ok(res)
60	}
61}
62
63// Returns the eventual (id, method) tuple
64fn take_rpc_ref(mut value: Value) -> (Option<Value>, Option<String>) {
65	let id = value.get_mut("id").map(Value::take);
66	let method = value.get_mut("method").and_then(|v| v.as_str().map(|s| s.to_string()));
67	(id, method)
68}
69
70fn get_method(value: Value) -> Option<String> {
71	value.get("method").and_then(|v| v.as_str().map(|s| s.to_string()))
72}
73
74/// Convenient TryFrom, and will execute the RpcRequest::from_value,
75/// which will perform the version validation.
76impl TryFrom<Value> for Request {
77	type Error = RequestParsingError;
78	fn try_from(value: Value) -> Result<Request, RequestParsingError> {
79		Request::from_value(value)
80	}
81}