mirror of
https://github.com/mimblewimble/grin.git
synced 2025-05-06 17:21:15 +03:00
Resolve JSON RPC 2.0 non-compliance (#3213)
Currently, the JSON RPC requests and responses are not compliant with the spec as the server only accepts strings. This change is *non-breaking* as it simply adds the ability to understand integers as expected. Any old code continuing to only send string requests will continue to work as before. Unit tests are included to ensure (de)serialization works on both strings and integers.
This commit is contained in:
parent
dcdbdd4bcc
commit
b01ab44475
1 changed files with 159 additions and 5 deletions
|
@ -52,17 +52,26 @@ type Tx = mpsc::UnboundedSender<String>;
|
|||
// http://www.jsonrpc.org/specification
|
||||
// RPC Methods
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
/// Represents a compliant JSON RPC 2.0 id.
|
||||
/// Valid id: Integer, String.
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
enum JsonId {
|
||||
IntId(u32),
|
||||
StrId(String),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
struct RpcRequest {
|
||||
id: String,
|
||||
id: JsonId,
|
||||
jsonrpc: String,
|
||||
method: String,
|
||||
params: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
struct RpcResponse {
|
||||
id: String,
|
||||
id: JsonId,
|
||||
jsonrpc: String,
|
||||
method: String,
|
||||
result: Option<Value>,
|
||||
|
@ -501,7 +510,7 @@ impl Handler {
|
|||
// Issue #1159 - use a serde_json Value type to avoid extra quoting
|
||||
let job_template_value: Value = serde_json::from_str(&job_template_json).unwrap();
|
||||
let job_request = RpcRequest {
|
||||
id: String::from("Stratum"),
|
||||
id: JsonId::StrId(String::from("Stratum")),
|
||||
jsonrpc: String::from("2.0"),
|
||||
method: String::from("job"),
|
||||
params: Some(job_template_value),
|
||||
|
@ -865,3 +874,148 @@ where
|
|||
.and_then(|v| serde_json::from_value(v).ok())
|
||||
.ok_or(RpcError::invalid_request())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Tests deserializing an `RpcRequest` given a String as the id.
|
||||
#[test]
|
||||
fn test_request_deserialize_str() {
|
||||
let expected = RpcRequest {
|
||||
id: JsonId::StrId(String::from("1")),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
params: None,
|
||||
};
|
||||
let json = r#"{"id":"1","method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let serialized: RpcRequest = serde_json::from_str(json).unwrap();
|
||||
|
||||
assert_eq!(expected, serialized);
|
||||
}
|
||||
|
||||
/// Tests serializing an `RpcRequest` given a String as the id.
|
||||
/// The extra step of deserializing again is due to associative structures not maintaining order.
|
||||
#[test]
|
||||
fn test_request_serialize_str() {
|
||||
let expected = r#"{"id":"1","method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let rpc = RpcRequest {
|
||||
id: JsonId::StrId(String::from("1")),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
params: None,
|
||||
};
|
||||
let json_actual = serde_json::to_string(&rpc).unwrap();
|
||||
|
||||
let expected_deserialized: RpcRequest = serde_json::from_str(expected).unwrap();
|
||||
let actual_deserialized: RpcRequest = serde_json::from_str(&json_actual).unwrap();
|
||||
|
||||
assert_eq!(expected_deserialized, actual_deserialized);
|
||||
}
|
||||
|
||||
/// Tests deserializing an `RpcResponse` given a String as the id.
|
||||
#[test]
|
||||
fn test_response_deserialize_str() {
|
||||
let expected = RpcResponse {
|
||||
id: JsonId::StrId(String::from("1")),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
result: None,
|
||||
error: None,
|
||||
};
|
||||
let json = r#"{"id":"1","method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let serialized: RpcResponse = serde_json::from_str(json).unwrap();
|
||||
|
||||
assert_eq!(expected, serialized);
|
||||
}
|
||||
|
||||
/// Tests serializing an `RpcResponse` given a String as the id.
|
||||
/// The extra step of deserializing again is due to associative structures not maintaining order.
|
||||
#[test]
|
||||
fn test_response_serialize_str() {
|
||||
let expected = r#"{"id":"1","method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let rpc = RpcResponse {
|
||||
id: JsonId::StrId(String::from("1")),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
result: None,
|
||||
error: None,
|
||||
};
|
||||
let json_actual = serde_json::to_string(&rpc).unwrap();
|
||||
|
||||
let expected_deserialized: RpcResponse = serde_json::from_str(expected).unwrap();
|
||||
let actual_deserialized: RpcResponse = serde_json::from_str(&json_actual).unwrap();
|
||||
|
||||
assert_eq!(expected_deserialized, actual_deserialized);
|
||||
}
|
||||
|
||||
/// Tests deserializing an `RpcRequest` given an integer as the id.
|
||||
#[test]
|
||||
fn test_request_deserialize_int() {
|
||||
let expected = RpcRequest {
|
||||
id: JsonId::IntId(1),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
params: None,
|
||||
};
|
||||
let json = r#"{"id":1,"method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let serialized: RpcRequest = serde_json::from_str(json).unwrap();
|
||||
|
||||
assert_eq!(expected, serialized);
|
||||
}
|
||||
|
||||
/// Tests serializing an `RpcRequest` given an integer as the id.
|
||||
/// The extra step of deserializing again is due to associative structures not maintaining order.
|
||||
#[test]
|
||||
fn test_request_serialize_int() {
|
||||
let expected = r#"{"id":1,"method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let rpc = RpcRequest {
|
||||
id: JsonId::IntId(1),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
params: None,
|
||||
};
|
||||
let json_actual = serde_json::to_string(&rpc).unwrap();
|
||||
|
||||
let expected_deserialized: RpcRequest = serde_json::from_str(expected).unwrap();
|
||||
let actual_deserialized: RpcRequest = serde_json::from_str(&json_actual).unwrap();
|
||||
|
||||
assert_eq!(expected_deserialized, actual_deserialized);
|
||||
}
|
||||
|
||||
/// Tests deserializing an `RpcResponse` given an integer as the id.
|
||||
#[test]
|
||||
fn test_response_deserialize_int() {
|
||||
let expected = RpcResponse {
|
||||
id: JsonId::IntId(1),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
result: None,
|
||||
error: None,
|
||||
};
|
||||
let json = r#"{"id":1,"method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let serialized: RpcResponse = serde_json::from_str(json).unwrap();
|
||||
|
||||
assert_eq!(expected, serialized);
|
||||
}
|
||||
|
||||
/// Tests serializing an `RpcResponse` given an integer as the id.
|
||||
/// The extra step of deserializing again is due to associative structures not maintaining order.
|
||||
#[test]
|
||||
fn test_response_serialize_int() {
|
||||
let expected = r#"{"id":1,"method":"login","jsonrpc":"2.0","params":null}"#;
|
||||
let rpc = RpcResponse {
|
||||
id: JsonId::IntId(1),
|
||||
method: String::from("login"),
|
||||
jsonrpc: String::from("2.0"),
|
||||
result: None,
|
||||
error: None,
|
||||
};
|
||||
let json_actual = serde_json::to_string(&rpc).unwrap();
|
||||
|
||||
let expected_deserialized: RpcResponse = serde_json::from_str(expected).unwrap();
|
||||
let actual_deserialized: RpcResponse = serde_json::from_str(&json_actual).unwrap();
|
||||
|
||||
assert_eq!(expected_deserialized, actual_deserialized);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue