Skip to content

Commit

Permalink
initial jsonrpc v1 support
Browse files Browse the repository at this point in the history
  • Loading branch information
debris committed Dec 19, 2016
1 parent cea6a79 commit 7f42294
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 31 deletions.
58 changes: 54 additions & 4 deletions core/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde_json;
use futures::{self, Future, BoxFuture};

use calls::{RemoteProcedure, Metadata, RpcMethodSync, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification};
use types::{Params, Error, ErrorCode};
use types::{Params, Error, ErrorCode, Version};
use types::{Request, Response, Call, Output};

fn read_request(request_str: &str) -> Result<Request, Error> {
Expand All @@ -17,13 +17,52 @@ fn write_response(response: Response) -> String {
serde_json::to_string(&response).unwrap()
}

/// `IoHandler` json-rpc protocol compatibility
#[derive(Clone, Copy)]
pub enum Compatibility {
/// Compatible only with JSON-RPC 1.x
V1,
/// Compatible only with JSON-RPC 2.0
V2,
/// Compatible with both
Both,
}

impl Default for Compatibility {
fn default() -> Self {
Compatibility::V2
}
}

impl Compatibility {
fn is_version_valid(&self, version: Option<Version>) -> bool {
match (*self, version) {
(Compatibility::V1, None) |
(Compatibility::V2, Some(Version::V2)) |
(Compatibility::Both, _) => true,
_ => false,
}
}
}

/// Request handler
///
/// By default compatible only with jsonrpc v2
#[derive(Default)]
pub struct MetaIoHandler<T: Metadata> {
compatibility: Compatibility,
methods: HashMap<String, RemoteProcedure<T>>,
}

impl<T: Metadata> MetaIoHandler<T> {
/// Creates new `MetaIoHandler` without any metadata compatible with specified protocol version.
pub fn new(compatibility: Compatibility) -> Self {
MetaIoHandler {
compatibility: compatibility,
methods: HashMap::default(),
}
}

/// Adds new supported synchronous method
pub fn add_method<F>(&mut self, name: &str, method: F) where
F: RpcMethodSync,
Expand Down Expand Up @@ -121,10 +160,12 @@ impl<T: Metadata> MetaIoHandler<T> {
let params = method.params.unwrap_or(Params::None);
let id = method.id;
let jsonrpc = method.jsonrpc;
let valid_version = self.compatibility.is_version_valid(jsonrpc);

let result = match self.methods.get(&method.method) {
Some(&RemoteProcedure::Method(ref method)) => method.call(params, meta),
_ => futures::failed(Error::new(ErrorCode::MethodNotFound)).boxed(),
let result = match (valid_version, self.methods.get(&method.method)) {
(false, _) => futures::failed(Error::invalid_request()).boxed(),
(true, Some(&RemoteProcedure::Method(ref method))) => method.call(params, meta),
(true, _) => futures::failed(Error::method_not_found()).boxed(),
};

result
Expand All @@ -133,6 +174,10 @@ impl<T: Metadata> MetaIoHandler<T> {
},
Call::Notification(notification) => {
let params = notification.params.unwrap_or(Params::None);
let jsonrpc = notification.jsonrpc;
if !self.compatibility.is_version_valid(jsonrpc) {
return futures::finished(None).boxed();
}

if let Some(&RemoteProcedure::Notification(ref notification)) = self.methods.get(&notification.method) {
notification.execute(params, meta);
Expand All @@ -157,6 +202,11 @@ impl IoHandler {
pub fn new() -> Self {
IoHandler::default()
}

/// Creates new `IoHandler` without any metadata compatible with specified protocol version.
pub fn new_compatible_with(compatibility: Compatibility) -> Self {
IoHandler(MetaIoHandler::new(compatibility))
}
}

impl<M: Metadata> IoHandler<M> {
Expand Down
2 changes: 1 addition & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ pub mod types;
pub mod reactor;

pub use calls::{RemoteProcedure, Metadata, RpcMethodSync, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification};
pub use io::{IoHandler, MetaIoHandler};
pub use io::{Compatibility, IoHandler, MetaIoHandler};
pub use types::*;
28 changes: 15 additions & 13 deletions core/src/types/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::{Id, Params, Version, Value};
pub struct MethodCall {
/// A String specifying the version of the JSON-RPC protocol.
/// MUST be exactly "2.0".
pub jsonrpc: Version,
pub jsonrpc: Option<Version>,
/// A String containing the name of the method to be invoked.
pub method: String,
/// A Structured value that holds the parameter values to be used
Expand All @@ -29,7 +29,7 @@ pub struct MethodCall {
pub struct Notification {
/// A String specifying the version of the JSON-RPC protocol.
/// MUST be exactly "2.0".
pub jsonrpc: Version,
pub jsonrpc: Option<Version>,
/// A String containing the name of the method to be invoked.
pub method: String,
/// A Structured value that holds the parameter values to be used
Expand Down Expand Up @@ -111,7 +111,7 @@ fn method_call_serialize() {
use serde_json::Value;

let m = MethodCall {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
id: Id::Num(1)
Expand All @@ -127,7 +127,7 @@ fn notification_serialize() {
use serde_json::Value;

let n = Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)]))
};
Expand All @@ -142,7 +142,7 @@ fn call_serialize() {
use serde_json::Value;

let n = Call::Notification(Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1)]))
});
Expand All @@ -157,13 +157,13 @@ fn request_serialize_batch() {

let batch = Request::Batch(vec![
Call::MethodCall(MethodCall {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
id: Id::Num(1)
}),
Call::Notification(Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1)]))
})
Expand All @@ -183,7 +183,7 @@ fn notification_deserialize() {
let deserialized: Notification = serde_json::from_str(s).unwrap();

assert_eq!(deserialized, Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)]))
});
Expand All @@ -192,7 +192,7 @@ fn notification_deserialize() {
let deserialized: Notification = serde_json::from_str(s).unwrap();

assert_eq!(deserialized, Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "foobar".to_owned(),
params: None
});
Expand All @@ -209,15 +209,15 @@ fn call_deserialize() {
let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1]}"#;
let deserialized: Call = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Call::Notification(Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1)]))
}));

let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1], "id": 1}"#;
let deserialized: Call = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Call::MethodCall(MethodCall {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1)])),
id: Id::Num(1)
Expand All @@ -233,20 +233,22 @@ fn request_deserialize_batch() {
assert_eq!(deserialized, Request::Batch(vec![
Call::Invalid(Id::Null),
Call::MethodCall(MethodCall {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1), Value::U64(2)])),
id: Id::Num(1)
}),
Call::Notification(Notification {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
method: "update".to_owned(),
params: Some(Params::Array(vec![Value::U64(1)]))
})
]))
}

// TODO: fix this test. it's valid now, since jsonrpc v1 does not require "version" field
#[test]
#[ignore]
fn request_invalid_returns_id() {
use serde_json;

Expand Down
24 changes: 12 additions & 12 deletions core/src/types/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::{Id, Value, Error, ErrorCode, Version};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Success {
/// Protocol version
pub jsonrpc: Version,
pub jsonrpc: Option<Version>,
/// Result
pub result: Value,
/// Correlation id
Expand All @@ -19,7 +19,7 @@ pub struct Success {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Failure {
/// Protocol Version
pub jsonrpc: Version,
pub jsonrpc: Option<Version>,
/// Error
pub error: Error,
/// Correlation id
Expand All @@ -37,7 +37,7 @@ pub enum Output {

impl Output {
/// Creates new output given `Result`, `Id` and `Version`.
pub fn from(result: Result<Value, Error>, id: Id, jsonrpc: Version) -> Self {
pub fn from(result: Result<Value, Error>, id: Id, jsonrpc: Option<Version>) -> Self {
match result {
Ok(result) => Output::Success(Success {
id: id,
Expand All @@ -56,7 +56,7 @@ impl Output {
pub fn invalid_request(id: Id) -> Self {
Output::Failure(Failure {
id: id,
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
error: Error::new(ErrorCode::InvalidRequest),
})
}
Expand Down Expand Up @@ -115,7 +115,7 @@ impl From<Error> for Response {
fn from(error: Error) -> Self {
Failure {
id: Id::Null,
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
error: error,
}.into()
}
Expand All @@ -139,7 +139,7 @@ fn success_output_serialize() {
use serde_json::Value;

let so = Output::Success(Success {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
result: Value::U64(1),
id: Id::Num(1)
});
Expand All @@ -157,7 +157,7 @@ fn success_output_deserialize() {

let deserialized: Output = serde_json::from_str(dso).unwrap();
assert_eq!(deserialized, Output::Success(Success {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
result: Value::U64(1),
id: Id::Num(1)
}));
Expand All @@ -168,7 +168,7 @@ fn failure_output_serialize() {
use serde_json;

let fo = Output::Failure(Failure {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
error: Error::parse_error(),
id: Id::Num(1)
});
Expand All @@ -185,7 +185,7 @@ fn failure_output_deserialize() {

let deserialized: Output = serde_json::from_str(dfo).unwrap();
assert_eq!(deserialized, Output::Failure(Failure {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
error: Error::parse_error(),
id: Id::Num(1)
}));
Expand All @@ -200,7 +200,7 @@ fn single_response_deserialize() {

let deserialized: Response = serde_json::from_str(dsr).unwrap();
assert_eq!(deserialized, Response::Single(Output::Success(Success {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
result: Value::U64(1),
id: Id::Num(1)
})));
Expand All @@ -216,12 +216,12 @@ fn batch_response_deserialize() {
let deserialized: Response = serde_json::from_str(dbr).unwrap();
assert_eq!(deserialized, Response::Batch(vec![
Output::Success(Success {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
result: Value::U64(1),
id: Id::Num(1)
}),
Output::Failure(Failure {
jsonrpc: Version::V2,
jsonrpc: Some(Version::V2),
error: Error::parse_error(),
id: Id::Num(1)
})
Expand Down
2 changes: 1 addition & 1 deletion core/src/types/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Serialize, Serializer, Deserialize, Deserializer, Error};
use serde::de::Visitor;

/// Protocol Version
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Version {
/// JSONRPC 2.0
V2
Expand Down

0 comments on commit 7f42294

Please sign in to comment.