From 9ecbf099ca17180fc07c4a5eae8ad20f8b5d77f4 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 7 Dec 2018 23:39:05 -0700 Subject: [PATCH 001/149] Extend build_rpc_trait fix to pubsub (#346) --- macros/src/auto_args.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index f9af4df49..e813e0d0d 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -188,12 +188,14 @@ macro_rules! build_rpc_trait { )* ); - $( - $(#[doc=$sub_doc])* - fn $sub_name ( $($sub_p)* ); - $(#[doc=$unsub_doc])* - fn $unsub_name ( $($unsub_p)* ) -> $sub_result <$sub_out $(, $error_unsub)* >; - )* + build_rpc_trait!(GENERATE_FUNCTIONS + $( + $(#[doc=$sub_doc])* + fn $sub_name ( $( $sub_p )* ); + $(#[doc=$unsub_doc])* + fn $unsub_name ( $( $unsub_p )* ) -> $sub_result <$sub_out $(, $error_unsub) *>; + )* + ); /// Transform this into an `IoDelegate`, automatically wrapping /// the parameters. @@ -227,12 +229,12 @@ macro_rules! build_rpc_trait { (GENERATE_FUNCTIONS $( $( #[doc=$m_doc:expr] )* - fn $m_name: ident (&self $(, $p: ty)* ) -> $result: ty; + fn $m_name: ident (&self $(, $p: ty)* ) $( -> $result: ty)*; )* ) => { $( $(#[doc=$m_doc])* - fn $m_name (&self $(, _: $p )* ) -> $result; + fn $m_name (&self $(, _: $p )* ) $( -> $result)*; )* }; From 0d78b8f145c18f08c1103f6b0b51991a89fb0a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 10 Dec 2018 10:00:33 +0000 Subject: [PATCH 002/149] Add metadata to unsubscribe. (#344) --- macros/examples/pubsub-macros.rs | 4 ++-- macros/src/auto_args.rs | 8 ++++---- macros/src/delegates.rs | 11 ++++++----- macros/tests/pubsub-macros.rs | 4 ++-- pubsub/examples/pubsub.rs | 14 +++++++++++--- pubsub/examples/pubsub_simple.rs | 2 +- pubsub/more-examples/examples/pubsub_ipc.rs | 2 +- pubsub/more-examples/examples/pubsub_ws.rs | 2 +- pubsub/src/handler.rs | 16 ++++++++-------- pubsub/src/subscription.rs | 18 +++++++++--------- 10 files changed, 45 insertions(+), 36 deletions(-) diff --git a/macros/examples/pubsub-macros.rs b/macros/examples/pubsub-macros.rs index 12d4a834d..4f474bbb0 100644 --- a/macros/examples/pubsub-macros.rs +++ b/macros/examples/pubsub-macros.rs @@ -29,7 +29,7 @@ build_rpc_trait! { /// Unsubscribe from hello subscription. #[rpc(name = "hello_unsubscribe")] - fn unsubscribe(&self, SubscriptionId) -> Result; + fn unsubscribe(&self, Self::Metadata, SubscriptionId) -> Result; } } } @@ -62,7 +62,7 @@ impl Rpc for RpcImpl { self.active.write().unwrap().insert(sub_id, sink); } - fn unsubscribe(&self, id: SubscriptionId) -> Result { + fn unsubscribe(&self, _meta: Self::Metadata, id: SubscriptionId) -> Result { let removed = self.active.write().unwrap().remove(&id); if removed.is_some() { Ok(true) diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index e813e0d0d..01df9512d 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -48,7 +48,7 @@ use util::{invalid_params, expect_no_params, to_value}; /// #[rpc(name = "hello_subscribe")] /// fn subscribe(&self, Self::Metadata, pubsub::Subscriber, u64); /// #[rpc(name = "hello_unsubscribe")] -/// fn unsubscribe(&self, SubscriptionId) -> Result; +/// fn unsubscribe(&self, Self::Metadata, SubscriptionId) -> Result; /// } /// ``` /// @@ -257,7 +257,7 @@ macro_rules! build_rpc_trait { subscribe: (name = $subscribe: expr $(, alias = [ $( $sub_alias: expr, )+ ])*) fn $sub_method: ident (&self, Self::Metadata $(, $sub_p: ty)+); unsubscribe: (name = $unsubscribe: expr $(, alias = [ $( $unsub_alias: expr, )+ ])*) - fn $unsub_method: ident (&self $(, $unsub_p: ty)+) -> $result: tt <$out: ty $(, $error_unsub: ty)* >; + fn $unsub_method: ident (&self, Self::Metadata $(, $unsub_p: ty)+) -> $result: tt <$out: ty $(, $error_unsub: ty)* >; ) => { $del.add_subscription( $name, @@ -270,9 +270,9 @@ macro_rules! build_rpc_trait { subscriber, ) }), - ($unsubscribe, move |base, id| { + ($unsubscribe, move |base, id, meta| { use $crate::jsonrpc_core::futures::{IntoFuture, Future}; - Self::$unsub_method(base, id).into_future() + Self::$unsub_method(base, meta, id).into_future() .map($crate::to_value) .map_err(Into::into) }), diff --git a/macros/src/delegates.rs b/macros/src/delegates.rs index 245536333..730020f1d 100644 --- a/macros/src/delegates.rs +++ b/macros/src/delegates.rs @@ -84,17 +84,18 @@ struct DelegateUnsubscribe { closure: F, } -impl jsonrpc_pubsub::UnsubscribeRpcMethod for DelegateUnsubscribe where - F: Fn(&T, SubscriptionId) -> I, +impl jsonrpc_pubsub::UnsubscribeRpcMethod for DelegateUnsubscribe where + M: PubSubMetadata, + F: Fn(&T, SubscriptionId, M) -> I, I: IntoFuture, T: Send + Sync + 'static, F: Send + Sync + 'static, I::Future: Send + 'static, { type Out = I::Future; - fn call(&self, id: SubscriptionId) -> Self::Out { + fn call(&self, id: SubscriptionId, meta: M) -> Self::Out { let closure = &self.closure; - closure(&self.delegate, id).into_future() + closure(&self.delegate, id, meta).into_future() } } @@ -182,7 +183,7 @@ impl IoDelegate where ) where Sub: Fn(&T, Params, M, Subscriber), Sub: Send + Sync + 'static, - Unsub: Fn(&T, SubscriptionId) -> I, + Unsub: Fn(&T, SubscriptionId, M) -> I, I: IntoFuture, Unsub: Send + Sync + 'static, I::Future: Send + 'static, diff --git a/macros/tests/pubsub-macros.rs b/macros/tests/pubsub-macros.rs index 609321486..866e9c264 100644 --- a/macros/tests/pubsub-macros.rs +++ b/macros/tests/pubsub-macros.rs @@ -29,7 +29,7 @@ build_rpc_trait! { /// Unsubscribe from hello subscription. #[rpc(name = "hello_unsubscribe")] - fn unsubscribe(&self, SubscriptionId) -> Result; + fn unsubscribe(&self, Self::Metadata, SubscriptionId) -> Result; } } } @@ -44,7 +44,7 @@ impl Rpc for RpcImpl { let _sink = subscriber.assign_id(SubscriptionId::Number(5)); } - fn unsubscribe(&self, _id: SubscriptionId) -> Result { + fn unsubscribe(&self, _meta: Self::Metadata, _id: SubscriptionId) -> Result { Ok(true) } } diff --git a/pubsub/examples/pubsub.rs b/pubsub/examples/pubsub.rs index 758baa2af..a598003ff 100644 --- a/pubsub/examples/pubsub.rs +++ b/pubsub/examples/pubsub.rs @@ -3,7 +3,7 @@ extern crate jsonrpc_pubsub; extern crate jsonrpc_tcp_server; use std::{time, thread}; -use std::sync::Arc; +use std::sync::{Arc, atomic}; use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; @@ -24,9 +24,11 @@ fn main() { Ok(Value::String("hello".to_string())) }); + let is_done = Arc::new(atomic::AtomicBool::default()); + let is_done2 = is_done.clone(); io.add_subscription( "hello", - ("subscribe_hello", |params: Params, _, subscriber: Subscriber| { + ("subscribe_hello", move |params: Params, _, subscriber: Subscriber| { if params != Params::None { subscriber.reject(Error { code: ErrorCode::ParseError, @@ -39,8 +41,13 @@ fn main() { let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); // or subscriber.reject(Error {} ); // or drop(subscriber) + let is_done = is_done.clone(); thread::spawn(move || { loop { + if is_done.load(atomic::Ordering::AcqRel) { + return; + } + thread::sleep(time::Duration::from_millis(100)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { Ok(_) => {}, @@ -52,8 +59,9 @@ fn main() { } }); }), - ("remove_hello", |_id: SubscriptionId| { + ("remove_hello", move |_id: SubscriptionId, _| { println!("Closing subscription"); + is_done2.store(true, atomic::Ordering::AcqRel); futures::future::ok(Value::Bool(true)) }), ); diff --git a/pubsub/examples/pubsub_simple.rs b/pubsub/examples/pubsub_simple.rs index fe85f3f44..db31d02d4 100644 --- a/pubsub/examples/pubsub_simple.rs +++ b/pubsub/examples/pubsub_simple.rs @@ -52,7 +52,7 @@ fn main() { } }); }), - ("remove_hello", |_id: SubscriptionId| { + ("remove_hello", |_id: SubscriptionId, _| { println!("Closing subscription"); futures::future::ok(Value::Bool(true)) }), diff --git a/pubsub/more-examples/examples/pubsub_ipc.rs b/pubsub/more-examples/examples/pubsub_ipc.rs index 8ffb1e3ba..2007820e6 100644 --- a/pubsub/more-examples/examples/pubsub_ipc.rs +++ b/pubsub/more-examples/examples/pubsub_ipc.rs @@ -52,7 +52,7 @@ fn main() { } }); }), - ("remove_hello", |_id: SubscriptionId| -> Result { + ("remove_hello", |_id: SubscriptionId, _meta| -> Result { println!("Closing subscription"); Ok(Value::Bool(true)) }), diff --git a/pubsub/more-examples/examples/pubsub_ws.rs b/pubsub/more-examples/examples/pubsub_ws.rs index bbec658f5..fd7e362f4 100644 --- a/pubsub/more-examples/examples/pubsub_ws.rs +++ b/pubsub/more-examples/examples/pubsub_ws.rs @@ -68,7 +68,7 @@ fn main() { } }); }), - ("remove_hello", |_id: SubscriptionId| -> BoxFuture { + ("remove_hello", |_id: SubscriptionId, _meta| -> BoxFuture { println!("Closing subscription"); Box::new(futures::future::ok(Value::Bool(true))) }), diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index eb7b0b243..9b73bd17e 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -20,21 +20,21 @@ impl SubscribeRpcMethod for F where } /// Unsubscribe handler -pub trait UnsubscribeRpcMethod: Send + Sync + 'static { +pub trait UnsubscribeRpcMethod: Send + Sync + 'static { /// Output type type Out: Future + Send + 'static; /// Called when client is requesting to cancel existing subscription. - fn call(&self, id: SubscriptionId) -> Self::Out; + fn call(&self, id: SubscriptionId, meta: M) -> Self::Out; } -impl UnsubscribeRpcMethod for F where - F: Fn(SubscriptionId) -> I + Send + Sync + 'static, +impl UnsubscribeRpcMethod for F where + F: Fn(SubscriptionId, M) -> I + Send + Sync + 'static, I: IntoFuture, I::Future: Send + 'static, { type Out = I::Future; - fn call(&self, id: SubscriptionId) -> Self::Out { - (*self)(id).into_future() + fn call(&self, id: SubscriptionId, meta: M) -> Self::Out { + (*self)(id, meta).into_future() } } @@ -67,7 +67,7 @@ impl> PubSubHandler { unsubscribe: (&str, G), ) where F: SubscribeRpcMethod, - G: UnsubscribeRpcMethod, + G: UnsubscribeRpcMethod, { let (sub, unsub) = new_subscription(notification, subscribe.1, unsubscribe.1); self.handler.add_method_with_meta(subscribe.0, sub); @@ -130,7 +130,7 @@ mod tests { assert_eq!(params, core::Params::None); let _sink = subscriber.assign_id(SubscriptionId::Number(5)); }), - ("unsubscribe_hello", move |id| { + ("unsubscribe_hello", move |id, _meta| { // Should be called because session is dropped. called2.store(true, Ordering::SeqCst); assert_eq!(id, SubscriptionId::Number(5)); diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 7e34778a3..66c333860 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -188,18 +188,18 @@ impl Subscriber { pub fn new_subscription(notification: &str, subscribe: F, unsubscribe: G) -> (Subscribe, Unsubscribe) where M: PubSubMetadata, F: SubscribeRpcMethod, - G: UnsubscribeRpcMethod, + G: UnsubscribeRpcMethod, { let unsubscribe = Arc::new(unsubscribe); let subscribe = Subscribe { notification: notification.to_owned(), - subscribe: subscribe, unsubscribe: unsubscribe.clone(), + subscribe, }; let unsubscribe = Unsubscribe { notification: notification.into(), - unsubscribe: unsubscribe, + unsubscribe, }; (subscribe, unsubscribe) @@ -231,7 +231,7 @@ pub struct Subscribe { impl core::RpcMethod for Subscribe where M: PubSubMetadata, F: SubscribeRpcMethod, - G: UnsubscribeRpcMethod, + G: UnsubscribeRpcMethod, { fn call(&self, params: core::Params, meta: M) -> BoxFuture { match meta.session() { @@ -244,7 +244,7 @@ impl core::RpcMethod for Subscribe where transport: session.sender(), sender: tx, }; - self.subscribe.call(params, meta, subscriber); + self.subscribe.call(params, meta.clone(), subscriber); let unsub = self.unsubscribe.clone(); let notification = self.notification.clone(); @@ -254,7 +254,7 @@ impl core::RpcMethod for Subscribe where futures::done(match result { Ok(id) => { session.add_subscription(¬ification, &id, move |id| { - let _ = unsub.call(id).wait(); + let _ = unsub.call(id, meta.clone()).wait(); }); Ok(id.into()) }, @@ -276,7 +276,7 @@ pub struct Unsubscribe { impl core::RpcMethod for Unsubscribe where M: PubSubMetadata, - G: UnsubscribeRpcMethod, + G: UnsubscribeRpcMethod, { fn call(&self, params: core::Params, meta: M) -> BoxFuture { let id = match params { @@ -288,7 +288,7 @@ impl core::RpcMethod for Unsubscribe where match (meta.session(), id) { (Some(session), Some(id)) => { session.remove_subscription(&self.notification, &id); - Box::new(self.unsubscribe.call(id)) + Box::new(self.unsubscribe.call(id, meta)) }, (Some(_), None) => Box::new(future::err(core::Error::invalid_params("Expected subscription id."))), _ => Box::new(future::err(subscriptions_unavailable())), @@ -458,7 +458,7 @@ mod tests { assert_eq!(params, core::Params::None); called2.store(true, Ordering::SeqCst); }, - |_id| Ok(core::Value::Bool(true)), + |_id, _meta| Ok(core::Value::Bool(true)), ); let meta = Metadata; From cd27b905868d7919bed6ec8113e846c459e93831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 11 Dec 2018 10:11:14 +0000 Subject: [PATCH 003/149] Test without pretty print (#347) * test: add make_request fn that returns value without pretty printing * use encoding enum --- test/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/test/src/lib.rs b/test/src/lib.rs index 6392eabd5..fbe03ccad 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -70,6 +70,11 @@ pub struct Rpc { pub options: Options, } +pub enum Encoding { + Compact, + Pretty, +} + impl From for Rpc { fn from(io: rpc::IoHandler) -> Self { Rpc { io, ..Default::default() } @@ -86,9 +91,21 @@ impl Rpc { io.into() } - /// Perform a single, synchronous method call. + /// Perform a single, synchronous method call and return pretty-printed value pub fn request(&self, method: &str, params: &T) -> String where T: serde::Serialize, + { + self.make_request(method, params, Encoding::Pretty) + } + + /// Perform a single, synchronous method call. + pub fn make_request( + &self, + method: &str, + params: &T, + encoding: Encoding, + ) -> String where + T: serde::Serialize, { use self::rpc::types::response; @@ -104,11 +121,20 @@ impl Rpc { // extract interesting part from the response let extracted = match serde_json::from_str(&response).expect("We will always get a single output.") { - response::Output::Success(response::Success { result, .. }) => serde_json::to_string_pretty(&result), - response::Output::Failure(response::Failure { error, .. }) => serde_json::to_string_pretty(&error), + response::Output::Success(response::Success { result, .. }) => { + match encoding { + Encoding::Compact => serde_json::to_string(&result), + Encoding::Pretty => serde_json::to_string_pretty(&result), + } + }, + response::Output::Failure(response::Failure { error, .. }) => { + match encoding { + Encoding::Compact => serde_json::to_string(&error), + Encoding::Pretty => serde_json::to_string_pretty(&error), + } + }, }.expect("Serialization is infallible; qed"); - println!("\n{}\n --> {}\n", request, extracted); extracted @@ -120,12 +146,12 @@ mod tests { use super::*; #[test] - fn should_test_simple_method() { + fn should_test_request_is_pretty() { // given let rpc = { let mut io = rpc::IoHandler::new(); io.add_method("test_method", |_| { - Ok(rpc::Value::Number(5.into())) + Ok(rpc::Value::Array(vec![5.into(), 10.into()])) }); Rpc::from(io) }; @@ -133,7 +159,25 @@ mod tests { // when assert_eq!( rpc.request("test_method", &[5u64]), - r#"5"# + "[\n 5,\n 10\n]" + ); + } + + #[test] + fn should_test_make_request_compact() { + // given + let rpc = { + let mut io = rpc::IoHandler::new(); + io.add_method("test_method", |_| { + Ok(rpc::Value::Array(vec![5.into(), 10.into()])) + }); + Rpc::from(io) + }; + + // when + assert_eq!( + rpc.make_request("test_method", &[5u64], Encoding::Compact), + "[5,10]" ); } } From 2ef6d924dc1dd81c2553b55bfa45ee5e67f8790e Mon Sep 17 00:00:00 2001 From: Seun LanLege Date: Thu, 3 Jan 2019 11:55:46 +0100 Subject: [PATCH 004/149] case insensitive charset matching (#350) * case insensitive charset matching * added test * revert vscode formatting, added e2e test * use eq_ignore_ascii_case, match without spacing * match without spacing --- http/src/handler.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- http/src/tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/http/src/handler.rs b/http/src/handler.rs index 63e6631cb..cd763eb72 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -562,10 +562,49 @@ impl> RpcHandler { /// Returns true if the `content_type` header indicates a valid JSON /// message. fn is_json(content_type: Option<&header::HeaderValue>) -> bool { - match content_type.and_then(|val| val.to_str().ok()) { - Some("application/json") => true, - Some("application/json; charset=utf-8") => true, + match content_type.and_then(|val| val.to_str().map(str::to_lowercase).ok()) { + Some(ref content) + if content.eq_ignore_ascii_case("application/json") + || content.eq_ignore_ascii_case("application/json; charset=utf-8") + || content.eq_ignore_ascii_case("application/json;charset=utf-8") => + { + true + }, _ => false, } } } + +#[cfg(test)] +mod test { + use super::{hyper, RpcHandler}; + use jsonrpc_core::middleware::Noop; + + #[test] + fn test_case_insensitive_content_type() { + let request = hyper::Request::builder() + .header("content-type", "Application/Json; charset=UTF-8") + .body(()) + .unwrap(); + + let request2 = hyper::Request::builder() + .header("content-type", "Application/Json;charset=UTF-8") + .body(()) + .unwrap(); + + + assert_eq!( + request.headers().get("content-type").unwrap(), + &"Application/Json; charset=UTF-8" + ); + + assert_eq!( + RpcHandler::<(), Noop>::is_json(request.headers().get("content-type")), + true + ); + assert_eq!( + RpcHandler::<(), Noop>::is_json(request2.headers().get("content-type")), + true + ); + } +} \ No newline at end of file diff --git a/http/src/tests.rs b/http/src/tests.rs index 3f063d22d..819c37f28 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -1114,6 +1114,31 @@ fn should_handle_rest_request_with_params() { assert_eq!(response.body, world_5()); } +#[test] +fn should_handle_rest_request_with_case_insensitive_content_type() { + // given + let server = serve(id); + let addr = server.address().clone(); + + // when + let req = ""; + let response = request(server, + &format!("\ + POST /hello/5 HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: Application/JSON; charset=UTF-8\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", addr.port(), req.as_bytes().len(), req) + ); + + // then + assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + assert_eq!(response.body, world_5()); +} + #[test] fn should_return_error_in_case_of_unsecure_rest_and_no_method() { // given From 3604d9990f051b8b440961b0877a46140bb87a50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 4 Jan 2019 10:23:04 +0100 Subject: [PATCH 005/149] Update parking_lot requirement from 0.6 to 0.7 (#338) Updates the requirements on [parking_lot](https://github.com/Amanieu/parking_lot) to permit the latest version. - [Release notes](https://github.com/Amanieu/parking_lot/releases) - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/commits) Signed-off-by: dependabot[bot] --- ipc/Cargo.toml | 2 +- minihttp/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 16b1e228e..5a76375be 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -14,7 +14,7 @@ tokio-service = "0.1" jsonrpc-core = { version = "9.0", path = "../core" } jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } parity-tokio-ipc = "0.1" -parking_lot = "0.6" +parking_lot = "0.7" [dev-dependencies] env_logger = "0.6" diff --git a/minihttp/Cargo.toml b/minihttp/Cargo.toml index 84d52a64c..90bf02704 100644 --- a/minihttp/Cargo.toml +++ b/minihttp/Cargo.toml @@ -14,7 +14,7 @@ bytes = "0.4" jsonrpc-core = { version = "9.0", path = "../core" } jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } log = "0.4" -parking_lot = "0.6" +parking_lot = "0.7" tokio-minihttp = { git = "https://github.com/tomusdrw/tokio-minihttp" } tokio-proto = { git = "https://github.com/tomusdrw/tokio-proto" } tokio-service = "0.1" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 52372970a..9386eeabd 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -11,7 +11,7 @@ documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_pubsub/index.html" [dependencies] log = "0.4" -parking_lot = "0.6" +parking_lot = "0.7" jsonrpc-core = { version = "9.0", path = "../core" } [dev-dependencies] diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 401f09930..753405a43 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -10,7 +10,7 @@ documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_tcp_server/index.h [dependencies] log = "0.4" -parking_lot = "0.6" +parking_lot = "0.7" tokio-service = "0.1" jsonrpc-core = { version = "9.0", path = "../core" } jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 2a6e857c5..95dd1d7a9 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -13,7 +13,7 @@ error-chain = "0.12" jsonrpc-core = { version = "9.0", path = "../core" } jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } log = "0.4" -parking_lot = "0.6" +parking_lot = "0.7" slab = "0.4" ws = { git = "https://github.com/tomusdrw/ws-rs" } From b4d2986b71a474875f5db473bcafd8c4ff09f0b5 Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Tue, 8 Jan 2019 13:18:30 +0300 Subject: [PATCH 006/149] travis: disable cache on Windows? (#348) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 061da8d0e..5d97870bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ matrix: rust: stable - os: windows rust: stable + cache: false allow_failures: - rust: nightly From 711ce2a27fe8dca81493b02363c70b6295bb8aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 8 Jan 2019 12:01:46 +0100 Subject: [PATCH 007/149] Remove extra allocation. (#353) --- http/src/handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http/src/handler.rs b/http/src/handler.rs index cd763eb72..c29829767 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -562,7 +562,7 @@ impl> RpcHandler { /// Returns true if the `content_type` header indicates a valid JSON /// message. fn is_json(content_type: Option<&header::HeaderValue>) -> bool { - match content_type.and_then(|val| val.to_str().map(str::to_lowercase).ok()) { + match content_type.and_then(|val| val.to_str().ok()) { Some(ref content) if content.eq_ignore_ascii_case("application/json") || content.eq_ignore_ascii_case("application/json; charset=utf-8") @@ -607,4 +607,4 @@ mod test { true ); } -} \ No newline at end of file +} From 0227e70fdce25cad9c71be222dc9e7f754c08033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 8 Jan 2019 13:09:41 +0100 Subject: [PATCH 008/149] Optional metadata for unsubscribe. (#352) --- macros/examples/pubsub-macros.rs | 4 ++-- macros/src/auto_args.rs | 4 ++-- macros/src/delegates.rs | 6 +++--- macros/tests/pubsub-macros.rs | 4 ++-- pubsub/src/handler.rs | 18 ++++++++++-------- pubsub/src/subscription.rs | 6 +++--- pubsub/src/types.rs | 4 ++++ 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/macros/examples/pubsub-macros.rs b/macros/examples/pubsub-macros.rs index 4f474bbb0..b833c5699 100644 --- a/macros/examples/pubsub-macros.rs +++ b/macros/examples/pubsub-macros.rs @@ -29,7 +29,7 @@ build_rpc_trait! { /// Unsubscribe from hello subscription. #[rpc(name = "hello_unsubscribe")] - fn unsubscribe(&self, Self::Metadata, SubscriptionId) -> Result; + fn unsubscribe(&self, Option, SubscriptionId) -> Result; } } } @@ -62,7 +62,7 @@ impl Rpc for RpcImpl { self.active.write().unwrap().insert(sub_id, sink); } - fn unsubscribe(&self, _meta: Self::Metadata, id: SubscriptionId) -> Result { + fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { let removed = self.active.write().unwrap().remove(&id); if removed.is_some() { Ok(true) diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index 01df9512d..6d6e642aa 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -48,7 +48,7 @@ use util::{invalid_params, expect_no_params, to_value}; /// #[rpc(name = "hello_subscribe")] /// fn subscribe(&self, Self::Metadata, pubsub::Subscriber, u64); /// #[rpc(name = "hello_unsubscribe")] -/// fn unsubscribe(&self, Self::Metadata, SubscriptionId) -> Result; +/// fn unsubscribe(&self, Option, SubscriptionId) -> Result; /// } /// ``` /// @@ -257,7 +257,7 @@ macro_rules! build_rpc_trait { subscribe: (name = $subscribe: expr $(, alias = [ $( $sub_alias: expr, )+ ])*) fn $sub_method: ident (&self, Self::Metadata $(, $sub_p: ty)+); unsubscribe: (name = $unsubscribe: expr $(, alias = [ $( $unsub_alias: expr, )+ ])*) - fn $unsub_method: ident (&self, Self::Metadata $(, $unsub_p: ty)+) -> $result: tt <$out: ty $(, $error_unsub: ty)* >; + fn $unsub_method: ident (&self, Option < Self::Metadata > $(, $unsub_p: ty)+) -> $result: tt <$out: ty $(, $error_unsub: ty)* >; ) => { $del.add_subscription( $name, diff --git a/macros/src/delegates.rs b/macros/src/delegates.rs index 730020f1d..10b2608fb 100644 --- a/macros/src/delegates.rs +++ b/macros/src/delegates.rs @@ -86,14 +86,14 @@ struct DelegateUnsubscribe { impl jsonrpc_pubsub::UnsubscribeRpcMethod for DelegateUnsubscribe where M: PubSubMetadata, - F: Fn(&T, SubscriptionId, M) -> I, + F: Fn(&T, SubscriptionId, Option) -> I, I: IntoFuture, T: Send + Sync + 'static, F: Send + Sync + 'static, I::Future: Send + 'static, { type Out = I::Future; - fn call(&self, id: SubscriptionId, meta: M) -> Self::Out { + fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out { let closure = &self.closure; closure(&self.delegate, id, meta).into_future() } @@ -183,7 +183,7 @@ impl IoDelegate where ) where Sub: Fn(&T, Params, M, Subscriber), Sub: Send + Sync + 'static, - Unsub: Fn(&T, SubscriptionId, M) -> I, + Unsub: Fn(&T, SubscriptionId, Option) -> I, I: IntoFuture, Unsub: Send + Sync + 'static, I::Future: Send + 'static, diff --git a/macros/tests/pubsub-macros.rs b/macros/tests/pubsub-macros.rs index 866e9c264..943c8e25f 100644 --- a/macros/tests/pubsub-macros.rs +++ b/macros/tests/pubsub-macros.rs @@ -29,7 +29,7 @@ build_rpc_trait! { /// Unsubscribe from hello subscription. #[rpc(name = "hello_unsubscribe")] - fn unsubscribe(&self, Self::Metadata, SubscriptionId) -> Result; + fn unsubscribe(&self, Option, SubscriptionId) -> Result; } } } @@ -44,7 +44,7 @@ impl Rpc for RpcImpl { let _sink = subscriber.assign_id(SubscriptionId::Number(5)); } - fn unsubscribe(&self, _meta: Self::Metadata, _id: SubscriptionId) -> Result { + fn unsubscribe(&self, _meta: Option, _id: SubscriptionId) -> Result { Ok(true) } } diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index 9b73bd17e..bf8675de1 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -24,16 +24,18 @@ pub trait UnsubscribeRpcMethod: Send + Sync + 'static { /// Output type type Out: Future + Send + 'static; /// Called when client is requesting to cancel existing subscription. - fn call(&self, id: SubscriptionId, meta: M) -> Self::Out; + /// + /// Metadata is not available if the session was closed without unsubscribing. + fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out; } impl UnsubscribeRpcMethod for F where - F: Fn(SubscriptionId, M) -> I + Send + Sync + 'static, + F: Fn(SubscriptionId, Option) -> I + Send + Sync + 'static, I: IntoFuture, I::Future: Send + 'static, { type Out = I::Future; - fn call(&self, id: SubscriptionId, meta: M) -> Self::Out { + fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out { (*self)(id, meta).into_future() } } @@ -108,13 +110,12 @@ mod tests { use super::PubSubHandler; - #[derive(Clone, Default)] - struct Metadata; + #[derive(Clone)] + struct Metadata(Arc); impl core::Metadata for Metadata {} impl PubSubMetadata for Metadata { fn session(&self) -> Option> { - let (tx, _rx) = mpsc::channel(1); - Some(Arc::new(Session::new(tx))) + Some(self.0.clone()) } } @@ -139,7 +140,8 @@ mod tests { ); // when - let meta = Metadata; + let (tx, _rx) = mpsc::channel(1); + let meta = Metadata(Arc::new(Session::new(tx))); let req = r#"{"jsonrpc":"2.0","id":1,"method":"subscribe_hello","params":null}"#; let res = handler.handle_request_sync(req, meta); diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 66c333860..d054ef72f 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -244,7 +244,7 @@ impl core::RpcMethod for Subscribe where transport: session.sender(), sender: tx, }; - self.subscribe.call(params, meta.clone(), subscriber); + self.subscribe.call(params, meta, subscriber); let unsub = self.unsubscribe.clone(); let notification = self.notification.clone(); @@ -254,7 +254,7 @@ impl core::RpcMethod for Subscribe where futures::done(match result { Ok(id) => { session.add_subscription(¬ification, &id, move |id| { - let _ = unsub.call(id, meta.clone()).wait(); + let _ = unsub.call(id, None).wait(); }); Ok(id.into()) }, @@ -288,7 +288,7 @@ impl core::RpcMethod for Unsubscribe where match (meta.session(), id) { (Some(session), Some(id)) => { session.remove_subscription(&self.notification, &id); - Box::new(self.unsubscribe.call(id, meta)) + Box::new(self.unsubscribe.call(id, Some(meta))) }, (Some(_), None) => Box::new(future::err(core::Error::invalid_params("Expected subscription id."))), _ => Box::new(future::err(subscriptions_unavailable())), diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index 4c58a771d..31e4a7cd8 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -12,6 +12,10 @@ pub type TransportError = mpsc::SendError; pub type SinkResult = core::futures::sink::Send; /// Metadata extension for pub-sub method handling. +/// +/// NOTE storing `PubSubMetadata` (or rather storing `Arc`) in +/// any other place outside of the handler will prevent `unsubscribe` methods +/// to be called in case the `Session` is dropped (i.e. transport connection is closed). pub trait PubSubMetadata: core::Metadata { /// Returns session object associated with given request/client. /// `None` indicates that sessions are not supported on the used transport. From 5fa6ad5e060ec0194b952c32d0be46cda4e06ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 8 Jan 2019 14:10:00 +0100 Subject: [PATCH 009/149] Fix handling multiple bound types. (#355) --- macros/examples/generic-trait-bounds.rs | 13 +++++++++++-- macros/src/auto_args.rs | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/macros/examples/generic-trait-bounds.rs b/macros/examples/generic-trait-bounds.rs index 101a6f92a..553ae29d1 100644 --- a/macros/examples/generic-trait-bounds.rs +++ b/macros/examples/generic-trait-bounds.rs @@ -3,7 +3,7 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_macros; -use serde::de::DeserializeOwned; +use serde::{Serialize, de::DeserializeOwned}; use jsonrpc_core::{IoHandler, Error, Result}; use jsonrpc_core::futures::future::{self, FutureResult}; @@ -11,6 +11,7 @@ use jsonrpc_core::futures::future::{self, FutureResult}; build_rpc_trait! { pub trait Rpc where Two: DeserializeOwned, + Three: Serialize, { /// Get One type. #[rpc(name = "getOne")] @@ -20,6 +21,10 @@ build_rpc_trait! { #[rpc(name = "setTwo")] fn set_two(&self, Two) -> Result<()>; + /// Adds two numbers and returns a result + #[rpc(name = "getThree")] + fn three(&self) -> Result; + /// Performs asynchronous operation #[rpc(name = "beFancy")] fn call(&self, One) -> FutureResult<(One, u64), Error>; @@ -38,7 +43,7 @@ build_rpc_trait! { struct RpcImpl; -impl Rpc for RpcImpl { +impl Rpc for RpcImpl { fn one(&self) -> Result { Ok(100) } @@ -48,6 +53,10 @@ impl Rpc for RpcImpl { Ok(()) } + fn three(&self) -> Result { + Ok(3) + } + fn call(&self, num: u64) -> FutureResult<(u64, u64), Error> { ::future::finished((num + 999, num)) } diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index 6d6e642aa..4d2a8b504 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -91,7 +91,7 @@ macro_rules! build_rpc_trait { $( $generics ,)* @BOUNDS // then specialised ones - $( $( $generics2 : $bounds $( + $morebounds )* )* )* + $( $( $generics2 : $bounds $( + $morebounds )* ),* )* > )* { $( $rest )+ } From 24b9befa3389d9aa64b49b2573d1250143f17743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 8 Jan 2019 16:20:39 +0100 Subject: [PATCH 010/149] Solve more issues with commas. (#356) * Solve more issues with commas. * Fix RPC naming. --- macros/examples/generic-trait-bounds.rs | 67 +++++++++++++++++++++++-- macros/src/auto_args.rs | 23 ++++----- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/macros/examples/generic-trait-bounds.rs b/macros/examples/generic-trait-bounds.rs index 553ae29d1..d9d9f8a51 100644 --- a/macros/examples/generic-trait-bounds.rs +++ b/macros/examples/generic-trait-bounds.rs @@ -9,10 +9,14 @@ use jsonrpc_core::futures::future::{self, FutureResult}; // Two only requires DeserializeOwned build_rpc_trait! { - pub trait Rpc where + pub trait Rpc where Two: DeserializeOwned, Three: Serialize, { + /// Get Zero type. + #[rpc(name = "getZero")] + fn zero(&self) -> Result; + /// Get One type. #[rpc(name = "getOne")] fn one(&self) -> Result; @@ -41,11 +45,44 @@ build_rpc_trait! { } } +build_rpc_trait! { + pub trait Rpc3 where + Two: DeserializeOwned, + Three: Serialize, + { + type Metadata; + + /// Get Zero type. + #[rpc(name = "getZero")] + fn zero(&self) -> Result; + + /// Get One type. + #[rpc(name = "getOne")] + fn one(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "setTwo")] + fn set_two(&self, Two) -> Result<()>; + + /// Adds two numbers and returns a result + #[rpc(name = "getThree")] + fn three(&self) -> Result; + + /// Performs asynchronous operation + #[rpc(name = "beFancy")] + fn call(&self, One) -> FutureResult<(One, u64), Error>; + } +} + struct RpcImpl; -impl Rpc for RpcImpl { +impl Rpc for RpcImpl { + fn zero(&self) -> Result { + Ok(0) + } + fn one(&self) -> Result { - Ok(100) + Ok(1) } fn set_two(&self, x: String) -> Result<()> { @@ -68,6 +105,30 @@ impl Rpc2 for RpcImpl { } } +impl Rpc3 for RpcImpl { + type Metadata = (); + + fn zero(&self) -> Result { + Ok(0) + } + + fn one(&self) -> Result { + Ok(1) + } + + fn set_two(&self, x: String) -> Result<()> { + println!("{}", x); + Ok(()) + } + + fn three(&self) -> Result { + Ok(3) + } + + fn call(&self, num: u64) -> FutureResult<(u64, u64), Error> { + ::future::finished((num + 999, num)) + } +} fn main() { let mut io = IoHandler::new(); diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index 4d2a8b504..4688f8013 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -75,10 +75,11 @@ macro_rules! build_rpc_trait { ( $(#[$t_attr: meta])* pub trait $name:ident $(<$( $generics:ident ),*> - $( - where - $( $generics2:ident : $bounds:tt $( + $morebounds:tt )* ,)+ - )* )* + $( + where + $( $generics2:ident : $bounds:tt $( + $morebounds:tt )* ,)+ + )* + )* { $( $rest: tt )+ } @@ -91,21 +92,19 @@ macro_rules! build_rpc_trait { $( $generics ,)* @BOUNDS // then specialised ones - $( $( $generics2 : $bounds $( + $morebounds )* ),* )* + $( $( $generics2 : $bounds $( + $morebounds )* ,)* )* > )* { $( $rest )+ } } }; - - // entry-point. todo: make another for traits w/ bounds. ( @WITH_BOUNDS $(#[$t_attr: meta])* pub trait $name:ident $(< $( $simple_generics:ident ,)* @BOUNDS - $( $generics:ident $(: $bounds:tt $( + $morebounds:tt )* )* ),* + $( $generics:ident : $bounds:tt $( + $morebounds:tt )* ,)* >)* { $( $( #[doc=$m_doc:expr] )* @@ -129,7 +128,7 @@ macro_rules! build_rpc_trait { fn to_delegate(self) -> $crate::IoDelegate where $( $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ,)* - $($generics: Send + Sync + 'static $( + $bounds $( + $morebounds )* )* ),* + $($generics: Send + Sync + 'static + $bounds $( + $morebounds )* ,)* )* { let mut del = $crate::IoDelegate::new(self.into()); @@ -151,7 +150,7 @@ macro_rules! build_rpc_trait { pub trait $name: ident $(< $( $simple_generics:ident ,)* @BOUNDS - $($generics:ident $( : $bounds:tt $( + $morebounds:tt )* )* ),* + $( $generics:ident : $bounds:tt $( + $morebounds:tt )* ,)* >)* { type Metadata; @@ -201,8 +200,8 @@ macro_rules! build_rpc_trait { /// the parameters. fn to_delegate(self) -> $crate::IoDelegate where $( - $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ),* - $($generics: Send + Sync + 'static $( + $bounds $( + $morebounds )* )* ),* + $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ,)* + $($generics: Send + Sync + 'static + $bounds $( + $morebounds )* , )* )* { let mut del = $crate::IoDelegate::new(self.into()); From 16ed9f5097cdde3a5641c2aa8cd1402838bd3dc4 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 11 Jan 2019 17:54:09 +1100 Subject: [PATCH 011/149] Fix tokio deprecation warnings (#358) * Fix thread_pool::Builder deprecation * Fix reactor::Handle::current() deprecaction warning --- http/src/lib.rs | 2 +- server-utils/src/reactor.rs | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/http/src/lib.rs b/http/src/lib.rs index 8185afd6a..4d89f46f8 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -459,7 +459,7 @@ fn serve>( ) { let (shutdown_signal, local_addr_tx) = signals; executor.spawn(future::lazy(move || { - let handle = tokio::reactor::Handle::current(); + let handle = tokio::reactor::Handle::default(); let bind = move || { let listener = match addr { diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index 63083d734..38352783a 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -107,20 +107,15 @@ impl RpcEventLoop { } let handle = tb.spawn(move || { - let mut tp_builder = tokio::executor::thread_pool::Builder::new(); - - let pool_size = match num_cpus::get_physical() { + let core_threads = match num_cpus::get_physical() { 1 => 1, 2...4 => 2, _ => 3, }; - tp_builder - .pool_size(pool_size) - .name_prefix("jsonrpc-eventloop-"); - let runtime = tokio::runtime::Builder::new() - .threadpool_builder(tp_builder) + .core_threads(core_threads) + .name_prefix("jsonrpc-eventloop-") .build(); match runtime { From 789c74ddc0e4ecdcb1355012e09435f8ff94ce7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Saparelli?= Date: Tue, 15 Jan 2019 23:20:58 +1300 Subject: [PATCH 012/149] Derive Clone for all core types::* (#359) * Derive Clone for request types * Derive Clone for Response --- core/src/types/request.rs | 8 ++++---- core/src/types/response.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/types/request.rs b/core/src/types/request.rs index 31518415f..4cf4e6596 100644 --- a/core/src/types/request.rs +++ b/core/src/types/request.rs @@ -3,7 +3,7 @@ use super::{Id, Params, Version}; /// Represents jsonrpc request which is a method call. -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct MethodCall { /// A String specifying the version of the JSON-RPC protocol. @@ -21,7 +21,7 @@ pub struct MethodCall { } /// Represents jsonrpc request which is a notification. -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct Notification { /// A String specifying the version of the JSON-RPC protocol. @@ -35,7 +35,7 @@ pub struct Notification { } /// Represents single jsonrpc call. -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum Call { /// Call method @@ -71,7 +71,7 @@ impl From for Call { } /// Represents jsonrpc request. -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum Request { /// Single request (call) diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 23406834a..7b562bd36 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -90,7 +90,7 @@ impl From for CoreResult { } /// Synchronous response -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum Response { /// Single response From ec5249ed25c0126ecf1088ab214ef9d01da65149 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 28 Jan 2019 13:43:20 +0000 Subject: [PATCH 013/149] Use procedural macros (#340) * Empty rpc trait with proc macro attribute * Generate skeleton to_delegate method * Move delegates to core - is this the right place * WIP: wrap rpc methods in to_delegate * Generate basic to_delegate * Add derive examples and tests * Add async rpc method example * Fix extern crate hygiene * Add where clause bounds to to_delegate function * WIP: Support one way serialisation for generics * Add Serialize bounds to generic RPC return types * Add Serialize bounds for RPC args * Clean up imports * Refactor and read aliases from rpc trait * Wire up aliases * Derive rpc with metadata associated type * Refactor * Rename trait level attribute to rpc * Refactor: move entry point function into lib * WIP: implement pubsub * Implement pubsub macro * Register pub/sub method aliases * Remove delegates file prematurely copied to core * Fix TCP tests? * Inline rpc method registration Removes need for Wrap* types * Extract return type when creating RpcMethod * Handle methods with no params * WIP: Add support for optional trailing params * Handle optional trailing params * Bring back aliases * Inline into_future to remove extraneous types/fns * Refactor extra args for use in pubsub * WIP: migrate pubsub delegate registration * Extract special args from delegate * Register sub/unsub using generated delgate closure * WIP: move macro delegates to core and pubsub * WIP: refactor to handle subscription method * Handle pubsub error * Add pub/sub IoDelegate * Refactor and fix pubsub compiler errors * Restore original delegates file and fix auto_args I've copied and split the delegates file into core and pubsub for use with proc macros, was originally reusing it in classic macros but putting it back * Wrap subscriber in typed subscriber * Handle pubsub unsubscribe method * Uncomment pubsub example * Unsubscribe optional metadata * Copy macro tests over and make them compile with derive * Fix pubsub trailing test * Rename to_delegate * Remove errant printlns * Copy and modify module docs from macros * Fix derive doc test * Mark subscribe/unsubscribe methods with attribute * Generic type param with metadata example, update some error messages * Refactor: extract a couple of functions for readability * Add compiletests to test compile errors * Remove test both for now * Remove top level future imports * Fix unsubscribe missing import * Correct derive crate author * Rust 2018: cargo fix --edition --package jsonrpc-derive * Remove redundant `extern crate` rust 2018 * Fix doc tests for edition 2018 * Add missing_docs warning * Replace &str to_string() with into() * Move all magic strings to constants * WIP * Bring pubsub methods back into same trait * Geoup pubsub methods by subscription name * Refactor attribute parsing * Update compilefail tests for new pubsub * Fix combined rpc and pubsub trait * Fix compilefail tests * added some tests (failing) * Remove multiple trailing params tests, will implement in later PR * Fix parse of single param tuple with trailing comma * Specify subscription name in missing annotation error * Add tests for trailing args * Handle single trailing param * Replace extern crates in macro output * Reduce recursion limit * Add comment for `to_delegate` trait method * Deprecate `to_delegate` method generated by jsonrpc-macros * Update README with derive example * Add pubsub example and rpc attribute docs * Remove commented out code * Bump major version --- Cargo.toml | 1 + README.md | 25 +- core/Cargo.toml | 2 +- core/src/delegates.rs | 144 +++++++ core/src/lib.rs | 2 + core/src/types/error.rs | 13 + core/src/types/params.rs | 15 + derive/Cargo.toml | 22 + derive/examples/generic-trait-bounds.rs | 61 +++ derive/examples/generic-trait.rs | 45 ++ derive/examples/meta-macros.rs | 74 ++++ derive/examples/pubsub-macros.rs | 88 ++++ derive/examples/std.rs | 41 ++ derive/src/lib.rs | 153 +++++++ derive/src/rpc_attr.rs | 182 ++++++++ derive/src/rpc_trait.rs | 170 ++++++++ derive/src/to_delegate.rs | 395 ++++++++++++++++++ derive/tests/compiletests.rs | 19 + derive/tests/macros.rs | 116 +++++ derive/tests/pubsub-macros.rs | 89 ++++ derive/tests/trailing.rs | 94 +++++ derive/tests/ui/pubsub/missing-subscribe.rs | 17 + .../tests/ui/pubsub/missing-subscribe.stderr | 10 + derive/tests/ui/pubsub/missing-unsubscribe.rs | 17 + .../ui/pubsub/missing-unsubscribe.stderr | 10 + http/Cargo.toml | 6 +- ipc/Cargo.toml | 6 +- macros/Cargo.toml | 8 +- macros/src/auto_args.rs | 2 + macros/tests/macros.rs | 48 +++ minihttp/Cargo.toml | 6 +- pubsub/Cargo.toml | 7 +- pubsub/more-examples/Cargo.toml | 10 +- pubsub/src/delegates.rs | 136 ++++++ pubsub/src/lib.rs | 4 + pubsub/src/typed.rs | 134 ++++++ server-utils/Cargo.toml | 4 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 6 +- test/Cargo.toml | 6 +- ws/Cargo.toml | 6 +- 41 files changed, 2148 insertions(+), 48 deletions(-) create mode 100644 core/src/delegates.rs create mode 100644 derive/Cargo.toml create mode 100644 derive/examples/generic-trait-bounds.rs create mode 100644 derive/examples/generic-trait.rs create mode 100644 derive/examples/meta-macros.rs create mode 100644 derive/examples/pubsub-macros.rs create mode 100644 derive/examples/std.rs create mode 100644 derive/src/lib.rs create mode 100644 derive/src/rpc_attr.rs create mode 100644 derive/src/rpc_trait.rs create mode 100644 derive/src/to_delegate.rs create mode 100644 derive/tests/compiletests.rs create mode 100644 derive/tests/macros.rs create mode 100644 derive/tests/pubsub-macros.rs create mode 100644 derive/tests/trailing.rs create mode 100644 derive/tests/ui/pubsub/missing-subscribe.rs create mode 100644 derive/tests/ui/pubsub/missing-subscribe.stderr create mode 100644 derive/tests/ui/pubsub/missing-unsubscribe.rs create mode 100644 derive/tests/ui/pubsub/missing-unsubscribe.stderr create mode 100644 pubsub/src/delegates.rs create mode 100644 pubsub/src/typed.rs diff --git a/Cargo.toml b/Cargo.toml index 4e73179d7..b7aa3fc07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "http", "ipc", "macros", + "derive", "minihttp", "pubsub", "pubsub/more-examples", diff --git a/README.md b/README.md index 72cc82648..fd853d994 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` - [jsonrpc-tcp-server](./tcp) [![crates.io][tcp-server-image]][tcp-server-url] - [jsonrpc-ws-server](./ws) - [jsonrpc-stdio-server](./stdio) -- [jsonrpc-macros](./macros) [![crates.io][macros-image]][macros-url] +- [jsonrpc-macros](./macros) [![crates.io][macros-image]][macros-url] *deprecated:* use `derive` instead +- [jsonrpc-derive](./derive) - [jsonrpc-server-utils](./server-utils) [![crates.io][server-utils-image]][server-utils-url] - [jsonrpc-pubsub](./pubsub) [![crates.io][pubsub-image]][pubsub-url] @@ -38,7 +39,8 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` ## Examples - [core](./core/examples) -- [macros](./macros/examples) +- [derive](./derive/examples) +- [macros](./macros/examples) *deprecated* - [pubsub](./pubsub/examples) ### Basic Usage (with HTTP transport) @@ -65,21 +67,17 @@ fn main() { } ``` -### Basic usage with macros +### Basic usage with derive ```rust -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_macros; - use jsonrpc_core::Result; +use jsonrpc_derive::rpc; -build_rpc_trait! { - pub trait Rpc { - /// Adds two numbers and returns a result - #[rpc(name = "add")] - fn add(&self, u64, u64) -> Result; - } +#[rpc] +pub trait Rpc { + /// Adds two numbers and returns a result + #[rpc(name = "add")] + fn add(&self, u64, u64) -> Result; } pub struct RpcImpl; @@ -89,7 +87,6 @@ impl Rpc for RpcImpl { } } - fn main() { let mut io = jsonrpc_core::IoHandler::new(); io.extend_with(RpcImpl.to_delegate()) diff --git a/core/Cargo.toml b/core/Cargo.toml index e9471ca02..4c6afec8b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-core" -version = "9.0.0" +version = "10.0.0" authors = ["debris "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_core/index.html" diff --git a/core/src/delegates.rs b/core/src/delegates.rs new file mode 100644 index 000000000..7dd6000aa --- /dev/null +++ b/core/src/delegates.rs @@ -0,0 +1,144 @@ +//! Delegate rpc calls + +use std::sync::Arc; +use std::collections::HashMap; + +use types::{Params, Value, Error}; +use calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification}; +use futures::IntoFuture; +use BoxFuture; + +struct DelegateAsyncMethod { + delegate: Arc, + closure: F, +} + +impl RpcMethod for DelegateAsyncMethod where + M: Metadata, + F: Fn(&T, Params) -> I, + I: IntoFuture, + T: Send + Sync + 'static, + F: Send + Sync + 'static, + I::Future: Send + 'static, +{ + fn call(&self, params: Params, _meta: M) -> BoxFuture { + let closure = &self.closure; + Box::new(closure(&self.delegate, params).into_future()) + } +} + +struct DelegateMethodWithMeta { + delegate: Arc, + closure: F, +} + +impl RpcMethod for DelegateMethodWithMeta where + M: Metadata, + F: Fn(&T, Params, M) -> I, + I: IntoFuture, + T: Send + Sync + 'static, + F: Send + Sync + 'static, + I::Future: Send + 'static, +{ + fn call(&self, params: Params, meta: M) -> BoxFuture { + let closure = &self.closure; + Box::new(closure(&self.delegate, params, meta).into_future()) + } +} + +struct DelegateNotification { + delegate: Arc, + closure: F, +} + +impl RpcNotification for DelegateNotification where + F: Fn(&T, Params) + 'static, + F: Send + Sync + 'static, + T: Send + Sync + 'static, + M: Metadata, +{ + fn execute(&self, params: Params, _meta: M) { + let closure = &self.closure; + closure(&self.delegate, params) + } +} + +/// A set of RPC methods and notifications tied to single `delegate` struct. +pub struct IoDelegate where + T: Send + Sync + 'static, + M: Metadata, +{ + delegate: Arc, + methods: HashMap>, +} + +impl IoDelegate where + T: Send + Sync + 'static, + M: Metadata, +{ + /// Creates new `IoDelegate` + pub fn new(delegate: Arc) -> Self { + IoDelegate { + delegate: delegate, + methods: HashMap::new(), + } + } + + /// Adds an alias to existing method. + /// NOTE: Aliases are not transitive, i.e. you cannot create alias to an alias. + pub fn add_alias(&mut self, from: &str, to: &str) { + self.methods.insert(from.into(), RemoteProcedure::Alias(to.into())); + } + + /// Adds async method to the delegate. + pub fn add_method(&mut self, name: &str, method: F) where + F: Fn(&T, Params) -> I, + I: IntoFuture, + F: Send + Sync + 'static, + I::Future: Send + 'static, + { + self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new( + DelegateAsyncMethod { + delegate: self.delegate.clone(), + closure: method, + } + ))); + } + + /// Adds async method with metadata to the delegate. + pub fn add_method_with_meta(&mut self, name: &str, method: F) where + F: Fn(&T, Params, M) -> I, + I: IntoFuture, + F: Send + Sync + 'static, + I::Future: Send + 'static, + { + self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new( + DelegateMethodWithMeta { + delegate: self.delegate.clone(), + closure: method, + } + ))); + } + + /// Adds notification to the delegate. + pub fn add_notification(&mut self, name: &str, notification: F) where + F: Fn(&T, Params), + F: Send + Sync + 'static, + { + self.methods.insert(name.into(), RemoteProcedure::Notification(Arc::new( + DelegateNotification { + delegate: self.delegate.clone(), + closure: notification, + } + ))); + } +} + +impl Into>> for IoDelegate where + T: Send + Sync + 'static, + M: Metadata, +{ + fn into(self) -> HashMap> { + self.methods + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index e7fa46731..6da2334af 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -37,6 +37,7 @@ mod io; pub mod middleware; pub mod types; +pub mod delegates; /// A `Future` trait object. pub type BoxFuture = Box + Send>; @@ -45,6 +46,7 @@ pub type BoxFuture = Box + Send>; pub type Result = ::std::result::Result; pub use calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; +pub use delegates::IoDelegate; pub use io::{Compatibility, IoHandler, MetaIoHandler, FutureOutput, FutureResult, FutureResponse, FutureRpcResult}; pub use middleware::{Middleware, Noop as NoopMiddleware}; pub use types::*; diff --git a/core/src/types/error.rs b/core/src/types/error.rs index 3ee72a1bf..6f7a573af 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -1,4 +1,5 @@ //! jsonrpc errors +use std::fmt; use serde::de::{Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use super::Value; @@ -124,6 +125,18 @@ impl Error { } } + /// Creates `InvalidParams` for given parameter, with details. + pub fn invalid_params_with_details(message: M, details: T) -> Error where + M: Into, + T: fmt::Debug + { + Error { + code: ErrorCode::InvalidParams, + message: format!("Invalid parameters: {}", message.into()), + data: Some(Value::String(format!("{:?}", details))), + } + } + /// Creates new `InternalError` pub fn internal_error() -> Self { Self::new(ErrorCode::InternalError) diff --git a/core/src/types/params.rs b/core/src/types/params.rs index 4d92a999f..60cfed9ae 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -31,6 +31,15 @@ impl Params { Error::invalid_params(format!("Invalid params: {}.", e)) }) } + + /// Check for no params, returns Err if any params + pub fn expect_no_params(self) -> Result<(), Error> { + match self { + Params::None => Ok(()), + Params::Array(ref v) if v.is_empty() => Ok(()), + p => Err(Error::invalid_params_with_details("No parameters were expected", p)), + } + } } #[cfg(test)] @@ -75,4 +84,10 @@ mod tests { assert_eq!(err2.message, "Invalid params: invalid length 2, expected a tuple of size 3."); assert_eq!(err2.data, None); } + + #[test] + fn single_param_parsed_as_tuple() { + let params: (u64,) = Params::Array(vec![Value::from(1)]).parse().unwrap(); + assert_eq!(params, (1,)); + } } diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 000000000..aa3fb7228 --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "jsonrpc-derive" +version = "10.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "^0.15.22", features = ["full", "extra-traits", "visit", "fold"] } +proc-macro2 = "0.4" +quote = "0.6" + +[dev-dependencies] +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-pubsub = { version = "10.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +compiletest_rs = { version = "*", features = ["stable"] } diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs new file mode 100644 index 000000000..ca2c39a71 --- /dev/null +++ b/derive/examples/generic-trait-bounds.rs @@ -0,0 +1,61 @@ +use serde_derive::{Serialize, Deserialize}; + +use jsonrpc_core::{IoHandler, Error, Result}; +use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_derive::rpc; + +// One is both parameter and a result so requires both Serialize and DeserializeOwned +// Two is only a parameter so only requires DeserializeOwned +// Three is only a result so only requires Serialize +#[rpc] +pub trait Rpc +{ + /// Get One type. + #[rpc(name = "getOne")] + fn one(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "setTwo")] + fn set_two(&self, _: Two) -> Result<()>; + + #[rpc(name = "getThree")] + fn get_three(&self) -> Result; + + /// Performs asynchronous operation + #[rpc(name = "beFancy")] + fn call(&self, _: One) -> FutureResult<(One, u64), Error>; +} + +struct RpcImpl; + +#[derive(Serialize, Deserialize)] +struct InAndOut { foo: u64 } +#[derive(Deserialize)] +struct In {} +#[derive(Serialize)] +struct Out {} + +impl Rpc for RpcImpl { + fn one(&self) -> Result { + Ok(InAndOut { foo: 1u64 }) + } + + fn set_two(&self, _x: In) -> Result<()> { + Ok(()) + } + + fn get_three(&self) -> Result { + Ok(Out {}) + } + + fn call(&self, num: InAndOut) -> FutureResult<(InAndOut, u64), Error> { + crate::future::finished((InAndOut {foo: num.foo + 999}, num.foo)) + } +} + +fn main() { + let mut io = IoHandler::new(); + + io.extend_with(Rpc::to_delegate(RpcImpl)); +} + diff --git a/derive/examples/generic-trait.rs b/derive/examples/generic-trait.rs new file mode 100644 index 000000000..94db24452 --- /dev/null +++ b/derive/examples/generic-trait.rs @@ -0,0 +1,45 @@ +extern crate jsonrpc_core; + +use jsonrpc_core::{IoHandler, Error, Result}; +use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_derive::rpc; + +#[rpc] +pub trait Rpc { + /// Get One type. + #[rpc(name = "getOne")] + fn one(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "setTwo")] + fn set_two(&self, _: Two) -> Result<()>; + + /// Performs asynchronous operation + #[rpc(name = "beFancy")] + fn call(&self, _: One) -> FutureResult<(One, Two), Error>; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn one(&self) -> Result { + Ok(100) + } + + fn set_two(&self, x: String) -> Result<()> { + println!("{}", x); + Ok(()) + } + + fn call(&self, num: u64) -> FutureResult<(u64, String), Error> { + crate::future::finished((num + 999, "hello".into())) + } +} + + +fn main() { + let mut io = IoHandler::new(); + let rpc = RpcImpl; + + io.extend_with(rpc.to_delegate()) +} diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs new file mode 100644 index 000000000..96dd0647c --- /dev/null +++ b/derive/examples/meta-macros.rs @@ -0,0 +1,74 @@ +use std::collections::BTreeMap; + +use jsonrpc_core::{futures, MetaIoHandler, Metadata, Error, Value, Result}; +use jsonrpc_core::futures::future::FutureResult; +use jsonrpc_derive::rpc; + +#[derive(Clone)] +struct Meta(String); +impl Metadata for Meta {} + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Get One type. + #[rpc(name = "getOne")] + fn one(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add")] + fn add(&self, _: u64, _: u64) -> Result; + + /// Multiplies two numbers. Second number is optional. + #[rpc(name = "mul")] + fn mul(&self, _: u64, _: Option) -> Result; + + /// Performs asynchronous operation + #[rpc(name = "callAsync")] + fn call(&self, _: u64) -> FutureResult; + + /// Performs asynchronous operation with meta + #[rpc(meta, name = "callAsyncMeta", alias("callAsyncMetaAlias"))] + fn call_meta(&self, _: Self::Metadata, _: BTreeMap) -> FutureResult; +} + +struct RpcImpl; +impl Rpc for RpcImpl { + type Metadata = Meta; + + fn one(&self) -> Result { Ok(100) } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } + + fn mul(&self, a: u64, b: Option) -> Result { + Ok(a * b.unwrap_or(1)) + } + + fn call(&self, x: u64) -> FutureResult { + futures::finished(format!("OK: {}", x)) + } + + fn call_meta(&self, meta: Self::Metadata, map: BTreeMap) -> FutureResult { + futures::finished(format!("From: {}, got: {:?}", meta.0, map)) + } +} + + +fn main() { + let mut io = MetaIoHandler::default(); + let rpc = RpcImpl; + + io.extend_with(rpc.to_delegate()); + + let server = jsonrpc_tcp_server::ServerBuilder + ::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| { + Meta(format!("{}", context.peer_addr)) + }) + .start(&"0.0.0.0:3030".parse().unwrap()) + .expect("Server must start with no issues"); + + server.wait() +} diff --git a/derive/examples/pubsub-macros.rs b/derive/examples/pubsub-macros.rs new file mode 100644 index 000000000..4104b8bed --- /dev/null +++ b/derive/examples/pubsub-macros.rs @@ -0,0 +1,88 @@ +use std::thread; +use std::sync::{atomic, Arc, RwLock}; +use std::collections::HashMap; + +use jsonrpc_core::{Error, ErrorCode, Result}; +use jsonrpc_core::futures::Future; +use jsonrpc_derive::rpc; +use jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId}; +use jsonrpc_pubsub::typed; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +#[derive(Default)] +struct RpcImpl { + uid: atomic::AtomicUsize, + active: Arc>>>, +} +impl Rpc for RpcImpl { + type Metadata = Arc; + + fn subscribe(&self, _meta: Self::Metadata, subscriber: typed::Subscriber, param: u64) { + if param != 10 { + subscriber.reject(Error { + code: ErrorCode::InvalidParams, + message: "Rejecting subscription - invalid parameters provided.".into(), + data: None, + }).unwrap(); + return; + } + + let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst); + let sub_id = SubscriptionId::Number(id as u64); + let sink = subscriber.assign_id(sub_id.clone()).unwrap(); + self.active.write().unwrap().insert(sub_id, sink); + } + + fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { + let removed = self.active.write().unwrap().remove(&id); + if removed.is_some() { + Ok(true) + } else { + Err(Error { + code: ErrorCode::InvalidParams, + message: "Invalid subscription.".into(), + data: None, + }) + } + } +} + + +fn main() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl::default(); + let active_subscriptions = rpc.active.clone(); + + thread::spawn(move || { + loop { + { + let subscribers = active_subscriptions.read().unwrap(); + for sink in subscribers.values() { + let _ = sink.notify(Ok("Hello World!".into())).wait(); + } + } + thread::sleep(::std::time::Duration::from_secs(1)); + } + }); + + io.extend_with(rpc.to_delegate()); + + let server = jsonrpc_tcp_server::ServerBuilder + ::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(context.sender.clone()))) + .start(&"0.0.0.0:3030".parse().unwrap()) + .expect("Server must start with no issues"); + + server.wait() +} diff --git a/derive/examples/std.rs b/derive/examples/std.rs new file mode 100644 index 000000000..dcab94e00 --- /dev/null +++ b/derive/examples/std.rs @@ -0,0 +1,41 @@ +use jsonrpc_core::{IoHandler, Error, Result}; +use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_derive::rpc; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add", alias("callAsyncMetaAlias"))] + fn add(&self, _: u64, _: u64) -> Result; + + /// Performs asynchronous operation + #[rpc(name = "callAsync")] + fn call(&self, _: u64) -> FutureResult; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn protocol_version(&self) -> Result { + Ok("version1".into()) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } + + fn call(&self, _: u64) -> FutureResult { + future::ok("OK".to_owned()) + } +} + +fn main() { + let mut io = IoHandler::new(); + let rpc = RpcImpl; + + io.extend_with(rpc.to_delegate()) +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 000000000..6ef5dbd7c --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,153 @@ +//! High level, typed wrapper for `jsonrpc_core`. +//! +//! Enables creation of "Service" objects grouping a set of RPC methods together in a typed manner. +//! +//! Example +//! +//! ``` +//! use jsonrpc_derive::rpc; +//! use jsonrpc_core::{IoHandler, Error, Result}; +//! use jsonrpc_core::futures::future::{self, FutureResult}; +//! +//! #[rpc] +//! pub trait Rpc { +//! #[rpc(name = "protocolVersion")] +//! fn protocol_version(&self) -> Result; +//! +//! #[rpc(name = "add")] +//! fn add(&self, _: u64, _: u64) -> Result; +//! +//! #[rpc(name = "callAsync")] +//! fn call(&self, _: u64) -> FutureResult; +//! } +//! +//! struct RpcImpl; +//! impl Rpc for RpcImpl { +//! fn protocol_version(&self) -> Result { +//! Ok("version1".into()) +//! } +//! +//! fn add(&self, a: u64, b: u64) -> Result { +//! Ok(a + b) +//! } +//! +//! fn call(&self, _: u64) -> FutureResult { +//! future::ok("OK".to_owned()).into() +//! } +//! } +//! +//! fn main() { +//! let mut io = IoHandler::new(); +//! let rpc = RpcImpl; +//! +//! io.extend_with(rpc.to_delegate()); +//! } +//! ``` +//! +//! Pub/Sub Example +//! +//! Each subscription must have `subscribe` and `unsubscribe` methods. They can +//! have any name but must be annotated with `subscribe` or `unsubscribe` and +//! have a matching unique subscription name. +//! +//! ``` +//! use std::thread; +//! use std::sync::{atomic, Arc, RwLock}; +//! use std::collections::HashMap; +//! +//! use jsonrpc_core::{Error, ErrorCode, Result}; +//! use jsonrpc_core::futures::Future; +//! use jsonrpc_derive::rpc; +//! use jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId, typed::{Subscriber, Sink}}; +//! +//! #[rpc] +//! pub trait Rpc { +//! type Metadata; +//! +//! /// Hello subscription +//! #[pubsub( +//! subscription = "hello", +//! subscribe, +//! name = "hello_subscribe", +//! alias("hello_sub") +//! )] +//! fn subscribe(&self, _: Self::Metadata, _: Subscriber, _: u64); +//! +//! /// Unsubscribe from hello subscription. +//! #[pubsub( +//! subscription = "hello", +//! unsubscribe, +//! name = "hello_unsubscribe" +//! )] +//! fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +//! } +//! +//! +//! #[derive(Default)] +//! struct RpcImpl { +//! uid: atomic::AtomicUsize, +//! active: Arc>>>, +//! } +//! impl Rpc for RpcImpl { +//! type Metadata = Arc; +//! +//! fn subscribe(&self, _meta: Self::Metadata, subscriber: Subscriber, param: u64) { +//! if param != 10 { +//! subscriber.reject(Error { +//! code: ErrorCode::InvalidParams, +//! message: "Rejecting subscription - invalid parameters provided.".into(), +//! data: None, +//! }).unwrap(); +//! return; +//! } +//! +//! let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst); +//! let sub_id = SubscriptionId::Number(id as u64); +//! let sink = subscriber.assign_id(sub_id.clone()).unwrap(); +//! self.active.write().unwrap().insert(sub_id, sink); +//! } +//! +//! fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { +//! let removed = self.active.write().unwrap().remove(&id); +//! if removed.is_some() { +//! Ok(true) +//! } else { +//! Err(Error { +//! code: ErrorCode::InvalidParams, +//! message: "Invalid subscription.".into(), +//! data: None, +//! }) +//! } +//! } +//! } +//! +//! # fn main() {} +//! ``` + +#![recursion_limit = "128"] +#![warn(missing_docs)] + +extern crate proc_macro; + +use proc_macro::TokenStream; +use syn::parse_macro_input; + +mod rpc_attr; +mod rpc_trait; +mod to_delegate; + +/// Apply `#[rpc]` to a trait, and a `to_delegate` method is generated which +/// wires up methods decorated with `#[rpc]` or `#[pubsub]` attributes. +/// Attach the delegate to an `IoHandler` and the methods are now callable +/// via JSON-RPC. +#[proc_macro_attribute] +pub fn rpc(_args: TokenStream, input: TokenStream) -> TokenStream { + let input_toks = parse_macro_input!(input as syn::Item); + + let output = match rpc_trait::rpc_impl( input_toks) { + Ok(output) => output, + Err(err) => panic!("[rpc] encountered error: {}", err), + }; + + output.into() +} diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs new file mode 100644 index 000000000..cce176c0b --- /dev/null +++ b/derive/src/rpc_attr.rs @@ -0,0 +1,182 @@ +#[derive(Clone, Debug)] +pub struct RpcMethodAttribute { + pub attr: syn::Attribute, + pub name: String, + pub aliases: Vec, + pub kind: AttributeKind, +} + +#[derive(Clone, Debug)] +pub enum AttributeKind { + Rpc { has_metadata: bool }, + PubSub { subscription_name: String, kind: PubSubMethodKind } +} + +#[derive(Clone, Debug)] +pub enum PubSubMethodKind { + Subscribe, + Unsubscribe, +} + +const RPC_ATTR_NAME: &'static str = "rpc"; +const RPC_NAME_KEY: &'static str = "name"; +const SUBSCRIPTION_NAME_KEY: &'static str = "subscription"; +const ALIASES_KEY: &'static str = "aliases"; +const PUB_SUB_ATTR_NAME: &'static str = "pubsub"; +const METADATA_META_WORD: &'static str = "meta"; +const SUBSCRIBE_META_WORD: &'static str = "subscribe"; +const UNSUBSCRIBE_META_WORD: &'static str = "unsubscribe"; + +impl RpcMethodAttribute { + pub fn parse_attr(method: &syn::TraitItemMethod) -> Result, String> { + let attrs = method.attrs + .iter() + .filter_map(Self::parse_meta) + .collect::, _>>()?; + + if attrs.len() <= 1 { + Ok(attrs.first().cloned()) + } else { + Err(format!("Expected only a single rpc attribute per method. Found {}", attrs.len())) + } + } + + fn parse_meta(attr: &syn::Attribute) -> Option> { + let parse_result = attr.parse_meta() + .map_err(|err| format!("Error parsing attribute: {}", err)); + match parse_result { + Ok(ref meta) => { + let attr_kind = + match meta.name().to_string().as_ref() { + RPC_ATTR_NAME => { + let has_metadata = get_meta_list(meta) + .map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); + Some(Ok(AttributeKind::Rpc { has_metadata })) + }, + PUB_SUB_ATTR_NAME => Some(Self::parse_pubsub(meta)), + _ => None, + }; + attr_kind.map(|kind| kind.and_then(|kind| { + get_meta_list(meta) + .and_then(|ml| get_name_value(RPC_NAME_KEY, ml)) + .map_or( + Err("rpc attribute should have a name e.g. `name = \"method_name\"`".into()), + |name| { + let aliases = get_meta_list(meta) + .map_or(Vec::new(), |ml| get_aliases(ml)); + Ok(RpcMethodAttribute { + attr: attr.clone(), + name: name.into(), + aliases, + kind + }) + }) + })) + }, + Err(err) => Some(Err(err)), + } + } + + fn parse_pubsub(meta: &syn::Meta) -> Result { + let name_and_list = get_meta_list(meta) + .and_then(|ml| + get_name_value(SUBSCRIPTION_NAME_KEY, ml).map(|name| (name, ml)) + ); + + name_and_list.map_or( + Err("pubsub attribute should have a subscription name".into()), + |(sub_name, ml)| { + let is_subscribe = has_meta_word(SUBSCRIBE_META_WORD, ml); + let is_unsubscribe = has_meta_word(UNSUBSCRIBE_META_WORD, ml); + let kind = match (is_subscribe, is_unsubscribe) { + (true, false) => + Ok(PubSubMethodKind::Subscribe), + (false, true) => + Ok(PubSubMethodKind::Unsubscribe), + (true, true) => + Err(format!("pubsub attribute for subscription '{}' annotated with both subscribe and unsubscribe", sub_name)), + (false, false) => + Err(format!("pubsub attribute for subscription '{}' not annotated with either subscribe or unsubscribe", sub_name)), + }; + kind.map(|kind| AttributeKind::PubSub { + subscription_name: sub_name.into(), + kind, + }) + }) + } + + pub fn is_pubsub(&self) -> bool { + match self.kind { + AttributeKind::PubSub { .. } => true, + AttributeKind::Rpc { .. } => false, + } + } +} + +fn get_meta_list(meta: &syn::Meta) -> Option<&syn::MetaList> { + if let syn::Meta::List(ml) = meta { + Some(ml) + } else { + None + } +} + +fn get_name_value(key: &str, ml: &syn::MetaList) -> Option { + ml.nested + .iter() + .find_map(|nested| + if let syn::NestedMeta::Meta(syn::Meta::NameValue(mnv)) = nested { + if mnv.ident == key { + if let syn::Lit::Str(ref lit) = mnv.lit { + Some(lit.value()) + } else { + None + } + } else { + None + } + } else { + None + } + ) +} + +fn has_meta_word(word: &str, ml: &syn::MetaList) -> bool { + ml.nested + .iter() + .any(|nested| + if let syn::NestedMeta::Meta(syn::Meta::Word(w)) = nested { + word == w.to_string() + } else { + false + } + ) +} + +fn get_aliases(ml: &syn::MetaList) -> Vec { + ml.nested + .iter() + .find_map(|nested| + if let syn::NestedMeta::Meta(syn::Meta::List(list)) = nested { + if list.ident == ALIASES_KEY { + Some(list) + } else { + None + } + } else { + None + } + ) + .map_or(Vec::new(), |list| + list.nested + .iter() + .filter_map(|nm| { + if let syn::NestedMeta::Literal(syn::Lit::Str(alias)) = nm { + Some(alias.value()) + } else { + None + } + }) + .collect() + ) +} diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs new file mode 100644 index 000000000..ce28476a7 --- /dev/null +++ b/derive/src/rpc_trait.rs @@ -0,0 +1,170 @@ +use std::collections::HashMap; +use quote::quote; +use syn::{ + parse_quote, Token, punctuated::Punctuated, + fold::{self, Fold}, +}; +use crate::rpc_attr::{RpcMethodAttribute, PubSubMethodKind, AttributeKind}; +use crate::to_delegate::{RpcMethod, MethodRegistration, generate_trait_item_method}; + +const METADATA_TYPE: &'static str = "Metadata"; + +const MISSING_SUBSCRIBE_METHOD_ERR: &'static str = + "Can't find subscribe method, expected a method annotated with `subscribe` \ + e.g. `#[pubsub(subscription = \"hello\", subscribe, name = \"hello_subscribe\")]`"; + +const MISSING_UNSUBSCRIBE_METHOD_ERR: &'static str = + "Can't find unsubscribe method, expected a method annotated with `unsubscribe` \ + e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`"; + +const RPC_MOD_NAME_PREFIX: &'static str = "rpc_impl_"; + +type Result = std::result::Result; + +struct RpcTrait { + has_pubsub_methods: bool, + methods: Vec, + has_metadata: bool, +} + +impl<'a> Fold for RpcTrait { + fn fold_trait_item_method(&mut self, method: syn::TraitItemMethod) -> syn::TraitItemMethod { + let mut method = method.clone(); + let method_item = method.clone(); + // strip rpc attributes + method.attrs.retain(|a| { + let rpc_method = + self.methods.iter().find(|m| m.trait_item == method_item); + rpc_method.map_or(true, |rpc| rpc.attr.attr != *a) + }); + fold::fold_trait_item_method(self, method) + } + + fn fold_trait_item_type(&mut self, ty: syn::TraitItemType) -> syn::TraitItemType { + if ty.ident == METADATA_TYPE { + self.has_metadata = true; + let mut ty = ty.clone(); + if self.has_pubsub_methods { + ty.bounds.push(parse_quote!(_jsonrpc_pubsub::PubSubMetadata)) + } else { + ty.bounds.push(parse_quote!(_jsonrpc_core::Metadata)) + } + return ty; + } + ty + } +} + +fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result { + let methods_result: Result> = item_trait.items + .iter() + .filter_map(|trait_item| { + if let syn::TraitItem::Method(method) = trait_item { + match RpcMethodAttribute::parse_attr(&method) { + Ok(Some(attr)) => + Some(Ok(RpcMethod::new( + attr.clone(), + method.clone(), + ))), + Ok(None) => None, // non rpc annotated trait method + Err(err) => Some(Err(err)), + } + } else { + None + } + }) + .collect(); + let methods = methods_result?; + let has_pubsub_methods = methods.iter().any(RpcMethod::is_pubsub); + let mut rpc_trait = RpcTrait { methods: methods.clone(), has_pubsub_methods, has_metadata: false }; + let mut item_trait = fold::fold_item_trait(&mut rpc_trait, item_trait.clone()); + + let mut pubsub_method_pairs: HashMap, Option)> = HashMap::new(); + let mut method_registrations: Vec = Vec::new(); + + for method in methods.iter() { + match &method.attr().kind { + AttributeKind::Rpc { has_metadata } => + method_registrations.push(MethodRegistration::Standard { + method: method.clone(), + has_metadata: *has_metadata + }), + AttributeKind::PubSub { subscription_name, kind } => { + let (ref mut sub, ref mut unsub) = pubsub_method_pairs + .entry(subscription_name.clone()) + .or_insert((None, None)); + match kind { + PubSubMethodKind::Subscribe => { + if sub.is_none() { + *sub = Some(method.clone()) + } else { + return Err(format!("Subscription '{}' has more than one subscribe method", subscription_name)) + } + }, + PubSubMethodKind::Unsubscribe => { + if unsub.is_none() { + *unsub = Some(method.clone()) + } else { + return Err(format!("Subscription '{}' has more than one unsubscribe method", subscription_name)) + } + }, + } + }, + } + } + + for (name, pair) in pubsub_method_pairs { + match pair { + (Some(subscribe), Some(unsubscribe)) => + method_registrations.push(MethodRegistration::PubSub { + name: name.clone(), + subscribe: subscribe.clone(), + unsubscribe: unsubscribe.clone() + }), + (Some(_), None) => return Err(format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR)), + (None, Some(_)) => return Err(format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR)), + (None, None) => unreachable!(), + } + } + + let to_delegate_method = + generate_trait_item_method(&method_registrations, &item_trait, rpc_trait.has_metadata, has_pubsub_methods); + item_trait.items.push(syn::TraitItem::Method(to_delegate_method)); + + let trait_bounds: Punctuated = + parse_quote!(Sized + Send + Sync + 'static); + item_trait.supertraits.extend(trait_bounds); + + Ok(item_trait) +} + +fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { + let name = rpc_trait.ident.clone(); + let mod_name = format!("{}{}", RPC_MOD_NAME_PREFIX, name.to_string()); + syn::Ident::new(&mod_name, proc_macro2::Span::call_site()) +} + +pub fn rpc_impl(input: syn::Item) -> Result { + let rpc_trait = match input { + syn::Item::Trait(item_trait) => item_trait, + _ => return Err("rpc_api trait only works with trait declarations".into()) + }; + + let rpc_trait = generate_rpc_item_trait(&rpc_trait)?; + + let name = rpc_trait.ident.clone(); + let mod_name_ident = rpc_wrapper_mod_name(&rpc_trait); + + Ok(quote! { + mod #mod_name_ident { + use jsonrpc_core as _jsonrpc_core; + use jsonrpc_pubsub as _jsonrpc_pubsub; + use serde as _serde; + use super::*; + use self::_jsonrpc_core::futures as _futures; + + #rpc_trait + } + pub use self::#mod_name_ident::#name; + }) +} diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs new file mode 100644 index 000000000..d9a8ad5be --- /dev/null +++ b/derive/src/to_delegate.rs @@ -0,0 +1,395 @@ +use std::collections::HashSet; + +use quote::quote; +use syn::{ + parse_quote, Token, punctuated::Punctuated, + visit::{self, Visit}, +}; +use crate::rpc_attr::RpcMethodAttribute; + +pub enum MethodRegistration { + Standard { + method: RpcMethod, + has_metadata: bool + }, + PubSub { + name: String, + subscribe: RpcMethod, + unsubscribe: RpcMethod, + } +} + +impl MethodRegistration { + fn generate(&self) -> proc_macro2::TokenStream { + match self { + MethodRegistration::Standard { method, has_metadata } => { + let rpc_name = &method.name(); + let add_method = + if *has_metadata { + quote!(add_method_with_meta) + } else { + quote!(add_method) + }; + let closure = method.generate_delegate_closure(false); + let add_aliases = method.generate_add_aliases(); + + quote! { + del.#add_method(#rpc_name, #closure); + #add_aliases + } + }, + MethodRegistration::PubSub { name, subscribe, unsubscribe } => { + let sub_name = subscribe.name(); + let sub_closure = subscribe.generate_delegate_closure(true); + let sub_aliases = subscribe.generate_add_aliases(); + + let unsub_name = unsubscribe.name(); + let unsub_method_ident = unsubscribe.ident(); + let unsub_closure = + quote! { + move |base, id, meta| { + use self::_futures::{Future, IntoFuture}; + Self::#unsub_method_ident(base, meta, id).into_future() + .map(|value| _jsonrpc_core::to_value(value) + .expect("Expected always-serializable type; qed")) + .map_err(Into::into) + } + }; + let unsub_aliases = unsubscribe.generate_add_aliases(); + + quote! { + del.add_subscription( + #name, + (#sub_name, #sub_closure), + (#unsub_name, #unsub_closure), + ); + #sub_aliases + #unsub_aliases + } + }, + } + } +} + +const SUBCRIBER_TYPE_IDENT: &'static str = "Subscriber"; +const METADATA_CLOSURE_ARG: &'static str = "meta"; +const SUBSCRIBER_CLOSURE_ARG: &'static str = "subscriber"; + +pub fn generate_trait_item_method( + methods: &[MethodRegistration], + trait_item: &syn::ItemTrait, + has_metadata: bool, + has_pubsub_methods: bool, +) -> syn::TraitItemMethod { + let io_delegate_type = + if has_pubsub_methods { + quote!(_jsonrpc_pubsub::IoDelegate) + } else { + quote!(_jsonrpc_core::IoDelegate) + }; + let add_methods: Vec<_> = methods + .iter() + .map(MethodRegistration::generate) + .collect(); + let to_delegate_body = + quote! { + let mut del = #io_delegate_type::new(self.into()); + #(#add_methods)* + del + }; + + let method: syn::TraitItemMethod = + if has_metadata { + parse_quote! { + /// Create an `IoDelegate`, wiring rpc calls to the trait methods. + fn to_delegate(self) -> #io_delegate_type { + #to_delegate_body + } + } + } else { + parse_quote! { + /// Create an `IoDelegate`, wiring rpc calls to the trait methods. + fn to_delegate(self) -> #io_delegate_type { + #to_delegate_body + } + } + }; + + let predicates = generate_where_clause_serialization_predicates(&trait_item); + let mut method = method.clone(); + method.sig.decl.generics + .make_where_clause() + .predicates + .extend(predicates); + method +} + +#[derive(Clone)] +pub struct RpcMethod { + pub attr: RpcMethodAttribute, + pub trait_item: syn::TraitItemMethod, +} + +impl RpcMethod { + pub fn new(attr: RpcMethodAttribute, trait_item: syn::TraitItemMethod) -> RpcMethod { + RpcMethod { attr, trait_item } + } + + pub fn attr(&self) -> &RpcMethodAttribute { &self.attr } + + pub fn name(&self) -> &str { + &self.attr.name + } + + pub fn ident(&self) -> &syn::Ident { + &self.trait_item.sig.ident + } + + pub fn is_pubsub(&self) -> bool { + self.attr.is_pubsub() + } + + fn generate_delegate_closure(&self, is_subscribe: bool) -> proc_macro2::TokenStream { + let mut param_types: Vec<_> = + self.trait_item.sig.decl.inputs + .iter() + .cloned() + .filter_map(|arg| { + match arg { + syn::FnArg::Captured(arg_captured) => Some(arg_captured.ty), + syn::FnArg::Ignored(ty) => Some(ty), + _ => None, + } + }) + .collect(); + + // special args are those which are not passed directly via rpc params: metadata, subscriber + let special_args = Self::special_args(¶m_types); + param_types.retain(|ty| + special_args.iter().find(|(_,sty)| sty == ty).is_none()); + + let tuple_fields : &Vec<_> = + &(0..param_types.len() as u8) + .map(|x| ident(&((x + 'a' as u8) as char).to_string())) + .collect(); + let param_types = ¶m_types; + let parse_params = + // if the last argument is an `Option` then it can be made an optional 'trailing' argument + if let Some(ref trailing) = param_types.iter().last().and_then(try_get_option) { + self.params_with_trailing(trailing, param_types, tuple_fields) + } else if param_types.is_empty() { + quote! { let params = params.expect_no_params(); } + } else { + quote! { let params = params.parse::<(#(#param_types, )*)>(); } + }; + + let method_ident = self.ident(); + let result = &self.trait_item.sig.decl.output; + let extra_closure_args: &Vec<_> = &special_args.iter().cloned().map(|arg| arg.0).collect(); + let extra_method_types: &Vec<_> = &special_args.iter().cloned().map(|arg| arg.1).collect(); + + let closure_args = quote! { base, params, #(#extra_closure_args), * }; + let method_sig = quote! { fn(&Self, #(#extra_method_types, ) * #(#param_types), *) #result }; + let method_call = quote! { (base, #(#extra_closure_args, )* #(#tuple_fields), *) }; + let match_params = + if is_subscribe { + quote! { + Ok((#(#tuple_fields, )*)) => { + let subscriber = _jsonrpc_pubsub::typed::Subscriber::new(subscriber); + (method)#method_call + }, + Err(e) => { + let _ = subscriber.reject(e); + return + } + } + } else { + quote! { + Ok((#(#tuple_fields, )*)) => { + use self::_futures::{Future, IntoFuture}; + let fut = (method)#method_call + .into_future() + .map(|value| _jsonrpc_core::to_value(value) + .expect("Expected always-serializable type; qed")) + .map_err(Into::into as fn(_) -> _jsonrpc_core::Error); + _futures::future::Either::A(fut) + }, + Err(e) => _futures::future::Either::B(_futures::failed(e)), + } + }; + + quote! { + move |#closure_args| { + let method = &(Self::#method_ident as #method_sig); + #parse_params + match params { + #match_params + } + } + } + } + + fn special_args(param_types: &[syn::Type]) -> Vec<(syn::Ident, syn::Type)> { + let meta_arg = + param_types.first().and_then(|ty| + if *ty == parse_quote!(Self::Metadata) { Some(ty.clone()) } else { None }); + let subscriber_arg = param_types.iter().nth(1).and_then(|ty| { + if let syn::Type::Path(path) = ty { + if path.path.segments.iter().any(|s| s.ident == SUBCRIBER_TYPE_IDENT) { + Some(ty.clone()) + } else { + None + } + } else { + None + } + }); + + let mut special_args = Vec::new(); + if let Some(ref meta) = meta_arg { + special_args.push((ident(METADATA_CLOSURE_ARG), meta.clone())); + } + if let Some(ref subscriber) = subscriber_arg { + special_args.push((ident(SUBSCRIBER_CLOSURE_ARG), subscriber.clone())); + } + special_args + } + + fn params_with_trailing( + &self, + trailing: &syn::Type, + param_types: &[syn::Type], + tuple_fields: &[syn::Ident], + ) -> proc_macro2::TokenStream { + let param_types_no_trailing: Vec<_> = + param_types.iter().cloned().filter(|arg| arg != trailing).collect(); + let tuple_fields_no_trailing: &Vec<_> = + &tuple_fields.iter().take(tuple_fields.len() - 1).collect(); + let num = param_types_no_trailing.len(); + let all_params_len = param_types.len(); + let no_trailing_branch = + if all_params_len > 1 { + quote! { + params.parse::<(#(#param_types_no_trailing, )*)>() + .map( |(#(#tuple_fields_no_trailing, )*)| + (#(#tuple_fields_no_trailing, )* None)) + .map_err(Into::into) + } + } else if all_params_len == 1 { + quote! ( Ok((None,)) ) + } else { + panic!("Should be at least one trailing param; qed") + }; + quote! { + let params_len = match params { + _jsonrpc_core::Params::Array(ref v) => Ok(v.len()), + _jsonrpc_core::Params::None => Ok(0), + _ => Err(_jsonrpc_core::Error::invalid_params("`params` should be an array")) + }; + + let params = params_len.and_then(|len| { + match len.checked_sub(#num) { + Some(0) => #no_trailing_branch, + Some(1) => params.parse::<(#(#param_types, )*) > () + .map( |(#(#tuple_fields_no_trailing, )* id,)| + (#(#tuple_fields_no_trailing, )* id,)) + .map_err(Into::into), + None => Err(_jsonrpc_core::Error::invalid_params( + format!("`params` should have at least {} argument(s)", #num))), + _ => Err(_jsonrpc_core::Error::invalid_params_with_details( + format!("Expected {} or {} parameters.", #num, #num + 1), + format!("Got: {}", len))), + } + }); + } + } + + fn generate_add_aliases(&self) -> proc_macro2::TokenStream { + let name = self.name(); + let add_aliases: Vec<_> = self.attr.aliases + .iter() + .map(|alias| quote! { del.add_alias(#alias, #name); }) + .collect(); + quote!{ #(#add_aliases)* } + } +} + +fn ident(s: &str) -> syn::Ident { + syn::Ident::new(s, proc_macro2::Span::call_site()) +} + +fn try_get_option(ty: &syn::Type) -> Option { + if let syn::Type::Path(path) = ty { + path.path.segments + .first() + .and_then(|t| { + if t.value().ident == "Option" { Some(ty.clone()) } else { None } + }) + } else { + None + } +} + +fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) -> Vec { + struct FindTyParams { + trait_generics: HashSet, + serialize_type_params: HashSet, + deserialize_type_params: HashSet, + visiting_return_type: bool, + visiting_fn_arg: bool, + } + impl<'ast> Visit<'ast> for FindTyParams { + fn visit_type_param(&mut self, ty_param: &'ast syn::TypeParam) { + self.trait_generics.insert(ty_param.ident.clone()); + } + fn visit_return_type(&mut self, return_type: &'ast syn::ReturnType) { + self.visiting_return_type = true; + visit::visit_return_type(self, return_type); + self.visiting_return_type = false + } + fn visit_path_segment(&mut self, segment: &'ast syn::PathSegment) { + if self.visiting_return_type && self.trait_generics.contains(&segment.ident) { + self.serialize_type_params.insert(segment.ident.clone()); + } + if self.visiting_fn_arg && self.trait_generics.contains(&segment.ident) { + self.deserialize_type_params.insert(segment.ident.clone()); + } + visit::visit_path_segment(self, segment) + } + fn visit_fn_arg(&mut self, arg: &'ast syn::FnArg) { + self.visiting_fn_arg = true; + visit::visit_fn_arg(self, arg); + self.visiting_fn_arg = false; + } + } + let mut visitor = FindTyParams { + visiting_return_type: false, + visiting_fn_arg: false, + trait_generics: HashSet::new(), + serialize_type_params: HashSet::new(), + deserialize_type_params: HashSet::new(), + }; + visitor.visit_item_trait(item_trait); + + item_trait.generics + .type_params() + .map(|ty| { + let ty_path = syn::TypePath { qself: None, path: ty.ident.clone().into() }; + let mut bounds: Punctuated = + parse_quote!(Send + Sync + 'static); + // add json serialization trait bounds + if visitor.serialize_type_params.contains(&ty.ident) { + bounds.push(parse_quote!(_serde::Serialize)) + } + if visitor.deserialize_type_params.contains(&ty.ident) { + bounds.push(parse_quote!(_serde::de::DeserializeOwned)) + } + syn::WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty: syn::Type::Path(ty_path), + colon_token: ::default(), + bounds, + }) + }) + .collect() +} diff --git a/derive/tests/compiletests.rs b/derive/tests/compiletests.rs new file mode 100644 index 000000000..a389ddb5d --- /dev/null +++ b/derive/tests/compiletests.rs @@ -0,0 +1,19 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::Config::default(); + + config.mode = mode.parse().expect("Invalid mode"); + config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.link_deps(); // Populate config.target_rustcflags with dependencies on the path + config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("ui"); +} diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs new file mode 100644 index 000000000..c0963c3f5 --- /dev/null +++ b/derive/tests/macros.rs @@ -0,0 +1,116 @@ +use serde_json; +use jsonrpc_derive::rpc; +use jsonrpc_core::{IoHandler, Response}; + +pub enum MyError {} +impl From for jsonrpc_core::Error { + fn from(_e: MyError) -> Self { + unreachable!() + } +} + +type Result = ::std::result::Result; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Negates number and returns a result + #[rpc(name = "neg")] + fn neg(&self, _: i64) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add")] + fn add(&self, _: u64, _: u64) -> Result; +} + +#[derive(Default)] +struct RpcImpl; + +impl Rpc for RpcImpl { + fn protocol_version(&self) -> Result { + Ok("version1".into()) + } + + fn neg(&self, a: i64) -> Result { + Ok(-a) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } +} + +#[test] +fn should_accept_empty_array_as_no_params() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"protocolVersion","params":[]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"protocolVersion","params":null}"#; + let req3 = r#"{"jsonrpc":"2.0","id":1,"method":"protocolVersion"}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + let res3 = io.handle_request_sync(req3); + let expected = r#"{ + "jsonrpc": "2.0", + "result": "version1", + "id": 1 + }"#; + let expected: Response = serde_json::from_str(expected).unwrap(); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(expected, result1); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!(expected, result2); + + let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); + assert_eq!(expected, result3); +} + +#[test] +fn should_accept_single_param() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"neg","params":[1]}"#; + + let res1 = io.handle_request_sync(req1); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": -1, + "id": 1 + }"#).unwrap()); +} + +#[test] +fn should_accept_multiple_params() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"add","params":[1, 2]}"#; + + let res1 = io.handle_request_sync(req1); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 3, + "id": 1 + }"#).unwrap()); +} diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs new file mode 100644 index 000000000..6bb2eacf9 --- /dev/null +++ b/derive/tests/pubsub-macros.rs @@ -0,0 +1,89 @@ +extern crate serde_json; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; +#[macro_use] +extern crate jsonrpc_derive; + +use std::sync::Arc; +use jsonrpc_core::futures::sync::mpsc; +use jsonrpc_pubsub::{PubSubHandler, SubscriptionId, Session, PubSubMetadata}; +use jsonrpc_pubsub::typed::Subscriber; + +pub enum MyError {} +impl From for jsonrpc_core::Error { + fn from(_e: MyError) -> Self { + unreachable!() + } +} + +type Result = ::std::result::Result; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: Subscriber, _: u32, _: Option); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; + + /// A regular rpc method alongside pubsub + #[rpc(name = "add")] + fn add(&self, _: u64, _: u64) -> Result; +} + +#[derive(Default)] +struct RpcImpl; + +impl Rpc for RpcImpl { + type Metadata = Metadata; + + fn subscribe(&self, _meta: Self::Metadata, subscriber: Subscriber, _pre: u32, _trailing: Option) { + let _sink = subscriber.assign_id(SubscriptionId::Number(5)); + } + + fn unsubscribe(&self, _meta: Option, _id: SubscriptionId) -> Result { + Ok(true) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } +} + +#[derive(Clone, Default)] +struct Metadata; +impl jsonrpc_core::Metadata for Metadata {} +impl PubSubMetadata for Metadata { + fn session(&self) -> Option> { + let (tx, _rx) = mpsc::channel(1); + Some(Arc::new(Session::new(tx))) + } +} + +#[test] +fn test_invalid_trailing_pubsub_params() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let meta = Metadata; + let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello_subscribe","params":[]}"#; + let res = io.handle_request_sync(req, meta); + let expected = r#"{ + "jsonrpc": "2.0", + "error": { + "code": -32602, + "message": "`params` should have at least 1 argument(s)" + }, + "id": 1 + }"#; + + let expected: jsonrpc_core::Response = serde_json::from_str(expected).unwrap(); + let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); + assert_eq!(expected, result); +} diff --git a/derive/tests/trailing.rs b/derive/tests/trailing.rs new file mode 100644 index 000000000..472e1293d --- /dev/null +++ b/derive/tests/trailing.rs @@ -0,0 +1,94 @@ +use serde_json; +use jsonrpc_derive::rpc; +use jsonrpc_core::{IoHandler, Response, Result}; + +#[rpc] +pub trait Rpc { + /// Multiplies two numbers. Second number is optional. + #[rpc(name = "mul")] + fn mul(&self, _: u64, _: Option) -> Result; + + /// Echos back the message, example of a single param trailing + #[rpc(name = "echo")] + fn echo(&self, _: Option) -> Result; +} + +#[derive(Default)] +struct RpcImpl; + +impl Rpc for RpcImpl { + fn mul(&self, a: u64, b: Option) -> Result { + Ok(a * b.unwrap_or(1)) + } + + fn echo(&self, x: Option) -> Result { + Ok(x.unwrap_or("".into())) + } +} + +#[test] +fn should_accept_trailing_param() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req = r#"{"jsonrpc":"2.0","id":1,"method":"mul","params":[2, 2]}"#; + let res = io.handle_request_sync(req); + + // then + let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); + assert_eq!(result, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 4, + "id": 1 + }"#).unwrap()); +} + +#[test] +fn should_accept_missing_trailing_param() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req = r#"{"jsonrpc":"2.0","id":1,"method":"mul","params":[2]}"#; + let res = io.handle_request_sync(req); + + // then + let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); + assert_eq!(result, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 2, + "id": 1 + }"#).unwrap()); +} + +#[test] +fn should_accept_single_trailing_param() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"echo","params":["hello"]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"echo","params":[]}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": "hello", + "id": 1 + }"#).unwrap()); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!(result2, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": "", + "id": 1 + }"#).unwrap()); +} diff --git a/derive/tests/ui/pubsub/missing-subscribe.rs b/derive/tests/ui/pubsub/missing-subscribe.rs new file mode 100644 index 000000000..8e4bc9e2a --- /dev/null +++ b/derive/tests/ui/pubsub/missing-subscribe.rs @@ -0,0 +1,17 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + // note that a subscribe method is missing + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, Option, SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/missing-subscribe.stderr b/derive/tests/ui/pubsub/missing-subscribe.stderr new file mode 100644 index 000000000..7fa484661 --- /dev/null +++ b/derive/tests/ui/pubsub/missing-subscribe.stderr @@ -0,0 +1,10 @@ +error: custom attribute panicked + --> $DIR/missing-subscribe.rs:6:1 + | +6 | #[rpc] + | ^^^^^^ + | + = help: message: [rpc] encountered error: subscription 'hello'. Can't find subscribe method, expected a method annotated with `subscribe` e.g. `#[pubsub(subscription = "hello", subscribe, name = "hello_subscribe")]` + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/missing-unsubscribe.rs b/derive/tests/ui/pubsub/missing-unsubscribe.rs new file mode 100644 index 000000000..ce281673f --- /dev/null +++ b/derive/tests/ui/pubsub/missing-unsubscribe.rs @@ -0,0 +1,17 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, Self::Metadata, typed::Subscriber, u64); + + // note that the unsubscribe method is missing +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/missing-unsubscribe.stderr b/derive/tests/ui/pubsub/missing-unsubscribe.stderr new file mode 100644 index 000000000..fc51fba2f --- /dev/null +++ b/derive/tests/ui/pubsub/missing-unsubscribe.stderr @@ -0,0 +1,10 @@ +error: custom attribute panicked + --> $DIR/missing-unsubscribe.rs:6:1 + | +6 | #[rpc] + | ^^^^^^ + | + = help: message: [rpc] encountered error: subscription 'hello'. Can't find unsubscribe method, expected a method annotated with `unsubscribe` e.g. `#[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")]` + +error: aborting due to previous error + diff --git a/http/Cargo.toml b/http/Cargo.toml index 2781d7805..d5954b647 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -4,15 +4,15 @@ homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-http-server" -version = "9.0.0" +version = "10.0.0" authors = ["debris "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_http_server/index.html" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "9.0", path = "../core" } -jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 5a76375be..e0e78d52a 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-ipc-server" description = "IPC server for JSON-RPC" -version = "9.0.0" +version = "10.0.0" authors = ["Nikolay Volf "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -11,8 +11,8 @@ documentation = "https://paritytech.github.io/jsonrpc/json_ipc_server/index.html [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "9.0", path = "../core" } -jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } parity-tokio-ipc = "0.1" parking_lot = "0.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index f45e6b543..915c7858a 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -4,19 +4,19 @@ homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-macros" -version = "9.0.0" +version = "10.0.0" authors = ["rphmeier "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_macros/index.html" [dependencies] serde = "1.0" -jsonrpc-core = { version = "9.0", path = "../core" } -jsonrpc-pubsub = { version = "9.0", path = "../pubsub" } +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-pubsub = { version = "10.0", path = "../pubsub" } [dev-dependencies] serde_json = "1.0" -jsonrpc-tcp-server = { version = "9.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index 4688f8013..90e2abd15 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -125,6 +125,7 @@ macro_rules! build_rpc_trait { /// Transform this into an `IoDelegate`, automatically wrapping /// the parameters. + #[deprecated(since = "10.0", note = "Generated by jsonrpc-macros. Please use `#[rpc]` from jsonrpc-derive instead")] fn to_delegate(self) -> $crate::IoDelegate where $( $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ,)* @@ -198,6 +199,7 @@ macro_rules! build_rpc_trait { /// Transform this into an `IoDelegate`, automatically wrapping /// the parameters. + #[deprecated(since = "10.0", note = "Generated by jsonrpc-macros. Please use `#[rpc]` from jsonrpc-derive instead")] fn to_delegate(self) -> $crate::IoDelegate where $( $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ,)* diff --git a/macros/tests/macros.rs b/macros/tests/macros.rs index b155127a7..feb0dc24f 100644 --- a/macros/tests/macros.rs +++ b/macros/tests/macros.rs @@ -21,6 +21,10 @@ build_rpc_trait! { #[rpc(name = "protocolVersion")] fn protocol_version(&self) -> Result; + /// Negates number and returns a result + #[rpc(name = "neg")] + fn neg(&self, i64) -> Result; + /// Adds two numbers and returns a result #[rpc(name = "add")] fn add(&self, u64, u64) -> Result; @@ -35,6 +39,10 @@ impl Rpc for RpcImpl { Ok("version1".into()) } + fn neg(&self, a: i64) -> Result { + Ok(-a) + } + fn add(&self, a: u64, b: u64) -> Result { Ok(a + b) } @@ -71,3 +79,43 @@ fn should_accept_empty_array_as_no_params() { let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); assert_eq!(expected, result3); } + +#[test] +fn should_accept_single_param() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"neg","params":[1]}"#; + + let res1 = io.handle_request_sync(req1); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": -1, + "id": 1 + }"#).unwrap()); +} + +#[test] +fn should_accept_multiple_params() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"add","params":[1, 2]}"#; + + let res1 = io.handle_request_sync(req1); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 3, + "id": 1 + }"#).unwrap()); +} diff --git a/minihttp/Cargo.toml b/minihttp/Cargo.toml index 90bf02704..4249bff52 100644 --- a/minihttp/Cargo.toml +++ b/minihttp/Cargo.toml @@ -4,15 +4,15 @@ homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-minihttp-server" -version = "9.0.0" +version = "10.0.0" authors = ["tomusdrw "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_minihttp_server/index.html" [dependencies] bytes = "0.4" -jsonrpc-core = { version = "9.0", path = "../core" } -jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } log = "0.4" parking_lot = "0.7" tokio-minihttp = { git = "https://github.com/tomusdrw/tokio-minihttp" } diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 9386eeabd..c0fd70972 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-pubsub" -version = "9.0.0" +version = "10.0.0" authors = ["tomusdrw "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_pubsub/index.html" @@ -12,10 +12,11 @@ documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_pubsub/index.html" [dependencies] log = "0.4" parking_lot = "0.7" -jsonrpc-core = { version = "9.0", path = "../core" } +jsonrpc-core = { version = "10.0", path = "../core" } +serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "9.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index ce9c3c0f4..0cdaf2145 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "9.0.0" +version = "10.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "9.0", path = "../../core" } -jsonrpc-pubsub = { version = "9.0", path = "../" } -jsonrpc-ws-server = { version = "9.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "9.0", path = "../../ipc" } +jsonrpc-core = { version = "10.0", path = "../../core" } +jsonrpc-pubsub = { version = "10.0", path = "../" } +jsonrpc-ws-server = { version = "10.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "10.0", path = "../../ipc" } diff --git a/pubsub/src/delegates.rs b/pubsub/src/delegates.rs new file mode 100644 index 000000000..6fcc0eb73 --- /dev/null +++ b/pubsub/src/delegates.rs @@ -0,0 +1,136 @@ +use std::marker::PhantomData; +use std::sync::Arc; +use std::collections::HashMap; + +use subscription::{Subscriber, new_subscription}; +use handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; +use types::{SubscriptionId, PubSubMetadata}; +use core::{self, Params, Value, Error, Metadata, RemoteProcedure, RpcMethod}; +use core::futures::IntoFuture; + +struct DelegateSubscription { + delegate: Arc, + closure: F, +} + +impl SubscribeRpcMethod for DelegateSubscription where + M: PubSubMetadata, + F: Fn(&T, Params, M, Subscriber), + T: Send + Sync + 'static, + F: Send + Sync + 'static, +{ + fn call(&self, params: Params, meta: M, subscriber: Subscriber) { + let closure = &self.closure; + closure(&self.delegate, params, meta, subscriber) + } +} + +impl UnsubscribeRpcMethod for DelegateSubscription where + M: PubSubMetadata, + F: Fn(&T, SubscriptionId, Option) -> I, + I: IntoFuture, + T: Send + Sync + 'static, + F: Send + Sync + 'static, + I::Future: Send + 'static, +{ + type Out = I::Future; + fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out { + let closure = &self.closure; + closure(&self.delegate, id, meta).into_future() + } +} + +/// Wire up rpc subscriptions to `delegate` struct +pub struct IoDelegate where + T: Send + Sync + 'static, + M: Metadata, +{ + inner: core::IoDelegate, + delegate: Arc, + _data: PhantomData, +} + +impl IoDelegate where + T: Send + Sync + 'static, + M: PubSubMetadata, +{ + /// Creates new `PubSubIoDelegate`, wrapping the core IoDelegate + pub fn new(delegate: Arc) -> Self { + IoDelegate { + inner: core::IoDelegate::new(delegate.clone()), + delegate, + _data: PhantomData, + } + } + + /// Adds subscription to the delegate. + pub fn add_subscription( + &mut self, + name: &str, + subscribe: (&str, Sub), + unsubscribe: (&str, Unsub), + ) where + Sub: Fn(&T, Params, M, Subscriber), + Sub: Send + Sync + 'static, + Unsub: Fn(&T, SubscriptionId, Option) -> I, + I: IntoFuture, + Unsub: Send + Sync + 'static, + I::Future: Send + 'static, + { + let (sub, unsub) = new_subscription( + name, + DelegateSubscription { + delegate: self.delegate.clone(), + closure: subscribe.1, + }, + DelegateSubscription { + delegate: self.delegate.clone(), + closure: unsubscribe.1, + } + ); + self.inner.add_method_with_meta(subscribe.0, move |_, params, meta| sub.call(params, meta)); + self.inner.add_method_with_meta(unsubscribe.0, move |_, params, meta| unsub.call(params, meta)); + } + + /// Adds an alias to existing method. + pub fn add_alias(&mut self, from: &str, to: &str) { + self.inner.add_alias(from, to) + } + + /// Adds async method to the delegate. + pub fn add_method(&mut self, name: &str, method: F) where + F: Fn(&T, Params) -> I, + I: IntoFuture, + F: Send + Sync + 'static, + I::Future: Send + 'static, + { + self.inner.add_method(name, method) + } + + /// Adds async method with metadata to the delegate. + pub fn add_method_with_meta(&mut self, name: &str, method: F) where + F: Fn(&T, Params, M) -> I, + I: IntoFuture, + F: Send + Sync + 'static, + I::Future: Send + 'static, + { + self.inner.add_method_with_meta(name, method) + } + + /// Adds notification to the delegate. + pub fn add_notification(&mut self, name: &str, notification: F) where + F: Fn(&T, Params), + F: Send + Sync + 'static, + { + self.inner.add_notification(name, notification) + } +} + +impl Into>> for IoDelegate where + T: Send + Sync + 'static, + M: Metadata, +{ + fn into(self) -> HashMap> { + self.inner.into() + } +} diff --git a/pubsub/src/lib.rs b/pubsub/src/lib.rs index 9e773df76..248134439 100644 --- a/pubsub/src/lib.rs +++ b/pubsub/src/lib.rs @@ -4,14 +4,18 @@ extern crate jsonrpc_core as core; extern crate parking_lot; +extern crate serde; #[macro_use] extern crate log; +mod delegates; mod handler; mod subscription; mod types; +pub mod typed; pub use self::handler::{PubSubHandler, SubscribeRpcMethod, UnsubscribeRpcMethod}; +pub use self::delegates::IoDelegate; pub use self::subscription::{Session, Sink, Subscriber, new_subscription}; pub use self::types::{PubSubMetadata, SubscriptionId, TransportError, SinkResult}; diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs new file mode 100644 index 000000000..6b94da52b --- /dev/null +++ b/pubsub/src/typed.rs @@ -0,0 +1,134 @@ +//! PUB-SUB auto-serializing structures. + +use std::marker::PhantomData; + +use serde; +use subscription; +use types::{SubscriptionId, TransportError, SinkResult}; + +use core::Value; +use core::futures::{self, Sink as FuturesSink, sync}; + +/// New PUB-SUB subscriber. +#[derive(Debug)] +pub struct Subscriber { + subscriber: subscription::Subscriber, + _data: PhantomData<(T, E)>, +} + +impl Subscriber { + /// Wrap non-typed subscriber. + pub fn new(subscriber: subscription::Subscriber) -> Self { + Subscriber { + subscriber: subscriber, + _data: PhantomData, + } + } + + /// Create new subscriber for tests. + pub fn new_test>(method: M) -> ( + Self, + sync::oneshot::Receiver>, + sync::mpsc::Receiver, + ) { + let (subscriber, id, subscription) = subscription::Subscriber::new_test(method); + (Subscriber::new(subscriber), id, subscription) + } + + /// Reject subscription with given error. + pub fn reject(self, error: core::Error) -> Result<(), ()> { + self.subscriber.reject(error) + } + + /// Assign id to this subscriber. + /// This method consumes `Subscriber` and returns `Sink` + /// if the connection is still open or error otherwise. + pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { + let sink = self.subscriber.assign_id(id.clone())?; + Ok(Sink { + id: id, + sink: sink, + buffered: None, + _data: PhantomData, + }) + } +} + +/// Subscriber sink. +#[derive(Debug, Clone)] +pub struct Sink { + sink: subscription::Sink, + id: SubscriptionId, + buffered: Option, + _data: PhantomData<(T, E)>, +} + +impl Sink { + /// Sends a notification to the subscriber. + pub fn notify(&self, val: Result) -> SinkResult { + self.sink.notify(self.val_to_params(val)) + } + + fn to_value(value: V) -> Value where V: serde::Serialize { + core::to_value(value).expect("Expected always-serializable type.") + } + + fn val_to_params(&self, val: Result) -> core::Params { + + let id = self.id.clone().into(); + let val = val.map(Self::to_value).map_err(Self::to_value); + + core::Params::Map(vec![ + ("subscription".to_owned(), id), + match val { + Ok(val) => ("result".to_owned(), val), + Err(err) => ("error".to_owned(), err), + }, + ].into_iter().collect()) + } + + fn poll(&mut self) -> futures::Poll<(), TransportError> { + if let Some(item) = self.buffered.take() { + let result = self.sink.start_send(item)?; + if let futures::AsyncSink::NotReady(item) = result { + self.buffered = Some(item); + } + } + + if self.buffered.is_some() { + Ok(futures::Async::NotReady) + } else { + Ok(futures::Async::Ready(())) + } + } +} + +impl futures::sink::Sink for Sink { + type SinkItem = Result; + type SinkError = TransportError; + + fn start_send(&mut self, item: Self::SinkItem) -> futures::StartSend { + // Make sure to always try to process the buffered entry. + // Since we're just a proxy to real `Sink` we don't need + // to schedule a `Task` wakeup. It will be done downstream. + if self.poll()?.is_not_ready() { + return Ok(futures::AsyncSink::NotReady(item)); + } + + let val = self.val_to_params(item); + self.buffered = Some(val); + self.poll()?; + + Ok(futures::AsyncSink::Ready) + } + + fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { + self.poll()?; + self.sink.poll_complete() + } + + fn close(&mut self) -> futures::Poll<(), Self::SinkError> { + self.poll()?; + self.sink.close() + } +} diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 194cc28a9..caad69dc5 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Server utils for jsonrpc-core crate." name = "jsonrpc-server-utils" -version = "9.0.0" +version = "10.0.0" authors = ["tomusdrw "] license = "MIT" keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/jsonrpc" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "9.0", path = "../core" } +jsonrpc-core = { version = "10.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 811390c35..61149e1a3 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -10,7 +10,7 @@ documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_stdio_server/index [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "9.0", path = "../core" } +jsonrpc-core = { version = "10.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 753405a43..e4e2d5460 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-tcp-server" description = "TCP/IP server for JSON-RPC" -version = "9.0.0" +version = "10.0.0" authors = ["NikVolf "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -12,8 +12,8 @@ documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_tcp_server/index.h log = "0.4" parking_lot = "0.7" tokio-service = "0.1" -jsonrpc-core = { version = "9.0", path = "../core" } -jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/test/Cargo.toml b/test/Cargo.toml index ca7496812..1aed926d9 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "9.0.0" +version = "10.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -9,9 +9,9 @@ repository = "https://github.com/paritytech/jsonrpc" documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_test/index.html" [dependencies] -jsonrpc-core = { path = "../core", version = "9.0" } +jsonrpc-core = { path = "../core", version = "10.0" } serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-macros = { path = "../macros", version = "9.0" } +jsonrpc-macros = { path = "../macros", version = "10.0" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 95dd1d7a9..7d5f53737 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-ws-server" description = "WebSockets server for JSON-RPC" -version = "9.0.0" +version = "10.0.0" authors = ["tomusdrw "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,8 +10,8 @@ documentation = "https://paritytech.github.io/jsonrpc/json_ws_server/index.html" [dependencies] error-chain = "0.12" -jsonrpc-core = { version = "9.0", path = "../core" } -jsonrpc-server-utils = { version = "9.0", path = "../server-utils" } +jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } log = "0.4" parking_lot = "0.7" slab = "0.4" From 80205d4694a96d4455b022c731d206be3de4c117 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 29 Jan 2019 13:18:55 +0300 Subject: [PATCH 014/149] Support multiple trailing arguments (#365) * support multiple trailing arguments * Try to get the Windows build to work --- derive/src/to_delegate.rs | 87 +++++++++++++++++++----------------- derive/tests/compiletests.rs | 2 +- derive/tests/trailing.rs | 55 +++++++++++++++++++++++ 3 files changed, 102 insertions(+), 42 deletions(-) diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index d9a8ad5be..870da9c88 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -173,15 +173,17 @@ impl RpcMethod { .map(|x| ident(&((x + 'a' as u8) as char).to_string())) .collect(); let param_types = ¶m_types; - let parse_params = - // if the last argument is an `Option` then it can be made an optional 'trailing' argument - if let Some(ref trailing) = param_types.iter().last().and_then(try_get_option) { - self.params_with_trailing(trailing, param_types, tuple_fields) + let parse_params = { + // last arguments that are `Option`-s are optional 'trailing' arguments + let trailing_args_num = param_types.iter().rev().take_while(|t| is_option_type(t)).count(); + if trailing_args_num != 0 { + self.params_with_trailing(trailing_args_num, param_types, tuple_fields) } else if param_types.is_empty() { quote! { let params = params.expect_no_params(); } } else { quote! { let params = params.parse::<(#(#param_types, )*)>(); } - }; + } + }; let method_ident = self.ident(); let result = &self.trait_item.sig.decl.output; @@ -257,48 +259,52 @@ impl RpcMethod { fn params_with_trailing( &self, - trailing: &syn::Type, + trailing_args_num: usize, param_types: &[syn::Type], tuple_fields: &[syn::Ident], ) -> proc_macro2::TokenStream { - let param_types_no_trailing: Vec<_> = - param_types.iter().cloned().filter(|arg| arg != trailing).collect(); - let tuple_fields_no_trailing: &Vec<_> = - &tuple_fields.iter().take(tuple_fields.len() - 1).collect(); - let num = param_types_no_trailing.len(); - let all_params_len = param_types.len(); - let no_trailing_branch = - if all_params_len > 1 { - quote! { - params.parse::<(#(#param_types_no_trailing, )*)>() - .map( |(#(#tuple_fields_no_trailing, )*)| - (#(#tuple_fields_no_trailing, )* None)) - .map_err(Into::into) + let total_args_num = param_types.len(); + let required_args_num = total_args_num - trailing_args_num; + + let switch_branches = (0..trailing_args_num+1) + .map(|passed_trailing_args_num| { + let passed_args_num = required_args_num + passed_trailing_args_num; + let passed_param_types = ¶m_types[..passed_args_num]; + let passed_tuple_fields = &tuple_fields[..passed_args_num]; + let missed_args_num = total_args_num - passed_args_num; + let missed_params_values = ::std::iter::repeat(quote! { None }).take(missed_args_num).collect::>(); + + if passed_args_num == 0 { + quote! { + #passed_args_num => params.expect_no_params() + .map(|_| (#(#missed_params_values, ) *)) + .map_err(Into::into) + } + } else { + quote! { + #passed_args_num => params.parse::<(#(#passed_param_types, )*)>() + .map(|(#(#passed_tuple_fields,)*)| + (#(#passed_tuple_fields, )* #(#missed_params_values, )*)) + .map_err(Into::into) + } } - } else if all_params_len == 1 { - quote! ( Ok((None,)) ) - } else { - panic!("Should be at least one trailing param; qed") - }; + }).collect::>(); + quote! { - let params_len = match params { + let passed_args_num = match params { _jsonrpc_core::Params::Array(ref v) => Ok(v.len()), _jsonrpc_core::Params::None => Ok(0), _ => Err(_jsonrpc_core::Error::invalid_params("`params` should be an array")) }; - let params = params_len.and_then(|len| { - match len.checked_sub(#num) { - Some(0) => #no_trailing_branch, - Some(1) => params.parse::<(#(#param_types, )*) > () - .map( |(#(#tuple_fields_no_trailing, )* id,)| - (#(#tuple_fields_no_trailing, )* id,)) - .map_err(Into::into), - None => Err(_jsonrpc_core::Error::invalid_params( - format!("`params` should have at least {} argument(s)", #num))), + let params = passed_args_num.and_then(|passed_args_num| { + match passed_args_num { + _ if passed_args_num < #required_args_num => Err(_jsonrpc_core::Error::invalid_params( + format!("`params` should have at least {} argument(s)", #required_args_num))), + #(#switch_branches),*, _ => Err(_jsonrpc_core::Error::invalid_params_with_details( - format!("Expected {} or {} parameters.", #num, #num + 1), - format!("Got: {}", len))), + format!("Expected from {} to {} parameters.", #required_args_num, #total_args_num), + format!("Got: {}", passed_args_num))), } }); } @@ -318,15 +324,14 @@ fn ident(s: &str) -> syn::Ident { syn::Ident::new(s, proc_macro2::Span::call_site()) } -fn try_get_option(ty: &syn::Type) -> Option { +fn is_option_type(ty: &syn::Type) -> bool { if let syn::Type::Path(path) = ty { path.path.segments .first() - .and_then(|t| { - if t.value().ident == "Option" { Some(ty.clone()) } else { None } - }) + .map(|t| t.value().ident == "Option") + .unwrap_or(false) } else { - None + false } } diff --git a/derive/tests/compiletests.rs b/derive/tests/compiletests.rs index a389ddb5d..dd9d90795 100644 --- a/derive/tests/compiletests.rs +++ b/derive/tests/compiletests.rs @@ -7,7 +7,7 @@ fn run_mode(mode: &'static str) { config.mode = mode.parse().expect("Invalid mode"); config.src_base = PathBuf::from(format!("tests/{}", mode)); - config.link_deps(); // Populate config.target_rustcflags with dependencies on the path + config.target_rustcflags = Some("-L ../target/debug/ -L ../target/debug/deps/".to_owned()); config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 compiletest::run_tests(&config); diff --git a/derive/tests/trailing.rs b/derive/tests/trailing.rs index 472e1293d..232cc8709 100644 --- a/derive/tests/trailing.rs +++ b/derive/tests/trailing.rs @@ -11,6 +11,10 @@ pub trait Rpc { /// Echos back the message, example of a single param trailing #[rpc(name = "echo")] fn echo(&self, _: Option) -> Result; + + /// Adds up to three numbers and returns a result + #[rpc(name = "add_multi")] + fn add_multi(&self, _: Option, _: Option, _: Option) -> Result; } #[derive(Default)] @@ -24,6 +28,10 @@ impl Rpc for RpcImpl { fn echo(&self, x: Option) -> Result { Ok(x.unwrap_or("".into())) } + + fn add_multi(&self, a: Option, b: Option, c: Option) -> Result { + Ok(a.unwrap_or_default() + b.unwrap_or_default() + c.unwrap_or_default()) + } } #[test] @@ -92,3 +100,50 @@ fn should_accept_single_trailing_param() { "id": 1 }"#).unwrap()); } + +#[test] +fn should_accept_multiple_trailing_params() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"add_multi","params":[]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"add_multi","params":[1]}"#; + let req3 = r#"{"jsonrpc":"2.0","id":1,"method":"add_multi","params":[1, 2]}"#; + let req4 = r#"{"jsonrpc":"2.0","id":1,"method":"add_multi","params":[1, 2, 3]}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + let res3 = io.handle_request_sync(req3); + let res4 = io.handle_request_sync(req4); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 0, + "id": 1 + }"#).unwrap()); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!(result2, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 1, + "id": 1 + }"#).unwrap()); + + let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); + assert_eq!(result3, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 3, + "id": 1 + }"#).unwrap()); + + let result4: Response = serde_json::from_str(&res4.unwrap()).unwrap(); + assert_eq!(result4, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 6, + "id": 1 + }"#).unwrap()); +} From 8160eb550e3994b7a3acdd9f8082ad70004d9a8b Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Tue, 29 Jan 2019 13:26:21 +0300 Subject: [PATCH 015/149] ci: bring appveyor back (#364) * ci: add appveyor * Try to get the Windows build to work --- .travis.yml | 3 --- README.md | 3 +++ appveyor.yml | 24 ++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index 5d97870bf..b3292843e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,6 @@ matrix: rust: nightly - os: osx rust: stable - - os: windows - rust: stable - cache: false allow_failures: - rust: nightly diff --git a/README.md b/README.md index fd853d994..74ffa2de7 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,12 @@ Rust implementation of JSON-RPC 2.0 Specification. Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` and `tcp`. [![Build Status][travis-image]][travis-url] +[![Build Status][appveyor-image]][appveyor-url] [travis-image]: https://travis-ci.org/paritytech/jsonrpc.svg?branch=master [travis-url]: https://travis-ci.org/paritytech/jsonrpc +[appveyor-image]: https://ci.appveyor.com/api/projects/status/github/paritytech/jsonrpc?svg=true +[appveyor-url]: https://ci.appveyor.com/project/paritytech/jsonrpc/branch/master [Documentation](http://paritytech.github.io/jsonrpc/) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..33956025d --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,24 @@ +environment: + matrix: + - TARGET: x86_64-pc-windows-msvc + +install: + - curl -sSf -o rustup-init.exe https://win.rustup.rs/ + - rustup-init.exe -y --default-host %TARGET% + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin;C:\MinGW\bin + - rustc -vV + - cargo -vV + +build: false + +test_script: + - cargo test --all --verbose + +# avoid running tests twice +branches: + only: + - master + - /^parity-.*$/ + +cache: + - C:\Users\appveyor\.cargo From 2b47a5c19eabbb970d5db24cf222989ebade7f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 29 Jan 2019 11:56:27 +0100 Subject: [PATCH 016/149] Use parity-ws-rs from crates.io (#361) * Remove minihttp. * Use crates.io version in README. --- Cargo.toml | 1 - README.md | 22 +- http/README.md | 2 +- ipc/README.md | 2 +- minihttp/Cargo.toml | 27 -- minihttp/README.md | 37 --- minihttp/examples/http_async.rs | 19 -- minihttp/examples/http_meta.rs | 26 -- minihttp/examples/server.rs | 20 -- minihttp/src/lib.rs | 358 ----------------------- minihttp/src/req.rs | 55 ---- minihttp/src/res.rs | 85 ------ minihttp/src/tests.rs | 497 -------------------------------- stdio/README.md | 2 +- tcp/README.md | 2 +- ws/Cargo.toml | 2 +- ws/README.md | 2 +- ws/src/lib.rs | 2 +- ws/src/server.rs | 6 +- 19 files changed, 23 insertions(+), 1144 deletions(-) delete mode 100644 minihttp/Cargo.toml delete mode 100644 minihttp/README.md delete mode 100644 minihttp/examples/http_async.rs delete mode 100644 minihttp/examples/http_meta.rs delete mode 100644 minihttp/examples/server.rs delete mode 100644 minihttp/src/lib.rs delete mode 100644 minihttp/src/req.rs delete mode 100644 minihttp/src/res.rs delete mode 100644 minihttp/src/tests.rs diff --git a/Cargo.toml b/Cargo.toml index b7aa3fc07..665aa1332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "ipc", "macros", "derive", - "minihttp", "pubsub", "pubsub/more-examples", "server-utils", diff --git a/README.md b/README.md index 74ffa2de7..224f2749b 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,12 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` ## Sub-projects - [jsonrpc-core](./core) [![crates.io][core-image]][core-url] - [jsonrpc-http-server](./http) [![crates.io][http-server-image]][http-server-url] -- [jsonrpc-minihttp-server](./minihttp) -- [jsonrpc-ipc-server](./ipc) +- [jsonrpc-ipc-server](./ipc) [![crates.io][ipc-server-image]][ipc-server-url] - [jsonrpc-tcp-server](./tcp) [![crates.io][tcp-server-image]][tcp-server-url] -- [jsonrpc-ws-server](./ws) -- [jsonrpc-stdio-server](./stdio) +- [jsonrpc-ws-server](./ws) [![crates.io][ws-server-image]][ws-server-url] +- [jsonrpc-stdio-server](./stdio) [![crates.io][stdio-server-image]][stdio-server-url] - [jsonrpc-macros](./macros) [![crates.io][macros-image]][macros-url] *deprecated:* use `derive` instead -- [jsonrpc-derive](./derive) +- [jsonrpc-derive](./derive) [![crates.io][derive-image]][derive-url] - [jsonrpc-server-utils](./server-utils) [![crates.io][server-utils-image]][server-utils-url] - [jsonrpc-pubsub](./pubsub) [![crates.io][pubsub-image]][pubsub-url] @@ -30,10 +29,18 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` [core-url]: https://crates.io/crates/jsonrpc-core [http-server-image]: https://img.shields.io/crates/v/jsonrpc-http-server.svg [http-server-url]: https://crates.io/crates/jsonrpc-http-server +[ipc-server-image]: https://img.shields.io/crates/v/jsonrpc-ipc-server.svg +[ipc-server-url]: https://crates.io/crates/jsonrpc-ipc-server [tcp-server-image]: https://img.shields.io/crates/v/jsonrpc-tcp-server.svg [tcp-server-url]: https://crates.io/crates/jsonrpc-tcp-server +[ws-server-image]: https://img.shields.io/crates/v/jsonrpc-ws-server.svg +[ws-server-url]: https://crates.io/crates/jsonrpc-ws-server +[stdio-server-image]: https://img.shields.io/crates/v/jsonrpc-stdio-server.svg +[stdio-server-url]: https://crates.io/crates/jsonrpc-stdio-server [macros-image]: https://img.shields.io/crates/v/jsonrpc-macros.svg [macros-url]: https://crates.io/crates/jsonrpc-macros +[derive-image]: https://img.shields.io/crates/v/jsonrpc-derive.svg +[derive-url]: https://crates.io/crates/jsonrpc-derive [server-utils-image]: https://img.shields.io/crates/v/jsonrpc-server-utils.svg [server-utils-url]: https://crates.io/crates/jsonrpc-server-utils [pubsub-image]: https://img.shields.io/crates/v/jsonrpc-pubsub.svg @@ -49,11 +56,8 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` ### Basic Usage (with HTTP transport) ```rust -extern crate jsonrpc_core; -extern crate jsonrpc_minihttp_server; - use jsonrpc_core::{IoHandler, Value, Params}; -use jsonrpc_minihttp_server::{ServerBuilder}; +use jsonrpc_http_server::{ServerBuilder}; fn main() { let mut io = IoHandler::new(); diff --git a/http/README.md b/http/README.md index 40c62df05..d2da0d720 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc" } +jsonrpc-http-server = "10.0" ``` `main.rs` diff --git a/ipc/README.md b/ipc/README.md index 14552d40e..7effc341c 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc" } +jsonrpc-ipc-server = "10.0" ``` `main.rs` diff --git a/minihttp/Cargo.toml b/minihttp/Cargo.toml deleted file mode 100644 index 4249bff52..000000000 --- a/minihttp/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -description = "Blazing fast http server for JSON-RPC 2.0." -homepage = "https://github.com/paritytech/jsonrpc" -repository = "https://github.com/paritytech/jsonrpc" -license = "MIT" -name = "jsonrpc-minihttp-server" -version = "10.0.0" -authors = ["tomusdrw "] -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_minihttp_server/index.html" - -[dependencies] -bytes = "0.4" -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } -log = "0.4" -parking_lot = "0.7" -tokio-minihttp = { git = "https://github.com/tomusdrw/tokio-minihttp" } -tokio-proto = { git = "https://github.com/tomusdrw/tokio-proto" } -tokio-service = "0.1" - -[dev-dependencies] -env_logger = "0.6" -reqwest = "0.6" - -[badges] -travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/minihttp/README.md b/minihttp/README.md deleted file mode 100644 index fa692b078..000000000 --- a/minihttp/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# jsonrpc-minihttp-server -Blazing fast HTTP server for JSON-RPC 2.0. - -[Documentation](http://paritytech.github.io/jsonrpc/jsonrpc_http_server/index.html) - -## Example - -`Cargo.toml` - -``` -[dependencies] -jsonrpc-minihttp-server = { git = "https://github.com/paritytech/jsonrpc" } -``` - -`main.rs` - -```rust -extern crate jsonrpc_minihttp_server; - -use jsonrpc_minihttp_server::*; -use jsonrpc_minihttp_server::jsonrpc_core::*; -use jsonrpc_minihttp_server::cors::AccessControlAllowOrigin; - -fn main() { - let mut io = IoHandler::default(); - io.add_method("say_hello", |_| { - Ok(Value::String("hello".into())) - }); - - let server = ServerBuilder::new(io) - .cors(DomainsValidation::AllowOnly(vec![AccessControlAllowOrigin::Null])) - .start_http(&"127.0.0.1:3030".parse().unwrap()) - .expect("Unable to start RPC server"); - - server.wait().unwrap(); -} -``` diff --git a/minihttp/examples/http_async.rs b/minihttp/examples/http_async.rs deleted file mode 100644 index 1fbe52e48..000000000 --- a/minihttp/examples/http_async.rs +++ /dev/null @@ -1,19 +0,0 @@ -extern crate jsonrpc_minihttp_server; - -use jsonrpc_minihttp_server::{cors, ServerBuilder, DomainsValidation}; -use jsonrpc_minihttp_server::jsonrpc_core::*; - -fn main() { - let mut io = IoHandler::default(); - io.add_method("say_hello", |_params| { - futures::finished(Value::String("hello".to_owned())) - }); - - let server = ServerBuilder::new(io) - .cors(DomainsValidation::AllowOnly(vec![cors::AccessControlAllowOrigin::Null])) - .start_http(&"127.0.0.1:3030".parse().unwrap()) - .expect("Unable to start RPC server"); - - server.wait().unwrap(); -} - diff --git a/minihttp/examples/http_meta.rs b/minihttp/examples/http_meta.rs deleted file mode 100644 index 630c9fb11..000000000 --- a/minihttp/examples/http_meta.rs +++ /dev/null @@ -1,26 +0,0 @@ -extern crate jsonrpc_minihttp_server; - -use jsonrpc_minihttp_server::{cors, ServerBuilder, DomainsValidation, Req}; -use jsonrpc_minihttp_server::jsonrpc_core::*; - -#[derive(Clone, Default)] -struct Meta(usize); -impl Metadata for Meta {} - -fn main() { - let mut io = MetaIoHandler::default(); - io.add_method_with_meta("say_hello", |_params: Params, meta: Meta| { - futures::finished(Value::String(format!("hello: {}", meta.0))) - }); - - let server = ServerBuilder::new(io) - .meta_extractor(|req: &Req| { - Meta(req.header("Origin").map(|v| v.len()).unwrap_or_default()) - }) - .cors(DomainsValidation::AllowOnly(vec![cors::AccessControlAllowOrigin::Null])) - .start_http(&"127.0.0.1:3030".parse().unwrap()) - .expect("Unable to start RPC server"); - - server.wait().unwrap(); -} - diff --git a/minihttp/examples/server.rs b/minihttp/examples/server.rs deleted file mode 100644 index 63a3e6065..000000000 --- a/minihttp/examples/server.rs +++ /dev/null @@ -1,20 +0,0 @@ -extern crate jsonrpc_minihttp_server; - -use jsonrpc_minihttp_server::{cors, ServerBuilder, DomainsValidation}; -use jsonrpc_minihttp_server::jsonrpc_core::*; - -fn main() { - let mut io = IoHandler::default(); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) - }); - - let server = ServerBuilder::new(io) - .threads(3) - .cors(DomainsValidation::AllowOnly(vec![cors::AccessControlAllowOrigin::Null])) - .start_http(&"127.0.0.1:3030".parse().unwrap()) - .expect("Unable to start RPC server"); - - server.wait().unwrap(); -} - diff --git a/minihttp/src/lib.rs b/minihttp/src/lib.rs deleted file mode 100644 index 9f7920fd6..000000000 --- a/minihttp/src/lib.rs +++ /dev/null @@ -1,358 +0,0 @@ -//! jsonrpc http server. -//! -//! ```no_run -//! extern crate jsonrpc_core; -//! extern crate jsonrpc_minihttp_server; -//! -//! use jsonrpc_core::*; -//! use jsonrpc_minihttp_server::*; -//! -//! fn main() { -//! let mut io = IoHandler::new(); -//! io.add_method("say_hello", |_: Params| { -//! Ok(Value::String("hello".to_string())) -//! }); -//! -//! let _server = ServerBuilder::new(io).start_http(&"127.0.0.1:3030".parse().unwrap()); -//! } -//! ``` - -#![warn(missing_docs)] - -extern crate bytes; -extern crate jsonrpc_server_utils; -extern crate parking_lot; -extern crate tokio_proto; -extern crate tokio_service; -extern crate tokio_minihttp; - -pub extern crate jsonrpc_core; - -#[macro_use] -extern crate log; - -mod req; -mod res; -#[cfg(test)] -mod tests; - -use std::io; -use std::sync::{Arc, mpsc}; -use std::net::SocketAddr; -use std::thread; -use parking_lot::RwLock; -use jsonrpc_core as jsonrpc; -use jsonrpc::futures::{self, future, Future}; -use jsonrpc::{FutureResult, MetaIoHandler, Response, Output}; -use jsonrpc_server_utils::hosts; - -pub use jsonrpc_server_utils::cors; -pub use jsonrpc_server_utils::hosts::{Host, DomainsValidation}; -pub use req::Req; - -/// Extracts metadata from the HTTP request. -pub trait MetaExtractor: Sync + Send + 'static { - /// Read the metadata from the request - fn read_metadata(&self, _: &req::Req) -> M; -} - -impl MetaExtractor for F where - M: jsonrpc::Metadata, - F: Fn(&req::Req) -> M + Sync + Send + 'static, -{ - fn read_metadata(&self, req: &req::Req) -> M { - (*self)(req) - } -} - -#[derive(Default)] -struct NoopExtractor; -impl MetaExtractor for NoopExtractor { - fn read_metadata(&self, _: &req::Req) -> M { - M::default() - } -} - -/// Convenient JSON-RPC HTTP Server builder. -pub struct ServerBuilder = jsonrpc::middleware::Noop> { - jsonrpc_handler: Arc>, - meta_extractor: Arc>, - cors_domains: Option>, - allowed_hosts: Option>, - threads: usize, -} - -const SENDER_PROOF: &'static str = "Server initialization awaits local address."; - -impl> ServerBuilder { - /// Creates new `ServerBuilder` for given `IoHandler`. - /// - /// By default: - /// 1. Server is not sending any CORS headers. - /// 2. Server is validating `Host` header. - pub fn new(handler: T) -> Self where T: Into> { - Self::with_meta_extractor(handler, NoopExtractor) - } -} - -impl> ServerBuilder { - /// Creates new `ServerBuilder` for given `IoHandler` and meta extractor. - /// - /// By default: - /// 1. Server is not sending any CORS headers. - /// 2. Server is validating `Host` header. - pub fn with_meta_extractor(handler: T, extractor: E) -> Self where - T: Into>, - E: MetaExtractor, - { - ServerBuilder { - jsonrpc_handler: Arc::new(handler.into()), - meta_extractor: Arc::new(extractor), - cors_domains: None, - allowed_hosts: None, - threads: 1, - } - } - - /// Sets number of threads of the server to run. (not available for windows) - /// Panics when set to `0`. - pub fn threads(mut self, threads: usize) -> Self { - assert!(threads > 0); - self.threads = threads; - self - } - - /// Configures a list of allowed CORS origins. - pub fn cors(mut self, cors_domains: DomainsValidation) -> Self { - self.cors_domains = cors_domains.into(); - self - } - - /// Configures metadata extractor - pub fn meta_extractor>(mut self, extractor: T) -> Self { - self.meta_extractor = Arc::new(extractor); - self - } - - /// Allow connections only with `Host` header set to binding address. - pub fn allow_only_bind_host(mut self) -> Self { - self.allowed_hosts = Some(Vec::new()); - self - } - - /// Specify a list of valid `Host` headers. Binding address is allowed automatically. - pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation) -> Self { - self.allowed_hosts = allowed_hosts.into(); - self - } - - /// Start this JSON-RPC HTTP server trying to bind to specified `SocketAddr`. - pub fn start_http(self, addr: &SocketAddr) -> io::Result { - let cors_domains = self.cors_domains; - let allowed_hosts = self.allowed_hosts; - let handler = self.jsonrpc_handler; - let meta_extractor = self.meta_extractor; - let threads = self.threads; - - let (local_addr_tx, local_addr_rx) = mpsc::channel(); - let (close, shutdown_signal) = futures::sync::oneshot::channel(); - let addr = addr.to_owned(); - let handle = thread::spawn(move || { - let run = move || { - let hosts = Arc::new(RwLock::new(allowed_hosts.clone())); - let hosts2 = hosts.clone(); - let mut hosts_setter = hosts2.write(); - - let mut server = tokio_proto::TcpServer::new(tokio_minihttp::Http, addr); - server.threads(threads); - let server = server.bind(move || Ok(RpcService { - handler: handler.clone(), - meta_extractor: meta_extractor.clone(), - hosts: hosts.read().clone(), - cors_domains: cors_domains.clone(), - }))?; - - let local_addr = server.local_addr()?; - // Add current host to allowed headers. - // NOTE: we need to use `local_address` instead of `addr` - // it might be different! - *hosts_setter = hosts::update(allowed_hosts, &local_addr); - - Ok((server, local_addr)) - }; - - match run() { - Ok((server, local_addr)) => { - // Send local address - local_addr_tx.send(Ok(local_addr)).expect(SENDER_PROOF); - - // Start the server and wait for shutdown signal - server.run_until(shutdown_signal.map_err(|_| { - warn!("Shutdown signaller dropped, closing server."); - })).expect("Expected clean shutdown.") - }, - Err(err) => { - // Send error - local_addr_tx.send(Err(err)).expect(SENDER_PROOF); - } - } - }); - - // Wait for server initialization - let local_addr: io::Result = local_addr_rx.recv().map_err(|_| { - io::Error::new(io::ErrorKind::Interrupted, "") - })?; - - Ok(Server { - address: local_addr?, - handle: Some(handle), - close: Some(close), - }) - } -} - -/// Tokio-proto JSON-RPC HTTP Service -pub struct RpcService> { - handler: Arc>, - meta_extractor: Arc>, - hosts: Option>, - cors_domains: Option>, -} - -fn is_json(content_type: Option<&str>) -> bool { - match content_type { - None => false, - Some(ref content_type) => { - let json = "application/json"; - content_type.eq_ignore_ascii_case(json) - } - } -} - -impl> tokio_service::Service for RpcService { - type Request = tokio_minihttp::Request; - type Response = tokio_minihttp::Response; - type Error = io::Error; - type Future = future::Either< - future::FutureResult, - RpcResponse, - >; - - fn call(&self, request: Self::Request) -> Self::Future { - use self::future::Either; - - let request = req::Req::new(request); - // Validate HTTP Method - let is_options = request.method() == req::Method::Options; - if !is_options && request.method() != req::Method::Post { - return Either::A(future::ok( - res::method_not_allowed() - )); - } - - // Validate allowed hosts - let host = request.header("Host"); - if !hosts::is_host_valid(host.clone(), &self.hosts) { - return Either::A(future::ok( - res::invalid_host() - )); - } - - // Extract CORS headers - let origin = request.header("Origin"); - let cors = cors::get_cors_allow_origin(origin, host, &self.cors_domains); - - // Validate cors header - if let cors::AllowCors::Invalid = cors { - return Either::A(future::ok( - res::invalid_allow_origin() - )); - } - - // Don't process data if it's OPTIONS - if is_options { - return Either::A(future::ok( - res::options(cors.into()) - )); - } - - // Validate content type - let content_type = request.header("Content-type"); - if !is_json(content_type) { - return Either::A(future::ok( - res::invalid_content_type() - )); - } - - // Extract metadata - let metadata = self.meta_extractor.read_metadata(&request); - - // Read & handle request - let data = request.body(); - let future = self.handler.handle_request(data, metadata); - Either::B(RpcResponse { - future: future, - cors: cors.into(), - }) - } -} - -/// RPC response wrapper -pub struct RpcResponse where - F: Future, Error = ()>, - G: Future, Error = ()>, -{ - future: FutureResult, - cors: Option, -} - -impl Future for RpcResponse where - F: Future, Error = ()>, - G: Future, Error = ()>, -{ - type Item = tokio_minihttp::Response; - type Error = io::Error; - - fn poll(&mut self) -> futures::Poll { - use self::futures::Async::*; - - match self.future.poll() { - Err(_) => Ok(Ready(res::internal_error())), - Ok(NotReady) => Ok(NotReady), - Ok(Ready(result)) => { - let result = format!("{}\n", result.unwrap_or_default()); - Ok(Ready(res::new(&result, self.cors.take()))) - }, - } - } -} - -/// jsonrpc http server instance -pub struct Server { - address: SocketAddr, - handle: Option>, - close: Option>, -} - -impl Server { - /// Returns addresses of this server - pub fn address(&self) -> &SocketAddr { - &self.address - } - - /// Closes the server. - pub fn close(mut self) { - let _ = self.close.take().expect("Close is always set before self is consumed.").send(()); - } - - /// Will block, waiting for the server to finish. - pub fn wait(mut self) -> thread::Result<()> { - self.handle.take().expect("Handle is always set before set is consumed.").join() - } -} - -impl Drop for Server { - fn drop(&mut self) { - self.close.take().map(|close| close.send(())); - } -} diff --git a/minihttp/src/req.rs b/minihttp/src/req.rs deleted file mode 100644 index 5405226f2..000000000 --- a/minihttp/src/req.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Convenient Request wrapper used internally. - -use tokio_minihttp; -use bytes::Bytes; - -/// HTTP Method used -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Method { - /// POST - Post, - /// OPTIONS - Options, - /// Other method - Other -} - -/// Request -pub struct Req { - request: tokio_minihttp::Request, - body: Bytes, -} - -impl Req { - /// Creates new `Req` object - pub fn new(request: tokio_minihttp::Request) -> Self { - let body = request.body(); - Req { - request: request, - body: body, - } - } - - /// Returns request method - pub fn method(&self) -> Method { - // RFC 2616: The method is case-sensitive - match self.request.method() { - "OPTIONS" => Method::Options, - "POST" => Method::Post, - _ => Method::Other, - } - } - - /// Returns value of first header with given name. - /// `None` if header is not found or value is not utf-8 encoded - pub fn header(&self, name: &str) -> Option<&str> { - self.request.headers() - .find(|header| header.0.eq_ignore_ascii_case(name)) - .and_then(|header| ::std::str::from_utf8(header.1).ok()) - } - - /// Returns body of the request as a string - pub fn body(&self) -> &str { - ::std::str::from_utf8(&self.body).unwrap_or("") - } -} diff --git a/minihttp/src/res.rs b/minihttp/src/res.rs deleted file mode 100644 index 6eb61052c..000000000 --- a/minihttp/src/res.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Convenient Response utils used internally - -use tokio_minihttp::Response; -use jsonrpc_server_utils::cors; - -const SERVER: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); - -pub fn options(cors: Option) -> Response { - let mut response = new("", cors); - response - .header("Allow", "OPTIONS, POST") - .header("Accept", "application/json"); - response -} - -pub fn method_not_allowed() -> Response { - let mut response = Response::new(); - response - .status_code(405, "Method Not Allowed") - .server(SERVER) - .header("Content-Type", "text/plain") - .body("Used HTTP Method is not allowed. POST or OPTIONS is required.\n"); - response -} - -pub fn invalid_host() -> Response { - let mut response = Response::new(); - response - .status_code(403, "Forbidden") - .server(SERVER) - .header("Content-Type", "text/plain") - .body("Provided Host header is not whitelisted.\n"); - response -} - -pub fn internal_error() -> Response { - let mut response = Response::new(); - response - .status_code(500, "Internal Error") - .server(SERVER) - .header("Content-Type", "text/plain") - .body("Interal Server Error has occured."); - response -} - -pub fn invalid_allow_origin() -> Response { - let mut response = Response::new(); - response - .status_code(403, "Forbidden") - .server(SERVER) - .header("Content-Type", "text/plain") - .body("Origin of the request is not whitelisted. CORS headers would not be sent and any side-effects were cancelled as well.\n"); - response -} - -pub fn invalid_content_type() -> Response { - let mut response = Response::new(); - response - .status_code(415, "Unsupported Media Type") - .server(SERVER) - .header("Content-Type", "text/plain") - .body("Supplied content type is not allowed. Content-Type: application/json is required.\n"); - response -} - -pub fn new(body: &str, cors: Option) -> Response { - let mut response = Response::new(); - response - .header("Content-Type", "application/json") - .server(SERVER) - .body(body); - - if let Some(cors) = cors { - response - .header("Access-Control-Allow-Methods", "OPTIONS, POST") - .header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept") - .header("Access-Control-Allow-Origin", match cors { - cors::AccessControlAllowOrigin::Null => "null", - cors::AccessControlAllowOrigin::Any => "*", - cors::AccessControlAllowOrigin::Value(ref val) => val, - }) - .header("Vary", "Origin"); - } - response -} diff --git a/minihttp/src/tests.rs b/minihttp/src/tests.rs deleted file mode 100644 index 31f954396..000000000 --- a/minihttp/src/tests.rs +++ /dev/null @@ -1,497 +0,0 @@ -extern crate jsonrpc_core; -extern crate env_logger; -extern crate reqwest; - -use std::io::Read; -use self::reqwest::{StatusCode, Method}; -use self::reqwest::header::{self, Headers}; -use self::jsonrpc_core::{IoHandler, Params, Value, Error}; -use self::jsonrpc_core::futures::{self, Future}; -use super::{ServerBuilder, Server, cors, hosts}; - -fn serve_hosts(hosts: Vec) -> Server { - let _ = env_logger::try_init(); - - ServerBuilder::new(IoHandler::default()) - .cors(hosts::DomainsValidation::AllowOnly(vec![cors::AccessControlAllowOrigin::Value("http://parity.io".into())])) - .allowed_hosts(hosts::DomainsValidation::AllowOnly(hosts)) - .start_http(&"127.0.0.1:0".parse().unwrap()) - .unwrap() -} - -fn serve() -> Server { - use std::thread; - - let _ = env_logger::try_init(); - let mut io = IoHandler::default(); - io.add_method("hello", |_params: Params| Ok(Value::String("world".into()))); - io.add_method("hello_async", |_params: Params| { - futures::finished(Value::String("world".into())) - }); - io.add_method("hello_async2", |_params: Params| { - let (c, p) = futures::oneshot(); - thread::spawn(move || { - thread::sleep(::std::time::Duration::from_millis(10)); - c.send(Value::String("world".into())).unwrap(); - }); - p.map_err(|_| Error::invalid_request()) - }); - - ServerBuilder::new(io) - .cors(hosts::DomainsValidation::AllowOnly(vec![ - cors::AccessControlAllowOrigin::Value("http://parity.io".into()), - cors::AccessControlAllowOrigin::Null, - ])) - .start_http(&"127.0.0.1:0".parse().unwrap()) - .unwrap() -} - -struct Response { - pub status: reqwest::StatusCode, - pub body: String, - pub headers: Headers, -} - -fn request(server: Server, method: Method, headers: Headers, body: &'static str) -> Response { - let client = reqwest::Client::new().unwrap(); - let mut res = client.request(method, &format!("http://{}", server.address())) - .headers(headers) - .body(body) - .send() - .unwrap(); - - let mut body = String::new(); - res.read_to_string(&mut body).unwrap(); - - Response { - status: res.status().clone(), - body: body, - headers: res.headers().clone(), - } -} - -#[test] -fn should_return_method_not_allowed_for_get() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Get, - Headers::new(), - "I shouldn't be read.", - ); - - // then - assert_eq!(response.status, StatusCode::MethodNotAllowed); - assert_eq!(response.body, "Used HTTP Method is not allowed. POST or OPTIONS is required.\n".to_owned()); -} - -#[test] -fn should_ignore_media_type_if_options() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Options, - Headers::new(), - "", - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!( - response.headers.get::(), - Some(&reqwest::header::Allow(vec![Method::Options, Method::Post])) - ); - assert!(response.headers.get::().is_some()); - assert_eq!(response.body, ""); -} - - -#[test] -fn should_return_403_for_options_if_origin_is_invalid() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.append_raw("origin", b"http://invalid.io".to_vec()); - headers - }, - "" - ); - - // then - assert_eq!(response.status, StatusCode::Forbidden); - assert_eq!(response.body, cors_invalid_allow_origin()); -} - -#[test] -fn should_return_unsupported_media_type_if_not_json() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - Headers::new(), - "{}", - ); - - // then - assert_eq!(response.status, StatusCode::UnsupportedMediaType); - assert_eq!(response.body, "Supplied content type is not allowed. Content-Type: application/json is required.\n".to_owned()); -} - -fn content_type_json() -> Headers { - let mut headers = Headers::new(); - headers.set_raw("content-type", vec![b"application/json".to_vec()]); - headers -} - -#[test] -fn should_return_error_for_malformed_request() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"3.0","method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, invalid_request()); -} - -#[test] -fn should_return_error_for_malformed_request2() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"2.0","metho1d":""}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, invalid_request()); -} - -#[test] -fn should_return_empty_response_for_notification() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"2.0","method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, "\n".to_owned()); -} - - -#[test] -fn should_return_method_not_found() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, method_not_found()); -} - -#[test] -fn should_add_cors_allow_origins() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.set(header::Origin::new("http", "parity.io", None)); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, method_not_found()); - assert_eq!( - response.headers.get::(), - Some(&reqwest::header::AccessControlAllowOrigin::Value("http://parity.io".into())) - ); -} - -#[test] -fn should_add_cors_allow_origins_for_options() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Options, - { - let mut headers = content_type_json(); - headers.set(header::Origin::new("http", "parity.io", None)); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, "".to_owned()); - println!("{:?}", response.headers); - assert_eq!( - response.headers.get::(), - Some(&reqwest::header::AccessControlAllowOrigin::Value("http://parity.io".into())) - ); -} - -#[test] -fn should_not_process_request_with_invalid_allow_origin() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.set(header::Origin::new("http", "fake.io", None)); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Forbidden); - assert_eq!(response.body, cors_invalid_allow_origin()); -} - -#[test] -fn should_add_cors_allow_origin_for_null_origin() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.append_raw("origin", b"null".to_vec()); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, method_not_found()); - assert_eq!( - response.headers.get::().cloned(), - Some(reqwest::header::AccessControlAllowOrigin::Null) - ); -} - -#[test] -fn should_reject_invalid_hosts() { - // given - let server = serve_hosts(vec!["parity.io".into()]); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.set_raw("Host", vec![b"127.0.0.1:8080".to_vec()]); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Forbidden); - assert_eq!(response.body, invalid_host()); -} - -#[test] -fn should_allow_if_host_is_valid() { - // given - let server = serve_hosts(vec!["parity.io".into()]); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.set_raw("Host", vec![b"parity.io".to_vec()]); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, method_not_found()); -} - -#[test] -fn should_always_allow_the_bind_address() { - // given - let server = serve_hosts(vec!["parity.io".into()]); - let addr = server.address().clone(); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.set_raw("Host", vec![format!("{}", addr).as_bytes().to_vec()]); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, method_not_found()); -} - -#[test] -fn should_always_allow_the_bind_address_as_localhost() { - // given - let server = serve_hosts(vec![]); - let addr = server.address().clone(); - - // when - let response = request(server, - Method::Post, - { - let mut headers = content_type_json(); - headers.set_raw("Host", vec![format!("localhost:{}", addr.port()).as_bytes().to_vec()]); - headers - }, - r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, method_not_found()); -} - -#[test] -fn should_handle_sync_requests_correctly() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"2.0","id":1,"method":"hello"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, world()); -} - -#[test] -fn should_handle_async_requests_with_immediate_response_correctly() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"2.0","id":1,"method":"hello_async"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, world()); -} - -#[test] -fn should_handle_async_requests_correctly() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"{"jsonrpc":"2.0","id":1,"method":"hello_async2"}"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, world()); -} - -#[test] -fn should_handle_sync_batch_requests_correctly() { - // given - let server = serve(); - - // when - let response = request(server, - Method::Post, - content_type_json(), - r#"[{"jsonrpc":"2.0","id":1,"method":"hello"}]"#, - ); - - // then - assert_eq!(response.status, StatusCode::Ok); - assert_eq!(response.body, world_batch()); -} - -fn invalid_host() -> String { - "Provided Host header is not whitelisted.\n".into() -} - -fn cors_invalid_allow_origin() -> String { - "Origin of the request is not whitelisted. CORS headers would not be sent and any side-effects were cancelled as well.\n".into() -} - -fn method_not_found() -> String { - "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":1}\n".into() -} - -fn invalid_request() -> String { - "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":null}\n".into() -} -fn world() -> String { - "{\"jsonrpc\":\"2.0\",\"result\":\"world\",\"id\":1}\n".into() -} -fn world_batch() -> String { - "[{\"jsonrpc\":\"2.0\",\"result\":\"world\",\"id\":1}]\n".into() -} diff --git a/stdio/README.md b/stdio/README.md index f4497901f..15c4054a2 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = { git = "https://github.com/paritytech/jsonrpc" } +jsonrpc-stdio-server = "10.0" ``` `main.rs` diff --git a/tcp/README.md b/tcp/README.md index e850da952..83edada87 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = { git = "https://github.com/paritytech/jsonrpc" } +jsonrpc-tcp-server = "10.0" ``` `main.rs` diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 7d5f53737..7f0251e49 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -15,7 +15,7 @@ jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } log = "0.4" parking_lot = "0.7" slab = "0.4" -ws = { git = "https://github.com/tomusdrw/ws-rs" } +parity-ws = "0.8" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ws/README.md b/ws/README.md index b95124c44..d8fb5b111 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc" } +jsonrpc-ws-server = "10.0" ``` `main.rs` diff --git a/ws/src/lib.rs b/ws/src/lib.rs index 905884cfd..1823ebce9 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -6,7 +6,7 @@ extern crate jsonrpc_server_utils as server_utils; extern crate parking_lot; extern crate slab; -pub extern crate ws; +pub extern crate parity_ws as ws; pub extern crate jsonrpc_core; #[macro_use] diff --git a/ws/src/server.rs b/ws/src/server.rs index 74a2f6435..e3d25441a 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{cmp, fmt}; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; use std::thread; @@ -56,10 +56,10 @@ impl Server { let mut config = ws::Settings::default(); config.max_connections = max_connections; // don't accept super large requests - config.max_in_buffer = max_payload_bytes; + config.max_fragment_size = max_payload_bytes; // don't grow non-final fragments (to prevent DOS) config.fragments_grow = false; - config.fragments_capacity = max_payload_bytes / config.fragment_size; + config.fragments_capacity = cmp::max(1, max_payload_bytes / config.fragment_size); // accept only handshakes beginning with GET config.method_strict = true; // require masking From 5a7be5f94f3536219c54d0b9878486457cc314a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 29 Jan 2019 12:22:42 +0100 Subject: [PATCH 017/149] Bump version of stdio (#367) --- stdio/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 61149e1a3..4403b3408 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-stdio-server" description = "STDIN/STDOUT server for JSON-RPC" -version = "0.1.0" +version = "10.0.0" authors = ["cmichi "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" From 37b62a37fd0d39735fac7a42933e0ebc154561f6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 29 Jan 2019 15:34:53 +0000 Subject: [PATCH 018/149] Migrate to edition 2018 (#368) * core: cargo fix --edition * core: edition = 2018 * http: cargo fix --edition * http: edition = 2018 * ipc: edition 2018 * pubsub: edition 2018 * pubsub: cargo fix --edition-idioms --broken-code, then fixed compile errors * server-utils: cargo fix --edition * server-utils: edition 2018 * server-utils: cargo fix --edition-idioms --broken-code * stdio: edition 2018 * stdio: edition 2018 idioms * tcp: edition 2018 * tcp: edition 2018 idioms * test: edition 2018 * test: edition 2018 idioms * ws: edition 2018 * ws: edition 2018 idioms * edition 2018: remove whitespaces from cargo fix * core: edition 2018 idioms * http: edition 2018 idioms * derive: edition 2018 idioms * ipc: edition 2018 idioms * pubsub: edition 2018 idioms * Remove more blank lines and unused * Remove all `dyn` keywords for trait objects * Remove anonymous lifetimes * Replace `r#try` with `?` * Remove `extern crate` in doctests * Remove `extern crate` in READMEs * Convert doctest to use derive instead of macros --- core/Cargo.toml | 1 + core/README.md | 4 --- core/examples/async.rs | 2 -- core/examples/basic.rs | 2 -- core/examples/meta.rs | 2 -- core/examples/middlewares.rs | 2 -- core/src/calls.rs | 4 +-- core/src/delegates.rs | 6 ++--- core/src/io.rs | 10 ++++---- core/src/lib.rs | 15 +++++------ core/src/middleware.rs | 4 +-- core/src/types/error.rs | 2 +- core/src/types/params.rs | 2 +- core/src/types/response.rs | 2 +- derive/examples/generic-trait.rs | 2 +- derive/tests/compiletests.rs | 2 +- derive/tests/pubsub-macros.rs | 6 ++--- http/Cargo.toml | 1 + http/README.md | 2 -- http/examples/http_async.rs | 2 -- http/examples/http_meta.rs | 3 --- http/examples/http_middleware.rs | 2 -- http/examples/server.rs | 2 -- http/src/handler.rs | 14 +++++----- http/src/lib.rs | 37 ++++++++++++--------------- http/src/tests.rs | 2 +- http/src/utils.rs | 2 +- ipc/Cargo.toml | 1 + ipc/README.md | 2 -- ipc/examples/ipc.rs | 2 +- ipc/src/lib.rs | 13 ++++------ ipc/src/meta.rs | 6 ++--- ipc/src/select_with_weak.rs | 4 +-- ipc/src/server.rs | 44 ++++++++++++++++---------------- pubsub/Cargo.toml | 1 + pubsub/examples/pubsub.rs | 4 --- pubsub/examples/pubsub_simple.rs | 4 --- pubsub/src/delegates.rs | 10 ++++---- pubsub/src/handler.rs | 18 ++++++------- pubsub/src/lib.rs | 4 +-- pubsub/src/subscription.rs | 20 +++++++-------- pubsub/src/typed.rs | 22 ++++++++-------- pubsub/src/types.rs | 8 +++--- server-utils/Cargo.toml | 1 + server-utils/src/cors.rs | 8 +++--- server-utils/src/hosts.rs | 2 +- server-utils/src/lib.rs | 15 +++++------ server-utils/src/reactor.rs | 4 +-- stdio/Cargo.toml | 1 + stdio/README.md | 2 -- stdio/examples/stdio.rs | 2 -- stdio/src/lib.rs | 10 +++----- tcp/Cargo.toml | 1 + tcp/README.md | 2 -- tcp/examples/tcp.rs | 3 +-- tcp/src/dispatch.rs | 6 ++--- tcp/src/lib.rs | 16 ++++-------- tcp/src/meta.rs | 4 +-- tcp/src/server.rs | 14 +++++----- tcp/src/service.rs | 2 +- tcp/src/tests.rs | 26 +++++++++---------- test/Cargo.toml | 5 +++- test/src/lib.rs | 27 +++++++++----------- ws/Cargo.toml | 1 + ws/README.md | 2 -- ws/examples/ws.rs | 2 -- ws/src/error.rs | 2 +- ws/src/lib.rs | 8 +++--- ws/src/metadata.rs | 12 ++++----- ws/src/server.rs | 20 +++++++-------- ws/src/server_builder.rs | 20 +++++++-------- ws/src/session.rs | 22 ++++++++-------- ws/src/tests.rs | 16 ++++++------ 73 files changed, 246 insertions(+), 308 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 4c6afec8b..d54bd2de8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,6 +8,7 @@ version = "10.0.0" authors = ["debris "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_core/index.html" +edition = "2018" categories = [ "asynchronous", diff --git a/core/README.md b/core/README.md index 7ec5b9d46..13f5e9f64 100644 --- a/core/README.md +++ b/core/README.md @@ -19,8 +19,6 @@ jsonrpc-core = "4.0" `main.rs` ```rust -extern crate jsonrpc_core; - use jsonrpc_core::*; fn main() { @@ -41,8 +39,6 @@ fn main() { `main.rs` ```rust -extern crate jsonrpc_core; - use jsonrpc_core::*; use jsonrpc_core::futures::Future; diff --git a/core/examples/async.rs b/core/examples/async.rs index 29d0b4f9c..5040e8bd8 100644 --- a/core/examples/async.rs +++ b/core/examples/async.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_core; - use jsonrpc_core::*; use jsonrpc_core::futures::Future; diff --git a/core/examples/basic.rs b/core/examples/basic.rs index 22300d575..8afb42b32 100644 --- a/core/examples/basic.rs +++ b/core/examples/basic.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_core; - use jsonrpc_core::*; fn main() { diff --git a/core/examples/meta.rs b/core/examples/meta.rs index e1d8db59a..2569e426f 100644 --- a/core/examples/meta.rs +++ b/core/examples/meta.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_core; - use jsonrpc_core::*; use jsonrpc_core::futures::Future; diff --git a/core/examples/middlewares.rs b/core/examples/middlewares.rs index 60a09fde4..a16c915f2 100644 --- a/core/examples/middlewares.rs +++ b/core/examples/middlewares.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_core; - use std::time::Instant; use std::sync::atomic::{self, AtomicUsize}; use jsonrpc_core::*; diff --git a/core/src/calls.rs b/core/src/calls.rs index 997f7276f..b02ad8a7d 100644 --- a/core/src/calls.rs +++ b/core/src/calls.rs @@ -1,8 +1,8 @@ use std::fmt; use std::sync::Arc; -use types::{Params, Value, Error}; +use crate::types::{Params, Value, Error}; use futures::{Future, IntoFuture}; -use BoxFuture; +use crate::BoxFuture; /// Metadata trait pub trait Metadata: Clone + Send + 'static {} diff --git a/core/src/delegates.rs b/core/src/delegates.rs index 7dd6000aa..2fa13f4af 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -3,10 +3,10 @@ use std::sync::Arc; use std::collections::HashMap; -use types::{Params, Value, Error}; -use calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification}; +use crate::types::{Params, Value, Error}; +use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification}; use futures::IntoFuture; -use BoxFuture; +use crate::BoxFuture; struct DelegateAsyncMethod { delegate: Arc, diff --git a/core/src/io.rs b/core/src/io.rs index 43d8cc4da..cc163d631 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -5,10 +5,10 @@ use std::ops::{Deref, DerefMut}; use serde_json; use futures::{self, future, Future}; -use calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; -use middleware::{self, Middleware}; -use types::{Error, ErrorCode, Version}; -use types::{Request, Response, Call, Output}; +use crate::calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; +use crate::middleware::{self, Middleware}; +use crate::types::{Error, ErrorCode, Version}; +use crate::types::{Request, Response, Call, Output}; /// A type representing middleware or RPC response before serialization. pub type FutureResponse = Box, Error=()> + Send>; @@ -372,7 +372,7 @@ fn write_response(response: Response) -> String { #[cfg(test)] mod tests { use futures; - use types::{Value}; + use crate::types::{Value}; use super::{IoHandler, Compatibility}; #[test] diff --git a/core/src/lib.rs b/core/src/lib.rs index 6da2334af..0bdeb2e92 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,8 +3,6 @@ //! Right now it supports only server side handling requests. //! //! ```rust -//! extern crate jsonrpc_core; -//! //! use jsonrpc_core::*; //! use jsonrpc_core::futures::Future; //! @@ -25,9 +23,8 @@ #[macro_use] extern crate log; #[macro_use] extern crate serde_derive; -extern crate serde; -pub extern crate futures; +pub use futures; #[doc(hidden)] pub extern crate serde_json; @@ -45,8 +42,8 @@ pub type BoxFuture = Box + Send>; /// A Result type. pub type Result = ::std::result::Result; -pub use calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; -pub use delegates::IoDelegate; -pub use io::{Compatibility, IoHandler, MetaIoHandler, FutureOutput, FutureResult, FutureResponse, FutureRpcResult}; -pub use middleware::{Middleware, Noop as NoopMiddleware}; -pub use types::*; +pub use crate::calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; +pub use crate::delegates::IoDelegate; +pub use crate::io::{Compatibility, IoHandler, MetaIoHandler, FutureOutput, FutureResult, FutureResponse, FutureRpcResult}; +pub use crate::middleware::{Middleware, Noop as NoopMiddleware}; +pub use crate::types::*; diff --git a/core/src/middleware.rs b/core/src/middleware.rs index 699e2853e..1300d219e 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -1,7 +1,7 @@ //! `IoHandler` middlewares -use calls::Metadata; -use types::{Request, Response, Call, Output}; +use crate::calls::Metadata; +use crate::types::{Request, Response, Call, Output}; use futures::{future::Either, Future}; /// RPC middleware diff --git a/core/src/types/error.rs b/core/src/types/error.rs index 6f7a573af..d3a7e088c 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -65,7 +65,7 @@ impl From for ErrorCode { impl<'a> Deserialize<'a> for ErrorCode { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - let code: i64 = try!(Deserialize::deserialize(deserializer)); + let code: i64 = Deserialize::deserialize(deserializer)?; Ok(ErrorCode::from(code)) } } diff --git a/core/src/types/params.rs b/core/src/types/params.rs index 60cfed9ae..27bf4b4af 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -46,7 +46,7 @@ impl Params { mod tests { use serde_json; use super::Params; - use types::{Value, Error, ErrorCode}; + use crate::types::{Value, Error, ErrorCode}; #[test] fn params_deserialization() { diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 7b562bd36..95e13520d 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -1,6 +1,6 @@ //! jsonrpc response use super::{Id, Value, Error, ErrorCode, Version}; -use {Result as CoreResult}; +use crate::{Result as CoreResult}; /// Successful response #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] diff --git a/derive/examples/generic-trait.rs b/derive/examples/generic-trait.rs index 94db24452..89504d56f 100644 --- a/derive/examples/generic-trait.rs +++ b/derive/examples/generic-trait.rs @@ -1,4 +1,4 @@ -extern crate jsonrpc_core; +use jsonrpc_core; use jsonrpc_core::{IoHandler, Error, Result}; use jsonrpc_core::futures::future::{self, FutureResult}; diff --git a/derive/tests/compiletests.rs b/derive/tests/compiletests.rs index dd9d90795..60d493da1 100644 --- a/derive/tests/compiletests.rs +++ b/derive/tests/compiletests.rs @@ -1,4 +1,4 @@ -extern crate compiletest_rs as compiletest; +use compiletest_rs as compiletest; use std::path::PathBuf; diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index 6bb2eacf9..c7f8a82b5 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -1,6 +1,6 @@ -extern crate serde_json; -extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; +use serde_json; +use jsonrpc_core; +use jsonrpc_pubsub; #[macro_use] extern crate jsonrpc_derive; diff --git a/http/Cargo.toml b/http/Cargo.toml index d5954b647..b3e6aa5d5 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,6 +8,7 @@ version = "10.0.0" authors = ["debris "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_http_server/index.html" +edition = "2018" [dependencies] hyper = "0.12" diff --git a/http/README.md b/http/README.md index d2da0d720..dd4e84ff7 100644 --- a/http/README.md +++ b/http/README.md @@ -15,8 +15,6 @@ jsonrpc-http-server = "10.0" `main.rs` ```rust -extern crate jsonrpc_http_server; - use jsonrpc_http_server::*; use jsonrpc_http_server::jsonrpc_core::*; diff --git a/http/examples/http_async.rs b/http/examples/http_async.rs index e2bed681a..f0da3cd32 100644 --- a/http/examples/http_async.rs +++ b/http/examples/http_async.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_http_server; - use jsonrpc_http_server::{ServerBuilder, DomainsValidation, AccessControlAllowOrigin}; use jsonrpc_http_server::jsonrpc_core::*; diff --git a/http/examples/http_meta.rs b/http/examples/http_meta.rs index 5338f1da0..a87df82bb 100644 --- a/http/examples/http_meta.rs +++ b/http/examples/http_meta.rs @@ -1,6 +1,3 @@ -extern crate jsonrpc_http_server; -extern crate unicase; - use jsonrpc_http_server::{ServerBuilder, hyper, RestApi, cors::AccessControlAllowHeaders}; use jsonrpc_http_server::jsonrpc_core::*; diff --git a/http/examples/http_middleware.rs b/http/examples/http_middleware.rs index 6caea36a7..d2ca5bee1 100644 --- a/http/examples/http_middleware.rs +++ b/http/examples/http_middleware.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_http_server; - use jsonrpc_http_server::{ hyper, ServerBuilder, DomainsValidation, AccessControlAllowOrigin, Response, RestApi }; diff --git a/http/examples/server.rs b/http/examples/server.rs index 11c7407b9..011d99abc 100644 --- a/http/examples/server.rs +++ b/http/examples/server.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_http_server; - use jsonrpc_http_server::{ServerBuilder, DomainsValidation, AccessControlAllowOrigin, RestApi}; use jsonrpc_http_server::jsonrpc_core::*; diff --git a/http/src/handler.rs b/http/src/handler.rs index c29829767..5972b6dc2 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -1,4 +1,4 @@ -use Rpc; +use crate::Rpc; use std::{fmt, mem, str}; use std::sync::Arc; @@ -6,13 +6,13 @@ use std::sync::Arc; use hyper::{self, service::Service, Body, Method}; use hyper::header::{self, HeaderMap, HeaderValue}; -use jsonrpc::{self as core, middleware, FutureResult, Metadata, Middleware}; -use jsonrpc::futures::{Future, Poll, Async, Stream, future}; -use jsonrpc::serde_json; -use response::Response; -use server_utils::cors; +use crate::jsonrpc::{self as core, middleware, FutureResult, Metadata, Middleware}; +use crate::jsonrpc::futures::{Future, Poll, Async, Stream, future}; +use crate::jsonrpc::serde_json; +use crate::response::Response; +use crate::server_utils::cors; -use {utils, RequestMiddleware, RequestMiddlewareAction, CorsDomains, AllowedHosts, RestApi}; +use crate::{utils, RequestMiddleware, RequestMiddlewareAction, CorsDomains, AllowedHosts, RestApi}; /// jsonrpc http request handler. pub struct ServerHandler = middleware::Noop> { diff --git a/http/src/lib.rs b/http/src/lib.rs index 4d89f46f8..4cdf20644 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -1,9 +1,6 @@ //! jsonrpc http server. //! //! ```no_run -//! extern crate jsonrpc_core; -//! extern crate jsonrpc_http_server; -//! //! use jsonrpc_core::*; //! use jsonrpc_http_server::*; //! @@ -23,12 +20,12 @@ #![warn(missing_docs)] -extern crate unicase; -extern crate jsonrpc_server_utils as server_utils; -extern crate net2; -pub extern crate jsonrpc_core; -pub extern crate hyper; +use jsonrpc_server_utils as server_utils; +use net2; + +pub use jsonrpc_core; +pub use hyper; #[macro_use] extern crate log; @@ -46,17 +43,17 @@ use std::thread; use hyper::{server, Body}; use jsonrpc_core as jsonrpc; -use jsonrpc::MetaIoHandler; -use jsonrpc::futures::{self, Future, Stream, future}; -use jsonrpc::futures::sync::oneshot; -use server_utils::reactor::{Executor, UninitializedExecutor}; - -pub use server_utils::hosts::{Host, DomainsValidation}; -pub use server_utils::cors::{self, AccessControlAllowOrigin, Origin, AllowCors}; -pub use server_utils::{tokio, SuspendableStream}; -pub use handler::ServerHandler; -pub use utils::{is_host_allowed, cors_allow_origin, cors_allow_headers}; -pub use response::Response; +use crate::jsonrpc::MetaIoHandler; +use crate::jsonrpc::futures::{self, Future, Stream, future}; +use crate::jsonrpc::futures::sync::oneshot; +use crate::server_utils::reactor::{Executor, UninitializedExecutor}; + +pub use crate::server_utils::hosts::{Host, DomainsValidation}; +pub use crate::server_utils::cors::{self, AccessControlAllowOrigin, Origin, AllowCors}; +pub use crate::server_utils::{tokio, SuspendableStream}; +pub use crate::handler::ServerHandler; +pub use crate::utils::{is_host_allowed, cors_allow_origin, cors_allow_headers}; +pub use crate::response::Response; /// Action undertaken by a middleware. pub enum RequestMiddlewareAction { @@ -540,7 +537,7 @@ fn configure_port(reuse: bool, tcp: &net2::TcpBuilder) -> io::Result<()> { use net2::unix::*; if reuse { - try!(tcp.reuse_port(true)); + tcp.reuse_port(true)?; } Ok(()) diff --git a/http/src/tests.rs b/http/src/tests.rs index 819c37f28..72b7eb263 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -1,4 +1,4 @@ -extern crate jsonrpc_core; +use jsonrpc_core; use std::str::Lines; use std::net::TcpStream; diff --git a/http/src/utils.rs b/http/src/utils.rs index 0640a4076..aaa417d6a 100644 --- a/http/src/utils.rs +++ b/http/src/utils.rs @@ -1,6 +1,6 @@ use hyper::{self, header}; -use server_utils::{cors, hosts}; +use crate::server_utils::{cors, hosts}; /// Extracts string value of a single header in request. fn read_header<'a>(req: &'a hyper::Request, header_name: &str) -> Option<&'a str> { diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index e0e78d52a..fe28e1cf6 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" documentation = "https://paritytech.github.io/jsonrpc/json_ipc_server/index.html" +edition = "2018" [dependencies] log = "0.4" diff --git a/ipc/README.md b/ipc/README.md index 7effc341c..b003fd74d 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -15,8 +15,6 @@ jsonrpc-ipc-server = "10.0" `main.rs` ```rust -extern crate jsonrpc_ipc_server; - use jsonrpc_ipc_server::Server; use jsonrpc_ipc_server::jsonrpc_core::*; diff --git a/ipc/examples/ipc.rs b/ipc/examples/ipc.rs index 89a45c0ce..8fb375b79 100644 --- a/ipc/examples/ipc.rs +++ b/ipc/examples/ipc.rs @@ -1,4 +1,4 @@ -extern crate jsonrpc_ipc_server; +use jsonrpc_ipc_server; use jsonrpc_ipc_server::jsonrpc_core::*; diff --git a/ipc/src/lib.rs b/ipc/src/lib.rs index deff37bf2..f7975ba2e 100644 --- a/ipc/src/lib.rs +++ b/ipc/src/lib.rs @@ -2,17 +2,14 @@ #![warn(missing_docs)] -extern crate jsonrpc_server_utils as server_utils; -extern crate parity_tokio_ipc; -extern crate tokio_service; -extern crate parking_lot; +use jsonrpc_server_utils as server_utils; -pub extern crate jsonrpc_core; +pub use jsonrpc_core; #[macro_use] extern crate log; #[cfg(test)] #[macro_use] extern crate lazy_static; -#[cfg(test)] extern crate env_logger; + #[cfg(test)] mod logger; mod server; @@ -21,8 +18,8 @@ mod meta; use jsonrpc_core as jsonrpc; -pub use meta::{MetaExtractor, NoopExtractor, RequestContext}; -pub use server::{Server, ServerBuilder, CloseHandle,SecurityAttributes}; +pub use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; +pub use crate::server::{Server, ServerBuilder, CloseHandle,SecurityAttributes}; pub use self::server_utils::{tokio, codecs::Separator}; pub use self::server_utils::session::{SessionStats, SessionId}; diff --git a/ipc/src/meta.rs b/ipc/src/meta.rs index 60e173f4a..ed3ac6ae1 100644 --- a/ipc/src/meta.rs +++ b/ipc/src/meta.rs @@ -1,6 +1,6 @@ -use jsonrpc::futures::sync::mpsc; -use jsonrpc::Metadata; -use server_utils::session; +use crate::jsonrpc::futures::sync::mpsc; +use crate::jsonrpc::Metadata; +use crate::server_utils::session; /// Request context pub struct RequestContext<'a> { diff --git a/ipc/src/select_with_weak.rs b/ipc/src/select_with_weak.rs index 204059c27..bc96bff45 100644 --- a/ipc/src/select_with_weak.rs +++ b/ipc/src/select_with_weak.rs @@ -1,5 +1,5 @@ -use jsonrpc::futures::{Poll, Async}; -use jsonrpc::futures::stream::{Stream, Fuse}; +use crate::jsonrpc::futures::{Poll, Async}; +use crate::jsonrpc::futures::stream::{Stream, Fuse}; pub trait SelectWithWeakExt: Stream { fn select_with_weak(self, other: S) -> SelectWithWeak diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 568805249..69443886d 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -4,19 +4,19 @@ use std; use std::sync::Arc; use tokio_service::{self, Service as TokioService}; -use jsonrpc::futures::{future, Future, Stream, Sink}; -use jsonrpc::futures::sync::{mpsc, oneshot}; -use jsonrpc::{middleware, FutureResult, Metadata, MetaIoHandler, Middleware}; +use crate::jsonrpc::futures::{future, Future, Stream, Sink}; +use crate::jsonrpc::futures::sync::{mpsc, oneshot}; +use crate::jsonrpc::{middleware, FutureResult, Metadata, MetaIoHandler, Middleware}; -use server_utils::{ +use crate::server_utils::{ tokio_codec::Framed, tokio::{self, runtime::TaskExecutor, reactor::Handle}, reactor, session, codecs, }; use parking_lot::Mutex; -use meta::{MetaExtractor, NoopExtractor, RequestContext}; -use select_with_weak::SelectWithWeakExt; +use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; +use crate::select_with_weak::SelectWithWeakExt; use parity_tokio_ipc::Endpoint; pub use parity_tokio_ipc::SecurityAttributes; @@ -309,24 +309,24 @@ impl CloseHandle { #[cfg(test)] #[cfg(not(windows))] mod tests { - extern crate tokio_uds; + use tokio_uds; use std::thread; use std::sync::Arc; use std::time; use std::time::{Instant, Duration}; use super::{ServerBuilder, Server}; - use jsonrpc::{MetaIoHandler, Value}; - use jsonrpc::futures::{Future, future, Stream, Sink}; - use jsonrpc::futures::sync::{mpsc, oneshot}; + use crate::jsonrpc::{MetaIoHandler, Value}; + use crate::jsonrpc::futures::{Future, future, Stream, Sink}; + use crate::jsonrpc::futures::sync::{mpsc, oneshot}; use self::tokio_uds::UnixStream; use parking_lot::Mutex; - use server_utils::{ + use crate::server_utils::{ tokio_codec::Decoder, tokio::{self, timer::Delay} }; - use server_utils::codecs; - use meta::{MetaExtractor, RequestContext, NoopExtractor}; + use crate::server_utils::codecs; + use crate::meta::{MetaExtractor, RequestContext, NoopExtractor}; use super::SecurityAttributes; fn server_builder() -> ServerBuilder { @@ -364,7 +364,7 @@ mod tests { #[test] fn start() { - ::logger::init_log(); + crate::logger::init_log(); let mut io = MetaIoHandler::<()>::default(); io.add_method("say_hello", |_params| { @@ -378,7 +378,7 @@ mod tests { #[test] fn connect() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-30000"; let _server = run(path); @@ -387,7 +387,7 @@ mod tests { #[test] fn request() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-40000"; let server = run(path); let (stop_signal, stop_receiver) = oneshot::channel(); @@ -413,7 +413,7 @@ mod tests { #[test] fn req_parallel() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-45000"; let server = run(path); let (stop_signal, stop_receiver) = mpsc::channel(400); @@ -451,7 +451,7 @@ mod tests { #[test] fn close() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-50000"; let server = run(path); server.close(); @@ -478,7 +478,7 @@ mod tests { #[test] fn test_huge_response() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-60000"; let mut io = MetaIoHandler::<()>::default(); @@ -538,7 +538,7 @@ mod tests { } } - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-30009"; let (signal, receiver) = mpsc::channel(16); let session_metadata_extractor = SessionEndExtractor { @@ -561,7 +561,7 @@ mod tests { #[test] fn close_handle() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-90000"; let server = run(path); let handle = server.close_handle(); @@ -571,7 +571,7 @@ mod tests { #[test] fn close_when_waiting() { - ::logger::init_log(); + crate::logger::init_log(); let path = "/tmp/test-ipc-70000"; let server = run(path); let close_handle = server.close_handle(); diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index c0fd70972..4881ec492 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,6 +8,7 @@ version = "10.0.0" authors = ["tomusdrw "] keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_pubsub/index.html" +edition = "2018" [dependencies] log = "0.4" diff --git a/pubsub/examples/pubsub.rs b/pubsub/examples/pubsub.rs index a598003ff..99d4335f1 100644 --- a/pubsub/examples/pubsub.rs +++ b/pubsub/examples/pubsub.rs @@ -1,7 +1,3 @@ -extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; -extern crate jsonrpc_tcp_server; - use std::{time, thread}; use std::sync::{Arc, atomic}; diff --git a/pubsub/examples/pubsub_simple.rs b/pubsub/examples/pubsub_simple.rs index db31d02d4..933d857ba 100644 --- a/pubsub/examples/pubsub_simple.rs +++ b/pubsub/examples/pubsub_simple.rs @@ -1,7 +1,3 @@ -extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; -extern crate jsonrpc_tcp_server; - use std::{time, thread}; use std::sync::Arc; diff --git a/pubsub/src/delegates.rs b/pubsub/src/delegates.rs index 6fcc0eb73..988cc6e86 100644 --- a/pubsub/src/delegates.rs +++ b/pubsub/src/delegates.rs @@ -2,11 +2,11 @@ use std::marker::PhantomData; use std::sync::Arc; use std::collections::HashMap; -use subscription::{Subscriber, new_subscription}; -use handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; -use types::{SubscriptionId, PubSubMetadata}; -use core::{self, Params, Value, Error, Metadata, RemoteProcedure, RpcMethod}; -use core::futures::IntoFuture; +use crate::subscription::{Subscriber, new_subscription}; +use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; +use crate::types::{SubscriptionId, PubSubMetadata}; +use crate::core::{self, Params, Value, Error, Metadata, RemoteProcedure, RpcMethod}; +use crate::core::futures::IntoFuture; struct DelegateSubscription { delegate: Arc, diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index bf8675de1..a2ca608cc 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -1,8 +1,8 @@ -use core; -use core::futures::{Future, IntoFuture}; +use crate::core; +use crate::core::futures::{Future, IntoFuture}; -use types::{PubSubMetadata, SubscriptionId}; -use subscription::{Subscriber, new_subscription}; +use crate::types::{PubSubMetadata, SubscriptionId}; +use crate::subscription::{Subscriber, new_subscription}; /// Subscribe handler pub trait SubscribeRpcMethod: Send + Sync + 'static { @@ -102,11 +102,11 @@ mod tests { use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; - use core; - use core::futures::future; - use core::futures::sync::mpsc; - use subscription::{Session, Subscriber}; - use types::{PubSubMetadata, SubscriptionId}; + use crate::core; + use crate::core::futures::future; + use crate::core::futures::sync::mpsc; + use crate::subscription::{Session, Subscriber}; + use crate::types::{PubSubMetadata, SubscriptionId}; use super::PubSubHandler; diff --git a/pubsub/src/lib.rs b/pubsub/src/lib.rs index 248134439..7949549e0 100644 --- a/pubsub/src/lib.rs +++ b/pubsub/src/lib.rs @@ -2,9 +2,7 @@ #![warn(missing_docs)] -extern crate jsonrpc_core as core; -extern crate parking_lot; -extern crate serde; +use jsonrpc_core as core; #[macro_use] extern crate log; diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index d054ef72f..97ba7a8de 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -5,12 +5,12 @@ use std::collections::HashMap; use std::sync::Arc; use parking_lot::Mutex; -use core::{self, BoxFuture}; -use core::futures::{self, future, Sink as FuturesSink, Future}; -use core::futures::sync::{mpsc, oneshot}; +use crate::core::{self, BoxFuture}; +use crate::core::futures::{self, future, Sink as FuturesSink, Future}; +use crate::core::futures::sync::{mpsc, oneshot}; -use handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; -use types::{PubSubMetadata, SubscriptionId, TransportSender, TransportError, SinkResult}; +use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; +use crate::types::{PubSubMetadata, SubscriptionId, TransportSender, TransportError, SinkResult}; /// RPC client session /// Keeps track of active subscriptions and unsubscribes from them upon dropping. @@ -300,11 +300,11 @@ impl core::RpcMethod for Unsubscribe where mod tests { use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; - use core; - use core::RpcMethod; - use core::futures::{Async, Future, Stream}; - use core::futures::sync::{mpsc, oneshot}; - use types::{SubscriptionId, PubSubMetadata}; + use crate::core; + use crate::core::RpcMethod; + use crate::core::futures::{Async, Future, Stream}; + use crate::core::futures::sync::{mpsc, oneshot}; + use crate::types::{SubscriptionId, PubSubMetadata}; use super::{Session, Sink, Subscriber, new_subscription}; diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs index 6b94da52b..99b9b9151 100644 --- a/pubsub/src/typed.rs +++ b/pubsub/src/typed.rs @@ -3,15 +3,15 @@ use std::marker::PhantomData; use serde; -use subscription; -use types::{SubscriptionId, TransportError, SinkResult}; +use crate::subscription; +use crate::types::{SubscriptionId, TransportError, SinkResult}; -use core::Value; -use core::futures::{self, Sink as FuturesSink, sync}; +use crate::core::{self, Value, Params, Error}; +use crate::core::futures::{self, Sink as FuturesSink, sync}; /// New PUB-SUB subscriber. #[derive(Debug)] -pub struct Subscriber { +pub struct Subscriber { subscriber: subscription::Subscriber, _data: PhantomData<(T, E)>, } @@ -28,7 +28,7 @@ impl Subscriber { /// Create new subscriber for tests. pub fn new_test>(method: M) -> ( Self, - sync::oneshot::Receiver>, + sync::oneshot::Receiver>, sync::mpsc::Receiver, ) { let (subscriber, id, subscription) = subscription::Subscriber::new_test(method); @@ -36,7 +36,7 @@ impl Subscriber { } /// Reject subscription with given error. - pub fn reject(self, error: core::Error) -> Result<(), ()> { + pub fn reject(self, error: Error) -> Result<(), ()> { self.subscriber.reject(error) } @@ -56,10 +56,10 @@ impl Subscriber { /// Subscriber sink. #[derive(Debug, Clone)] -pub struct Sink { +pub struct Sink { sink: subscription::Sink, id: SubscriptionId, - buffered: Option, + buffered: Option, _data: PhantomData<(T, E)>, } @@ -73,12 +73,12 @@ impl Sink { core::to_value(value).expect("Expected always-serializable type.") } - fn val_to_params(&self, val: Result) -> core::Params { + fn val_to_params(&self, val: Result) -> Params { let id = self.id.clone().into(); let val = val.map(Self::to_value).map_err(Self::to_value); - core::Params::Map(vec![ + Params::Map(vec![ ("subscription".to_owned(), id), match val { Ok(val) => ("result".to_owned(), val), diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index 31e4a7cd8..5b8aa98c1 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -1,8 +1,8 @@ use std::sync::Arc; -use core; -use core::futures::sync::mpsc; +use crate::core; +use crate::core::futures::sync::mpsc; -use subscription::Session; +use crate::subscription::Session; /// Raw transport sink for specific client. pub type TransportSender = mpsc::Sender; @@ -78,7 +78,7 @@ impl From for core::Value { #[cfg(test)] mod tests { - use core::Value; + use crate::core::Value; use super::SubscriptionId; #[test] diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index caad69dc5..986b69c2a 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,6 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_core/index.html" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" +edition = "2018" [dependencies] bytes = "0.4" diff --git a/server-utils/src/cors.rs b/server-utils/src/cors.rs index 78b8157d6..a0e080206 100644 --- a/server-utils/src/cors.rs +++ b/server-utils/src/cors.rs @@ -1,9 +1,9 @@ //! CORS handling utility functions -extern crate unicase; +use unicase; use std::{fmt, ops}; -use hosts::{Host, Port}; -use matcher::{Matcher, Pattern}; +use crate::hosts::{Host, Port}; +use crate::matcher::{Matcher, Pattern}; use std::collections::HashSet; pub use self::unicase::Ascii; @@ -303,7 +303,7 @@ mod tests { use std::iter; use super::*; - use hosts::Host; + use crate::hosts::Host; #[test] fn should_parse_origin() { diff --git a/server-utils/src/hosts.rs b/server-utils/src/hosts.rs index 50196fccc..457a4a4c0 100644 --- a/server-utils/src/hosts.rs +++ b/server-utils/src/hosts.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use std::net::SocketAddr; -use matcher::{Matcher, Pattern}; +use crate::matcher::{Matcher, Pattern}; const SPLIT_PROOF: &'static str = "split always returns non-empty iterator."; diff --git a/server-utils/src/lib.rs b/server-utils/src/lib.rs index b4e64ce65..ee6810999 100644 --- a/server-utils/src/lib.rs +++ b/server-utils/src/lib.rs @@ -8,13 +8,10 @@ extern crate log; #[macro_use] extern crate lazy_static; -extern crate globset; -extern crate jsonrpc_core as core; -extern crate bytes; -extern crate num_cpus; +use jsonrpc_core as core; -pub extern crate tokio; -pub extern crate tokio_codec; +pub use tokio; +pub use tokio_codec; pub mod cors; pub mod hosts; @@ -24,11 +21,11 @@ mod matcher; mod stream_codec; mod suspendable_stream; -pub use suspendable_stream::SuspendableStream; -pub use matcher::Pattern; +pub use crate::suspendable_stream::SuspendableStream; +pub use crate::matcher::Pattern; /// Codecs utilities pub mod codecs { - pub use stream_codec::{StreamCodec, Separator}; + pub use crate::stream_codec::{StreamCodec, Separator}; } diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index 38352783a..d1eb6d8ba 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -6,7 +6,7 @@ use std::sync::mpsc; use tokio; use num_cpus; -use core::futures::{self, Future}; +use crate::core::futures::{self, Future}; /// Possibly uninitialized event loop executor. #[derive(Debug)] @@ -109,7 +109,7 @@ impl RpcEventLoop { let handle = tb.spawn(move || { let core_threads = match num_cpus::get_physical() { 1 => 1, - 2...4 => 2, + 2..=4 => 2, _ => 3, }; diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 4403b3408..a2e1c6b22 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_stdio_server/index.html" +edition = "2018" [dependencies] futures = "0.1.23" diff --git a/stdio/README.md b/stdio/README.md index 15c4054a2..1df9601f6 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -16,8 +16,6 @@ jsonrpc-stdio-server = "10.0" `main.rs` ```rust -extern crate jsonrpc_stdio_server; - use jsonrpc_stdio_server::server; use jsonrpc_stdio_server::jsonrpc_core::*; diff --git a/stdio/examples/stdio.rs b/stdio/examples/stdio.rs index 1a907c9d0..cf1ad56ee 100644 --- a/stdio/examples/stdio.rs +++ b/stdio/examples/stdio.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_stdio_server; - use jsonrpc_stdio_server::ServerBuilder; use jsonrpc_stdio_server::jsonrpc_core::*; diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index cfeb90f3f..1fd6d9ee8 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -1,7 +1,6 @@ //! jsonrpc server using stdin/stdout //! //! ```no_run -//! extern crate jsonrpc_stdio_server; //! //! use jsonrpc_stdio_server::ServerBuilder; //! use jsonrpc_stdio_server::jsonrpc_core::*; @@ -18,14 +17,11 @@ #![warn(missing_docs)] -extern crate futures; -extern crate tokio; -extern crate tokio_codec; -extern crate tokio_io; -extern crate tokio_stdin_stdout; +use tokio; +use tokio_stdin_stdout; #[macro_use] extern crate log; -pub extern crate jsonrpc_core; +pub use jsonrpc_core; use std::sync::Arc; use tokio::prelude::{Future, Stream}; diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index e4e2d5460..b7edae8cf 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_tcp_server/index.html" +edition = "2018" [dependencies] log = "0.4" diff --git a/tcp/README.md b/tcp/README.md index 83edada87..c912220a9 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -15,8 +15,6 @@ jsonrpc-tcp-server = "10.0" `main.rs` ```rust -extern crate jsonrpc_tcp_server; - use jsonrpc_tcp_server::*; use jsonrpc_tcp_server::jsonrpc_core::*; diff --git a/tcp/examples/tcp.rs b/tcp/examples/tcp.rs index ce3ffb473..1433d1a43 100644 --- a/tcp/examples/tcp.rs +++ b/tcp/examples/tcp.rs @@ -1,5 +1,4 @@ -extern crate jsonrpc_tcp_server; -extern crate env_logger; +use env_logger; use jsonrpc_tcp_server::ServerBuilder; use jsonrpc_tcp_server::jsonrpc_core::*; diff --git a/tcp/src/dispatch.rs b/tcp/src/dispatch.rs index ad0756e1d..50b50c303 100644 --- a/tcp/src/dispatch.rs +++ b/tcp/src/dispatch.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::sync::{Arc}; -use jsonrpc::futures::{Stream, Poll, Async, Sink, Future}; -use jsonrpc::futures::sync::mpsc; +use crate::jsonrpc::futures::{Stream, Poll, Async, Sink, Future}; +use crate::jsonrpc::futures::sync::mpsc; use parking_lot::Mutex; @@ -66,7 +66,7 @@ impl Dispatcher { match channels.get_mut(peer_addr) { Some(channel) => { // todo: maybe async here later? - try!(channel.send(msg).wait().map_err(|e| PushMessageError::from(e))); + channel.send(msg).wait().map_err(|e| PushMessageError::from(e))?; Ok(()) }, None => { diff --git a/tcp/src/lib.rs b/tcp/src/lib.rs index ae089c680..a90d6ce09 100644 --- a/tcp/src/lib.rs +++ b/tcp/src/lib.rs @@ -1,9 +1,6 @@ //! jsonrpc server over tcp/ip //! //! ```no_run -//! extern crate jsonrpc_core; -//! extern crate jsonrpc_tcp_server; -//! //! use jsonrpc_core::*; //! use jsonrpc_tcp_server::ServerBuilder; //! @@ -22,16 +19,13 @@ #![warn(missing_docs)] -extern crate jsonrpc_server_utils as server_utils; -extern crate parking_lot; -extern crate tokio_service; +use jsonrpc_server_utils as server_utils; -pub extern crate jsonrpc_core; +pub use jsonrpc_core; #[macro_use] extern crate log; #[cfg(test)] #[macro_use] extern crate lazy_static; -#[cfg(test)] extern crate env_logger; mod dispatch; mod meta; @@ -43,7 +37,7 @@ mod service; use jsonrpc_core as jsonrpc; -pub use dispatch::{Dispatcher, PushMessageError}; -pub use meta::{MetaExtractor, RequestContext}; -pub use server::{ServerBuilder, Server}; +pub use crate::dispatch::{Dispatcher, PushMessageError}; +pub use crate::meta::{MetaExtractor, RequestContext}; +pub use crate::server::{ServerBuilder, Server}; pub use self::server_utils::{tokio, codecs::Separator}; diff --git a/tcp/src/meta.rs b/tcp/src/meta.rs index 795fe9ead..6aac384c7 100644 --- a/tcp/src/meta.rs +++ b/tcp/src/meta.rs @@ -1,7 +1,7 @@ use std::net::SocketAddr; -use jsonrpc::futures::sync::mpsc; -use jsonrpc::Metadata; +use crate::jsonrpc::futures::sync::mpsc; +use crate::jsonrpc::Metadata; /// Request context pub struct RequestContext { diff --git a/tcp/src/server.rs b/tcp/src/server.rs index b5e6e7fbd..fcb2c74c0 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -4,18 +4,18 @@ use std::sync::Arc; use tokio_service::Service as TokioService; -use jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; -use jsonrpc::futures::{future, Future, Stream, Sink}; -use jsonrpc::futures::sync::{mpsc, oneshot}; -use server_utils::{ +use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; +use crate::jsonrpc::futures::{future, Future, Stream, Sink}; +use crate::jsonrpc::futures::sync::{mpsc, oneshot}; +use crate::server_utils::{ tokio_codec::Framed, tokio, reactor, codecs, SuspendableStream }; -use dispatch::{Dispatcher, SenderChannels, PeerMessageQueue}; -use meta::{MetaExtractor, RequestContext, NoopExtractor}; -use service::Service; +use crate::dispatch::{Dispatcher, SenderChannels, PeerMessageQueue}; +use crate::meta::{MetaExtractor, RequestContext, NoopExtractor}; +use crate::service::Service; /// TCP server builder pub struct ServerBuilder = middleware::Noop> { diff --git a/tcp/src/service.rs b/tcp/src/service.rs index 532f2d411..1c8cd7336 100644 --- a/tcp/src/service.rs +++ b/tcp/src/service.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use tokio_service; -use jsonrpc::{middleware, FutureResult, Metadata, MetaIoHandler, Middleware}; +use crate::jsonrpc::{middleware, FutureResult, Metadata, MetaIoHandler, Middleware}; pub struct Service = middleware::Noop> { handler: Arc>, diff --git a/tcp/src/tests.rs b/tcp/src/tests.rs index 21326a4f2..a063ac3eb 100644 --- a/tcp/src/tests.rs +++ b/tcp/src/tests.rs @@ -3,10 +3,10 @@ use std::str::FromStr; use std::sync::Arc; use std::time::{Instant, Duration}; -use jsonrpc::{MetaIoHandler, Value, Metadata}; -use jsonrpc::futures::{self, Future, future}; +use crate::jsonrpc::{MetaIoHandler, Value, Metadata}; +use crate::jsonrpc::futures::{self, Future, future}; -use server_utils::tokio::{ +use crate::server_utils::tokio::{ timer::Delay, net::TcpStream, io::{self}, @@ -15,9 +15,9 @@ use server_utils::tokio::{ use parking_lot::Mutex; -use ServerBuilder; -use MetaExtractor; -use RequestContext; +use crate::ServerBuilder; +use crate::MetaExtractor; +use crate::RequestContext; fn casual_server() -> ServerBuilder { let mut io = MetaIoHandler::<()>::default(); @@ -29,7 +29,7 @@ fn casual_server() -> ServerBuilder { #[test] fn doc_test() { - ::logger::init_log(); + crate::logger::init_log(); let mut io = MetaIoHandler::<()>::default(); io.add_method("say_hello", |_params| { @@ -44,7 +44,7 @@ fn doc_test() { #[test] fn doc_test_connect() { - ::logger::init_log(); + crate::logger::init_log(); let addr: SocketAddr = "127.0.0.1:17775".parse().unwrap(); let server = casual_server(); let _server = server.start(&addr).expect("Server must run with no issues"); @@ -60,7 +60,7 @@ fn doc_test_connect() { #[test] fn disconnect() { - ::logger::init_log(); + crate::logger::init_log(); let addr: SocketAddr = "127.0.0.1:17777".parse().unwrap(); let server = casual_server(); let dispatcher = server.dispatcher(); @@ -106,7 +106,7 @@ fn dummy_request_str(addr: &SocketAddr, data: Vec) -> String { #[test] fn doc_test_handle() { - ::logger::init_log(); + crate::logger::init_log(); let addr: SocketAddr = "127.0.0.1:17780".parse().unwrap(); let server = casual_server(); @@ -128,7 +128,7 @@ fn doc_test_handle() { fn req_parallel() { use std::thread; - ::logger::init_log(); + crate::logger::init_log(); let addr: SocketAddr = "127.0.0.1:17782".parse().unwrap(); let server = casual_server(); let _server = server.start(&addr).expect("Server must run with no issues"); @@ -202,7 +202,7 @@ fn meta_server() -> ServerBuilder { #[test] fn peer_meta() { - ::logger::init_log(); + crate::logger::init_log(); let addr: SocketAddr = "127.0.0.1:17785".parse().unwrap(); let server = meta_server(); let _server = server.start(&addr).expect("Server must run with no issues"); @@ -236,7 +236,7 @@ impl MetaExtractor for PeerListMetaExtractor { #[test] fn message() { // MASSIVE SETUP - ::logger::init_log(); + crate::logger::init_log(); let addr: SocketAddr = "127.0.0.1:17790".parse().unwrap(); let mut io = MetaIoHandler::::default(); io.add_method_with_meta("say_hello", |_params, _: SocketMetadata| { diff --git a/test/Cargo.toml b/test/Cargo.toml index 1aed926d9..e28d0be7d 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -7,11 +7,14 @@ license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_test/index.html" +edition = "2018" [dependencies] jsonrpc-core = { path = "../core", version = "10.0" } +jsonrpc-pubsub = { path = "../pubsub", version = "10.0" } serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-macros = { path = "../macros", version = "10.0" } +jsonrpc-derive = { path = "../derive", version = "10.0" } + diff --git a/test/src/lib.rs b/test/src/lib.rs index fbe03ccad..5015c07b7 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -1,21 +1,18 @@ //! An utility package to test jsonrpc-core based projects. //! //! ``` -//! #[macro_use] -//! extern crate jsonrpc_macros; +//! use jsonrpc_derive::rpc; +//! use jsonrpc_test as test; //! -//! extern crate jsonrpc_core as core; -//! extern crate jsonrpc_test as test; +//! use jsonrpc_core::{Result, Error, IoHandler}; //! -//! use core::Result; -//! -//! build_rpc_trait! { -//! pub trait Test { -//! #[rpc(name = "rpc_some_method")] -//! fn some_method(&self, u64) -> Result; -//! } +//! #[rpc] +//! pub trait Test { +//! #[rpc(name = "rpc_some_method")] +//! fn some_method(&self, _: u64) -> Result; //! } //! +//! //! struct Dummy; //! impl Test for Dummy { //! fn some_method(&self, x: u64) -> Result { @@ -32,9 +29,9 @@ //! //! // You can also test RPC created without macros: //! let rpc = { -//! let mut io = core::IoHandler::new(); +//! let mut io = IoHandler::new(); //! io.add_method("rpc_test_method", |_| { -//! Err(core::Error::internal_error()) +//! Err(Error::internal_error()) //! }); //! test::Rpc::from(io) //! }; @@ -49,8 +46,8 @@ #[warn(missing_docs)] extern crate jsonrpc_core as rpc; -extern crate serde; -extern crate serde_json; +use serde; +use serde_json; use std::collections::HashMap; diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 7f0251e49..6dfe5ce15 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" documentation = "https://paritytech.github.io/jsonrpc/json_ws_server/index.html" +edition = "2018" [dependencies] error-chain = "0.12" diff --git a/ws/README.md b/ws/README.md index d8fb5b111..307df20f0 100644 --- a/ws/README.md +++ b/ws/README.md @@ -15,8 +15,6 @@ jsonrpc-ws-server = "10.0" `main.rs` ```rust -extern crate jsonrpc_ws_server; - use jsonrpc_ws_server::*; use jsonrpc_ws_server::jsonrpc_core::*; diff --git a/ws/examples/ws.rs b/ws/examples/ws.rs index dee6359c2..027fa5421 100644 --- a/ws/examples/ws.rs +++ b/ws/examples/ws.rs @@ -1,5 +1,3 @@ -extern crate jsonrpc_ws_server; - use jsonrpc_ws_server::ServerBuilder; use jsonrpc_ws_server::jsonrpc_core::*; diff --git a/ws/src/error.rs b/ws/src/error.rs index 2602eb770..c1b0ceea1 100644 --- a/ws/src/error.rs +++ b/ws/src/error.rs @@ -2,7 +2,7 @@ use std::io; -use ws; +use crate::ws; error_chain! { foreign_links { diff --git a/ws/src/lib.rs b/ws/src/lib.rs index 1823ebce9..733feb81c 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -2,12 +2,10 @@ #![warn(missing_docs)] -extern crate jsonrpc_server_utils as server_utils; -extern crate parking_lot; -extern crate slab; +use jsonrpc_server_utils as server_utils; -pub extern crate parity_ws as ws; -pub extern crate jsonrpc_core; +pub use parity_ws as ws; +pub use jsonrpc_core; #[macro_use] extern crate error_chain; diff --git a/ws/src/metadata.rs b/ws/src/metadata.rs index 60835c1d4..13dc750dd 100644 --- a/ws/src/metadata.rs +++ b/ws/src/metadata.rs @@ -1,13 +1,13 @@ use std::fmt; use std::sync::{atomic, Arc}; -use core::{self, futures}; -use core::futures::sync::mpsc; -use server_utils::{session, tokio::runtime::TaskExecutor}; -use ws; +use crate::core::{self, futures}; +use crate::core::futures::sync::mpsc; +use crate::server_utils::{session, tokio::runtime::TaskExecutor}; +use crate::ws; -use error; -use {Origin}; +use crate::error; +use crate::{Origin}; /// Output of WebSocket connection. Use this to send messages to the other endpoint. #[derive(Clone)] diff --git a/ws/src/server.rs b/ws/src/server.rs index e3d25441a..3fdeb93bb 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -3,16 +3,16 @@ use std::net::SocketAddr; use std::sync::{Arc, Mutex}; use std::thread; -use core; -use server_utils::cors::Origin; -use server_utils::hosts::{self, Host}; -use server_utils::reactor::{UninitializedExecutor, Executor}; -use server_utils::session::SessionStats; -use ws; - -use error::{Error, Result}; -use metadata; -use session; +use crate::core; +use crate::server_utils::cors::Origin; +use crate::server_utils::hosts::{self, Host}; +use crate::server_utils::reactor::{UninitializedExecutor, Executor}; +use crate::server_utils::session::SessionStats; +use crate::ws; + +use crate::error::{Error, Result}; +use crate::metadata; +use crate::session; /// `WebSockets` server implementation. pub struct Server { diff --git a/ws/src/server_builder.rs b/ws/src/server_builder.rs index 2a9760f44..1b6a0aed1 100644 --- a/ws/src/server_builder.rs +++ b/ws/src/server_builder.rs @@ -1,17 +1,17 @@ use std::net::SocketAddr; use std::sync::Arc; -use core; -use server_utils; -use server_utils::cors::Origin; -use server_utils::hosts::{Host, DomainsValidation}; -use server_utils::reactor::UninitializedExecutor; -use server_utils::session::SessionStats; +use crate::core; +use crate::server_utils; +use crate::server_utils::cors::Origin; +use crate::server_utils::hosts::{Host, DomainsValidation}; +use crate::server_utils::reactor::UninitializedExecutor; +use crate::server_utils::session::SessionStats; -use error::Result; -use metadata::{MetaExtractor, NoopExtractor}; -use server::Server; -use session; +use crate::error::Result; +use crate::metadata::{MetaExtractor, NoopExtractor}; +use crate::server::Server; +use crate::session; /// Builder for `WebSockets` server pub struct ServerBuilder> { diff --git a/ws/src/session.rs b/ws/src/session.rs index 512a10e0c..f3303cb52 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -1,22 +1,22 @@ use std; use std::sync::{atomic, Arc}; -use core; -use core::futures::{Async, Future, Poll}; -use core::futures::sync::oneshot; +use crate::core; +use crate::core::futures::{Async, Future, Poll}; +use crate::core::futures::sync::oneshot; use parking_lot::Mutex; use slab::Slab; -use server_utils::Pattern; -use server_utils::cors::Origin; -use server_utils::hosts::Host; -use server_utils::session::{SessionId, SessionStats}; -use server_utils::tokio::runtime::TaskExecutor; -use ws; +use crate::server_utils::Pattern; +use crate::server_utils::cors::Origin; +use crate::server_utils::hosts::Host; +use crate::server_utils::session::{SessionId, SessionStats}; +use crate::server_utils::tokio::runtime::TaskExecutor; +use crate::ws; -use error; -use metadata; +use crate::error; +use crate::metadata; /// Middleware to intercept server requests. /// You can either terminate the request (by returning a response) diff --git a/ws/src/tests.rs b/ws/src/tests.rs index 2667ebd31..e92874efb 100644 --- a/ws/src/tests.rs +++ b/ws/src/tests.rs @@ -6,13 +6,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use std::time::Duration; -use core; -use core::futures::Future; -use server_utils::hosts::DomainsValidation; -use ws; +use crate::core; +use crate::core::futures::Future; +use crate::server_utils::hosts::DomainsValidation; +use crate::ws; -use server::Server; -use server_builder::ServerBuilder; +use crate::server::Server; +use crate::server_builder::ServerBuilder; struct Response { status: String, @@ -62,7 +62,7 @@ fn request(server: Server, request: &str) -> Response { fn serve(port: u16) -> (Server, Arc) { use std::time::Duration; - use core::futures::sync::oneshot; + use crate::core::futures::sync::oneshot; let pending = Arc::new(AtomicUsize::new(0)); @@ -193,7 +193,7 @@ fn should_intercept_in_middleware() { #[test] fn drop_session_should_cancel() { - use ws::{connect, CloseCode}; + use crate::ws::{connect, CloseCode}; // given let (_server, incomplete) = serve(30005); From 2fe3bfc717775ba76537e00bb3d053e8948f5372 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 30 Jan 2019 13:14:56 +0000 Subject: [PATCH 019/149] One way serialize for subscriber type (#371) * WIP: 2018 compile tests with one way serialization * WIP: attempting to get traitbounds to work with compiletests * Failing compile test for one way serialise Subscription type * One way Serialize for generic type only used in Subscriber --- derive/src/to_delegate.rs | 18 +++---- derive/tests/compiletests.rs | 7 ++- ...b-subscription-type-without-deserialize.rs | 49 +++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 870da9c88..b4d2073c6 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -336,12 +336,14 @@ fn is_option_type(ty: &syn::Type) -> bool { } fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) -> Vec { + #[derive(Default)] struct FindTyParams { trait_generics: HashSet, serialize_type_params: HashSet, deserialize_type_params: HashSet, visiting_return_type: bool, visiting_fn_arg: bool, + visiting_subscriber_arg: bool, } impl<'ast> Visit<'ast> for FindTyParams { fn visit_type_param(&mut self, ty_param: &'ast syn::TypeParam) { @@ -356,10 +358,14 @@ fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) - if self.visiting_return_type && self.trait_generics.contains(&segment.ident) { self.serialize_type_params.insert(segment.ident.clone()); } - if self.visiting_fn_arg && self.trait_generics.contains(&segment.ident) { + if self.visiting_fn_arg && + self.trait_generics.contains(&segment.ident) && + !self.visiting_subscriber_arg { self.deserialize_type_params.insert(segment.ident.clone()); } - visit::visit_path_segment(self, segment) + self.visiting_subscriber_arg = self.visiting_fn_arg && segment.ident == SUBCRIBER_TYPE_IDENT; + visit::visit_path_segment(self, segment); + self.visiting_subscriber_arg = false; } fn visit_fn_arg(&mut self, arg: &'ast syn::FnArg) { self.visiting_fn_arg = true; @@ -367,13 +373,7 @@ fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) - self.visiting_fn_arg = false; } } - let mut visitor = FindTyParams { - visiting_return_type: false, - visiting_fn_arg: false, - trait_generics: HashSet::new(), - serialize_type_params: HashSet::new(), - deserialize_type_params: HashSet::new(), - }; + let mut visitor = FindTyParams::default(); visitor.visit_item_trait(item_trait); item_trait.generics diff --git a/derive/tests/compiletests.rs b/derive/tests/compiletests.rs index 60d493da1..00ca38cf9 100644 --- a/derive/tests/compiletests.rs +++ b/derive/tests/compiletests.rs @@ -7,7 +7,11 @@ fn run_mode(mode: &'static str) { config.mode = mode.parse().expect("Invalid mode"); config.src_base = PathBuf::from(format!("tests/{}", mode)); - config.target_rustcflags = Some("-L ../target/debug/ -L ../target/debug/deps/".to_owned()); + config.target_rustcflags = Some(String::from( + "\ + -L ../target/debug/ \ + -L ../target/debug/deps/ \ + ")); config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 compiletest::run_tests(&config); @@ -16,4 +20,5 @@ fn run_mode(mode: &'static str) { #[test] fn compile_test() { run_mode("ui"); + run_mode("run-pass"); } diff --git a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs new file mode 100644 index 000000000..af012656d --- /dev/null +++ b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs @@ -0,0 +1,49 @@ +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; +#[macro_use] +extern crate jsonrpc_derive; + +use std::sync::Arc; +use jsonrpc_core::Result; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, Session, PubSubHandler}; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: Subscriber); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +// One way serialization +#[derive(Serialize)] +struct SerializeOnly { + foo: String, +} + +struct RpcImpl; +impl Rpc for RpcImpl { + type Metadata = Arc; + + fn subscribe(&self, _: Self::Metadata, _: Subscriber) { + unimplemented!(); + } + + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result { + unimplemented!(); + } +} + +fn main() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl; + io.extend_with(rpc.to_delegate()); +} From 8ffc7f30a633d405adae5ead3d6e6422b0c612d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 31 Jan 2019 15:05:06 +0100 Subject: [PATCH 020/149] Add jsonrpc-derive details. (#372) * Add jsonrpc-derive details. * Update docs and authors. * Bump version. --- core/Cargo.toml | 12 ++++++------ derive/Cargo.toml | 8 ++++++-- http/Cargo.toml | 12 ++++++------ ipc/Cargo.toml | 12 ++++++------ macros/Cargo.toml | 10 +++++----- pubsub/Cargo.toml | 12 ++++++------ pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 14 +++++++------- stdio/Cargo.toml | 12 ++++++------ tcp/Cargo.toml | 12 ++++++------ test/Cargo.toml | 4 ++-- ws/Cargo.toml | 12 ++++++------ 12 files changed, 63 insertions(+), 59 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index d54bd2de8..3e93c9eb0 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,14 +1,14 @@ [package] +authors = ["Parity Technologies "] description = "Transport agnostic rust implementation of JSON-RPC 2.0 Specification." +documentation = "https://docs.rs/jsonrpc-core/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" -repository = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" -version = "10.0.0" -authors = ["debris "] -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_core/index.html" -edition = "2018" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.0.1" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index aa3fb7228..4b394b87f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -1,8 +1,12 @@ [package] -name = "jsonrpc-derive" -version = "10.0.0" authors = ["Parity Technologies "] +documentation = "https://docs.rs/jsonrpc-derive/" edition = "2018" +homepage = "https://github.com/paritytech/jsonrpc" +license = "MIT" +name = "jsonrpc-derive" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.0.1" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index b3e6aa5d5..02fd131f2 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -1,14 +1,14 @@ [package] +authors = ["Parity Technologies "] description = "Rust http server using JSONRPC 2.0." +documentation = "https://docs.rs/jsonrpc-http-server/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" -repository = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" -version = "10.0.0" -authors = ["debris "] -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_http_server/index.html" -edition = "2018" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.0.1" [dependencies] hyper = "0.12" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index fe28e1cf6..3f131d5e9 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "jsonrpc-ipc-server" +authors = ["Parity Technologies "] description = "IPC server for JSON-RPC" -version = "10.0.0" -authors = ["Nikolay Volf "] -license = "MIT" +documentation = "https://docs.rs/jsonrpc-ipc-server/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" +license = "MIT" +name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -documentation = "https://paritytech.github.io/jsonrpc/json_ipc_server/index.html" -edition = "2018" +version = "10.0.1" [dependencies] log = "0.4" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 915c7858a..37185db7e 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,13 +1,13 @@ [package] +authors = ["Parity Technologies "] description = "Helper macros for jsonrpc-core" +documentation = "https://docs.rs/jsonrpc-macros/" homepage = "https://github.com/paritytech/jsonrpc" -repository = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-macros" -version = "10.0.0" -authors = ["rphmeier "] -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_macros/index.html" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.0.1" [dependencies] serde = "1.0" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 4881ec492..abf731ad5 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -1,14 +1,14 @@ [package] +authors = ["Parity Technologies "] description = "Publish-Subscribe extension for jsonrpc." +documentation = "https://docs.rs/jsonrpc-pubsub/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" -repository = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" -version = "10.0.0" -authors = ["tomusdrw "] -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_pubsub/index.html" -edition = "2018" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.0.1" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 0cdaf2145..60955156d 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.0" +version = "10.0.1" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 986b69c2a..c6fce05bd 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -1,14 +1,14 @@ [package] +authors = ["Parity Technologies "] description = "Server utils for jsonrpc-core crate." -name = "jsonrpc-server-utils" -version = "10.0.0" -authors = ["tomusdrw "] -license = "MIT" -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_core/index.html" +documentation = "https://docs.rs/jsonrpc-server-utils/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] +license = "MIT" +name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -edition = "2018" +version = "10.0.1" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index a2e1c6b22..6089f4003 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "jsonrpc-stdio-server" +authors = ["Parity Technologies "] description = "STDIN/STDOUT server for JSON-RPC" -version = "10.0.0" -authors = ["cmichi "] -license = "MIT" +documentation = "https://docs.rs/jsonrpc-stdio-server/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" +license = "MIT" +name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_stdio_server/index.html" -edition = "2018" +version = "10.0.1" [dependencies] futures = "0.1.23" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index b7edae8cf..26c089426 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "jsonrpc-tcp-server" +authors = ["Parity Technologies "] description = "TCP/IP server for JSON-RPC" -version = "10.0.0" -authors = ["NikVolf "] -license = "MIT" +documentation = "https://docs.rs/jsonrpc-tcp-server/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" +license = "MIT" +name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_tcp_server/index.html" -edition = "2018" +version = "10.0.1" [dependencies] log = "0.4" diff --git a/test/Cargo.toml b/test/Cargo.toml index e28d0be7d..a8e1b9e26 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "10.0.0" +version = "10.0.1" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_test/index.html" +documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 6dfe5ce15..c262681e9 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "jsonrpc-ws-server" +authors = ["Parity Technologies "] description = "WebSockets server for JSON-RPC" -version = "10.0.0" -authors = ["tomusdrw "] -license = "MIT" +documentation = "https://docs.rs/jsonrpc-ws-server/" +edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" +license = "MIT" +name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -documentation = "https://paritytech.github.io/jsonrpc/json_ws_server/index.html" -edition = "2018" +version = "10.0.1" [dependencies] error-chain = "0.12" From 5e82ae04e2ccdded32115cae947011006781b154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 31 Jan 2019 16:08:22 +0100 Subject: [PATCH 021/149] Fix pieces of derive. (#374) --- derive/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 4b394b87f..3e57c2eec 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -1,6 +1,7 @@ [package] authors = ["Parity Technologies "] documentation = "https://docs.rs/jsonrpc-derive/" +description = "High level, typed wrapper for `jsonrpc-core`" edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" @@ -23,4 +24,4 @@ jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -compiletest_rs = { version = "*", features = ["stable"] } +compiletest_rs = { version = "0.3", features = ["stable"] } From 52342882ccda3b65d6d3d91481480a8d8c8916b9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 5 Feb 2019 13:21:54 +0000 Subject: [PATCH 022/149] Fix rpc alias(es) key constant (#375) * Fix rpc alias(es) key constant * Bump derive patch --- derive/Cargo.toml | 2 +- derive/src/rpc_attr.rs | 2 +- derive/tests/macros.rs | 32 +++++++++++++++++++++++++++++++- derive/tests/pubsub-macros.rs | 24 +++++++++++++++++++++++- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 3e57c2eec..0d2351e4f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.0.2" [lib] proc-macro = true diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index cce176c0b..b4585dff8 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -21,7 +21,7 @@ pub enum PubSubMethodKind { const RPC_ATTR_NAME: &'static str = "rpc"; const RPC_NAME_KEY: &'static str = "name"; const SUBSCRIPTION_NAME_KEY: &'static str = "subscription"; -const ALIASES_KEY: &'static str = "aliases"; +const ALIASES_KEY: &'static str = "alias"; const PUB_SUB_ATTR_NAME: &'static str = "pubsub"; const METADATA_META_WORD: &'static str = "meta"; const SUBSCRIBE_META_WORD: &'static str = "subscribe"; diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index c0963c3f5..0c119e5af 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -22,7 +22,7 @@ pub trait Rpc { fn neg(&self, _: i64) -> Result; /// Adds two numbers and returns a result - #[rpc(name = "add")] + #[rpc(name = "add", alias("add_alias1", "add_alias2"))] fn add(&self, _: u64, _: u64) -> Result; } @@ -114,3 +114,33 @@ fn should_accept_multiple_params() { "id": 1 }"#).unwrap()); } + +#[test] +fn should_use_method_name_aliases() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"add_alias1","params":[1, 2]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"add_alias2","params":[1, 2]}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(result1, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 3, + "id": 1 + }"#).unwrap()); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!(result2, serde_json::from_str(r#"{ + "jsonrpc": "2.0", + "result": 3, + "id": 1 + }"#).unwrap()); +} + diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index c7f8a82b5..4f1f05e6e 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -23,7 +23,7 @@ pub trait Rpc { type Metadata; /// Hello subscription - #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_alias"))] fn subscribe(&self, _: Self::Metadata, _: Subscriber, _: u32, _: Option); /// Unsubscribe from hello subscription. @@ -87,3 +87,25 @@ fn test_invalid_trailing_pubsub_params() { let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); assert_eq!(expected, result); } + +#[test] +fn test_subscribe_with_alias() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let meta = Metadata; + let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello_alias","params":[1]}"#; + let res = io.handle_request_sync(req, meta); + let expected = r#"{ + "jsonrpc": "2.0", + "result": 5, + "id": 1 + }"#; + + let expected: jsonrpc_core::Response = serde_json::from_str(expected).unwrap(); + let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); + assert_eq!(expected, result); +} + From c519eeb4975fa4868ec42877c735c3c6d61fa749 Mon Sep 17 00:00:00 2001 From: thiolliere Date: Thu, 7 Feb 2019 14:43:53 +0100 Subject: [PATCH 023/149] use compile error (#382) instead of panic --- derive/src/lib.rs | 10 ++++------ derive/src/rpc_trait.rs | 20 ++++++++++--------- .../tests/ui/pubsub/missing-subscribe.stderr | 15 +++++++------- .../ui/pubsub/missing-unsubscribe.stderr | 15 +++++++------- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 6ef5dbd7c..c5d26f706 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -144,10 +144,8 @@ mod to_delegate; pub fn rpc(_args: TokenStream, input: TokenStream) -> TokenStream { let input_toks = parse_macro_input!(input as syn::Item); - let output = match rpc_trait::rpc_impl( input_toks) { - Ok(output) => output, - Err(err) => panic!("[rpc] encountered error: {}", err), - }; - - output.into() + match rpc_trait::rpc_impl( input_toks) { + Ok(output) => output.into(), + Err(err) => err.to_compile_error().into(), + } } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index ce28476a7..2aeccd4d7 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use quote::quote; use syn::{ parse_quote, Token, punctuated::Punctuated, - fold::{self, Fold}, + fold::{self, Fold}, Result, }; use crate::rpc_attr::{RpcMethodAttribute, PubSubMethodKind, AttributeKind}; use crate::to_delegate::{RpcMethod, MethodRegistration, generate_trait_item_method}; @@ -19,8 +19,6 @@ const MISSING_UNSUBSCRIBE_METHOD_ERR: &'static str = const RPC_MOD_NAME_PREFIX: &'static str = "rpc_impl_"; -type Result = std::result::Result; - struct RpcTrait { has_pubsub_methods: bool, methods: Vec, @@ -67,7 +65,7 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result None, // non rpc annotated trait method - Err(err) => Some(Err(err)), + Err(err) => Some(Err(syn::Error::new_spanned(method, err))), } } else { None @@ -98,14 +96,16 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result { if unsub.is_none() { *unsub = Some(method.clone()) } else { - return Err(format!("Subscription '{}' has more than one unsubscribe method", subscription_name)) + return Err(syn::Error::new_spanned(&method.trait_item, + format!("Subscription '{}' unsubscribe method is already defined", subscription_name))) } }, } @@ -121,8 +121,10 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result return Err(format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR)), - (None, Some(_)) => return Err(format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR)), + (Some(method), None) => return Err(syn::Error::new_spanned(&method.trait_item, + format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR))), + (None, Some(method)) => return Err(syn::Error::new_spanned(&method.trait_item, + format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR))), (None, None) => unreachable!(), } } @@ -147,7 +149,7 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { pub fn rpc_impl(input: syn::Item) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, - _ => return Err("rpc_api trait only works with trait declarations".into()) + item @ _ => return Err(syn::Error::new_spanned(item, "rpc_api trait only works with trait declarations")), }; let rpc_trait = generate_rpc_item_trait(&rpc_trait)?; diff --git a/derive/tests/ui/pubsub/missing-subscribe.stderr b/derive/tests/ui/pubsub/missing-subscribe.stderr index 7fa484661..41303994e 100644 --- a/derive/tests/ui/pubsub/missing-subscribe.stderr +++ b/derive/tests/ui/pubsub/missing-subscribe.stderr @@ -1,10 +1,11 @@ -error: custom attribute panicked - --> $DIR/missing-subscribe.rs:6:1 - | -6 | #[rpc] - | ^^^^^^ - | - = help: message: [rpc] encountered error: subscription 'hello'. Can't find subscribe method, expected a method annotated with `subscribe` e.g. `#[pubsub(subscription = "hello", subscribe, name = "hello_subscribe")]` +error: subscription 'hello'. Can't find subscribe method, expected a method annotated with `subscribe` e.g. `#[pubsub(subscription = "hello", subscribe, name = "hello_subscribe")]` + --> $DIR/missing-subscribe.rs:12:2 + | +12 | /// Unsubscribe from hello subscription. + | _____^ +13 | | #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] +14 | | fn unsubscribe(&self, Option, SubscriptionId) -> Result; + | |__________________________________________________________________________________^ error: aborting due to previous error diff --git a/derive/tests/ui/pubsub/missing-unsubscribe.stderr b/derive/tests/ui/pubsub/missing-unsubscribe.stderr index fc51fba2f..3ef924f20 100644 --- a/derive/tests/ui/pubsub/missing-unsubscribe.stderr +++ b/derive/tests/ui/pubsub/missing-unsubscribe.stderr @@ -1,10 +1,11 @@ -error: custom attribute panicked - --> $DIR/missing-unsubscribe.rs:6:1 - | -6 | #[rpc] - | ^^^^^^ - | - = help: message: [rpc] encountered error: subscription 'hello'. Can't find unsubscribe method, expected a method annotated with `unsubscribe` e.g. `#[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")]` +error: subscription 'hello'. Can't find unsubscribe method, expected a method annotated with `unsubscribe` e.g. `#[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")]` + --> $DIR/missing-unsubscribe.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] +12 | | fn subscribe(&self, Self::Metadata, typed::Subscriber, u64); + | |________________________________________________________________________^ error: aborting due to previous error From b608779484d81942bad5eb220d6c1504b78f2765 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 8 Feb 2019 11:09:03 +0000 Subject: [PATCH 024/149] derive: don't require pubsub dependency if no pubsub methods (#384) * Fix rpc attribute error message * Don't require pubsub dependency if no pubsub methods --- derive/src/rpc_trait.rs | 17 ++++++--- ...dependency-not-required-for-vanilla-rpc.rs | 36 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 2aeccd4d7..7cbd741df 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -53,7 +53,7 @@ impl<'a> Fold for RpcTrait { } } -fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result { +fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrait, bool)> { let methods_result: Result> = item_trait.items .iter() .filter_map(|trait_item| { @@ -137,7 +137,7 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result syn::Ident { @@ -149,18 +149,25 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { pub fn rpc_impl(input: syn::Item) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, - item @ _ => return Err(syn::Error::new_spanned(item, "rpc_api trait only works with trait declarations")), + item @ _ => return Err(syn::Error::new_spanned(item, "The #[rpc] custom attribute only works with trait declarations")), }; - let rpc_trait = generate_rpc_item_trait(&rpc_trait)?; + let (rpc_trait, has_pubsub_methods) = generate_rpc_item_trait(&rpc_trait)?; let name = rpc_trait.ident.clone(); let mod_name_ident = rpc_wrapper_mod_name(&rpc_trait); + let optional_pubsub_import = + if has_pubsub_methods { + quote!(use jsonrpc_pubsub as _jsonrpc_pubsub;) + } else { + quote!() + }; + Ok(quote! { mod #mod_name_ident { use jsonrpc_core as _jsonrpc_core; - use jsonrpc_pubsub as _jsonrpc_pubsub; + #optional_pubsub_import use serde as _serde; use super::*; use self::_jsonrpc_core::futures as _futures; diff --git a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs new file mode 100644 index 000000000..7f277661c --- /dev/null +++ b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs @@ -0,0 +1,36 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::{Result, IoHandler}; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add", alias("callAsyncMetaAlias"))] + fn add(&self, _: u64, _: u64) -> Result; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn protocol_version(&self) -> Result { + Ok("version1".into()) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } +} + +fn main() { + let mut io = IoHandler::new(); + let rpc = RpcImpl; + + io.extend_with(rpc.to_delegate()) +} From 72964433887c8d7eda7c880c5670d959f0fd3b90 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Feb 2019 09:23:37 +0000 Subject: [PATCH 025/149] Fail to compile with invalid attribute parameters (#385) * Use syn::Error when parsing attributes, error message constants * Add compiletests for attribute parse errors * Compile errors for invalid attribute parameters * Replace anon fn args in compilefail examples with underscore * Fix spaces and blank line * Revert accidental change to macros auto_args * Revert accidental macros changes * Revert accidental change to pubsuc macros tests * Fix build on rust 1.33-beta: remove Rpc impls * Clone after filter * Remove redundant static lifetime on constants * Remove redundant braces --- derive/src/rpc_attr.rs | 97 +++++++++++++++---- derive/src/rpc_trait.rs | 8 +- derive/src/to_delegate.rs | 6 +- .../tests/ui/attr-invalid-meta-list-names.rs | 15 +++ .../ui/attr-invalid-meta-list-names.stderr | 11 +++ derive/tests/ui/attr-invalid-meta-words.rs | 15 +++ .../tests/ui/attr-invalid-meta-words.stderr | 11 +++ derive/tests/ui/attr-invalid-name-values.rs | 15 +++ .../tests/ui/attr-invalid-name-values.stderr | 11 +++ derive/tests/ui/attr-missing-rpc-name.rs | 15 +++ derive/tests/ui/attr-missing-rpc-name.stderr | 11 +++ derive/tests/ui/multiple-rpc-attributes.rs | 16 +++ .../tests/ui/multiple-rpc-attributes.stderr | 12 +++ .../attr-both-subscribe-and-unsubscribe.rs | 19 ++++ ...attr-both-subscribe-and-unsubscribe.stderr | 11 +++ .../ui/pubsub/attr-invalid-meta-list-names.rs | 19 ++++ .../attr-invalid-meta-list-names.stderr | 11 +++ .../ui/pubsub/attr-invalid-meta-words.rs | 19 ++++ .../ui/pubsub/attr-invalid-meta-words.stderr | 11 +++ .../ui/pubsub/attr-invalid-name-values.rs | 19 ++++ .../ui/pubsub/attr-invalid-name-values.stderr | 11 +++ .../pubsub/attr-missing-subscription-name.rs | 19 ++++ .../attr-missing-subscription-name.stderr | 11 +++ .../attr-neither-subscribe-or-unsubscribe.rs | 19 ++++ ...tr-neither-subscribe-or-unsubscribe.stderr | 11 +++ derive/tests/ui/pubsub/missing-subscribe.rs | 2 +- .../tests/ui/pubsub/missing-subscribe.stderr | 4 +- derive/tests/ui/pubsub/missing-unsubscribe.rs | 2 +- .../ui/pubsub/missing-unsubscribe.stderr | 4 +- http/src/lib.rs | 2 +- server-utils/src/hosts.rs | 2 +- 31 files changed, 404 insertions(+), 35 deletions(-) create mode 100644 derive/tests/ui/attr-invalid-meta-list-names.rs create mode 100644 derive/tests/ui/attr-invalid-meta-list-names.stderr create mode 100644 derive/tests/ui/attr-invalid-meta-words.rs create mode 100644 derive/tests/ui/attr-invalid-meta-words.stderr create mode 100644 derive/tests/ui/attr-invalid-name-values.rs create mode 100644 derive/tests/ui/attr-invalid-name-values.stderr create mode 100644 derive/tests/ui/attr-missing-rpc-name.rs create mode 100644 derive/tests/ui/attr-missing-rpc-name.stderr create mode 100644 derive/tests/ui/multiple-rpc-attributes.rs create mode 100644 derive/tests/ui/multiple-rpc-attributes.stderr create mode 100644 derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.rs create mode 100644 derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.stderr create mode 100644 derive/tests/ui/pubsub/attr-invalid-meta-list-names.rs create mode 100644 derive/tests/ui/pubsub/attr-invalid-meta-list-names.stderr create mode 100644 derive/tests/ui/pubsub/attr-invalid-meta-words.rs create mode 100644 derive/tests/ui/pubsub/attr-invalid-meta-words.stderr create mode 100644 derive/tests/ui/pubsub/attr-invalid-name-values.rs create mode 100644 derive/tests/ui/pubsub/attr-invalid-name-values.stderr create mode 100644 derive/tests/ui/pubsub/attr-missing-subscription-name.rs create mode 100644 derive/tests/ui/pubsub/attr-missing-subscription-name.stderr create mode 100644 derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.rs create mode 100644 derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.stderr diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index b4585dff8..ac3f73e67 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -1,3 +1,5 @@ +use syn::{Error, Result, visit::{self, Visit}}; + #[derive(Clone, Debug)] pub struct RpcMethodAttribute { pub attr: syn::Attribute, @@ -18,33 +20,38 @@ pub enum PubSubMethodKind { Unsubscribe, } -const RPC_ATTR_NAME: &'static str = "rpc"; -const RPC_NAME_KEY: &'static str = "name"; -const SUBSCRIPTION_NAME_KEY: &'static str = "subscription"; -const ALIASES_KEY: &'static str = "alias"; -const PUB_SUB_ATTR_NAME: &'static str = "pubsub"; -const METADATA_META_WORD: &'static str = "meta"; -const SUBSCRIBE_META_WORD: &'static str = "subscribe"; -const UNSUBSCRIBE_META_WORD: &'static str = "unsubscribe"; +const RPC_ATTR_NAME: &str = "rpc"; +const RPC_NAME_KEY: &str = "name"; +const SUBSCRIPTION_NAME_KEY: &str = "subscription"; +const ALIASES_KEY: &str = "alias"; +const PUB_SUB_ATTR_NAME: &str = "pubsub"; +const METADATA_META_WORD: &str = "meta"; +const SUBSCRIBE_META_WORD: &str = "subscribe"; +const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; + +const MULTIPLE_RPC_ATTRIBUTES_ERR: &str = "Expected only a single rpc attribute per method"; +const INVALID_ATTR_PARAM_NAMES_ERR: &str = "Invalid attribute parameter(s):"; +const MISSING_NAME_ERR: &str = "rpc attribute should have a name e.g. `name = \"method_name\"`"; +const MISSING_SUB_NAME_ERR: &str = "pubsub attribute should have a subscription name"; +const BOTH_SUB_AND_UNSUB_ERR: &str = "pubsub attribute annotated with both subscribe and unsubscribe"; +const NEITHER_SUB_OR_UNSUB_ERR: &str = "pubsub attribute not annotated with either subscribe or unsubscribe"; impl RpcMethodAttribute { - pub fn parse_attr(method: &syn::TraitItemMethod) -> Result, String> { + pub fn parse_attr(method: &syn::TraitItemMethod) -> Result> { let attrs = method.attrs .iter() .filter_map(Self::parse_meta) - .collect::, _>>()?; + .collect::>>()?; if attrs.len() <= 1 { Ok(attrs.first().cloned()) } else { - Err(format!("Expected only a single rpc attribute per method. Found {}", attrs.len())) + Err(Error::new_spanned(method, MULTIPLE_RPC_ATTRIBUTES_ERR)) } } - fn parse_meta(attr: &syn::Attribute) -> Option> { - let parse_result = attr.parse_meta() - .map_err(|err| format!("Error parsing attribute: {}", err)); - match parse_result { + fn parse_meta(attr: &syn::Attribute) -> Option> { + match attr.parse_meta().and_then(validate_attribute_meta) { Ok(ref meta) => { let attr_kind = match meta.name().to_string().as_ref() { @@ -60,7 +67,7 @@ impl RpcMethodAttribute { get_meta_list(meta) .and_then(|ml| get_name_value(RPC_NAME_KEY, ml)) .map_or( - Err("rpc attribute should have a name e.g. `name = \"method_name\"`".into()), + Err(Error::new_spanned(attr, MISSING_NAME_ERR)), |name| { let aliases = get_meta_list(meta) .map_or(Vec::new(), |ml| get_aliases(ml)); @@ -77,14 +84,14 @@ impl RpcMethodAttribute { } } - fn parse_pubsub(meta: &syn::Meta) -> Result { + fn parse_pubsub(meta: &syn::Meta) -> Result { let name_and_list = get_meta_list(meta) .and_then(|ml| get_name_value(SUBSCRIPTION_NAME_KEY, ml).map(|name| (name, ml)) ); name_and_list.map_or( - Err("pubsub attribute should have a subscription name".into()), + Err(Error::new_spanned(meta, MISSING_SUB_NAME_ERR)), |(sub_name, ml)| { let is_subscribe = has_meta_word(SUBSCRIBE_META_WORD, ml); let is_unsubscribe = has_meta_word(UNSUBSCRIBE_META_WORD, ml); @@ -94,9 +101,9 @@ impl RpcMethodAttribute { (false, true) => Ok(PubSubMethodKind::Unsubscribe), (true, true) => - Err(format!("pubsub attribute for subscription '{}' annotated with both subscribe and unsubscribe", sub_name)), + Err(Error::new_spanned(meta, BOTH_SUB_AND_UNSUB_ERR)), (false, false) => - Err(format!("pubsub attribute for subscription '{}' not annotated with either subscribe or unsubscribe", sub_name)), + Err(Error::new_spanned(meta, NEITHER_SUB_OR_UNSUB_ERR)), }; kind.map(|kind| AttributeKind::PubSub { subscription_name: sub_name.into(), @@ -113,6 +120,56 @@ impl RpcMethodAttribute { } } +fn validate_attribute_meta(meta: syn::Meta) -> Result { + #[derive(Default)] + struct Visitor { + meta_words: Vec, + name_value_names: Vec, + meta_list_names: Vec, + } + impl<'a> Visit<'a> for Visitor { + fn visit_meta(&mut self, meta: &syn::Meta) { + match meta { + syn::Meta::List(list) => self.meta_list_names.push(list.ident.to_string()), + syn::Meta::Word(ident) => self.meta_words.push(ident.to_string()), + syn::Meta::NameValue(nv) => self.name_value_names.push(nv.ident.to_string()) + } + } + } + + let mut visitor = Visitor::default(); + visit::visit_meta(&mut visitor, &meta); + + match meta.name().to_string().as_ref() { + RPC_ATTR_NAME => { + validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD])?; + validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY])?; + validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) + }, + PUB_SUB_ATTR_NAME => { + validate_idents(&meta, &visitor.meta_words, &[SUBSCRIBE_META_WORD, UNSUBSCRIBE_META_WORD])?; + validate_idents(&meta, &visitor.name_value_names, &[SUBSCRIPTION_NAME_KEY, RPC_NAME_KEY])?; + validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) + }, + _ => Ok(meta), // ignore other attributes - compiler will catch unknown ones + } +} + +fn validate_idents(meta: &syn::Meta, attr_idents: &[String], valid: &[&str]) -> Result { + let invalid_meta_words: Vec<_> = attr_idents + .into_iter() + .filter(|w| !valid.iter().any(|v| v == w)) + .cloned() + .collect(); + if !invalid_meta_words.is_empty() { + let expected = format!("Expected '{}'", valid.join(", ")); + let msg = format!("{} '{}'. {}", INVALID_ATTR_PARAM_NAMES_ERR, invalid_meta_words.join(", "), expected); + Err(Error::new_spanned(meta, msg)) + } else { + Ok(meta.clone()) + } +} + fn get_meta_list(meta: &syn::Meta) -> Option<&syn::MetaList> { if let syn::Meta::List(ml) = meta { Some(ml) diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 7cbd741df..fcda0c2a0 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -7,17 +7,17 @@ use syn::{ use crate::rpc_attr::{RpcMethodAttribute, PubSubMethodKind, AttributeKind}; use crate::to_delegate::{RpcMethod, MethodRegistration, generate_trait_item_method}; -const METADATA_TYPE: &'static str = "Metadata"; +const METADATA_TYPE: &str = "Metadata"; -const MISSING_SUBSCRIBE_METHOD_ERR: &'static str = +const MISSING_SUBSCRIBE_METHOD_ERR: &str = "Can't find subscribe method, expected a method annotated with `subscribe` \ e.g. `#[pubsub(subscription = \"hello\", subscribe, name = \"hello_subscribe\")]`"; -const MISSING_UNSUBSCRIBE_METHOD_ERR: &'static str = +const MISSING_UNSUBSCRIBE_METHOD_ERR: &str = "Can't find unsubscribe method, expected a method annotated with `unsubscribe` \ e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`"; -const RPC_MOD_NAME_PREFIX: &'static str = "rpc_impl_"; +const RPC_MOD_NAME_PREFIX: &str = "rpc_impl_"; struct RpcTrait { has_pubsub_methods: bool, diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index b4d2073c6..bffad76cf 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -71,9 +71,9 @@ impl MethodRegistration { } } -const SUBCRIBER_TYPE_IDENT: &'static str = "Subscriber"; -const METADATA_CLOSURE_ARG: &'static str = "meta"; -const SUBSCRIBER_CLOSURE_ARG: &'static str = "subscriber"; +const SUBCRIBER_TYPE_IDENT: &str = "Subscriber"; +const METADATA_CLOSURE_ARG: &str = "meta"; +const SUBSCRIBER_CLOSURE_ARG: &str = "subscriber"; pub fn generate_trait_item_method( methods: &[MethodRegistration], diff --git a/derive/tests/ui/attr-invalid-meta-list-names.rs b/derive/tests/ui/attr-invalid-meta-list-names.rs new file mode 100644 index 000000000..099e7d661 --- /dev/null +++ b/derive/tests/ui/attr-invalid-meta-list-names.rs @@ -0,0 +1,15 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::Result; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion", Xalias("alias"))] + fn protocol_version(&self) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/attr-invalid-meta-list-names.stderr b/derive/tests/ui/attr-invalid-meta-list-names.stderr new file mode 100644 index 000000000..ba0ff9b5f --- /dev/null +++ b/derive/tests/ui/attr-invalid-meta-list-names.stderr @@ -0,0 +1,11 @@ +error: Invalid attribute parameter(s): 'Xalias'. Expected 'alias' + --> $DIR/attr-invalid-meta-list-names.rs:10:2 + | +10 | /// Returns a protocol version + | _____^ +11 | | #[rpc(name = "protocolVersion", Xalias("alias"))] +12 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/attr-invalid-meta-words.rs b/derive/tests/ui/attr-invalid-meta-words.rs new file mode 100644 index 000000000..74a7c9ffc --- /dev/null +++ b/derive/tests/ui/attr-invalid-meta-words.rs @@ -0,0 +1,15 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::Result; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion", Xmeta)] + fn protocol_version(&self) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr new file mode 100644 index 000000000..3c2d0e0a7 --- /dev/null +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -0,0 +1,11 @@ +error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta' + --> $DIR/attr-invalid-meta-words.rs:10:2 + | +10 | /// Returns a protocol version + | _____^ +11 | | #[rpc(name = "protocolVersion", Xmeta)] +12 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/attr-invalid-name-values.rs b/derive/tests/ui/attr-invalid-name-values.rs new file mode 100644 index 000000000..e37c24c24 --- /dev/null +++ b/derive/tests/ui/attr-invalid-name-values.rs @@ -0,0 +1,15 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::Result; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(Xname = "protocolVersion")] + fn protocol_version(&self) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr new file mode 100644 index 000000000..cc1f50bd6 --- /dev/null +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -0,0 +1,11 @@ +error: Invalid attribute parameter(s): 'Xname'. Expected 'name' + --> $DIR/attr-invalid-name-values.rs:10:2 + | +10 | /// Returns a protocol version + | _____^ +11 | | #[rpc(Xname = "protocolVersion")] +12 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/attr-missing-rpc-name.rs b/derive/tests/ui/attr-missing-rpc-name.rs new file mode 100644 index 000000000..16018b302 --- /dev/null +++ b/derive/tests/ui/attr-missing-rpc-name.rs @@ -0,0 +1,15 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::Result; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc] + fn protocol_version(&self) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/attr-missing-rpc-name.stderr b/derive/tests/ui/attr-missing-rpc-name.stderr new file mode 100644 index 000000000..a96c0aa42 --- /dev/null +++ b/derive/tests/ui/attr-missing-rpc-name.stderr @@ -0,0 +1,11 @@ +error: rpc attribute should have a name e.g. `name = "method_name"` + --> $DIR/attr-missing-rpc-name.rs:10:2 + | +10 | /// Returns a protocol version + | _____^ +11 | | #[rpc] +12 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/multiple-rpc-attributes.rs b/derive/tests/ui/multiple-rpc-attributes.rs new file mode 100644 index 000000000..10297c01b --- /dev/null +++ b/derive/tests/ui/multiple-rpc-attributes.rs @@ -0,0 +1,16 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::Result; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + #[rpc(name = "protocolVersionAgain")] + fn protocol_version(&self) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/multiple-rpc-attributes.stderr b/derive/tests/ui/multiple-rpc-attributes.stderr new file mode 100644 index 000000000..16af3ee29 --- /dev/null +++ b/derive/tests/ui/multiple-rpc-attributes.stderr @@ -0,0 +1,12 @@ +error: Expected only a single rpc attribute per method + --> $DIR/multiple-rpc-attributes.rs:10:2 + | +10 | /// Returns a protocol version + | _____^ +11 | | #[rpc(name = "protocolVersion")] +12 | | #[rpc(name = "protocolVersionAgain")] +13 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.rs b/derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.rs new file mode 100644 index 000000000..a8946eb1b --- /dev/null +++ b/derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, unsubscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.stderr b/derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.stderr new file mode 100644 index 000000000..bab557883 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-both-subscribe-and-unsubscribe.stderr @@ -0,0 +1,11 @@ +error: pubsub attribute annotated with both subscribe and unsubscribe + --> $DIR/attr-both-subscribe-and-unsubscribe.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(subscription = "hello", subscribe, unsubscribe, name = "hello_subscribe", alias("hello_sub"))] +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/attr-invalid-meta-list-names.rs b/derive/tests/ui/pubsub/attr-invalid-meta-list-names.rs new file mode 100644 index 000000000..75412aaf4 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-invalid-meta-list-names.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", Xalias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/attr-invalid-meta-list-names.stderr b/derive/tests/ui/pubsub/attr-invalid-meta-list-names.stderr new file mode 100644 index 000000000..2f7ddd478 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-invalid-meta-list-names.stderr @@ -0,0 +1,11 @@ +error: Invalid attribute parameter(s): 'Xalias'. Expected 'alias' + --> $DIR/attr-invalid-meta-list-names.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", Xalias("hello_sub"))] +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/attr-invalid-meta-words.rs b/derive/tests/ui/pubsub/attr-invalid-meta-words.rs new file mode 100644 index 000000000..d3a6d8372 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-invalid-meta-words.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", Xsubscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/attr-invalid-meta-words.stderr b/derive/tests/ui/pubsub/attr-invalid-meta-words.stderr new file mode 100644 index 000000000..cca937234 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-invalid-meta-words.stderr @@ -0,0 +1,11 @@ +error: Invalid attribute parameter(s): 'Xsubscribe'. Expected 'subscribe, unsubscribe' + --> $DIR/attr-invalid-meta-words.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(subscription = "hello", Xsubscribe, name = "hello_subscribe", alias("hello_sub"))] +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/attr-invalid-name-values.rs b/derive/tests/ui/pubsub/attr-invalid-name-values.rs new file mode 100644 index 000000000..3933ad67d --- /dev/null +++ b/derive/tests/ui/pubsub/attr-invalid-name-values.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(Xsubscription = "hello", subscribe, Xname = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/attr-invalid-name-values.stderr b/derive/tests/ui/pubsub/attr-invalid-name-values.stderr new file mode 100644 index 000000000..fa38e1a82 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-invalid-name-values.stderr @@ -0,0 +1,11 @@ +error: Invalid attribute parameter(s): 'Xsubscription, Xname'. Expected 'subscription, name' + --> $DIR/attr-invalid-name-values.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(Xsubscription = "hello", subscribe, Xname = "hello_subscribe", alias("hello_sub"))] +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/attr-missing-subscription-name.rs b/derive/tests/ui/pubsub/attr-missing-subscription-name.rs new file mode 100644 index 000000000..4139b7155 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-missing-subscription-name.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/attr-missing-subscription-name.stderr b/derive/tests/ui/pubsub/attr-missing-subscription-name.stderr new file mode 100644 index 000000000..ab1638f51 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-missing-subscription-name.stderr @@ -0,0 +1,11 @@ +error: pubsub attribute should have a subscription name + --> $DIR/attr-missing-subscription-name.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(subscribe, name = "hello_subscribe", alias("hello_sub"))] +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.rs b/derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.rs new file mode 100644 index 000000000..c79512c82 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.stderr b/derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.stderr new file mode 100644 index 000000000..505627965 --- /dev/null +++ b/derive/tests/ui/pubsub/attr-neither-subscribe-or-unsubscribe.stderr @@ -0,0 +1,11 @@ +error: pubsub attribute not annotated with either subscribe or unsubscribe + --> $DIR/attr-neither-subscribe-or-unsubscribe.rs:10:2 + | +10 | /// Hello subscription + | _____^ +11 | | #[pubsub(subscription = "hello", name = "hello_subscribe", alias("hello_sub"))] +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ + +error: aborting due to previous error + diff --git a/derive/tests/ui/pubsub/missing-subscribe.rs b/derive/tests/ui/pubsub/missing-subscribe.rs index 8e4bc9e2a..ae91c4050 100644 --- a/derive/tests/ui/pubsub/missing-subscribe.rs +++ b/derive/tests/ui/pubsub/missing-subscribe.rs @@ -11,7 +11,7 @@ pub trait Rpc { /// Unsubscribe from hello subscription. #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] - fn unsubscribe(&self, Option, SubscriptionId) -> Result; + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; } fn main() {} diff --git a/derive/tests/ui/pubsub/missing-subscribe.stderr b/derive/tests/ui/pubsub/missing-subscribe.stderr index 41303994e..cc7d14f23 100644 --- a/derive/tests/ui/pubsub/missing-subscribe.stderr +++ b/derive/tests/ui/pubsub/missing-subscribe.stderr @@ -4,8 +4,8 @@ error: subscription 'hello'. Can't find subscribe method, expected a method anno 12 | /// Unsubscribe from hello subscription. | _____^ 13 | | #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] -14 | | fn unsubscribe(&self, Option, SubscriptionId) -> Result; - | |__________________________________________________________________________________^ +14 | | fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; + | |________________________________________________________________________________________^ error: aborting due to previous error diff --git a/derive/tests/ui/pubsub/missing-unsubscribe.rs b/derive/tests/ui/pubsub/missing-unsubscribe.rs index ce281673f..093751390 100644 --- a/derive/tests/ui/pubsub/missing-unsubscribe.rs +++ b/derive/tests/ui/pubsub/missing-unsubscribe.rs @@ -9,7 +9,7 @@ pub trait Rpc { /// Hello subscription #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] - fn subscribe(&self, Self::Metadata, typed::Subscriber, u64); + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); // note that the unsubscribe method is missing } diff --git a/derive/tests/ui/pubsub/missing-unsubscribe.stderr b/derive/tests/ui/pubsub/missing-unsubscribe.stderr index 3ef924f20..56b8b6dfb 100644 --- a/derive/tests/ui/pubsub/missing-unsubscribe.stderr +++ b/derive/tests/ui/pubsub/missing-unsubscribe.stderr @@ -4,8 +4,8 @@ error: subscription 'hello'. Can't find unsubscribe method, expected a method an 10 | /// Hello subscription | _____^ 11 | | #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] -12 | | fn subscribe(&self, Self::Metadata, typed::Subscriber, u64); - | |________________________________________________________________________^ +12 | | fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | |_________________________________________________________________________________^ error: aborting due to previous error diff --git a/http/src/lib.rs b/http/src/lib.rs index 4cdf20644..cc705f8b7 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -555,7 +555,7 @@ pub struct Server { close: Option>>, } -const PROOF: &'static str = "Server is always Some until self is consumed."; +const PROOF: &str = "Server is always Some until self is consumed."; impl Server { /// Returns address of this server pub fn address(&self) -> &SocketAddr { diff --git a/server-utils/src/hosts.rs b/server-utils/src/hosts.rs index 457a4a4c0..52dbe1ab9 100644 --- a/server-utils/src/hosts.rs +++ b/server-utils/src/hosts.rs @@ -4,7 +4,7 @@ use std::collections::HashSet; use std::net::SocketAddr; use crate::matcher::{Matcher, Pattern}; -const SPLIT_PROOF: &'static str = "split always returns non-empty iterator."; +const SPLIT_PROOF: &str = "split always returns non-empty iterator."; /// Port pattern #[derive(Clone, Hash, PartialEq, Eq, Debug)] From 09fc6089635395137f0d28065cf23f47d72b0f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 12 Feb 2019 18:25:01 +0100 Subject: [PATCH 026/149] Bump version (#386) --- core/Cargo.toml | 2 +- derive/Cargo.toml | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- macros/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- test/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 3e93c9eb0..2db2e77ac 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 0d2351e4f..ae52383c3 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.2" +version = "10.1.0" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index 02fd131f2..e6fd57fbb 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] hyper = "0.12" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 3f131d5e9..9c9339ede 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] log = "0.4" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 37185db7e..6fe975db8 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -7,7 +7,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-macros" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] serde = "1.0" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index abf731ad5..8b1187480 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 60955156d..59246570b 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index c6fce05bd..c024a462c 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 6089f4003..fe52acab2 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] futures = "0.1.23" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 26c089426..e9b9c394a 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] log = "0.4" diff --git a/test/Cargo.toml b/test/Cargo.toml index a8e1b9e26..1d083f80d 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "10.0.1" +version = "10.1.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index c262681e9..8345cb642 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.0.1" +version = "10.1.0" [dependencies] error-chain = "0.12" From 28156b92f5cb79635300703b3e103ca39cefa565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 13 Feb 2019 09:17:31 +0100 Subject: [PATCH 027/149] Bump dependencies and add some scripts. (#388) --- _automate/bump_version.sh | 17 +++++++++++++++++ _automate/publish.sh | 12 ++++++++++++ derive/Cargo.toml | 6 +++--- http/Cargo.toml | 4 ++-- ipc/Cargo.toml | 4 ++-- macros/Cargo.toml | 6 +++--- pubsub/Cargo.toml | 4 ++-- pubsub/more-examples/Cargo.toml | 8 ++++---- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 4 ++-- ws/Cargo.toml | 4 ++-- 12 files changed, 51 insertions(+), 22 deletions(-) create mode 100755 _automate/bump_version.sh create mode 100755 _automate/publish.sh diff --git a/_automate/bump_version.sh b/_automate/bump_version.sh new file mode 100755 index 000000000..6e1d0ec8d --- /dev/null +++ b/_automate/bump_version.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -xeu + +VERSION=$1 +PREV_DEPS=$2 +NEW_DEPS=$3 + +ack "^version = \"" -l | \ + grep toml | \ + xargs sed -i "s/^version = \".*/version = \"$VERSION\"/" + +ack "{ version = \"$PREV_DEPS" -l | \ + grep toml | \ + xargs sed -i "s/{ version = \"$PREV_DEPS/{ version = \"$NEW_DEPS/" + +cargo check diff --git a/_automate/publish.sh b/_automate/publish.sh new file mode 100755 index 000000000..188f95605 --- /dev/null +++ b/_automate/publish.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -exu + +ORDER=(core server-utils tcp ws http ipc stdio pubsub macros derive test) + +for crate in ${ORDER[@]}; do + cd $crate + cargo publish $@ + cd - +done + diff --git a/derive/Cargo.toml b/derive/Cargo.toml index ae52383c3..6686c9f74 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -18,9 +18,9 @@ proc-macro2 = "0.4" quote = "0.6" [dev-dependencies] -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-pubsub = { version = "10.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } +jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-pubsub = { version = "10.1", path = "../pubsub" } +jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index e6fd57fbb..5b982cf59 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -12,8 +12,8 @@ version = "10.1.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } +jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 9c9339ede..001bcbf04 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -12,8 +12,8 @@ version = "10.1.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } +jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } parity-tokio-ipc = "0.1" parking_lot = "0.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 6fe975db8..d0fd17f17 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -11,12 +11,12 @@ version = "10.1.0" [dependencies] serde = "1.0" -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-pubsub = { version = "10.0", path = "../pubsub" } +jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-pubsub = { version = "10.1", path = "../pubsub" } [dev-dependencies] serde_json = "1.0" -jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 8b1187480..795e63951 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -13,11 +13,11 @@ version = "10.1.0" [dependencies] log = "0.4" parking_lot = "0.7" -jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-core = { version = "10.1", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "10.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 59246570b..e7c5d2cac 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -8,7 +8,7 @@ authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "10.0", path = "../../core" } -jsonrpc-pubsub = { version = "10.0", path = "../" } -jsonrpc-ws-server = { version = "10.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "10.0", path = "../../ipc" } +jsonrpc-core = { version = "10.1", path = "../../core" } +jsonrpc-pubsub = { version = "10.1", path = "../" } +jsonrpc-ws-server = { version = "10.1", path = "../../ws" } +jsonrpc-ipc-server = { version = "10.1", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index c024a462c..1c3b9a716 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -13,7 +13,7 @@ version = "10.1.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-core = { version = "10.1", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index fe52acab2..ead4ad42c 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -11,7 +11,7 @@ version = "10.1.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "10.0", path = "../core" } +jsonrpc-core = { version = "10.1", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index e9b9c394a..08a100e58 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -13,8 +13,8 @@ version = "10.1.0" log = "0.4" parking_lot = "0.7" tokio-service = "0.1" -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } +jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 8345cb642..f38d22324 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -11,8 +11,8 @@ version = "10.1.0" [dependencies] error-chain = "0.12" -jsonrpc-core = { version = "10.0", path = "../core" } -jsonrpc-server-utils = { version = "10.0", path = "../server-utils" } +jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } log = "0.4" parking_lot = "0.7" slab = "0.4" From 6ec3640d172c35f4ad6fd96482441a6157dda25c Mon Sep 17 00:00:00 2001 From: Hyungsuk Kang Date: Sat, 16 Feb 2019 17:54:06 +0900 Subject: [PATCH 028/149] Add example for request params (#379) * Add example for request params * Update params.rs * Use serde_derive to import Deserialize macro --- core/examples/params.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 core/examples/params.rs diff --git a/core/examples/params.rs b/core/examples/params.rs new file mode 100644 index 000000000..c5bbb2e8b --- /dev/null +++ b/core/examples/params.rs @@ -0,0 +1,21 @@ +use jsonrpc_core::*; +use serde_derive::{Deserialize}; + +#[derive(Deserialize)] +struct HelloParams { + name:String, +} + +fn main() { + let mut io = IoHandler::new(); + + io.add_method("say_hello", |params: Params| { + let parsed: HelloParams = params.parse().unwrap(); + Ok(Value::String(format!("hello, {}", parsed.name))) + }); + + let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": { "name": "world" }, "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"hello, world","id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} From 007a8d428b64eec3c89b6fa7ab734c2a835292fc Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 18 Feb 2019 09:58:41 +0000 Subject: [PATCH 029/149] Use proc_macro_crate to handle import renames (#390) --- derive/Cargo.toml | 1 + derive/src/rpc_trait.rs | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 6686c9f74..ee95bc732 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true syn = { version = "^0.15.22", features = ["full", "extra-traits", "visit", "fold"] } proc-macro2 = "0.4" quote = "0.6" +proc-macro-crate = "0.1.3" [dev-dependencies] jsonrpc-core = { version = "10.1", path = "../core" } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index fcda0c2a0..1b68a5f9e 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -1,8 +1,9 @@ use std::collections::HashMap; +use proc_macro2::Span; use quote::quote; use syn::{ parse_quote, Token, punctuated::Punctuated, - fold::{self, Fold}, Result, + fold::{self, Fold}, Result, Error, Ident }; use crate::rpc_attr::{RpcMethodAttribute, PubSubMethodKind, AttributeKind}; use crate::to_delegate::{RpcMethod, MethodRegistration, generate_trait_item_method}; @@ -157,23 +158,33 @@ pub fn rpc_impl(input: syn::Item) -> Result { let name = rpc_trait.ident.clone(); let mod_name_ident = rpc_wrapper_mod_name(&rpc_trait); + let crate_name = |name| { + proc_macro_crate::crate_name(name) + .map(|name| Ident::new(&name, Span::call_site())) + .map_err(|e| Error::new(Span::call_site(), &e)) + }; + let optional_pubsub_import = if has_pubsub_methods { - quote!(use jsonrpc_pubsub as _jsonrpc_pubsub;) + crate_name("jsonrpc-pubsub") + .map(|pubsub_name| quote!(use #pubsub_name as _jsonrpc_pubsub;)) } else { - quote!() - }; + Ok(quote!()) + }?; + + let core_name = crate_name("jsonrpc-core")?; + let serde_name = crate_name("serde")?; - Ok(quote! { + Ok(quote!( mod #mod_name_ident { - use jsonrpc_core as _jsonrpc_core; + use #core_name as _jsonrpc_core; #optional_pubsub_import - use serde as _serde; + use #serde_name as _serde; use super::*; use self::_jsonrpc_core::futures as _futures; #rpc_trait } pub use self::#mod_name_ident::#name; - }) + )) } From 4db37987830dcd798666c96df7634172678eeb92 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Fri, 22 Feb 2019 13:43:12 +0100 Subject: [PATCH 030/149] Use less restrictive signature for Middleware trait's `next` (#391) --- core/src/middleware.rs | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/core/src/middleware.rs b/core/src/middleware.rs index 1300d219e..9bc17f388 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -16,7 +16,7 @@ pub trait Middleware: Send + Sync + 'static { /// Allows you to either respond directly (without executing RPC call) /// or do any additional work before and/or after processing the request. fn on_request(&self, request: Request, meta: M, next: F) -> Either where - F: FnOnce(Request, M) -> X + Send, + F: Fn(Request, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { Either::B(next(request, meta)) @@ -26,7 +26,7 @@ pub trait Middleware: Send + Sync + 'static { /// /// Allows you to either handle the call directly (without executing RPC call). fn on_call(&self, call: Call, meta: M, next: F) -> Either where - F: FnOnce(Call, M) -> X + Send, + F: Fn(Call, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { Either::B(next(call, meta)) @@ -53,20 +53,20 @@ impl, B: Middleware> type CallFuture = Either; fn on_request(&self, request: Request, meta: M, process: F) -> Either where - F: FnOnce(Request, M) -> X + Send, + F: Fn(Request, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { - repack(self.0.on_request(request, meta, move |request, meta| { - self.1.on_request(request, meta, process) + repack(self.0.on_request(request, meta, |request, meta| { + self.1.on_request(request, meta, &process) })) } fn on_call(&self, call: Call, meta: M, process: F) -> Either where - F: FnOnce(Call, M) -> X + Send, + F: Fn(Call, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { - repack(self.0.on_call(call, meta, move |call, meta| { - self.1.on_call(call, meta, process) + repack(self.0.on_call(call, meta, |call, meta| { + self.1.on_call(call, meta, &process) })) } } @@ -78,23 +78,23 @@ impl, B: Middleware, C: Middleware> type CallFuture = Either>; fn on_request(&self, request: Request, meta: M, process: F) -> Either where - F: FnOnce(Request, M) -> X + Send, + F: Fn(Request, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { - repack(self.0.on_request(request, meta, move |request, meta| { - repack(self.1.on_request(request, meta, move |request, meta| { - self.2.on_request(request, meta, process) + repack(self.0.on_request(request, meta, |request, meta| { + repack(self.1.on_request(request, meta, |request, meta| { + self.2.on_request(request, meta, &process) })) })) } fn on_call(&self, call: Call, meta: M, process: F) -> Either where - F: FnOnce(Call, M) -> X + Send, + F: Fn(Call, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { - repack(self.0.on_call(call, meta, move |call, meta| { - repack(self.1.on_call(call, meta, move |call, meta| { - self.2.on_call(call, meta, process) + repack(self.0.on_call(call, meta, |call, meta| { + repack(self.1.on_call(call, meta, |call, meta| { + self.2.on_call(call, meta, &process) })) })) } @@ -107,26 +107,26 @@ impl, B: Middleware, C: Middleware, D: Middl type CallFuture = Either>>; fn on_request(&self, request: Request, meta: M, process: F) -> Either where - F: FnOnce(Request, M) -> X + Send, + F: Fn(Request, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { - repack(self.0.on_request(request, meta, move |request, meta| { - repack(self.1.on_request(request, meta, move |request, meta| { - repack(self.2.on_request(request, meta, move |request, meta| { - self.3.on_request(request, meta, process) + repack(self.0.on_request(request, meta, |request, meta| { + repack(self.1.on_request(request, meta, |request, meta| { + repack(self.2.on_request(request, meta, |request, meta| { + self.3.on_request(request, meta, &process) })) })) })) } fn on_call(&self, call: Call, meta: M, process: F) -> Either where - F: FnOnce(Call, M) -> X + Send, + F: Fn(Call, M) -> X + Send + Sync, X: Future, Error=()> + Send + 'static, { - repack(self.0.on_call(call, meta, move |call, meta| { - repack(self.1.on_call(call, meta, move |call, meta| { - repack(self.2.on_call(call, meta, move |call, meta| { - self.3.on_call(call, meta, process) + repack(self.0.on_call(call, meta, |call, meta| { + repack(self.1.on_call(call, meta, |call, meta| { + repack(self.2.on_call(call, meta, |call, meta| { + self.3.on_call(call, meta, &process) })) })) })) From d71d3bb63ee5d86ecb58c27ffa16d5d81d6d8c35 Mon Sep 17 00:00:00 2001 From: Kirill Pimenov Date: Mon, 25 Feb 2019 11:12:20 +0100 Subject: [PATCH 031/149] Fix most clippy warnings (#392) * Get rid of scary conversions in to_delegate.rs * Fix minor Clippy complaints * Update derive/src/to_delegate.rs Co-Authored-By: kirushik --- derive/src/rpc_attr.rs | 14 +++++++------- derive/src/rpc_trait.rs | 15 +++++++-------- derive/src/to_delegate.rs | 8 ++++---- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index ac3f73e67..3f372fe7e 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -73,7 +73,7 @@ impl RpcMethodAttribute { .map_or(Vec::new(), |ml| get_aliases(ml)); Ok(RpcMethodAttribute { attr: attr.clone(), - name: name.into(), + name: name, aliases, kind }) @@ -106,7 +106,7 @@ impl RpcMethodAttribute { Err(Error::new_spanned(meta, NEITHER_SUB_OR_UNSUB_ERR)), }; kind.map(|kind| AttributeKind::PubSub { - subscription_name: sub_name.into(), + subscription_name: sub_name, kind, }) }) @@ -157,16 +157,16 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { fn validate_idents(meta: &syn::Meta, attr_idents: &[String], valid: &[&str]) -> Result { let invalid_meta_words: Vec<_> = attr_idents - .into_iter() + .iter() .filter(|w| !valid.iter().any(|v| v == w)) .cloned() .collect(); - if !invalid_meta_words.is_empty() { + if invalid_meta_words.is_empty() { + Ok(meta.clone()) + } else { let expected = format!("Expected '{}'", valid.join(", ")); let msg = format!("{} '{}'. {}", INVALID_ATTR_PARAM_NAMES_ERR, invalid_meta_words.join(", "), expected); Err(Error::new_spanned(meta, msg)) - } else { - Ok(meta.clone()) } } @@ -203,7 +203,7 @@ fn has_meta_word(word: &str, ml: &syn::MetaList) -> bool { .iter() .any(|nested| if let syn::NestedMeta::Meta(syn::Meta::Word(w)) = nested { - word == w.to_string() + w == word } else { false } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 1b68a5f9e..7f17aad8b 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -28,15 +28,14 @@ struct RpcTrait { impl<'a> Fold for RpcTrait { fn fold_trait_item_method(&mut self, method: syn::TraitItemMethod) -> syn::TraitItemMethod { - let mut method = method.clone(); - let method_item = method.clone(); + let mut foldable_method = method.clone(); // strip rpc attributes - method.attrs.retain(|a| { + foldable_method.attrs.retain(|a| { let rpc_method = - self.methods.iter().find(|m| m.trait_item == method_item); + self.methods.iter().find(|m| m.trait_item == method); rpc_method.map_or(true, |rpc| rpc.attr.attr != *a) }); - fold::fold_trait_item_method(self, method) + fold::fold_trait_item_method(self, foldable_method) } fn fold_trait_item_type(&mut self, ty: syn::TraitItemType) -> syn::TraitItemType { @@ -59,10 +58,10 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai .iter() .filter_map(|trait_item| { if let syn::TraitItem::Method(method) = trait_item { - match RpcMethodAttribute::parse_attr(&method) { + match RpcMethodAttribute::parse_attr(method) { Ok(Some(attr)) => Some(Ok(RpcMethod::new( - attr.clone(), + attr, method.clone(), ))), Ok(None) => None, // non rpc annotated trait method @@ -150,7 +149,7 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { pub fn rpc_impl(input: syn::Item) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, - item @ _ => return Err(syn::Error::new_spanned(item, "The #[rpc] custom attribute only works with trait declarations")), + item => return Err(syn::Error::new_spanned(item, "The #[rpc] custom attribute only works with trait declarations")), }; let (rpc_trait, has_pubsub_methods) = generate_rpc_item_trait(&rpc_trait)?; diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index bffad76cf..54bfcad8a 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -75,6 +75,8 @@ const SUBCRIBER_TYPE_IDENT: &str = "Subscriber"; const METADATA_CLOSURE_ARG: &str = "meta"; const SUBSCRIBER_CLOSURE_ARG: &str = "subscriber"; +const TUPLE_FIELD_NAMES: [&str; 26] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; + pub fn generate_trait_item_method( methods: &[MethodRegistration], trait_item: &syn::ItemTrait, @@ -169,9 +171,7 @@ impl RpcMethod { special_args.iter().find(|(_,sty)| sty == ty).is_none()); let tuple_fields : &Vec<_> = - &(0..param_types.len() as u8) - .map(|x| ident(&((x + 'a' as u8) as char).to_string())) - .collect(); + &(TUPLE_FIELD_NAMES.iter().take(param_types.len()).map(|name| ident(name)).collect()); let param_types = ¶m_types; let parse_params = { // last arguments that are `Option`-s are optional 'trailing' arguments @@ -235,7 +235,7 @@ impl RpcMethod { let meta_arg = param_types.first().and_then(|ty| if *ty == parse_quote!(Self::Metadata) { Some(ty.clone()) } else { None }); - let subscriber_arg = param_types.iter().nth(1).and_then(|ty| { + let subscriber_arg = param_types.get(1).and_then(|ty| { if let syn::Type::Path(path) = ty { if path.path.segments.iter().any(|s| s.ident == SUBCRIBER_TYPE_IDENT) { Some(ty.clone()) From d86d7a1c8d6c1d58ab15efb59358a5968e1d2090 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 25 Feb 2019 11:26:14 +0000 Subject: [PATCH 032/149] Error on too many params (#394) * Get rid of scary conversions in to_delegate.rs * Fix minor Clippy complaints * Update derive/src/to_delegate.rs Co-Authored-By: kirushik * error on too many params * Fix too-many-params.stderr * Add comment explaining 16 field limit --- derive/src/rpc_trait.rs | 2 +- derive/src/to_delegate.rs | 39 +++++++++++++++----------- derive/tests/ui/too-many-params.rs | 19 +++++++++++++ derive/tests/ui/too-many-params.stderr | 15 ++++++++++ 4 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 derive/tests/ui/too-many-params.rs create mode 100644 derive/tests/ui/too-many-params.stderr diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 7f17aad8b..630b54389 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -130,7 +130,7 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai } let to_delegate_method = - generate_trait_item_method(&method_registrations, &item_trait, rpc_trait.has_metadata, has_pubsub_methods); + generate_trait_item_method(&method_registrations, &item_trait, rpc_trait.has_metadata, has_pubsub_methods)?; item_trait.items.push(syn::TraitItem::Method(to_delegate_method)); let trait_bounds: Punctuated = diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 54bfcad8a..9dd31b9d4 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use quote::quote; use syn::{ parse_quote, Token, punctuated::Punctuated, - visit::{self, Visit}, + visit::{self, Visit}, Result, }; use crate::rpc_attr::RpcMethodAttribute; @@ -20,7 +20,7 @@ pub enum MethodRegistration { } impl MethodRegistration { - fn generate(&self) -> proc_macro2::TokenStream { + fn generate(&self) -> Result { match self { MethodRegistration::Standard { method, has_metadata } => { let rpc_name = &method.name(); @@ -30,17 +30,17 @@ impl MethodRegistration { } else { quote!(add_method) }; - let closure = method.generate_delegate_closure(false); + let closure = method.generate_delegate_closure(false)?; let add_aliases = method.generate_add_aliases(); - quote! { + Ok(quote! { del.#add_method(#rpc_name, #closure); #add_aliases - } + }) }, MethodRegistration::PubSub { name, subscribe, unsubscribe } => { let sub_name = subscribe.name(); - let sub_closure = subscribe.generate_delegate_closure(true); + let sub_closure = subscribe.generate_delegate_closure(true)?; let sub_aliases = subscribe.generate_add_aliases(); let unsub_name = unsubscribe.name(); @@ -57,7 +57,7 @@ impl MethodRegistration { }; let unsub_aliases = unsubscribe.generate_add_aliases(); - quote! { + Ok(quote! { del.add_subscription( #name, (#sub_name, #sub_closure), @@ -65,7 +65,7 @@ impl MethodRegistration { ); #sub_aliases #unsub_aliases - } + }) }, } } @@ -75,24 +75,25 @@ const SUBCRIBER_TYPE_IDENT: &str = "Subscriber"; const METADATA_CLOSURE_ARG: &str = "meta"; const SUBSCRIBER_CLOSURE_ARG: &str = "subscriber"; -const TUPLE_FIELD_NAMES: [&str; 26] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; +// tuples are limited to 16 fields: the maximum supported by `serde::Deserialize` +const TUPLE_FIELD_NAMES: [&str; 16] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"]; pub fn generate_trait_item_method( methods: &[MethodRegistration], trait_item: &syn::ItemTrait, has_metadata: bool, has_pubsub_methods: bool, -) -> syn::TraitItemMethod { +) -> Result { let io_delegate_type = if has_pubsub_methods { quote!(_jsonrpc_pubsub::IoDelegate) } else { quote!(_jsonrpc_core::IoDelegate) }; - let add_methods: Vec<_> = methods + let add_methods = methods .iter() .map(MethodRegistration::generate) - .collect(); + .collect::>>()?; let to_delegate_body = quote! { let mut del = #io_delegate_type::new(self.into()); @@ -123,7 +124,7 @@ pub fn generate_trait_item_method( .make_where_clause() .predicates .extend(predicates); - method + Ok(method) } #[derive(Clone)] @@ -151,7 +152,7 @@ impl RpcMethod { self.attr.is_pubsub() } - fn generate_delegate_closure(&self, is_subscribe: bool) -> proc_macro2::TokenStream { + fn generate_delegate_closure(&self, is_subscribe: bool) -> Result { let mut param_types: Vec<_> = self.trait_item.sig.decl.inputs .iter() @@ -170,6 +171,12 @@ impl RpcMethod { param_types.retain(|ty| special_args.iter().find(|(_,sty)| sty == ty).is_none()); + if param_types.len() > TUPLE_FIELD_NAMES.len() { + return Err(syn::Error::new_spanned( + &self.trait_item, + &format!("Maximum supported number of params is {}", TUPLE_FIELD_NAMES.len()) + )); + } let tuple_fields : &Vec<_> = &(TUPLE_FIELD_NAMES.iter().take(param_types.len()).map(|name| ident(name)).collect()); let param_types = ¶m_types; @@ -220,7 +227,7 @@ impl RpcMethod { } }; - quote! { + Ok(quote! { move |#closure_args| { let method = &(Self::#method_ident as #method_sig); #parse_params @@ -228,7 +235,7 @@ impl RpcMethod { #match_params } } - } + }) } fn special_args(param_types: &[syn::Type]) -> Vec<(syn::Ident, syn::Type)> { diff --git a/derive/tests/ui/too-many-params.rs b/derive/tests/ui/too-many-params.rs new file mode 100644 index 000000000..31ec98b14 --- /dev/null +++ b/derive/tests/ui/too-many-params.rs @@ -0,0 +1,19 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::Result; + +#[rpc] +pub trait Rpc { + /// Has too many params + #[rpc(name = "tooManyParams")] + fn to_many_params( + &self, + _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, + _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, + ) -> Result; +} + +fn main() {} \ No newline at end of file diff --git a/derive/tests/ui/too-many-params.stderr b/derive/tests/ui/too-many-params.stderr new file mode 100644 index 000000000..f12ae8470 --- /dev/null +++ b/derive/tests/ui/too-many-params.stderr @@ -0,0 +1,15 @@ +error: Maximum supported number of params is 16 + --> $DIR/too-many-params.rs:10:2 + | +10 | /// Has too many params + | _____^ +11 | | #[rpc(name = "tooManyParams")] +12 | | fn to_many_params( +13 | | &self, +14 | | _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, +15 | | _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, +16 | | ) -> Result; + | |________________________^ + +error: aborting due to previous error + From 5bab07d48bc1b181e5bb4f74b6f145ac480f3e66 Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Mon, 25 Feb 2019 17:49:23 +0300 Subject: [PATCH 033/149] Fix other clippy warning (#395) * fix missing docs attribute * use shorthand syntax * fix some other clippy warnings --- core/src/delegates.rs | 2 +- core/src/io.rs | 16 ++++++++-------- core/src/types/error.rs | 2 +- core/src/types/response.rs | 20 ++++++++++---------- core/src/types/version.rs | 4 ++-- derive/src/rpc_attr.rs | 2 +- derive/src/to_delegate.rs | 2 +- http/src/handler.rs | 12 ++++++------ http/src/lib.rs | 6 +++--- http/src/response.rs | 2 +- http/src/tests.rs | 6 +++--- http/src/utils.rs | 6 +++--- ipc/src/server.rs | 9 ++++----- macros/src/auto_args.rs | 6 ++---- macros/src/delegates.rs | 2 +- macros/src/pubsub.rs | 6 +++--- pubsub/src/handler.rs | 2 +- pubsub/src/subscription.rs | 4 ++-- pubsub/src/typed.rs | 6 +++--- server-utils/src/cors.rs | 10 +++++----- server-utils/src/hosts.rs | 8 ++++---- server-utils/src/stream_codec.rs | 4 ++-- tcp/src/dispatch.rs | 8 ++++---- tcp/src/server.rs | 8 ++++---- tcp/src/service.rs | 2 +- test/src/lib.rs | 5 ++++- ws/src/metadata.rs | 4 ++-- ws/src/server.rs | 4 ++-- ws/src/session.rs | 22 +++++++++++----------- ws/src/tests.rs | 4 ++-- 30 files changed, 97 insertions(+), 97 deletions(-) diff --git a/core/src/delegates.rs b/core/src/delegates.rs index 2fa13f4af..b21c7dec7 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -79,7 +79,7 @@ impl IoDelegate where /// Creates new `IoDelegate` pub fn new(delegate: Arc) -> Self { IoDelegate { - delegate: delegate, + delegate, methods: HashMap::new(), } } diff --git a/core/src/io.rs b/core/src/io.rs index cc163d631..89ca37571 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -64,8 +64,8 @@ impl Default for Compatibility { } impl Compatibility { - fn is_version_valid(&self, version: Option) -> bool { - match (*self, version) { + fn is_version_valid(self, version: Option) -> bool { + match (self, version) { (Compatibility::V1, None) | (Compatibility::V2, Some(Version::V2)) | (Compatibility::Both, _) => true, @@ -73,8 +73,8 @@ impl Compatibility { } } - fn default_version(&self) -> Option { - match *self { + fn default_version(self) -> Option { + match self { Compatibility::V1 => None, Compatibility::V2 | Compatibility::Both => Some(Version::V2), } @@ -101,7 +101,7 @@ impl MetaIoHandler { /// Creates new `MetaIoHandler` compatible with specified protocol version. pub fn with_compatibility(compatibility: Compatibility) -> Self { MetaIoHandler { - compatibility: compatibility, + compatibility, middleware: Default::default(), methods: Default::default(), } @@ -113,8 +113,8 @@ impl> MetaIoHandler { /// Creates new `MetaIoHandler` pub fn new(compatibility: Compatibility, middleware: S) -> Self { MetaIoHandler { - compatibility: compatibility, - middleware: middleware, + compatibility, + middleware, methods: Default::default(), } } @@ -123,7 +123,7 @@ impl> MetaIoHandler { pub fn with_middleware(middleware: S) -> Self { MetaIoHandler { compatibility: Default::default(), - middleware: middleware, + middleware, methods: Default::default(), } } diff --git a/core/src/types/error.rs b/core/src/types/error.rs index d3a7e088c..dea0866f6 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -94,7 +94,7 @@ impl Error { pub fn new(code: ErrorCode) -> Self { Error { message: code.description(), - code: code, + code, data: None } } diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 95e13520d..60bd80152 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -41,14 +41,14 @@ impl Output { pub fn from(result: CoreResult, id: Id, jsonrpc: Option) -> Self { match result { Ok(result) => Output::Success(Success { - id: id, - jsonrpc: jsonrpc, - result: result, + id, + jsonrpc, + result, }), Err(error) => Output::Failure(Failure { - id: id, - jsonrpc: jsonrpc, - error: error, + id, + jsonrpc, + error, }), } } @@ -56,8 +56,8 @@ impl Output { /// Creates new failure output indicating malformed request. pub fn invalid_request(id: Id, jsonrpc: Option) -> Self { Output::Failure(Failure { - id: id, - jsonrpc: jsonrpc, + id, + jsonrpc, error: Error::new(ErrorCode::InvalidRequest), }) } @@ -104,8 +104,8 @@ impl Response { pub fn from(error: Error, jsonrpc: Option) -> Self { Failure { id: Id::Null, - jsonrpc: jsonrpc, - error: error, + jsonrpc, + error, }.into() } } diff --git a/core/src/types/version.rs b/core/src/types/version.rs index a3e42fdb0..c5254949b 100644 --- a/core/src/types/version.rs +++ b/core/src/types/version.rs @@ -14,8 +14,8 @@ pub enum Version { impl Serialize for Version { fn serialize(&self, serializer: S) -> Result where S: Serializer { - match self { - &Version::V2 => serializer.serialize_str("2.0") + match *self { + Version::V2 => serializer.serialize_str("2.0") } } } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 3f372fe7e..09286e049 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -73,7 +73,7 @@ impl RpcMethodAttribute { .map_or(Vec::new(), |ml| get_aliases(ml)); Ok(RpcMethodAttribute { attr: attr.clone(), - name: name, + name, aliases, kind }) diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 9dd31b9d4..6cc08aff5 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -273,7 +273,7 @@ impl RpcMethod { let total_args_num = param_types.len(); let required_args_num = total_args_num - trailing_args_num; - let switch_branches = (0..trailing_args_num+1) + let switch_branches = (0..=trailing_args_num) .map(|passed_trailing_args_num| { let passed_args_num = required_args_num + passed_trailing_args_num; let passed_param_types = ¶m_types[..passed_args_num]; diff --git a/http/src/handler.rs b/http/src/handler.rs index 5972b6dc2..8db5f4f2b 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -260,7 +260,7 @@ impl> Future for RpcHandler { }, RpcHandlerState::WaitingForResponse(mut waiting) => { match waiting.poll() { - Ok(Async::Ready(response)) => RpcPollState::Ready(RpcHandlerState::Writing(response.into())), + Ok(Async::Ready(response)) => RpcPollState::Ready(RpcHandlerState::Writing(response)), Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::WaitingForResponse(waiting)), Err(e) => RpcPollState::Ready(RpcHandlerState::Writing( Response::internal_error(format!("{:?}", e)) @@ -275,7 +275,7 @@ impl> Future for RpcHandler { None => Response::ok(String::new()), // Add new line to have nice output when using CLI clients (curl) Some(result) => Response::ok(format!("{}\n", result)), - }.into())) + })) }, Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::Waiting(waiting)), Err(e) => RpcPollState::Ready(RpcHandlerState::Writing( @@ -404,7 +404,7 @@ impl> RpcHandler { id: Id::Num(1), })); - return Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse( + Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse( future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)) .map(|res| match res { Some(core::Response::Single(Output::Success(Success { result, .. }))) => { @@ -421,7 +421,7 @@ impl> RpcHandler { }, e => Response::internal_error(format!("Invalid response for health request: {:?}", e)), }) - ))); + ))) } fn process_rest( @@ -452,12 +452,12 @@ impl> RpcHandler { id: Id::Num(1), })); - return Ok(RpcPollState::Ready(RpcHandlerState::Waiting( + Ok(RpcPollState::Ready(RpcHandlerState::Waiting( future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)) .map(|res| res.map(|x| serde_json::to_string(&x) .expect("Serialization of response is infallible;qed") )) - ))); + ))) } fn process_body( diff --git a/http/src/lib.rs b/http/src/lib.rs index cc705f8b7..e73fb45a2 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -321,7 +321,7 @@ impl> ServerBuilder { /// Configure the CORS `AccessControlAllowHeaders` header which are allowed. pub fn cors_allow_headers(mut self, allowed_headers: cors::AccessControlAllowHeaders) -> Self { - self.allowed_headers = allowed_headers.into(); + self.allowed_headers = allowed_headers; self } @@ -583,8 +583,8 @@ impl Server { impl Drop for Server { fn drop(&mut self) { - self.executor.take().map(|executors| { + if let Some(executors) = self.executor.take() { for executor in executors { executor.close(); } - }); + }; } } diff --git a/http/src/response.rs b/http/src/response.rs index 50814b68a..7d94a908d 100644 --- a/http/src/response.rs +++ b/http/src/response.rs @@ -42,7 +42,7 @@ impl Response { Response { code: StatusCode::SERVICE_UNAVAILABLE, content_type: HeaderValue::from_static("application/json; charset=utf-8"), - content: format!("{}", msg.into()), + content: msg.into(), } } diff --git a/http/src/tests.rs b/http/src/tests.rs index 72b7eb263..0fd325722 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -113,9 +113,9 @@ fn request(server: Server, request: &str) -> Response { let body = read_block(&mut lines); Response { - status: status, - headers: headers, - body: body, + status, + headers, + body, } } diff --git a/http/src/utils.rs b/http/src/utils.rs index aaa417d6a..5d3214747 100644 --- a/http/src/utils.rs +++ b/http/src/utils.rs @@ -23,7 +23,7 @@ pub fn cors_allow_origin( cors::get_cors_allow_origin(read_header(request, "origin"), read_header(request, "host"), cors_domains).map(|origin| { use self::cors::AccessControlAllowOrigin::*; match origin { - Value(ref val) => header::HeaderValue::from_str(val).unwrap_or(header::HeaderValue::from_static("null")), + Value(ref val) => header::HeaderValue::from_str(val).unwrap_or_else(|_| header::HeaderValue::from_static("null")), Null => header::HeaderValue::from_static("null"), Any => header::HeaderValue::from_static("*"), } @@ -42,12 +42,12 @@ pub fn cors_allow_headers( .iter() .filter_map(|val| val.to_str().ok()) .flat_map(|val| val.split(", ")) - .flat_map(|val| val.split(",")); + .flat_map(|val| val.split(',')); cors::get_cors_allow_headers( headers, requested_headers, - cors_allow_headers.into(), + cors_allow_headers, |name| header::HeaderValue::from_str(name) .unwrap_or_else(|_| header::HeaderValue::from_static("unknown")) ) diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 69443886d..6128e525f 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -29,7 +29,7 @@ pub struct Service = middleware::Noop> { impl> Service { /// Create new IPC server session with given handler and metadata. pub fn new(handler: Arc>, meta: M) -> Self { - Service { handler: handler, meta: meta } + Service { handler, meta } } } @@ -167,7 +167,7 @@ impl> ServerBuilder { let session_id = id; let session_stats = session_stats.clone(); trace!(target: "ipc", "Accepted incoming IPC connection: {}", session_id); - session_stats.as_ref().map(|stats| stats.open_session(session_id)); + if let Some(stats) = session_stats.as_ref() { stats.open_session(session_id) } let (sender, receiver) = mpsc::channel(16); let meta = meta_extractor.extract(&RequestContext { @@ -207,7 +207,7 @@ impl> ServerBuilder { let writer = writer.send_all(responses).then(move |_| { trace!(target: "ipc", "Peer: service finished"); - session_stats.as_ref().map(|stats| stats.close_session(session_id)); + if let Some(stats) = session_stats.as_ref() { stats.close_session(session_id) } Ok(()) }); @@ -222,7 +222,6 @@ impl> ServerBuilder { server.select(stop) .map(|_| { let _ = wait_signal.send(()); - () }) .map_err(|_| ()) ) @@ -283,7 +282,7 @@ struct InnerHandles { impl InnerHandles { pub fn close(&mut self) { let _ = self.stop.take().map(|stop| stop.send(())); - self.executor.take().map(|executor| executor.close()); + if let Some(executor) = self.executor.take() { executor.close() } let _ = ::std::fs::remove_file(&self.path); // ignore error, file could have been gone somewhere } } diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index 90e2abd15..f0a21352f 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -515,13 +515,11 @@ fn require_len(params: &Params, required: usize) -> Result { fn parse_trailing_param(params: Params) -> Result<(Option, )> { let len = try!(params_len(¶ms)); - let id = match len { + match len { 0 => Ok((None,)), 1 => params.parse::<(T,)>().map(|(x, )| (Some(x), )), _ => Err(invalid_params("Expecting only one optional parameter.", "")), - }; - - id + } } // special impl for no parameters other than block parameter. diff --git a/macros/src/delegates.rs b/macros/src/delegates.rs index 10b2608fb..2d95efe84 100644 --- a/macros/src/delegates.rs +++ b/macros/src/delegates.rs @@ -115,7 +115,7 @@ impl IoDelegate where /// Creates new `IoDelegate` pub fn new(delegate: Arc) -> Self { IoDelegate { - delegate: delegate, + delegate, methods: HashMap::new(), } } diff --git a/macros/src/pubsub.rs b/macros/src/pubsub.rs index 5007feaab..8ee75824c 100644 --- a/macros/src/pubsub.rs +++ b/macros/src/pubsub.rs @@ -22,7 +22,7 @@ impl Subscriber { /// Wrap non-typed subscriber. pub fn new(subscriber: pubsub::Subscriber) -> Self { Subscriber { - subscriber: subscriber, + subscriber, _data: PhantomData, } } @@ -48,8 +48,8 @@ impl Subscriber { pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { let sink = self.subscriber.assign_id(id.clone())?; Ok(Sink { - id: id, - sink: sink, + id, + sink, buffered: None, _data: PhantomData, }) diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index a2ca608cc..fb4a134f7 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -57,7 +57,7 @@ impl> PubSubHandler { /// Creates new `PubSubHandler` pub fn new(handler: core::MetaIoHandler) -> Self { PubSubHandler { - handler: handler, + handler, } } diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 97ba7a8de..f31299300 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -397,7 +397,7 @@ mod tests { let (tx, mut rx) = oneshot::channel(); let subscriber = Subscriber { notification: "test".into(), - transport: transport, + transport, sender: tx, }; @@ -419,7 +419,7 @@ mod tests { let (tx, mut rx) = oneshot::channel(); let subscriber = Subscriber { notification: "test".into(), - transport: transport, + transport, sender: tx, }; let error = core::Error { diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs index 99b9b9151..9142733c3 100644 --- a/pubsub/src/typed.rs +++ b/pubsub/src/typed.rs @@ -20,7 +20,7 @@ impl Subscriber { /// Wrap non-typed subscriber. pub fn new(subscriber: subscription::Subscriber) -> Self { Subscriber { - subscriber: subscriber, + subscriber, _data: PhantomData, } } @@ -46,8 +46,8 @@ impl Subscriber { pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { let sink = self.subscriber.assign_id(id.clone())?; Ok(Sink { - id: id, - sink: sink, + id, + sink, buffered: None, _data: PhantomData, }) diff --git a/server-utils/src/cors.rs b/server-utils/src/cors.rs index a0e080206..5162b6380 100644 --- a/server-utils/src/cors.rs +++ b/server-utils/src/cors.rs @@ -39,10 +39,10 @@ impl Origin { let matcher = Matcher::new(&string); Origin { - protocol: protocol, - host: host, + protocol, + host, as_string: string, - matcher: matcher, + matcher, } } @@ -239,7 +239,7 @@ pub fn get_cors_allow_headers, O, F: Fn(T) -> O>( let are_all_allowed = headers .all(|header| { let name = &Ascii::new(header.as_ref()); - only.iter().any(|h| &Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name) + only.iter().any(|h| Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name) }); if !are_all_allowed { @@ -259,7 +259,7 @@ pub fn get_cors_allow_headers, O, F: Fn(T) -> O>( .filter(|header| { let name = &Ascii::new(header.as_ref()); filtered = true; - only.iter().any(|h| &Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name) + only.iter().any(|h| Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name) }) .map(to_result) .collect(); diff --git a/server-utils/src/hosts.rs b/server-utils/src/hosts.rs index 52dbe1ab9..c02efcb6e 100644 --- a/server-utils/src/hosts.rs +++ b/server-utils/src/hosts.rs @@ -56,10 +56,10 @@ impl Host { let matcher = Matcher::new(&string); Host { - hostname: hostname, - port: port, + hostname, + port, as_string: string, - matcher: matcher, + matcher, } } @@ -71,7 +71,7 @@ impl Host { let host = hostname.next().expect(SPLIT_PROOF); let port = match hostname.next() { None => Port::None, - Some(port) => match port.clone().parse::().ok() { + Some(port) => match port.parse::().ok() { Some(num) => Port::Fixed(num), None => Port::Pattern(port.into()), } diff --git a/server-utils/src/stream_codec.rs b/server-utils/src/stream_codec.rs index 9a20a7311..cc18f372d 100644 --- a/server-utils/src/stream_codec.rs +++ b/server-utils/src/stream_codec.rs @@ -35,8 +35,8 @@ impl StreamCodec { /// New custom stream codec pub fn new(incoming_separator: Separator, outgoing_separator: Separator) -> Self { StreamCodec { - incoming_separator: incoming_separator, - outgoing_separator: outgoing_separator, + incoming_separator, + outgoing_separator, } } } diff --git a/tcp/src/dispatch.rs b/tcp/src/dispatch.rs index 50b50c303..f7c0e4050 100644 --- a/tcp/src/dispatch.rs +++ b/tcp/src/dispatch.rs @@ -24,7 +24,7 @@ impl PeerMessageQueue { ) -> Self { PeerMessageQueue { up: response_stream, - receiver: receiver, + receiver, _addr: addr, } } @@ -55,7 +55,7 @@ impl Dispatcher { /// Creates a new dispatcher pub fn new(channels: Arc) -> Self { Dispatcher { - channels: channels, + channels, } } @@ -66,11 +66,11 @@ impl Dispatcher { match channels.get_mut(peer_addr) { Some(channel) => { // todo: maybe async here later? - channel.send(msg).wait().map_err(|e| PushMessageError::from(e))?; + channel.send(msg).wait().map_err(PushMessageError::from)?; Ok(()) }, None => { - return Err(PushMessageError::NoSuchPeer); + Err(PushMessageError::NoSuchPeer) } } } diff --git a/tcp/src/server.rs b/tcp/src/server.rs index fcb2c74c0..c094ff0eb 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -95,7 +95,7 @@ impl + 'static> ServerBuilder { let (sender, receiver) = mpsc::channel(65536); let context = RequestContext { - peer_addr: peer_addr, + peer_addr, sender: sender.clone(), }; @@ -128,12 +128,12 @@ impl + 'static> ServerBuilder { let peer_message_queue = { let mut channels = channels.lock(); - channels.insert(peer_addr.clone(), sender.clone()); + channels.insert(peer_addr, sender.clone()); PeerMessageQueue::new( responses, receiver, - peer_addr.clone(), + peer_addr, ) }; @@ -209,6 +209,6 @@ impl Server { impl Drop for Server { fn drop(&mut self) { let _ = self.stop.take().map(|sg| sg.send(())); - self.executor.take().map(|executor| executor.close()); + if let Some(executor) = self.executor.take() { executor.close() } } } diff --git a/tcp/src/service.rs b/tcp/src/service.rs index 1c8cd7336..d20e6b5c1 100644 --- a/tcp/src/service.rs +++ b/tcp/src/service.rs @@ -13,7 +13,7 @@ pub struct Service = middleware::Noop> { impl> Service { pub fn new(peer_addr: SocketAddr, handler: Arc>, meta: M) -> Self { - Service { peer_addr: peer_addr, handler: handler, meta: meta } + Service { peer_addr, handler, meta } } } diff --git a/test/src/lib.rs b/test/src/lib.rs index 5015c07b7..453306a5b 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -43,7 +43,7 @@ //! } //! ``` -#[warn(missing_docs)] +#![warn(missing_docs)] extern crate jsonrpc_core as rpc; use serde; @@ -67,8 +67,11 @@ pub struct Rpc { pub options: Options, } +/// Encoding format. pub enum Encoding { + /// Encodes params using `serde::to_string`. Compact, + /// Encodes params using `serde::to_string_pretty`. Pretty, } diff --git a/ws/src/metadata.rs b/ws/src/metadata.rs index 13dc750dd..8a184cd97 100644 --- a/ws/src/metadata.rs +++ b/ws/src/metadata.rs @@ -20,8 +20,8 @@ impl Sender { /// Creates a new `Sender`. pub fn new(out: ws::Sender, active: Arc) -> Self { Sender { - out: out, - active: active, + out, + active, } } diff --git a/ws/src/server.rs b/ws/src/server.rs index 3fdeb93bb..1ee89f58f 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -103,7 +103,7 @@ impl Server { addr: local_addr, handle: Some(handle), executor: Arc::new(Mutex::new(Some(eloop))), - broadcaster: broadcaster, + broadcaster, }) } } @@ -148,6 +148,6 @@ impl CloseHandle { /// Closes the `Server`. pub fn close(self) { let _ = self.broadcaster.shutdown(); - self.executor.lock().unwrap().take().map(|executor| executor.close()); + if let Some(executor) = self.executor.lock().unwrap().take() { executor.close() } } } diff --git a/ws/src/session.rs b/ws/src/session.rs index f3303cb52..a5c28d9e9 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -109,7 +109,7 @@ impl LivenessPoll { (index, rx) }; - LivenessPoll { task_slab: task_slab, slab_handle: index, rx: rx } + LivenessPoll { task_slab, slab_handle: index, rx } } } @@ -152,7 +152,7 @@ pub struct Session> { impl> Drop for Session { fn drop(&mut self) { self.active.store(false, atomic::Ordering::SeqCst); - self.stats.as_ref().map(|stats| stats.close_session(self.context.session_id)); + if let Some(stats) = self.stats.as_ref() { stats.close_session(self.context.session_id) } // signal to all still-live tasks that the session has been dropped. for (_index, task) in self.task_slab.lock().iter_mut() { @@ -297,13 +297,13 @@ impl> Factory { ) -> Self { Factory { session_id: 0, - handler: handler, - meta_extractor: meta_extractor, - allowed_origins: allowed_origins, - allowed_hosts: allowed_hosts, - request_middleware: request_middleware, - stats: stats, - executor: executor, + handler, + meta_extractor, + allowed_origins, + allowed_hosts, + request_middleware, + stats, + executor, } } } @@ -313,7 +313,7 @@ impl> ws::Factory for Factory { fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { self.session_id += 1; - self.stats.as_ref().map(|stats| stats.open_session(self.session_id)); + if let Some(executor) = self.stats.as_ref() { executor.open_session(self.session_id) } let active = Arc::new(atomic::AtomicBool::new(true)); Session { @@ -367,7 +367,7 @@ fn forbidden(title: &str, message: &str) -> ws::Response { let mut forbidden = ws::Response::new(403, "Forbidden", format!("{}\n{}\n", title, message).into_bytes()); { let headers = forbidden.headers_mut(); - headers.push(("Connection".to_owned(), "close".as_bytes().to_vec())); + headers.push(("Connection".to_owned(), b"close".to_vec())); } forbidden } diff --git a/ws/src/tests.rs b/ws/src/tests.rs index e92874efb..6195f6552 100644 --- a/ws/src/tests.rs +++ b/ws/src/tests.rs @@ -28,9 +28,9 @@ impl Response { let body = Self::read_block(&mut lines); Response { - status: status, + status, _headers: headers, - body: body, + body, } } From 1296a127293a598e56988543af88d57dccd1fffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 27 Feb 2019 13:16:32 +0100 Subject: [PATCH 034/149] Switch back to upstream ws (#398) --- ws/Cargo.toml | 2 +- ws/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ws/Cargo.toml b/ws/Cargo.toml index f38d22324..5b6e7b8f7 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -16,7 +16,7 @@ jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } log = "0.4" parking_lot = "0.7" slab = "0.4" -parity-ws = "0.8" +ws = "0.8" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ws/src/lib.rs b/ws/src/lib.rs index 733feb81c..1f34c0d48 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -4,7 +4,7 @@ use jsonrpc_server_utils as server_utils; -pub use parity_ws as ws; +pub use ws; pub use jsonrpc_core; #[macro_use] From ac197bde308f1915170e76f2ae7265140676bab6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 1 Mar 2019 09:45:53 +0000 Subject: [PATCH 035/149] ws: replace error-chain with vanilla Error impl (#399) * ws: replace error-chain with vanilla Error impl * ws: document Error --- ws/Cargo.toml | 1 - ws/src/error.rs | 50 ++++++++++++++++++++++++++++++++++------------ ws/src/lib.rs | 4 +--- ws/src/metadata.rs | 2 +- ws/src/session.rs | 2 +- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 5b6e7b8f7..3c6887455 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -10,7 +10,6 @@ repository = "https://github.com/paritytech/jsonrpc" version = "10.1.0" [dependencies] -error-chain = "0.12" jsonrpc-core = { version = "10.1", path = "../core" } jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } log = "0.4" diff --git a/ws/src/error.rs b/ws/src/error.rs index c1b0ceea1..3b01a7b24 100644 --- a/ws/src/error.rs +++ b/ws/src/error.rs @@ -1,28 +1,52 @@ -#![allow(missing_docs)] - -use std::io; +use std::{io, error, fmt, result}; use crate::ws; -error_chain! { - foreign_links { - Io(io::Error); +/// WebSockets Server Error +#[derive(Debug)] +pub enum Error { + /// Io Error + Io(io::Error), + /// WebSockets Error + WsError(ws::Error), + /// Connection Closed + ConnectionClosed, +} + +/// WebSockets Server Result +pub type Result = result::Result; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + match self { + Error::ConnectionClosed => write!(f, "Action on closed connection."), + Error::WsError(err) => write!(f, "WebSockets Error: {}", err), + Error::Io(err) => write!(f, "Io Error: {}", err), + } } +} - errors { - /// Attempted action on closed connection. - ConnectionClosed { - description("connection is closed"), - display("Action on closed connection."), +impl error::Error for Error { + fn source(&self) -> Option<&(error::Error + 'static)> { + match self { + Error::Io(io) => Some(io), + Error::WsError(ws) => Some(ws), + Error::ConnectionClosed => None, } } } +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} + impl From for Error { fn from(err: ws::Error) -> Self { match err.kind { - ws::ErrorKind::Io(e) => e.into(), - _ => Error::with_chain(err, "WebSockets Error"), + ws::ErrorKind::Io(err) => Error::Io(err), + _ => Error::WsError(err), } } } diff --git a/ws/src/lib.rs b/ws/src/lib.rs index 1f34c0d48..7eb64b09d 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -7,8 +7,6 @@ use jsonrpc_server_utils as server_utils; pub use ws; pub use jsonrpc_core; -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; @@ -22,7 +20,7 @@ mod tests; use jsonrpc_core as core; -pub use self::error::{Error, ErrorKind, Result}; +pub use self::error::{Error, Result}; pub use self::metadata::{RequestContext, MetaExtractor, NoopExtractor}; pub use self::session::{RequestMiddleware, MiddlewareAction}; pub use self::server::{CloseHandle, Server}; diff --git a/ws/src/metadata.rs b/ws/src/metadata.rs index 8a184cd97..f9c27e503 100644 --- a/ws/src/metadata.rs +++ b/ws/src/metadata.rs @@ -29,7 +29,7 @@ impl Sender { if self.active.load(atomic::Ordering::SeqCst) { Ok(()) } else { - bail!(error::ErrorKind::ConnectionClosed) + Err(error::Error::ConnectionClosed) } } diff --git a/ws/src/session.rs b/ws/src/session.rs index a5c28d9e9..7e3198a3a 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -254,7 +254,7 @@ impl> ws::Handler for Session { if let Some(result) = response { let res = out.send(result); match res { - Err(error::Error(error::ErrorKind::ConnectionClosed, _)) => { + Err(error::Error::ConnectionClosed) => { active_lock.store(false, atomic::Ordering::SeqCst); }, Err(e) => { From e8913345b022e9ab9f6630225c092113827c3a65 Mon Sep 17 00:00:00 2001 From: "Kyle M. Douglass" Date: Fri, 1 Mar 2019 10:48:18 +0100 Subject: [PATCH 036/149] Update example in IPC README (#400) --- ipc/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ipc/README.md b/ipc/README.md index b003fd74d..c0f4c7af6 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -15,7 +15,9 @@ jsonrpc-ipc-server = "10.0" `main.rs` ```rust -use jsonrpc_ipc_server::Server; +extern crate jsonrpc_ipc_server; + +use jsonrpc_ipc_server::ServerBuilder; use jsonrpc_ipc_server::jsonrpc_core::*; fn main() { @@ -24,8 +26,9 @@ fn main() { Ok(Value::String("hello".into())) }); - let server = Server::new("/tmp/json-ipc-test.ipc", io).unwrap(); - ::std::thread::spawn(move || server.run()); + let builder = ServerBuilder::new(io); + let server = builder.start("/tmp/json-ipc-test.ipc").expect("Couldn't open socket"); + server.wait(); } ``` From 4933feb45707ca535632eaf38c8e4c2dad165d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 12 Mar 2019 10:52:12 +0100 Subject: [PATCH 037/149] Add asynchronous methods to assign_id or reject in pubsub. (#401) * Add asynchronous methods to assign_id or reject in pubsub. * Make the channel impl more clear. * Fix typos. --- ...ait-bounds.rs => generic-trait-bounds2.rs} | 0 .../{generic-trait.rs => generic-trait2.rs} | 0 .../{meta-macros.rs => meta-macros2.rs} | 0 .../{pubsub-macros.rs => pubsub-macros2.rs} | 0 macros/examples/{std.rs => std2.rs} | 0 macros/src/auto_args.rs | 1 + macros/src/pubsub.rs | 37 +++++-- pubsub/examples/pubsub.rs | 7 +- pubsub/examples/pubsub_simple.rs | 7 +- pubsub/more-examples/examples/pubsub_ipc.rs | 7 +- pubsub/more-examples/examples/pubsub_ws.rs | 7 +- pubsub/src/lib.rs | 1 + pubsub/src/oneshot.rs | 99 +++++++++++++++++++ pubsub/src/subscription.rs | 62 ++++++++---- pubsub/src/typed.rs | 38 +++++-- 15 files changed, 219 insertions(+), 47 deletions(-) rename macros/examples/{generic-trait-bounds.rs => generic-trait-bounds2.rs} (100%) rename macros/examples/{generic-trait.rs => generic-trait2.rs} (100%) rename macros/examples/{meta-macros.rs => meta-macros2.rs} (100%) rename macros/examples/{pubsub-macros.rs => pubsub-macros2.rs} (100%) rename macros/examples/{std.rs => std2.rs} (100%) create mode 100644 pubsub/src/oneshot.rs diff --git a/macros/examples/generic-trait-bounds.rs b/macros/examples/generic-trait-bounds2.rs similarity index 100% rename from macros/examples/generic-trait-bounds.rs rename to macros/examples/generic-trait-bounds2.rs diff --git a/macros/examples/generic-trait.rs b/macros/examples/generic-trait2.rs similarity index 100% rename from macros/examples/generic-trait.rs rename to macros/examples/generic-trait2.rs diff --git a/macros/examples/meta-macros.rs b/macros/examples/meta-macros2.rs similarity index 100% rename from macros/examples/meta-macros.rs rename to macros/examples/meta-macros2.rs diff --git a/macros/examples/pubsub-macros.rs b/macros/examples/pubsub-macros2.rs similarity index 100% rename from macros/examples/pubsub-macros.rs rename to macros/examples/pubsub-macros2.rs diff --git a/macros/examples/std.rs b/macros/examples/std2.rs similarity index 100% rename from macros/examples/std.rs rename to macros/examples/std2.rs diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs index f0a21352f..2fa2f4eb3 100644 --- a/macros/src/auto_args.rs +++ b/macros/src/auto_args.rs @@ -70,6 +70,7 @@ macro_rules! metadata { }; } +/// Build an RPC trait definition. #[macro_export] macro_rules! build_rpc_trait { ( diff --git a/macros/src/pubsub.rs b/macros/src/pubsub.rs index 8ee75824c..54fb84ef2 100644 --- a/macros/src/pubsub.rs +++ b/macros/src/pubsub.rs @@ -7,7 +7,7 @@ use jsonrpc_pubsub as pubsub; use serde; use util::to_value; -use self::core::futures::{self, Sink as FuturesSink, sync}; +use self::core::futures::{self, Future, Sink as FuturesSink, sync}; pub use self::pubsub::SubscriptionId; @@ -30,7 +30,7 @@ impl Subscriber { /// Create new subscriber for tests. pub fn new_test>(method: M) -> ( Self, - sync::oneshot::Receiver>, + pubsub::oneshot::Receiver>, sync::mpsc::Receiver, ) { let (subscriber, id, subscription) = pubsub::Subscriber::new_test(method); @@ -42,18 +42,37 @@ impl Subscriber { self.subscriber.reject(error) } + /// Reject subscription with given error. + pub fn reject_async(self, error: core::Error) -> impl Future { + self.subscriber.reject_async(error) + } + /// Assign id to this subscriber. /// This method consumes `Subscriber` and returns `Sink` /// if the connection is still open or error otherwise. pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { - let sink = self.subscriber.assign_id(id.clone())?; - Ok(Sink { - id, - sink, - buffered: None, - _data: PhantomData, - }) + self.subscriber.assign_id(id.clone()) + .map(|sink| Sink { + id, + sink, + buffered: None, + _data: PhantomData, + }) + } + + /// Assign id to this subscriber. + /// This method consumes `Subscriber` and returns `Sink` + /// if the connection is still open or error otherwise. + pub fn assign_id_async(self, id: SubscriptionId) -> impl Future, Error = ()> { + self.subscriber.assign_id_async(id.clone()) + .map(|sink| Sink { + id, + sink, + buffered: None, + _data: PhantomData, + }) } + } /// Subscriber sink. diff --git a/pubsub/examples/pubsub.rs b/pubsub/examples/pubsub.rs index 99d4335f1..c17169d4d 100644 --- a/pubsub/examples/pubsub.rs +++ b/pubsub/examples/pubsub.rs @@ -34,11 +34,12 @@ fn main() { return; } - let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); - // or subscriber.reject(Error {} ); - // or drop(subscriber) let is_done = is_done.clone(); thread::spawn(move || { + let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + // or subscriber.reject(Error {} ); + // or drop(subscriber) + loop { if is_done.load(atomic::Ordering::AcqRel) { return; diff --git a/pubsub/examples/pubsub_simple.rs b/pubsub/examples/pubsub_simple.rs index 933d857ba..991a17bf5 100644 --- a/pubsub/examples/pubsub_simple.rs +++ b/pubsub/examples/pubsub_simple.rs @@ -32,10 +32,11 @@ fn main() { return; } - let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); - // or subscriber.reject(Error {} ); - // or drop(subscriber) thread::spawn(move || { + let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + // or subscriber.reject(Error {} ); + // or drop(subscriber) + loop { thread::sleep(time::Duration::from_millis(100)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { diff --git a/pubsub/more-examples/examples/pubsub_ipc.rs b/pubsub/more-examples/examples/pubsub_ipc.rs index 2007820e6..05dca8ce7 100644 --- a/pubsub/more-examples/examples/pubsub_ipc.rs +++ b/pubsub/more-examples/examples/pubsub_ipc.rs @@ -36,10 +36,11 @@ fn main() { return; } - let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); - // or subscriber.reject(Error {} ); - // or drop(subscriber) thread::spawn(move || { + let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + // or subscriber.reject(Error {} ); + // or drop(subscriber) + loop { thread::sleep(time::Duration::from_millis(100)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { diff --git a/pubsub/more-examples/examples/pubsub_ws.rs b/pubsub/more-examples/examples/pubsub_ws.rs index fd7e362f4..7dc87ddf9 100644 --- a/pubsub/more-examples/examples/pubsub_ws.rs +++ b/pubsub/more-examples/examples/pubsub_ws.rs @@ -52,10 +52,11 @@ fn main() { return; } - let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); - // or subscriber.reject(Error {} ); - // or drop(subscriber) thread::spawn(move || { + let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + // or subscriber.reject(Error {} ); + // or drop(subscriber) + loop { thread::sleep(time::Duration::from_millis(1000)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { diff --git a/pubsub/src/lib.rs b/pubsub/src/lib.rs index 7949549e0..f16319bb6 100644 --- a/pubsub/src/lib.rs +++ b/pubsub/src/lib.rs @@ -11,6 +11,7 @@ mod delegates; mod handler; mod subscription; mod types; +pub mod oneshot; pub mod typed; pub use self::handler::{PubSubHandler, SubscribeRpcMethod, UnsubscribeRpcMethod}; diff --git a/pubsub/src/oneshot.rs b/pubsub/src/oneshot.rs new file mode 100644 index 000000000..dd0eb72ff --- /dev/null +++ b/pubsub/src/oneshot.rs @@ -0,0 +1,99 @@ +//! A futures oneshot channel that can be used for rendezvous. + +use std::ops::{Deref, DerefMut}; +use crate::core::futures::{self, Future, future, sync::oneshot}; + +/// Create a new future-base rendezvous channel. +/// +/// The returned `Sender` and `Receiver` objects are wrapping +/// the regular `futures::sync::oneshot` counterparts and have the same functionality. +/// Additionaly `Sender::send_and_wait` allows you to send a message to the channel +/// and get a future that resolves when the message is consumed. +pub fn channel() -> (Sender, Receiver) { + let (sender, receiver) = oneshot::channel(); + let (receipt_tx, receipt_rx) = oneshot::channel(); + + ( + Sender { sender, receipt: receipt_rx }, + Receiver { receiver, receipt: Some(receipt_tx) } + ) +} + +/// A sender part of the channel. +#[derive(Debug)] +pub struct Sender { + sender: oneshot::Sender, + receipt: oneshot::Receiver<()>, +} + +impl Sender { + /// Consume the sender and queue up an item to send. + /// + /// This method returns right away and never blocks, + /// there is no guarantee though that the message is received + /// by the other end. + pub fn send(self, t: T) -> Result<(), T> { + self.sender.send(t) + } + + /// Consume the sender and send an item. + /// + /// The returned future will resolve when the message is received + /// on the other end. Note that polling the future is actually not required + /// to send the message as that happens synchronously. + /// The future resolves to error in case the receiving end was dropped before + /// being able to process the message. + pub fn send_and_wait(self, t: T) -> impl Future { + let Self { sender, receipt } = self; + + if let Err(_) = sender.send(t) { + return future::Either::A(future::err(())) + } + + future::Either::B(receipt.map_err(|_| ())) + } +} + +impl Deref for Sender { + type Target = oneshot::Sender; + + fn deref(&self) -> &Self::Target { + &self.sender + } +} + +impl DerefMut for Sender { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.sender + } +} + +/// Receiving end of the channel. +/// +/// When this object is `polled` and the result is `Ready` +/// the other end (`Sender`) is also notified about the fact +/// that the item has been consumed and the future returned +/// by `send_and_wait` resolves. +#[must_use = "futures do nothing unless polled"] +#[derive(Debug)] +pub struct Receiver { + receiver: oneshot::Receiver, + receipt: Option>, +} + +impl Future for Receiver { + type Item = as Future>::Item; + type Error = as Future>::Error; + + fn poll(&mut self) -> futures::Poll { + match self.receiver.poll() { + Ok(futures::Async::Ready(r)) => { + if let Some(receipt) = self.receipt.take() { + let _ = receipt.send(()); + } + Ok(futures::Async::Ready(r)) + }, + e => e, + } + } +} diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index f31299300..7cb2740e5 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -7,7 +7,7 @@ use parking_lot::Mutex; use crate::core::{self, BoxFuture}; use crate::core::futures::{self, future, Sink as FuturesSink, Future}; -use crate::core::futures::sync::{mpsc, oneshot}; +use crate::core::futures::sync::mpsc; use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; use crate::types::{PubSubMetadata, SubscriptionId, TransportSender, TransportError, SinkResult}; @@ -140,7 +140,7 @@ impl FuturesSink for Sink { pub struct Subscriber { notification: String, transport: TransportSender, - sender: oneshot::Sender>, + sender: crate::oneshot::Sender>, } impl Subscriber { @@ -149,10 +149,10 @@ impl Subscriber { /// Should only be used for tests. pub fn new_test>(method: T) -> ( Self, - oneshot::Receiver>, + crate::oneshot::Receiver>, mpsc::Receiver, ) { - let (sender, id_receiver) = oneshot::channel(); + let (sender, id_receiver) = crate::oneshot::channel(); let (transport, transport_receiver) = mpsc::channel(1); let subscriber = Subscriber { @@ -165,21 +165,47 @@ impl Subscriber { } /// Consumes `Subscriber` and assigns unique id to a requestor. + /// /// Returns `Err` if request has already terminated. pub fn assign_id(self, id: SubscriptionId) -> Result { - self.sender.send(Ok(id)).map_err(|_| ())?; + let Self { notification, transport, sender } = self; + sender.send(Ok(id)) + .map(|_| Sink { + notification, + transport, + }) + .map_err(|_| ()) + } - Ok(Sink { - notification: self.notification, - transport: self.transport, - }) + /// Consumes `Subscriber` and assigns unique id to a requestor. + /// + /// The returned `Future` resolves when the subscriber receives subscription id. + /// Resolves to `Err` if request has already terminated. + pub fn assign_id_async(self, id: SubscriptionId) -> impl Future { + let Self { notification, transport, sender } = self; + sender.send_and_wait(Ok(id)) + .map(|_| Sink { + notification, + transport, + }) + .map_err(|_| ()) } /// Rejects this subscription request with given error. + /// /// Returns `Err` if request has already terminated. pub fn reject(self, error: core::Error) -> Result<(), ()> { - self.sender.send(Err(error)).map_err(|_| ())?; - Ok(()) + self.sender.send(Err(error)).map_err(|_| ()) + } + + /// Rejects this subscription request with given error. + /// + /// The returned `Future` resolves when the rejection is sent to the client. + /// Resolves to `Err` if request has already terminated. + pub fn reject_async(self, error: core::Error) -> impl Future { + self.sender.send_and_wait(Err(error)) + .map(|_| ()) + .map_err(|_| ()) } } @@ -236,7 +262,7 @@ impl core::RpcMethod for Subscribe where fn call(&self, params: core::Params, meta: M) -> BoxFuture { match meta.session() { Some(session) => { - let (tx, rx) = oneshot::channel(); + let (tx, rx) = crate::oneshot::channel(); // Register the subscription let subscriber = Subscriber { @@ -303,7 +329,7 @@ mod tests { use crate::core; use crate::core::RpcMethod; use crate::core::futures::{Async, Future, Stream}; - use crate::core::futures::sync::{mpsc, oneshot}; + use crate::core::futures::sync::mpsc; use crate::types::{SubscriptionId, PubSubMetadata}; use super::{Session, Sink, Subscriber, new_subscription}; @@ -394,7 +420,7 @@ mod tests { fn should_assign_id() { // given let (transport, _) = mpsc::channel(1); - let (tx, mut rx) = oneshot::channel(); + let (tx, mut rx) = crate::oneshot::channel(); let subscriber = Subscriber { notification: "test".into(), transport, @@ -402,13 +428,14 @@ mod tests { }; // when - let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); + let sink = subscriber.assign_id_async(SubscriptionId::Number(5)); // then assert_eq!( rx.poll().unwrap(), Async::Ready(Ok(SubscriptionId::Number(5))) ); + let sink = sink.wait().unwrap(); assert_eq!(sink.notification, "test".to_owned()); } @@ -416,7 +443,7 @@ mod tests { fn should_reject() { // given let (transport, _) = mpsc::channel(1); - let (tx, mut rx) = oneshot::channel(); + let (tx, mut rx) = crate::oneshot::channel(); let subscriber = Subscriber { notification: "test".into(), transport, @@ -429,13 +456,14 @@ mod tests { }; // when - subscriber.reject(error.clone()).unwrap(); + let reject = subscriber.reject_async(error.clone()); // then assert_eq!( rx.poll().unwrap(), Async::Ready(Err(error)) ); + reject.wait().unwrap(); } #[derive(Clone, Default)] diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs index 9142733c3..b1c20cf4a 100644 --- a/pubsub/src/typed.rs +++ b/pubsub/src/typed.rs @@ -7,7 +7,7 @@ use crate::subscription; use crate::types::{SubscriptionId, TransportError, SinkResult}; use crate::core::{self, Value, Params, Error}; -use crate::core::futures::{self, Sink as FuturesSink, sync}; +use crate::core::futures::{self, Sink as FuturesSink, Future, sync}; /// New PUB-SUB subscriber. #[derive(Debug)] @@ -28,7 +28,7 @@ impl Subscriber { /// Create new subscriber for tests. pub fn new_test>(method: M) -> ( Self, - sync::oneshot::Receiver>, + crate::oneshot::Receiver>, sync::mpsc::Receiver, ) { let (subscriber, id, subscription) = subscription::Subscriber::new_test(method); @@ -40,17 +40,37 @@ impl Subscriber { self.subscriber.reject(error) } + /// Reject subscription with given error. + /// + /// The returned future will resolve when the response is sent to the client. + pub fn reject_async(self, error: Error) -> impl Future { + self.subscriber.reject_async(error) + } + /// Assign id to this subscriber. /// This method consumes `Subscriber` and returns `Sink` /// if the connection is still open or error otherwise. pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { - let sink = self.subscriber.assign_id(id.clone())?; - Ok(Sink { - id, - sink, - buffered: None, - _data: PhantomData, - }) + self.subscriber.assign_id(id.clone()) + .map(|sink| Sink { + id, + sink, + buffered: None, + _data: PhantomData, + }) + } + + /// Assign id to this subscriber. + /// This method consumes `Subscriber` and resolves to `Sink` + /// if the connection is still open and the id has been sent or to error otherwise. + pub fn assign_id_async(self, id: SubscriptionId) -> impl Future, Error = ()> { + self.subscriber.assign_id_async(id.clone()) + .map(|sink| Sink { + id, + sink, + buffered: None, + _data: PhantomData, + }) } } From c24ce487214b1827f21b335a3518b81209e40934 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 29 Mar 2019 12:35:22 +0000 Subject: [PATCH 038/149] Delete jsonrpc-macros (superseded by derive) (#409) --- Cargo.toml | 1 - README.md | 4 - macros/Cargo.toml | 22 - macros/examples/generic-trait-bounds2.rs | 139 ----- macros/examples/generic-trait2.rs | 47 -- macros/examples/meta-macros2.rs | 73 --- macros/examples/pubsub-macros2.rs | 105 ---- macros/examples/std2.rs | 46 -- macros/src/auto_args.rs | 705 ----------------------- macros/src/delegates.rs | 214 ------- macros/src/lib.rs | 70 --- macros/src/pubsub.rs | 150 ----- macros/src/util.rs | 28 - macros/tests/macros.rs | 121 ---- macros/tests/pubsub-macros.rs | 85 --- 15 files changed, 1810 deletions(-) delete mode 100644 macros/Cargo.toml delete mode 100644 macros/examples/generic-trait-bounds2.rs delete mode 100644 macros/examples/generic-trait2.rs delete mode 100644 macros/examples/meta-macros2.rs delete mode 100644 macros/examples/pubsub-macros2.rs delete mode 100644 macros/examples/std2.rs delete mode 100644 macros/src/auto_args.rs delete mode 100644 macros/src/delegates.rs delete mode 100644 macros/src/lib.rs delete mode 100644 macros/src/pubsub.rs delete mode 100644 macros/src/util.rs delete mode 100644 macros/tests/macros.rs delete mode 100644 macros/tests/pubsub-macros.rs diff --git a/Cargo.toml b/Cargo.toml index 665aa1332..b338a53b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "core", "http", "ipc", - "macros", "derive", "pubsub", "pubsub/more-examples", diff --git a/README.md b/README.md index 224f2749b..18670ccf3 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` - [jsonrpc-tcp-server](./tcp) [![crates.io][tcp-server-image]][tcp-server-url] - [jsonrpc-ws-server](./ws) [![crates.io][ws-server-image]][ws-server-url] - [jsonrpc-stdio-server](./stdio) [![crates.io][stdio-server-image]][stdio-server-url] -- [jsonrpc-macros](./macros) [![crates.io][macros-image]][macros-url] *deprecated:* use `derive` instead - [jsonrpc-derive](./derive) [![crates.io][derive-image]][derive-url] - [jsonrpc-server-utils](./server-utils) [![crates.io][server-utils-image]][server-utils-url] - [jsonrpc-pubsub](./pubsub) [![crates.io][pubsub-image]][pubsub-url] @@ -37,8 +36,6 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` [ws-server-url]: https://crates.io/crates/jsonrpc-ws-server [stdio-server-image]: https://img.shields.io/crates/v/jsonrpc-stdio-server.svg [stdio-server-url]: https://crates.io/crates/jsonrpc-stdio-server -[macros-image]: https://img.shields.io/crates/v/jsonrpc-macros.svg -[macros-url]: https://crates.io/crates/jsonrpc-macros [derive-image]: https://img.shields.io/crates/v/jsonrpc-derive.svg [derive-url]: https://crates.io/crates/jsonrpc-derive [server-utils-image]: https://img.shields.io/crates/v/jsonrpc-server-utils.svg @@ -50,7 +47,6 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` - [core](./core/examples) - [derive](./derive/examples) -- [macros](./macros/examples) *deprecated* - [pubsub](./pubsub/examples) ### Basic Usage (with HTTP transport) diff --git a/macros/Cargo.toml b/macros/Cargo.toml deleted file mode 100644 index d0fd17f17..000000000 --- a/macros/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -authors = ["Parity Technologies "] -description = "Helper macros for jsonrpc-core" -documentation = "https://docs.rs/jsonrpc-macros/" -homepage = "https://github.com/paritytech/jsonrpc" -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] -license = "MIT" -name = "jsonrpc-macros" -repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" - -[dependencies] -serde = "1.0" -jsonrpc-core = { version = "10.1", path = "../core" } -jsonrpc-pubsub = { version = "10.1", path = "../pubsub" } - -[dev-dependencies] -serde_json = "1.0" -jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } - -[badges] -travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/macros/examples/generic-trait-bounds2.rs b/macros/examples/generic-trait-bounds2.rs deleted file mode 100644 index d9d9f8a51..000000000 --- a/macros/examples/generic-trait-bounds2.rs +++ /dev/null @@ -1,139 +0,0 @@ -extern crate serde; -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_macros; - -use serde::{Serialize, de::DeserializeOwned}; -use jsonrpc_core::{IoHandler, Error, Result}; -use jsonrpc_core::futures::future::{self, FutureResult}; - -// Two only requires DeserializeOwned -build_rpc_trait! { - pub trait Rpc where - Two: DeserializeOwned, - Three: Serialize, - { - /// Get Zero type. - #[rpc(name = "getZero")] - fn zero(&self) -> Result; - - /// Get One type. - #[rpc(name = "getOne")] - fn one(&self) -> Result; - - /// Adds two numbers and returns a result - #[rpc(name = "setTwo")] - fn set_two(&self, Two) -> Result<()>; - - /// Adds two numbers and returns a result - #[rpc(name = "getThree")] - fn three(&self) -> Result; - - /// Performs asynchronous operation - #[rpc(name = "beFancy")] - fn call(&self, One) -> FutureResult<(One, u64), Error>; - } -} - -build_rpc_trait! { - pub trait Rpc2<> where - Two: DeserializeOwned, - { - /// Adds two numbers and returns a result - #[rpc(name = "setTwo")] - fn set_two(&self, Two) -> Result<()>; - } -} - -build_rpc_trait! { - pub trait Rpc3 where - Two: DeserializeOwned, - Three: Serialize, - { - type Metadata; - - /// Get Zero type. - #[rpc(name = "getZero")] - fn zero(&self) -> Result; - - /// Get One type. - #[rpc(name = "getOne")] - fn one(&self) -> Result; - - /// Adds two numbers and returns a result - #[rpc(name = "setTwo")] - fn set_two(&self, Two) -> Result<()>; - - /// Adds two numbers and returns a result - #[rpc(name = "getThree")] - fn three(&self) -> Result; - - /// Performs asynchronous operation - #[rpc(name = "beFancy")] - fn call(&self, One) -> FutureResult<(One, u64), Error>; - } -} - -struct RpcImpl; - -impl Rpc for RpcImpl { - fn zero(&self) -> Result { - Ok(0) - } - - fn one(&self) -> Result { - Ok(1) - } - - fn set_two(&self, x: String) -> Result<()> { - println!("{}", x); - Ok(()) - } - - fn three(&self) -> Result { - Ok(3) - } - - fn call(&self, num: u64) -> FutureResult<(u64, u64), Error> { - ::future::finished((num + 999, num)) - } -} - -impl Rpc2 for RpcImpl { - fn set_two(&self, _: String) -> Result<()> { - unimplemented!() - } -} - -impl Rpc3 for RpcImpl { - type Metadata = (); - - fn zero(&self) -> Result { - Ok(0) - } - - fn one(&self) -> Result { - Ok(1) - } - - fn set_two(&self, x: String) -> Result<()> { - println!("{}", x); - Ok(()) - } - - fn three(&self) -> Result { - Ok(3) - } - - fn call(&self, num: u64) -> FutureResult<(u64, u64), Error> { - ::future::finished((num + 999, num)) - } -} - -fn main() { - let mut io = IoHandler::new(); - - io.extend_with(Rpc::to_delegate(RpcImpl)); - io.extend_with(Rpc2::to_delegate(RpcImpl)); -} - diff --git a/macros/examples/generic-trait2.rs b/macros/examples/generic-trait2.rs deleted file mode 100644 index a8f6362cd..000000000 --- a/macros/examples/generic-trait2.rs +++ /dev/null @@ -1,47 +0,0 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_macros; - -use jsonrpc_core::{IoHandler, Error, Result}; -use jsonrpc_core::futures::future::{self, FutureResult}; - -build_rpc_trait! { - pub trait Rpc { - /// Get One type. - #[rpc(name = "getOne")] - fn one(&self) -> Result; - - /// Adds two numbers and returns a result - #[rpc(name = "setTwo")] - fn set_two(&self, Two) -> Result<()>; - - /// Performs asynchronous operation - #[rpc(name = "beFancy")] - fn call(&self, One) -> FutureResult<(One, Two), Error>; - } -} - -struct RpcImpl; - -impl Rpc for RpcImpl { - fn one(&self) -> Result { - Ok(100) - } - - fn set_two(&self, x: String) -> Result<()> { - println!("{}", x); - Ok(()) - } - - fn call(&self, num: u64) -> FutureResult<(u64, String), Error> { - ::future::finished((num + 999, "hello".into())) - } -} - - -fn main() { - let mut io = IoHandler::new(); - let rpc = RpcImpl; - - io.extend_with(rpc.to_delegate()) -} diff --git a/macros/examples/meta-macros2.rs b/macros/examples/meta-macros2.rs deleted file mode 100644 index 446018104..000000000 --- a/macros/examples/meta-macros2.rs +++ /dev/null @@ -1,73 +0,0 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_macros; -extern crate jsonrpc_tcp_server; - -use std::collections::BTreeMap; - -use jsonrpc_core::{futures, MetaIoHandler, Metadata, Error, Value, Result}; -use jsonrpc_core::futures::future::FutureResult; - -#[derive(Clone)] -struct Meta(String); -impl Metadata for Meta {} - -build_rpc_trait! { - pub trait Rpc { - type Metadata; - - /// Adds two numbers and returns a result - #[rpc(name = "add")] - fn add(&self, u64, u64) -> Result; - - /// Multiplies two numbers. Second number is optional. - #[rpc(name = "mul")] - fn mul(&self, u64, jsonrpc_macros::Trailing) -> Result; - - /// Performs asynchronous operation - #[rpc(name = "callAsync")] - fn call(&self, u64) -> FutureResult; - - /// Performs asynchronous operation with meta - #[rpc(meta, name = "callAsyncMeta", alias = [ "callAsyncMetaAlias", ])] - fn call_meta(&self, Self::Metadata, BTreeMap) -> FutureResult; - } -} - -struct RpcImpl; -impl Rpc for RpcImpl { - type Metadata = Meta; - - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) - } - - fn mul(&self, a: u64, b: jsonrpc_macros::Trailing) -> Result { - Ok(a * b.unwrap_or(1)) - } - - fn call(&self, x: u64) -> FutureResult { - futures::finished(format!("OK: {}", x)) - } - - fn call_meta(&self, meta: Self::Metadata, map: BTreeMap) -> FutureResult { - futures::finished(format!("From: {}, got: {:?}", meta.0, map)) - } -} - - -fn main() { - let mut io = MetaIoHandler::default(); - let rpc = RpcImpl; - - io.extend_with(rpc.to_delegate()); - - let server = jsonrpc_tcp_server::ServerBuilder - ::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| { - Meta(format!("{}", context.peer_addr)) - }) - .start(&"0.0.0.0:3030".parse().unwrap()) - .expect("Server must start with no issues"); - - server.wait() -} diff --git a/macros/examples/pubsub-macros2.rs b/macros/examples/pubsub-macros2.rs deleted file mode 100644 index b833c5699..000000000 --- a/macros/examples/pubsub-macros2.rs +++ /dev/null @@ -1,105 +0,0 @@ -extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; -#[macro_use] -extern crate jsonrpc_macros; -extern crate jsonrpc_tcp_server; - -use std::thread; -use std::sync::{atomic, Arc, RwLock}; -use std::collections::HashMap; - -use jsonrpc_core::{Error, ErrorCode, Result}; -use jsonrpc_core::futures::Future; -use jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId}; - -use jsonrpc_macros::pubsub; - -build_rpc_trait! { - pub trait Rpc { - type Metadata; - - /// Adds two numbers and returns a result - #[rpc(name = "add")] - fn add(&self, u64, u64) -> Result; - - #[pubsub(name = "hello")] { - /// Hello subscription - #[rpc(name = "hello_subscribe", alias = ["hello_sub", ])] - fn subscribe(&self, Self::Metadata, pubsub::Subscriber, u64); - - /// Unsubscribe from hello subscription. - #[rpc(name = "hello_unsubscribe")] - fn unsubscribe(&self, Option, SubscriptionId) -> Result; - } - } -} - -#[derive(Default)] -struct RpcImpl { - uid: atomic::AtomicUsize, - active: Arc>>>, -} -impl Rpc for RpcImpl { - type Metadata = Arc; - - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) - } - - fn subscribe(&self, _meta: Self::Metadata, subscriber: pubsub::Subscriber, param: u64) { - if param != 10 { - subscriber.reject(Error { - code: ErrorCode::InvalidParams, - message: "Rejecting subscription - invalid parameters provided.".into(), - data: None, - }).unwrap(); - return; - } - - let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst); - let sub_id = SubscriptionId::Number(id as u64); - let sink = subscriber.assign_id(sub_id.clone()).unwrap(); - self.active.write().unwrap().insert(sub_id, sink); - } - - fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { - let removed = self.active.write().unwrap().remove(&id); - if removed.is_some() { - Ok(true) - } else { - Err(Error { - code: ErrorCode::InvalidParams, - message: "Invalid subscription.".into(), - data: None, - }) - } - } -} - - -fn main() { - let mut io = PubSubHandler::default(); - let rpc = RpcImpl::default(); - let active_subscriptions = rpc.active.clone(); - - thread::spawn(move || { - loop { - { - let subscribers = active_subscriptions.read().unwrap(); - for sink in subscribers.values() { - let _ = sink.notify(Ok("Hello World!".into())).wait(); - } - } - thread::sleep(::std::time::Duration::from_secs(1)); - } - }); - - io.extend_with(rpc.to_delegate()); - - let server = jsonrpc_tcp_server::ServerBuilder - ::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(context.sender.clone()))) - .start(&"0.0.0.0:3030".parse().unwrap()) - .expect("Server must start with no issues"); - - server.wait() -} diff --git a/macros/examples/std2.rs b/macros/examples/std2.rs deleted file mode 100644 index dbf5d97c3..000000000 --- a/macros/examples/std2.rs +++ /dev/null @@ -1,46 +0,0 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_macros; - -use jsonrpc_core::{IoHandler, Error, Result}; -use jsonrpc_core::futures::future::{self, FutureResult}; - -build_rpc_trait! { - pub trait Rpc { - /// Returns a protocol version - #[rpc(name = "protocolVersion")] - fn protocol_version(&self) -> Result; - - /// Adds two numbers and returns a result - #[rpc(name = "add")] - fn add(&self, u64, u64) -> Result; - - /// Performs asynchronous operation - #[rpc(name = "callAsync")] - fn call(&self, u64) -> FutureResult; - } -} - -struct RpcImpl; - -impl Rpc for RpcImpl { - fn protocol_version(&self) -> Result { - Ok("version1".into()) - } - - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) - } - - fn call(&self, _: u64) -> FutureResult { - future::ok("OK".to_owned()) - } -} - - -fn main() { - let mut io = IoHandler::new(); - let rpc = RpcImpl; - - io.extend_with(rpc.to_delegate()) -} diff --git a/macros/src/auto_args.rs b/macros/src/auto_args.rs deleted file mode 100644 index 2fa2f4eb3..000000000 --- a/macros/src/auto_args.rs +++ /dev/null @@ -1,705 +0,0 @@ -// because we reuse the type names as idents in the macros as a dirty hack to -// work around `concat_idents!` being unstable. -#![allow(non_snake_case)] - -///! Automatically serialize and deserialize parameters around a strongly-typed function. - -use jsonrpc_core::{Error, Params, Value, Metadata, Result}; -use jsonrpc_core::futures::{self, Future, IntoFuture}; -use jsonrpc_core::futures::future::{self, Either}; -use jsonrpc_pubsub::{PubSubMetadata, Subscriber}; -use pubsub; -use serde::Serialize; -use serde::de::DeserializeOwned; -use util::{invalid_params, expect_no_params, to_value}; - -/// Auto-generates an RPC trait from trait definition. -/// -/// This just copies out all the methods, docs, and adds another -/// function `to_delegate` which will automatically wrap each strongly-typed -/// function in a wrapper which handles parameter and output type serialization. -/// -/// RPC functions may come in a couple forms: synchronous, async and async with metadata. -/// These are parsed with the custom `#[rpc]` attribute, which must follow -/// documentation. -/// -/// ## The #[rpc] attribute -/// -/// Valid forms: -/// - `#[rpc(name = "name_here")]` (an async rpc function which should be bound to the given name) -/// - `#[rpc(meta, name = "name_here")]` (an async rpc function with metadata which should be bound to the given name) -/// -/// Synchronous function format: -/// `fn foo(&self, Param1, Param2, Param3) -> Result`. -/// -/// Asynchronous RPC functions must come in this form: -/// `fn foo(&self, Param1, Param2, Param3) -> BoxFuture; -/// -/// Asynchronous RPC functions with metadata must come in this form: -/// `fn foo(&self, Self::Metadata, Param1, Param2, Param3) -> BoxFuture; -/// -/// Anything else will be rejected by the code generator. -/// -/// ## The #[pubsub] attribute -/// -/// Valid form: -/// ```rust,ignore -/// #[pubsub(name = "hello")] { -/// #[rpc(name = "hello_subscribe")] -/// fn subscribe(&self, Self::Metadata, pubsub::Subscriber, u64); -/// #[rpc(name = "hello_unsubscribe")] -/// fn unsubscribe(&self, Option, SubscriptionId) -> Result; -/// } -/// ``` -/// -/// The attribute is used to create a new pair of subscription methods -/// (if underlying transport supports that.) - - -#[macro_export] -macro_rules! metadata { - () => { - /// Requests metadata - type Metadata: $crate::jsonrpc_core::Metadata; - }; - ( - $( $sub_name: ident )+ - ) => { - /// Requests metadata - type Metadata: $crate::jsonrpc_pubsub::PubSubMetadata; - }; -} - -/// Build an RPC trait definition. -#[macro_export] -macro_rules! build_rpc_trait { - ( - $(#[$t_attr: meta])* - pub trait $name:ident $(<$( $generics:ident ),*> - $( - where - $( $generics2:ident : $bounds:tt $( + $morebounds:tt )* ,)+ - )* - )* - { - $( $rest: tt )+ - } - ) => { - build_rpc_trait! { - @WITH_BOUNDS - $(#[$t_attr])* - pub trait $name $(< - // first generic parameters with both bounds - $( $generics ,)* - @BOUNDS - // then specialised ones - $( $( $generics2 : $bounds $( + $morebounds )* ,)* )* - > )* { - $( $rest )+ - } - } - }; - ( - @WITH_BOUNDS - $(#[$t_attr: meta])* - pub trait $name:ident $(< - $( $simple_generics:ident ,)* - @BOUNDS - $( $generics:ident : $bounds:tt $( + $morebounds:tt )* ,)* - >)* { - $( - $( #[doc=$m_doc:expr] )* - #[ rpc( $($t:tt)* ) ] - fn $m_name: ident ( $( $p: tt )* ) -> $result: tt <$out: ty $(, $error: ty)* >; - )* - } - ) => { - $(#[$t_attr])* - pub trait $name $(<$( $simple_generics ,)* $( $generics , )*>)* : Sized + Send + Sync + 'static { - build_rpc_trait!( - GENERATE_FUNCTIONS - $( - $(#[doc=$m_doc])* - fn $m_name ( $( $p )* ) -> $result <$out $(, $error) *>; - )* - ); - - /// Transform this into an `IoDelegate`, automatically wrapping - /// the parameters. - #[deprecated(since = "10.0", note = "Generated by jsonrpc-macros. Please use `#[rpc]` from jsonrpc-derive instead")] - fn to_delegate(self) -> $crate::IoDelegate - where $( - $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ,)* - $($generics: Send + Sync + 'static + $bounds $( + $morebounds )* ,)* - )* - { - let mut del = $crate::IoDelegate::new(self.into()); - $( - build_rpc_trait!(WRAP del => - ( $($t)* ) - fn $m_name ( $( $p )* ) -> $result <$out $(, $error)* > - ); - )* - del - } - } - }; - - // entry-point for trait with metadata methods - ( - @WITH_BOUNDS - $(#[$t_attr: meta])* - pub trait $name: ident $(< - $( $simple_generics:ident ,)* - @BOUNDS - $( $generics:ident : $bounds:tt $( + $morebounds:tt )* ,)* - >)* { - type Metadata; - - $( - $( #[ doc=$m_doc:expr ] )* - #[ rpc( $($t:tt)* ) ] - fn $m_name: ident ( $( $p: tt )* ) -> $result: tt <$out: ty $(, $error_std: ty) *>; - )* - - $( - #[ pubsub( $($pubsub_t:tt)+ ) ] { - $( #[ doc= $sub_doc:expr ] )* - #[ rpc( $($sub_t:tt)* ) ] - fn $sub_name: ident ( $($sub_p: tt)* ); - $( #[ doc= $unsub_doc:expr ] )* - #[ rpc( $($unsub_t:tt)* ) ] - fn $unsub_name: ident ( $($unsub_p: tt)* ) -> $sub_result: tt <$sub_out: ty $(, $error_unsub: ty)* >; - } - )* - - } - ) => { - $(#[$t_attr])* - pub trait $name $(<$( $simple_generics ,)* $( $generics , )* >)* : Sized + Send + Sync + 'static { - // Metadata bound differs for traits with subscription methods. - metadata! ( - $( $sub_name )* - ); - - build_rpc_trait!(GENERATE_FUNCTIONS - $( - $(#[doc=$m_doc])* - fn $m_name ( $( $p )* ) -> $result <$out $(, $error_std) *>; - )* - ); - - build_rpc_trait!(GENERATE_FUNCTIONS - $( - $(#[doc=$sub_doc])* - fn $sub_name ( $( $sub_p )* ); - $(#[doc=$unsub_doc])* - fn $unsub_name ( $( $unsub_p )* ) -> $sub_result <$sub_out $(, $error_unsub) *>; - )* - ); - - /// Transform this into an `IoDelegate`, automatically wrapping - /// the parameters. - #[deprecated(since = "10.0", note = "Generated by jsonrpc-macros. Please use `#[rpc]` from jsonrpc-derive instead")] - fn to_delegate(self) -> $crate::IoDelegate - where $( - $($simple_generics: Send + Sync + 'static + $crate::Serialize + $crate::DeserializeOwned ,)* - $($generics: Send + Sync + 'static + $bounds $( + $morebounds )* , )* - )* - { - let mut del = $crate::IoDelegate::new(self.into()); - $( - build_rpc_trait!(WRAP del => - ( $($t)* ) - fn $m_name ( $( $p )* ) -> $result <$out $(, $error_std)* > - ); - )* - $( - build_rpc_trait!(WRAP del => - pubsub: ( $($pubsub_t)* ) - subscribe: ( $($sub_t)* ) - fn $sub_name ( $($sub_p)* ); - unsubscribe: ( $($unsub_t)* ) - fn $unsub_name ( $($unsub_p)* ) -> $sub_result <$sub_out $(, $error_unsub)* >; - ); - )* - del - } - } - }; - - (GENERATE_FUNCTIONS - $( - $( #[doc=$m_doc:expr] )* - fn $m_name: ident (&self $(, $p: ty)* ) $( -> $result: ty)*; - )* - ) => { - $( - $(#[doc=$m_doc])* - fn $m_name (&self $(, _: $p )* ) $( -> $result)*; - )* - }; - - ( WRAP $del: expr => - (meta, name = $name: expr $(, alias = [ $( $alias: expr, )+ ])*) - fn $method: ident (&self, Self::Metadata $(, $param: ty)*) -> $result: tt <$out: ty $(, $error: ty)* > - ) => { - $del.add_method_with_meta($name, move |base, params, meta| { - $crate::WrapMeta::wrap_rpc(&(Self::$method as fn(&_, Self::Metadata $(, $param)*) -> $result <$out $(, $error)* >), base, params, meta) - }); - $( - $( - $del.add_alias($alias, $name); - )+ - )* - }; - - ( WRAP $del: expr => - pubsub: (name = $name: expr) - subscribe: (name = $subscribe: expr $(, alias = [ $( $sub_alias: expr, )+ ])*) - fn $sub_method: ident (&self, Self::Metadata $(, $sub_p: ty)+); - unsubscribe: (name = $unsubscribe: expr $(, alias = [ $( $unsub_alias: expr, )+ ])*) - fn $unsub_method: ident (&self, Option < Self::Metadata > $(, $unsub_p: ty)+) -> $result: tt <$out: ty $(, $error_unsub: ty)* >; - ) => { - $del.add_subscription( - $name, - ($subscribe, move |base, params, meta, subscriber| { - $crate::WrapSubscribe::wrap_rpc( - &(Self::$sub_method as fn(&_, Self::Metadata $(, $sub_p)*)), - base, - params, - meta, - subscriber, - ) - }), - ($unsubscribe, move |base, id, meta| { - use $crate::jsonrpc_core::futures::{IntoFuture, Future}; - Self::$unsub_method(base, meta, id).into_future() - .map($crate::to_value) - .map_err(Into::into) - }), - ); - - $( - $( - $del.add_alias($sub_alias, $subscribe); - )* - )* - $( - $( - $del.add_alias($unsub_alias, $unsubscribe); - )* - )* - }; - - ( WRAP $del: expr => - (name = $name: expr $(, alias = [ $( $alias: expr, )+ ])*) - fn $method: ident (&self $(, $param: ty)*) -> $result: tt <$out: ty $(, $error: ty)* > - ) => { - $del.add_method($name, move |base, params| { - $crate::WrapAsync::wrap_rpc(&(Self::$method as fn(&_ $(, $param)*) -> $result <$out $(, $error)*>), base, params) - }); - $( - $( - $del.add_alias($alias, $name); - )+ - )* - }; -} - -/// A wrapper type without an implementation of `Deserialize` -/// which allows a special implementation of `Wrap` for functions -/// that take a trailing default parameter. -pub struct Trailing(Option); - -impl Into> for Trailing { - fn into(self) -> Option { - self.0 - } -} - -impl From> for Trailing { - fn from(o: Option) -> Self { - Trailing(o) - } -} - -impl Trailing { - /// Returns a underlying value if present or provided value. - pub fn unwrap_or(self, other: T) -> T { - self.0.unwrap_or(other) - } - - /// Returns an underlying value or computes it if not present. - pub fn unwrap_or_else T>(self, f: F) -> T { - self.0.unwrap_or_else(f) - } -} - -impl Trailing { - /// Returns an underlying value or the default value. - pub fn unwrap_or_default(self) -> T { - self.0.unwrap_or_default() - } -} - -type WrappedFuture = future::MapErr< - future::Map Value>, - fn(E) -> Error ->; -type WrapResult = Either< - WrappedFuture, - future::FutureResult, ->; - -fn as_future(el: I) -> WrappedFuture where - OUT: Serialize, - E: Into, - F: Future, - I: IntoFuture -{ - el.into_future() - .map(to_value as fn(OUT) -> Value) - .map_err(Into::into as fn(E) -> Error) -} - -/// Wrapper trait for asynchronous RPC functions. -pub trait WrapAsync { - /// Output type. - type Out: IntoFuture; - - /// Invokes asynchronous RPC method. - fn wrap_rpc(&self, base: &B, params: Params) -> Self::Out; -} - -/// Wrapper trait for meta RPC functions. -pub trait WrapMeta { - /// Output type. - type Out: IntoFuture; - /// Invokes asynchronous RPC method with Metadata. - fn wrap_rpc(&self, base: &B, params: Params, meta: M) -> Self::Out; -} - -/// Wrapper trait for subscribe RPC functions. -pub trait WrapSubscribe { - /// Invokes subscription. - fn wrap_rpc(&self, base: &B, params: Params, meta: M, subscriber: Subscriber); -} - -// special impl for no parameters. -impl WrapAsync for fn(&B) -> I where - B: Send + Sync + 'static, - OUT: Serialize + 'static, - E: Into + 'static, - F: Future + Send + 'static, - I: IntoFuture, -{ - type Out = WrapResult; - - fn wrap_rpc(&self, base: &B, params: Params) -> Self::Out { - match expect_no_params(params) { - Ok(()) => Either::A(as_future((self)(base))), - Err(e) => Either::B(futures::failed(e)), - } - } -} - -impl WrapMeta for fn(&B, M) -> I where - M: Metadata, - B: Send + Sync + 'static, - OUT: Serialize + 'static, - E: Into + 'static, - F: Future + Send + 'static, - I: IntoFuture, -{ - type Out = WrapResult; - - fn wrap_rpc(&self, base: &B, params: Params, meta: M) -> Self::Out { - match expect_no_params(params) { - Ok(()) => Either::A(as_future((self)(base, meta))), - Err(e) => Either::B(futures::failed(e)), - } - } -} - -impl WrapSubscribe for fn(&B, M, pubsub::Subscriber) where - M: PubSubMetadata, - B: Send + Sync + 'static, - OUT: Serialize, -{ - fn wrap_rpc(&self, base: &B, params: Params, meta: M, subscriber: Subscriber) { - match expect_no_params(params) { - Ok(()) => (self)(base, meta, pubsub::Subscriber::new(subscriber)), - Err(e) => { - let _ = subscriber.reject(e); - }, - } - } -} - -// creates a wrapper implementation which deserializes the parameters, -// calls the function with concrete type, and serializes the output. -macro_rules! wrap { - ($($x: ident),+) => { - - // asynchronous implementation - impl < - BASE: Send + Sync + 'static, - OUT: Serialize + 'static, - $($x: DeserializeOwned,)+ - ERR: Into + 'static, - X: Future + Send + 'static, - Z: IntoFuture, - > WrapAsync for fn(&BASE, $($x,)+ ) -> Z { - type Out = WrapResult; - fn wrap_rpc(&self, base: &BASE, params: Params) -> Self::Out { - match params.parse::<($($x,)+)>() { - Ok(($($x,)+)) => Either::A(as_future((self)(base, $($x,)+))), - Err(e) => Either::B(futures::failed(e)), - } - } - } - - // asynchronous implementation with meta - impl < - BASE: Send + Sync + 'static, - META: Metadata, - OUT: Serialize + 'static, - $($x: DeserializeOwned,)+ - ERR: Into + 'static, - X: Future + Send + 'static, - Z: IntoFuture, - > WrapMeta for fn(&BASE, META, $($x,)+) -> Z { - type Out = WrapResult; - fn wrap_rpc(&self, base: &BASE, params: Params, meta: META) -> Self::Out { - match params.parse::<($($x,)+)>() { - Ok(($($x,)+)) => Either::A(as_future((self)(base, meta, $($x,)+))), - Err(e) => Either::B(futures::failed(e)), - } - } - } - - // subscribe implementation - impl < - BASE: Send + Sync + 'static, - META: PubSubMetadata, - OUT: Serialize, - $($x: DeserializeOwned,)+ - > WrapSubscribe for fn(&BASE, META, pubsub::Subscriber, $($x,)+) { - fn wrap_rpc(&self, base: &BASE, params: Params, meta: META, subscriber: Subscriber) { - match params.parse::<($($x,)+)>() { - Ok(($($x,)+)) => (self)(base, meta, pubsub::Subscriber::new(subscriber), $($x,)+), - Err(e) => { - let _ = subscriber.reject(e); - }, - } - } - } - } -} - -fn params_len(params: &Params) -> Result { - match *params { - Params::Array(ref v) => Ok(v.len()), - Params::None => Ok(0), - _ => Err(invalid_params("`params` should be an array", "")), - } -} - -fn require_len(params: &Params, required: usize) -> Result { - let len = params_len(params)?; - if len < required { - return Err(invalid_params(&format!("`params` should have at least {} argument(s)", required), "")); - } - Ok(len) -} - -fn parse_trailing_param(params: Params) -> Result<(Option, )> { - let len = try!(params_len(¶ms)); - match len { - 0 => Ok((None,)), - 1 => params.parse::<(T,)>().map(|(x, )| (Some(x), )), - _ => Err(invalid_params("Expecting only one optional parameter.", "")), - } -} - -// special impl for no parameters other than block parameter. -impl WrapAsync for fn(&B, Trailing) -> I where - B: Send + Sync + 'static, - OUT: Serialize + 'static, - T: DeserializeOwned, - E: Into + 'static, - F: Future + Send + 'static, - I: IntoFuture, -{ - type Out = WrapResult; - fn wrap_rpc(&self, base: &B, params: Params) -> Self::Out { - let id = parse_trailing_param(params); - - match id { - Ok((id,)) => Either::A(as_future((self)(base, Trailing(id)))), - Err(e) => Either::B(futures::failed(e)), - } - } -} - -impl WrapMeta for fn(&B, M, Trailing) -> I where - M: Metadata, - B: Send + Sync + 'static, - OUT: Serialize + 'static, - T: DeserializeOwned, - E: Into + 'static, - F: Future + Send + 'static, - I: IntoFuture, -{ - type Out = WrapResult; - fn wrap_rpc(&self, base: &B, params: Params, meta: M) -> Self::Out { - let id = parse_trailing_param(params); - - match id { - Ok((id,)) => Either::A(as_future((self)(base, meta, Trailing(id)))), - Err(e) => Either::B(futures::failed(e)), - } - } -} - -impl WrapSubscribe for fn(&B, M, pubsub::Subscriber, Trailing) where - M: PubSubMetadata, - B: Send + Sync + 'static, - OUT: Serialize, - T: DeserializeOwned, -{ - fn wrap_rpc(&self, base: &B, params: Params, meta: M, subscriber: Subscriber) { - let id = parse_trailing_param(params); - - match id { - Ok((id,)) => (self)(base, meta, pubsub::Subscriber::new(subscriber), Trailing(id)), - Err(e) => { - let _ = subscriber.reject(e); - }, - } - } -} - -// similar to `wrap!`, but handles a single default trailing parameter -// accepts an additional argument indicating the number of non-trailing parameters. -macro_rules! wrap_with_trailing { - ($num: expr, $($x: ident),+) => { - // asynchronous implementation - impl < - BASE: Send + Sync + 'static, - OUT: Serialize + 'static, - $($x: DeserializeOwned,)+ - TRAILING: DeserializeOwned, - ERR: Into + 'static, - X: Future + Send + 'static, - Z: IntoFuture, - > WrapAsync for fn(&BASE, $($x,)+ Trailing) -> Z { - type Out = WrapResult; - fn wrap_rpc(&self, base: &BASE, params: Params) -> Self::Out { - let len = match require_len(¶ms, $num) { - Ok(len) => len, - Err(e) => return Either::B(futures::failed(e)), - }; - - let params = match len - $num { - 0 => params.parse::<($($x,)+)>() - .map(|($($x,)+)| ($($x,)+ None)).map_err(Into::into), - 1 => params.parse::<($($x,)+ TRAILING)>() - .map(|($($x,)+ id)| ($($x,)+ Some(id))).map_err(Into::into), - _ => Err(invalid_params(&format!("Expected {} or {} parameters.", $num, $num + 1), format!("Got: {}", len))), - }; - - match params { - Ok(($($x,)+ id)) => Either::A(as_future((self)(base, $($x,)+ Trailing(id)))), - Err(e) => Either::B(futures::failed(e)), - } - } - } - - // asynchronous implementation with meta - impl < - BASE: Send + Sync + 'static, - META: Metadata, - OUT: Serialize + 'static, - $($x: DeserializeOwned,)+ - TRAILING: DeserializeOwned, - ERR: Into + 'static, - X: Future + Send + 'static, - Z: IntoFuture, - > WrapMeta for fn(&BASE, META, $($x,)+ Trailing) -> Z { - type Out = WrapResult; - fn wrap_rpc(&self, base: &BASE, params: Params, meta: META) -> Self::Out { - let len = match require_len(¶ms, $num) { - Ok(len) => len, - Err(e) => return Either::B(futures::failed(e)), - }; - - let params = match len - $num { - 0 => params.parse::<($($x,)+)>() - .map(|($($x,)+)| ($($x,)+ None)).map_err(Into::into), - 1 => params.parse::<($($x,)+ TRAILING)>() - .map(|($($x,)+ id)| ($($x,)+ Some(id))).map_err(Into::into), - _ => Err(invalid_params(&format!("Expected {} or {} parameters.", $num, $num + 1), format!("Got: {}", len))), - }; - - match params { - Ok(($($x,)+ id)) => Either::A(as_future((self)(base, meta, $($x,)+ Trailing(id)))), - Err(e) => Either::B(futures::failed(e)), - } - } - } - - // subscribe implementation - impl < - BASE: Send + Sync + 'static, - META: PubSubMetadata, - OUT: Serialize, - $($x: DeserializeOwned,)+ - TRAILING: DeserializeOwned, - > WrapSubscribe for fn(&BASE, META, pubsub::Subscriber, $($x,)+ Trailing) { - fn wrap_rpc(&self, base: &BASE, params: Params, meta: META, subscriber: Subscriber) { - let len = match require_len(¶ms, $num) { - Ok(len) => len, - Err(e) => { - let _ = subscriber.reject(e); - return; - }, - }; - - let params = match len - $num { - 0 => params.parse::<($($x,)+)>() - .map(|($($x,)+)| ($($x,)+ None)), - 1 => params.parse::<($($x,)+ TRAILING)>() - .map(|($($x,)+ id)| ($($x,)+ Some(id))), - _ => { - let _ = subscriber.reject(invalid_params(&format!("Expected {} or {} parameters.", $num, $num + 1), format!("Got: {}", len))); - return; - }, - }; - - match params { - Ok(($($x,)+ id)) => (self)(base, meta, pubsub::Subscriber::new(subscriber), $($x,)+ Trailing(id)), - Err(e) => { - let _ = subscriber.reject(e); - return; - }, - } - } - } - } -} - -wrap!(A, B, C, D, E, F); -wrap!(A, B, C, D, E); -wrap!(A, B, C, D); -wrap!(A, B, C); -wrap!(A, B); -wrap!(A); - -wrap_with_trailing!(6, A, B, C, D, E, F); -wrap_with_trailing!(5, A, B, C, D, E); -wrap_with_trailing!(4, A, B, C, D); -wrap_with_trailing!(3, A, B, C); -wrap_with_trailing!(2, A, B); -wrap_with_trailing!(1, A); diff --git a/macros/src/delegates.rs b/macros/src/delegates.rs deleted file mode 100644 index 2d95efe84..000000000 --- a/macros/src/delegates.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::sync::Arc; -use std::collections::HashMap; - -use jsonrpc_core::{Params, Value, Error}; -use jsonrpc_core::{BoxFuture, Metadata, RemoteProcedure, RpcMethod, RpcNotification}; -use jsonrpc_core::futures::IntoFuture; - -use jsonrpc_pubsub::{self, SubscriptionId, Subscriber, PubSubMetadata}; - -struct DelegateAsyncMethod { - delegate: Arc, - closure: F, -} - -impl RpcMethod for DelegateAsyncMethod where - M: Metadata, - F: Fn(&T, Params) -> I, - I: IntoFuture, - T: Send + Sync + 'static, - F: Send + Sync + 'static, - I::Future: Send + 'static, -{ - fn call(&self, params: Params, _meta: M) -> BoxFuture { - let closure = &self.closure; - Box::new(closure(&self.delegate, params).into_future()) - } -} - -struct DelegateMethodWithMeta { - delegate: Arc, - closure: F, -} - -impl RpcMethod for DelegateMethodWithMeta where - M: Metadata, - F: Fn(&T, Params, M) -> I, - I: IntoFuture, - T: Send + Sync + 'static, - F: Send + Sync + 'static, - I::Future: Send + 'static, -{ - fn call(&self, params: Params, meta: M) -> BoxFuture { - let closure = &self.closure; - Box::new(closure(&self.delegate, params, meta).into_future()) - } -} - -struct DelegateNotification { - delegate: Arc, - closure: F, -} - -impl RpcNotification for DelegateNotification where - F: Fn(&T, Params) + 'static, - F: Send + Sync + 'static, - T: Send + Sync + 'static, - M: Metadata, -{ - fn execute(&self, params: Params, _meta: M) { - let closure = &self.closure; - closure(&self.delegate, params) - } -} - -struct DelegateSubscribe { - delegate: Arc, - closure: F, -} - -impl jsonrpc_pubsub::SubscribeRpcMethod for DelegateSubscribe where - M: PubSubMetadata, - F: Fn(&T, Params, M, Subscriber), - T: Send + Sync + 'static, - F: Send + Sync + 'static, -{ - fn call(&self, params: Params, meta: M, subscriber: Subscriber) { - let closure = &self.closure; - closure(&self.delegate, params, meta, subscriber) - } -} - -struct DelegateUnsubscribe { - delegate: Arc, - closure: F, -} - -impl jsonrpc_pubsub::UnsubscribeRpcMethod for DelegateUnsubscribe where - M: PubSubMetadata, - F: Fn(&T, SubscriptionId, Option) -> I, - I: IntoFuture, - T: Send + Sync + 'static, - F: Send + Sync + 'static, - I::Future: Send + 'static, -{ - type Out = I::Future; - fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out { - let closure = &self.closure; - closure(&self.delegate, id, meta).into_future() - } -} - -/// A set of RPC methods and notifications tied to single `delegate` struct. -pub struct IoDelegate where - T: Send + Sync + 'static, - M: Metadata, -{ - delegate: Arc, - methods: HashMap>, -} - -impl IoDelegate where - T: Send + Sync + 'static, - M: Metadata, -{ - /// Creates new `IoDelegate` - pub fn new(delegate: Arc) -> Self { - IoDelegate { - delegate, - methods: HashMap::new(), - } - } - - /// Adds an alias to existing method. - /// NOTE: Aliases are not transitive, i.e. you cannot create alias to an alias. - pub fn add_alias(&mut self, from: &str, to: &str) { - self.methods.insert(from.into(), RemoteProcedure::Alias(to.into())); - } - - /// Adds async method to the delegate. - pub fn add_method(&mut self, name: &str, method: F) where - F: Fn(&T, Params) -> I, - I: IntoFuture, - F: Send + Sync + 'static, - I::Future: Send + 'static, - { - self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new( - DelegateAsyncMethod { - delegate: self.delegate.clone(), - closure: method, - } - ))); - } - - /// Adds async method with metadata to the delegate. - pub fn add_method_with_meta(&mut self, name: &str, method: F) where - F: Fn(&T, Params, M) -> I, - I: IntoFuture, - F: Send + Sync + 'static, - I::Future: Send + 'static, - { - self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new( - DelegateMethodWithMeta { - delegate: self.delegate.clone(), - closure: method, - } - ))); - } - - /// Adds notification to the delegate. - pub fn add_notification(&mut self, name: &str, notification: F) where - F: Fn(&T, Params), - F: Send + Sync + 'static, - { - self.methods.insert(name.into(), RemoteProcedure::Notification(Arc::new( - DelegateNotification { - delegate: self.delegate.clone(), - closure: notification, - } - ))); - } -} - -impl IoDelegate where - T: Send + Sync + 'static, - M: PubSubMetadata, -{ - /// Adds subscription to the delegate. - pub fn add_subscription( - &mut self, - name: &str, - subscribe: (&str, Sub), - unsubscribe: (&str, Unsub), - ) where - Sub: Fn(&T, Params, M, Subscriber), - Sub: Send + Sync + 'static, - Unsub: Fn(&T, SubscriptionId, Option) -> I, - I: IntoFuture, - Unsub: Send + Sync + 'static, - I::Future: Send + 'static, - { - let (sub, unsub) = jsonrpc_pubsub::new_subscription( - name, - DelegateSubscribe { - delegate: self.delegate.clone(), - closure: subscribe.1, - }, - DelegateUnsubscribe { - delegate: self.delegate.clone(), - closure: unsubscribe.1, - } - ); - self.add_method_with_meta(subscribe.0, move |_, params, meta| sub.call(params, meta)); - self.add_method_with_meta(unsubscribe.0, move |_, params, meta| unsub.call(params, meta)); - } -} - -impl Into>> for IoDelegate where - T: Send + Sync + 'static, - M: Metadata, -{ - fn into(self) -> HashMap> { - self.methods - } -} diff --git a/macros/src/lib.rs b/macros/src/lib.rs deleted file mode 100644 index 6d6989c03..000000000 --- a/macros/src/lib.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! High level, typed wrapper for `jsonrpc_core`. -//! -//! Enables creation of "Service" objects grouping a set of RPC methods together in a typed manner. -//! -//! Example -//! -//! ``` -//! extern crate jsonrpc_core; -//! #[macro_use] extern crate jsonrpc_macros; -//! use jsonrpc_core::{IoHandler, Error, Result}; -//! use jsonrpc_core::futures::future::{self, FutureResult}; -//! build_rpc_trait! { -//! pub trait Rpc { -//! /// Returns a protocol version -//! #[rpc(name = "protocolVersion")] -//! fn protocol_version(&self) -> Result; -//! -//! /// Adds two numbers and returns a result -//! #[rpc(name = "add")] -//! fn add(&self, u64, u64) -> Result; -//! -//! /// Performs asynchronous operation -//! #[rpc(name = "callAsync")] -//! fn call(&self, u64) -> FutureResult; -//! } -//! } -//! struct RpcImpl; -//! impl Rpc for RpcImpl { -//! fn protocol_version(&self) -> Result { -//! Ok("version1".into()) -//! } -//! -//! fn add(&self, a: u64, b: u64) -> Result { -//! Ok(a + b) -//! } -//! -//! fn call(&self, _: u64) -> FutureResult { -//! future::ok("OK".to_owned()).into() -//! } -//! } -//! -//! fn main() { -//! let mut io = IoHandler::new(); -//! let rpc = RpcImpl; -//! -//! io.extend_with(rpc.to_delegate()); -//! } -//! ``` - -#![warn(missing_docs)] - -pub extern crate jsonrpc_core; -pub extern crate jsonrpc_pubsub; -extern crate serde; - -mod auto_args; -mod delegates; -mod util; - -pub mod pubsub; - -#[doc(hidden)] -pub use auto_args::{WrapAsync, WrapMeta, WrapSubscribe}; - -#[doc(hidden)] -pub use serde::{de::DeserializeOwned, Serialize}; - -pub use auto_args::Trailing; -pub use delegates::IoDelegate; -pub use util::to_value; diff --git a/macros/src/pubsub.rs b/macros/src/pubsub.rs deleted file mode 100644 index 54fb84ef2..000000000 --- a/macros/src/pubsub.rs +++ /dev/null @@ -1,150 +0,0 @@ -//! PUB-SUB auto-serializing structures. - -use std::marker::PhantomData; - -use jsonrpc_core as core; -use jsonrpc_pubsub as pubsub; -use serde; -use util::to_value; - -use self::core::futures::{self, Future, Sink as FuturesSink, sync}; - -pub use self::pubsub::SubscriptionId; - -/// New PUB-SUB subcriber. -#[derive(Debug)] -pub struct Subscriber { - subscriber: pubsub::Subscriber, - _data: PhantomData<(T, E)>, -} - -impl Subscriber { - /// Wrap non-typed subscriber. - pub fn new(subscriber: pubsub::Subscriber) -> Self { - Subscriber { - subscriber, - _data: PhantomData, - } - } - - /// Create new subscriber for tests. - pub fn new_test>(method: M) -> ( - Self, - pubsub::oneshot::Receiver>, - sync::mpsc::Receiver, - ) { - let (subscriber, id, subscription) = pubsub::Subscriber::new_test(method); - (Subscriber::new(subscriber), id, subscription) - } - - /// Reject subscription with given error. - pub fn reject(self, error: core::Error) -> Result<(), ()> { - self.subscriber.reject(error) - } - - /// Reject subscription with given error. - pub fn reject_async(self, error: core::Error) -> impl Future { - self.subscriber.reject_async(error) - } - - /// Assign id to this subscriber. - /// This method consumes `Subscriber` and returns `Sink` - /// if the connection is still open or error otherwise. - pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { - self.subscriber.assign_id(id.clone()) - .map(|sink| Sink { - id, - sink, - buffered: None, - _data: PhantomData, - }) - } - - /// Assign id to this subscriber. - /// This method consumes `Subscriber` and returns `Sink` - /// if the connection is still open or error otherwise. - pub fn assign_id_async(self, id: SubscriptionId) -> impl Future, Error = ()> { - self.subscriber.assign_id_async(id.clone()) - .map(|sink| Sink { - id, - sink, - buffered: None, - _data: PhantomData, - }) - } - -} - -/// Subscriber sink. -#[derive(Debug, Clone)] -pub struct Sink { - sink: pubsub::Sink, - id: SubscriptionId, - buffered: Option, - _data: PhantomData<(T, E)>, -} - -impl Sink { - /// Sends a notification to the subscriber. - pub fn notify(&self, val: Result) -> pubsub::SinkResult { - self.sink.notify(self.val_to_params(val)) - } - - fn val_to_params(&self, val: Result) -> core::Params { - let id = self.id.clone().into(); - let val = val.map(to_value).map_err(to_value); - - core::Params::Map(vec![ - ("subscription".to_owned(), id), - match val { - Ok(val) => ("result".to_owned(), val), - Err(err) => ("error".to_owned(), err), - }, - ].into_iter().collect()) - } - - fn poll(&mut self) -> futures::Poll<(), pubsub::TransportError> { - if let Some(item) = self.buffered.take() { - let result = self.sink.start_send(item)?; - if let futures::AsyncSink::NotReady(item) = result { - self.buffered = Some(item); - } - } - - if self.buffered.is_some() { - Ok(futures::Async::NotReady) - } else { - Ok(futures::Async::Ready(())) - } - } -} - -impl futures::sink::Sink for Sink { - type SinkItem = Result; - type SinkError = pubsub::TransportError; - - fn start_send(&mut self, item: Self::SinkItem) -> futures::StartSend { - // Make sure to always try to process the buffered entry. - // Since we're just a proxy to real `Sink` we don't need - // to schedule a `Task` wakeup. It will be done downstream. - if self.poll()?.is_not_ready() { - return Ok(futures::AsyncSink::NotReady(item)); - } - - let val = self.val_to_params(item); - self.buffered = Some(val); - self.poll()?; - - Ok(futures::AsyncSink::Ready) - } - - fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { - self.poll()?; - self.sink.poll_complete() - } - - fn close(&mut self) -> futures::Poll<(), Self::SinkError> { - self.poll()?; - self.sink.close() - } -} diff --git a/macros/src/util.rs b/macros/src/util.rs deleted file mode 100644 index 4b06f5afe..000000000 --- a/macros/src/util.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Param & Value utilities - -use std::fmt; -use jsonrpc_core::{self as core, Error, Params, ErrorCode, Value}; -use serde; - -/// Returns an `InvalidParams` for given parameter. -pub fn invalid_params(param: &str, details: T) -> Error where T: fmt::Debug { - Error { - code: ErrorCode::InvalidParams, - message: format!("Couldn't parse parameters: {}", param), - data: Some(Value::String(format!("{:?}", details))), - } -} - -/// Validates if the method was invoked without any params. -pub fn expect_no_params(params: Params) -> core::Result<()> { - match params { - Params::None => Ok(()), - Params::Array(ref v) if v.is_empty() => Ok(()), - p => Err(invalid_params("No parameters were expected", p)), - } -} - -/// Converts a serializable value into `Value`. -pub fn to_value(value: T) -> Value where T: serde::Serialize { - core::to_value(value).expect("Expected always-serializable type.") -} diff --git a/macros/tests/macros.rs b/macros/tests/macros.rs deleted file mode 100644 index feb0dc24f..000000000 --- a/macros/tests/macros.rs +++ /dev/null @@ -1,121 +0,0 @@ - -extern crate serde_json; -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_macros; - -use jsonrpc_core::{IoHandler, Response}; - -pub enum MyError {} -impl From for jsonrpc_core::Error { - fn from(_e: MyError) -> Self { - unreachable!() - } -} - -type Result = ::std::result::Result; - -build_rpc_trait! { - pub trait Rpc { - /// Returns a protocol version - #[rpc(name = "protocolVersion")] - fn protocol_version(&self) -> Result; - - /// Negates number and returns a result - #[rpc(name = "neg")] - fn neg(&self, i64) -> Result; - - /// Adds two numbers and returns a result - #[rpc(name = "add")] - fn add(&self, u64, u64) -> Result; - } -} - -#[derive(Default)] -struct RpcImpl; - -impl Rpc for RpcImpl { - fn protocol_version(&self) -> Result { - Ok("version1".into()) - } - - fn neg(&self, a: i64) -> Result { - Ok(-a) - } - - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) - } -} - -#[test] -fn should_accept_empty_array_as_no_params() { - let mut io = IoHandler::new(); - let rpc = RpcImpl::default(); - io.extend_with(rpc.to_delegate()); - - // when - let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"protocolVersion","params":[]}"#; - let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"protocolVersion","params":null}"#; - let req3 = r#"{"jsonrpc":"2.0","id":1,"method":"protocolVersion"}"#; - - let res1 = io.handle_request_sync(req1); - let res2 = io.handle_request_sync(req2); - let res3 = io.handle_request_sync(req3); - let expected = r#"{ - "jsonrpc": "2.0", - "result": "version1", - "id": 1 - }"#; - let expected: Response = serde_json::from_str(expected).unwrap(); - - // then - let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(expected, result1); - - let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); - assert_eq!(expected, result2); - - let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); - assert_eq!(expected, result3); -} - -#[test] -fn should_accept_single_param() { - let mut io = IoHandler::new(); - let rpc = RpcImpl::default(); - io.extend_with(rpc.to_delegate()); - - // when - let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"neg","params":[1]}"#; - - let res1 = io.handle_request_sync(req1); - - // then - let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ - "jsonrpc": "2.0", - "result": -1, - "id": 1 - }"#).unwrap()); -} - -#[test] -fn should_accept_multiple_params() { - let mut io = IoHandler::new(); - let rpc = RpcImpl::default(); - io.extend_with(rpc.to_delegate()); - - // when - let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"add","params":[1, 2]}"#; - - let res1 = io.handle_request_sync(req1); - - // then - let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ - "jsonrpc": "2.0", - "result": 3, - "id": 1 - }"#).unwrap()); -} diff --git a/macros/tests/pubsub-macros.rs b/macros/tests/pubsub-macros.rs deleted file mode 100644 index 943c8e25f..000000000 --- a/macros/tests/pubsub-macros.rs +++ /dev/null @@ -1,85 +0,0 @@ -extern crate serde_json; -extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; -#[macro_use] -extern crate jsonrpc_macros; - -use std::sync::Arc; -use jsonrpc_core::futures::sync::mpsc; -use jsonrpc_pubsub::{PubSubHandler, SubscriptionId, Session, PubSubMetadata}; -use jsonrpc_macros::{pubsub, Trailing}; - -pub enum MyError {} -impl From for jsonrpc_core::Error { - fn from(_e: MyError) -> Self { - unreachable!() - } -} - -type Result = ::std::result::Result; - -build_rpc_trait! { - pub trait Rpc { - type Metadata; - - #[pubsub(name = "hello")] { - /// Hello subscription - #[rpc(name = "hello_subscribe")] - fn subscribe(&self, Self::Metadata, pubsub::Subscriber, u32, Trailing); - - /// Unsubscribe from hello subscription. - #[rpc(name = "hello_unsubscribe")] - fn unsubscribe(&self, Option, SubscriptionId) -> Result; - } - } -} - -#[derive(Default)] -struct RpcImpl; - -impl Rpc for RpcImpl { - type Metadata = Metadata; - - fn subscribe(&self, _meta: Self::Metadata, subscriber: pubsub::Subscriber, _pre: u32, _trailing: Trailing) { - let _sink = subscriber.assign_id(SubscriptionId::Number(5)); - } - - fn unsubscribe(&self, _meta: Option, _id: SubscriptionId) -> Result { - Ok(true) - } -} - -#[derive(Clone, Default)] -struct Metadata; -impl jsonrpc_core::Metadata for Metadata {} -impl PubSubMetadata for Metadata { - fn session(&self) -> Option> { - let (tx, _rx) = mpsc::channel(1); - Some(Arc::new(Session::new(tx))) - } -} - -#[test] -fn test_invalid_trailing_pubsub_params() { - let mut io = PubSubHandler::default(); - let rpc = RpcImpl::default(); - io.extend_with(rpc.to_delegate()); - - // when - let meta = Metadata; - let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello_subscribe","params":[]}"#; - let res = io.handle_request_sync(req, meta); - let expected = r#"{ - "jsonrpc": "2.0", - "error": { - "code": -32602, - "message": "Couldn't parse parameters: `params` should have at least 1 argument(s)", - "data": "\"\"" - }, - "id": 1 - }"#; - - let expected: jsonrpc_core::Response = serde_json::from_str(expected).unwrap(); - let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(expected, result); -} From 31ec6d67f2ab338a26f5080af5804960f7ab39e4 Mon Sep 17 00:00:00 2001 From: David Craven Date: Sat, 30 Mar 2019 08:37:32 +0100 Subject: [PATCH 039/149] Use rustfmt (#407) * Use rustfmt. * Run rustfmt. --- .travis.yml | 5 + core/examples/async.rs | 2 +- core/examples/basic.rs | 4 +- core/examples/meta.rs | 2 +- core/examples/middlewares.rs | 13 +- core/examples/params.rs | 6 +- core/src/calls.rs | 20 +- core/src/delegates.rs | 60 +- core/src/io.rs | 156 +-- core/src/lib.rs | 14 +- core/src/middleware.rs | 83 +- core/src/types/error.rs | 30 +- core/src/types/id.rs | 14 +- core/src/types/mod.rs | 10 +- core/src/types/params.rs | 49 +- core/src/types/request.rs | 153 ++- core/src/types/response.rs | 111 +- core/src/types/version.rs | 21 +- derive/examples/generic-trait-bounds.rs | 14 +- derive/examples/generic-trait.rs | 3 +- derive/examples/meta-macros.rs | 11 +- derive/examples/pubsub-macros.rs | 41 +- derive/examples/std.rs | 2 +- derive/src/lib.rs | 2 +- derive/src/rpc_attr.rs | 160 +-- derive/src/rpc_trait.rs | 129 +- derive/src/to_delegate.rs | 243 ++-- derive/tests/compiletests.rs | 7 +- derive/tests/macros.rs | 45 +- derive/tests/pubsub-macros.rs | 7 +- derive/tests/trailing.rs | 84 +- http/examples/http_async.rs | 3 +- http/examples/http_meta.rs | 17 +- http/examples/http_middleware.rs | 7 +- http/examples/server.rs | 7 +- http/src/handler.rs | 220 ++-- http/src/lib.rs | 130 +- http/src/response.rs | 6 +- http/src/tests.rs | 1310 +++++++++++-------- http/src/utils.rs | 41 +- ipc/examples/ipc.rs | 7 +- ipc/src/lib.rs | 20 +- ipc/src/logger.rs | 4 +- ipc/src/meta.rs | 9 +- ipc/src/select_with_weak.rs | 34 +- ipc/src/server.rs | 250 ++-- pubsub/examples/pubsub.rs | 25 +- pubsub/examples/pubsub_simple.rs | 31 +- pubsub/more-examples/examples/pubsub_ipc.rs | 35 +- pubsub/more-examples/examples/pubsub_ws.rs | 29 +- pubsub/src/delegates.rs | 50 +- pubsub/src/handler.rs | 22 +- pubsub/src/lib.rs | 10 +- pubsub/src/oneshot.rs | 16 +- pubsub/src/subscription.rs | 125 +- pubsub/src/typed.rs | 62 +- pubsub/src/types.rs | 4 +- rustfmt.toml | 2 + server-utils/src/cors.rs | 156 ++- server-utils/src/hosts.rs | 44 +- server-utils/src/lib.rs | 9 +- server-utils/src/matcher.rs | 9 +- server-utils/src/reactor.rs | 70 +- server-utils/src/stream_codec.rs | 69 +- server-utils/src/suspendable_stream.rs | 19 +- stdio/examples/stdio.rs | 6 +- stdio/src/lib.rs | 14 +- tcp/examples/tcp.rs | 3 +- tcp/src/dispatch.rs | 27 +- tcp/src/lib.rs | 17 +- tcp/src/logger.rs | 4 +- tcp/src/meta.rs | 9 +- tcp/src/server.rs | 73 +- tcp/src/service.rs | 10 +- tcp/src/tests.rs | 112 +- test/src/lib.rs | 59 +- ws/examples/ws.rs | 2 +- ws/src/error.rs | 2 +- ws/src/lib.rs | 10 +- ws/src/metadata.rs | 30 +- ws/src/server.rs | 39 +- ws/src/server_builder.rs | 9 +- ws/src/session.rs | 58 +- ws/src/tests.rs | 90 +- 84 files changed, 2740 insertions(+), 2187 deletions(-) create mode 100644 rustfmt.toml diff --git a/.travis.yml b/.travis.yml index b3292843e..e7274e7d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,9 +21,14 @@ matrix: allow_failures: - rust: nightly +before_script: + - rustup component add rustfmt + script: - cargo build --all - cargo test --all + - | + ([ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true after_success: | [ $TRAVIS_OS_NAME == 'linux' ] && diff --git a/core/examples/async.rs b/core/examples/async.rs index 5040e8bd8..619cd3742 100644 --- a/core/examples/async.rs +++ b/core/examples/async.rs @@ -1,5 +1,5 @@ -use jsonrpc_core::*; use jsonrpc_core::futures::Future; +use jsonrpc_core::*; fn main() { let mut io = IoHandler::new(); diff --git a/core/examples/basic.rs b/core/examples/basic.rs index 8afb42b32..24dbbca1b 100644 --- a/core/examples/basic.rs +++ b/core/examples/basic.rs @@ -3,9 +3,7 @@ use jsonrpc_core::*; fn main() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_: Params| { - Ok(Value::String("Hello World!".to_owned())) - }); + io.add_method("say_hello", |_: Params| Ok(Value::String("Hello World!".to_owned()))); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; diff --git a/core/examples/meta.rs b/core/examples/meta.rs index 2569e426f..3e19a4bdf 100644 --- a/core/examples/meta.rs +++ b/core/examples/meta.rs @@ -1,5 +1,5 @@ -use jsonrpc_core::*; use jsonrpc_core::futures::Future; +use jsonrpc_core::*; #[derive(Clone, Default)] struct Meta(usize); diff --git a/core/examples/middlewares.rs b/core/examples/middlewares.rs index a16c915f2..48af3e595 100644 --- a/core/examples/middlewares.rs +++ b/core/examples/middlewares.rs @@ -1,8 +1,8 @@ -use std::time::Instant; -use std::sync::atomic::{self, AtomicUsize}; -use jsonrpc_core::*; -use jsonrpc_core::futures::Future; use jsonrpc_core::futures::future::Either; +use jsonrpc_core::futures::Future; +use jsonrpc_core::*; +use std::sync::atomic::{self, AtomicUsize}; +use std::time::Instant; #[derive(Clone, Debug)] struct Meta(usize); @@ -14,9 +14,10 @@ impl Middleware for MyMiddleware { type Future = FutureResponse; type CallFuture = middleware::NoopCallFuture; - fn on_request(&self, request: Request, meta: Meta, next: F) -> Either where + fn on_request(&self, request: Request, meta: Meta, next: F) -> Either + where F: FnOnce(Request, Meta) -> X + Send, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { let start = Instant::now(); let request_number = self.0.fetch_add(1, atomic::Ordering::SeqCst); diff --git a/core/examples/params.rs b/core/examples/params.rs index c5bbb2e8b..d528b3578 100644 --- a/core/examples/params.rs +++ b/core/examples/params.rs @@ -1,16 +1,16 @@ use jsonrpc_core::*; -use serde_derive::{Deserialize}; +use serde_derive::Deserialize; #[derive(Deserialize)] struct HelloParams { - name:String, + name: String, } fn main() { let mut io = IoHandler::new(); io.add_method("say_hello", |params: Params| { - let parsed: HelloParams = params.parse().unwrap(); + let parsed: HelloParams = params.parse().unwrap(); Ok(Value::String(format!("hello, {}", parsed.name))) }); diff --git a/core/src/calls.rs b/core/src/calls.rs index b02ad8a7d..251e669fc 100644 --- a/core/src/calls.rs +++ b/core/src/calls.rs @@ -1,8 +1,8 @@ +use crate::types::{Error, Params, Value}; +use crate::BoxFuture; +use futures::{Future, IntoFuture}; use std::fmt; use std::sync::Arc; -use crate::types::{Params, Value, Error}; -use futures::{Future, IntoFuture}; -use crate::BoxFuture; /// Metadata trait pub trait Metadata: Clone + Send + 'static {} @@ -54,12 +54,13 @@ impl fmt::Debug for RemoteProcedure { match *self { Method(..) => write!(fmt, ""), Notification(..) => write!(fmt, ""), - Alias(ref alias) => write!(fmt, "alias => {:?}", alias) + Alias(ref alias) => write!(fmt, "alias => {:?}", alias), } } } -impl RpcMethodSimple for F where +impl RpcMethodSimple for F +where F: Fn(Params) -> I, X: Future, I: IntoFuture, @@ -70,7 +71,8 @@ impl RpcMethodSimple for F where } } -impl RpcNotificationSimple for F where +impl RpcNotificationSimple for F +where F: Fn(Params), { fn execute(&self, params: Params) { @@ -78,7 +80,8 @@ impl RpcNotificationSimple for F where } } -impl RpcMethod for F where +impl RpcMethod for F +where T: Metadata, F: Fn(Params, T) -> I, I: IntoFuture, @@ -89,7 +92,8 @@ impl RpcMethod for F where } } -impl RpcNotification for F where +impl RpcNotification for F +where T: Metadata, F: Fn(Params, T), { diff --git a/core/src/delegates.rs b/core/src/delegates.rs index b21c7dec7..f487aa202 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -1,19 +1,20 @@ //! Delegate rpc calls -use std::sync::Arc; use std::collections::HashMap; +use std::sync::Arc; -use crate::types::{Params, Value, Error}; use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification}; -use futures::IntoFuture; +use crate::types::{Error, Params, Value}; use crate::BoxFuture; +use futures::IntoFuture; struct DelegateAsyncMethod { delegate: Arc, closure: F, } -impl RpcMethod for DelegateAsyncMethod where +impl RpcMethod for DelegateAsyncMethod +where M: Metadata, F: Fn(&T, Params) -> I, I: IntoFuture, @@ -32,7 +33,8 @@ struct DelegateMethodWithMeta { closure: F, } -impl RpcMethod for DelegateMethodWithMeta where +impl RpcMethod for DelegateMethodWithMeta +where M: Metadata, F: Fn(&T, Params, M) -> I, I: IntoFuture, @@ -51,7 +53,8 @@ struct DelegateNotification { closure: F, } -impl RpcNotification for DelegateNotification where +impl RpcNotification for DelegateNotification +where F: Fn(&T, Params) + 'static, F: Send + Sync + 'static, T: Send + Sync + 'static, @@ -64,7 +67,8 @@ impl RpcNotification for DelegateNotification where } /// A set of RPC methods and notifications tied to single `delegate` struct. -pub struct IoDelegate where +pub struct IoDelegate +where T: Send + Sync + 'static, M: Metadata, { @@ -72,7 +76,8 @@ pub struct IoDelegate where methods: HashMap>, } -impl IoDelegate where +impl IoDelegate +where T: Send + Sync + 'static, M: Metadata, { @@ -91,50 +96,57 @@ impl IoDelegate where } /// Adds async method to the delegate. - pub fn add_method(&mut self, name: &str, method: F) where + pub fn add_method(&mut self, name: &str, method: F) + where F: Fn(&T, Params) -> I, I: IntoFuture, F: Send + Sync + 'static, I::Future: Send + 'static, { - self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new( - DelegateAsyncMethod { + self.methods.insert( + name.into(), + RemoteProcedure::Method(Arc::new(DelegateAsyncMethod { delegate: self.delegate.clone(), closure: method, - } - ))); + })), + ); } /// Adds async method with metadata to the delegate. - pub fn add_method_with_meta(&mut self, name: &str, method: F) where + pub fn add_method_with_meta(&mut self, name: &str, method: F) + where F: Fn(&T, Params, M) -> I, I: IntoFuture, F: Send + Sync + 'static, I::Future: Send + 'static, { - self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new( - DelegateMethodWithMeta { + self.methods.insert( + name.into(), + RemoteProcedure::Method(Arc::new(DelegateMethodWithMeta { delegate: self.delegate.clone(), closure: method, - } - ))); + })), + ); } /// Adds notification to the delegate. - pub fn add_notification(&mut self, name: &str, notification: F) where + pub fn add_notification(&mut self, name: &str, notification: F) + where F: Fn(&T, Params), F: Send + Sync + 'static, { - self.methods.insert(name.into(), RemoteProcedure::Notification(Arc::new( - DelegateNotification { + self.methods.insert( + name.into(), + RemoteProcedure::Notification(Arc::new(DelegateNotification { delegate: self.delegate.clone(), closure: notification, - } - ))); + })), + ); } } -impl Into>> for IoDelegate where +impl Into>> for IoDelegate +where T: Send + Sync + 'static, M: Metadata, { diff --git a/core/src/io.rs b/core/src/io.rs index 89ca37571..c5537e6bd 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -1,20 +1,20 @@ -use std::sync::Arc; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; -use serde_json; use futures::{self, future, Future}; +use serde_json; -use crate::calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; +use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple}; use crate::middleware::{self, Middleware}; +use crate::types::{Call, Output, Request, Response}; use crate::types::{Error, ErrorCode, Version}; -use crate::types::{Request, Response, Call, Output}; /// A type representing middleware or RPC response before serialization. -pub type FutureResponse = Box, Error=()> + Send>; +pub type FutureResponse = Box, Error = ()> + Send>; /// A type representing middleware or RPC call output. -pub type FutureOutput = Box, Error=()> + Send>; +pub type FutureOutput = Box, Error = ()> + Send>; /// A type representing future string response. pub type FutureResult = future::Map< @@ -23,26 +23,14 @@ pub type FutureResult = future::Map< >; /// A type representing a result of a single method call. -pub type FutureRpcOutput = future::Either< - F, - future::Either< - FutureOutput, - future::FutureResult, ()>, - >, ->; +pub type FutureRpcOutput = future::Either, ()>>>; /// A type representing an optional `Response` for RPC `Request`. pub type FutureRpcResult = future::Either< F, future::Either< - future::Map< - FutureRpcOutput, - fn(Option) -> Option, - >, - future::Map< - future::JoinAll>>, - fn(Vec>) -> Option, - >, + future::Map, fn(Option) -> Option>, + future::Map>>, fn(Vec>) -> Option>, >, >; @@ -66,9 +54,7 @@ impl Default for Compatibility { impl Compatibility { fn is_version_valid(self, version: Option) -> bool { match (self, version) { - (Compatibility::V1, None) | - (Compatibility::V2, Some(Version::V2)) | - (Compatibility::Both, _) => true, + (Compatibility::V1, None) | (Compatibility::V2, Some(Version::V2)) | (Compatibility::Both, _) => true, _ => false, } } @@ -108,7 +94,6 @@ impl MetaIoHandler { } } - impl> MetaIoHandler { /// Creates new `MetaIoHandler` pub fn new(compatibility: Compatibility, middleware: S) -> Self { @@ -130,51 +115,47 @@ impl> MetaIoHandler { /// Adds an alias to a method. pub fn add_alias(&mut self, alias: &str, other: &str) { - self.methods.insert( - alias.into(), - RemoteProcedure::Alias(other.into()), - ); + self.methods.insert(alias.into(), RemoteProcedure::Alias(other.into())); } /// Adds new supported asynchronous method - pub fn add_method(&mut self, name: &str, method: F) where + pub fn add_method(&mut self, name: &str, method: F) + where F: RpcMethodSimple, { - self.add_method_with_meta(name, move |params, _meta| { - method.call(params) - }) + self.add_method_with_meta(name, move |params, _meta| method.call(params)) } /// Adds new supported notification - pub fn add_notification(&mut self, name: &str, notification: F) where + pub fn add_notification(&mut self, name: &str, notification: F) + where F: RpcNotificationSimple, { self.add_notification_with_meta(name, move |params, _meta| notification.execute(params)) } /// Adds new supported asynchronous method with metadata support. - pub fn add_method_with_meta(&mut self, name: &str, method: F) where + pub fn add_method_with_meta(&mut self, name: &str, method: F) + where F: RpcMethod, { - self.methods.insert( - name.into(), - RemoteProcedure::Method(Arc::new(method)), - ); + self.methods + .insert(name.into(), RemoteProcedure::Method(Arc::new(method))); } /// Adds new supported notification with metadata support. - pub fn add_notification_with_meta(&mut self, name: &str, notification: F) where + pub fn add_notification_with_meta(&mut self, name: &str, notification: F) + where F: RpcNotification, { - self.methods.insert( - name.into(), - RemoteProcedure::Notification(Arc::new(notification)), - ); + self.methods + .insert(name.into(), RemoteProcedure::Notification(Arc::new(notification))); } /// Extend this `MetaIoHandler` with methods defined elsewhere. - pub fn extend_with(&mut self, methods: F) where - F: Into>> + pub fn extend_with(&mut self, methods: F) + where + F: Into>>, { self.methods.extend(methods.into()) } @@ -183,7 +164,9 @@ impl> MetaIoHandler { /// If you have any asynchronous methods in your RPC it is much wiser to use /// `handle_request` instead and deal with asynchronous requests in a non-blocking fashion. pub fn handle_request_sync(&self, request: &str, meta: T) -> Option { - self.handle_request(request, meta).wait().expect("Handler calls can never fail.") + self.handle_request(request, meta) + .wait() + .expect("Handler calls can never fail.") } /// Handle given request asynchronously. @@ -201,7 +184,10 @@ impl> MetaIoHandler { trace!(target: "rpc", "Request: {}.", request); let request = read_request(request); let result = match request { - Err(error) => A(futures::finished(Some(Response::from(error, self.compatibility.default_version())))), + Err(error) => A(futures::finished(Some(Response::from( + error, + self.compatibility.default_version(), + )))), Ok(request) => B(self.handle_rpc_request(request, meta)), }; @@ -225,17 +211,20 @@ impl> MetaIoHandler { } } - self.middleware.on_request(request, meta, |request, meta| match request { - Request::Single(call) => { - A(self.handle_call(call, meta).map(output_as_response as fn(Option) -> - Option)) - }, - Request::Batch(calls) => { - let futures: Vec<_> = calls.into_iter().map(move |call| self.handle_call(call, meta.clone())).collect(); - B(futures::future::join_all(futures).map(outputs_as_batch as fn(Vec>) -> - Option)) - }, - }) + self.middleware + .on_request(request, meta, |request, meta| match request { + Request::Single(call) => A(self + .handle_call(call, meta) + .map(output_as_response as fn(Option) -> Option)), + Request::Batch(calls) => { + let futures: Vec<_> = calls + .into_iter() + .map(move |call| self.handle_call(call, meta.clone())) + .collect(); + B(futures::future::join_all(futures) + .map(outputs_as_batch as fn(Vec>) -> Option)) + } + }) } /// Handle single call asynchronously. @@ -266,11 +255,11 @@ impl> MetaIoHandler { match result { Ok(result) => A(Box::new( - result.then(move |result| futures::finished(Some(Output::from(result, id, jsonrpc)))) + result.then(move |result| futures::finished(Some(Output::from(result, id, jsonrpc)))), ) as _), Err(err) => B(futures::finished(Some(Output::from(Err(err), id, jsonrpc)))), } - }, + } Call::Notification(notification) => { let params = notification.params; let jsonrpc = notification.jsonrpc; @@ -281,20 +270,21 @@ impl> MetaIoHandler { match self.methods.get(¬ification.method) { Some(&RemoteProcedure::Notification(ref notification)) => { notification.execute(params, meta); - }, + } Some(&RemoteProcedure::Alias(ref alias)) => { if let Some(&RemoteProcedure::Notification(ref notification)) = self.methods.get(alias) { notification.execute(params, meta); } - }, - _ => {}, + } + _ => {} } B(futures::finished(None)) - }, - Call::Invalid { id } => { - B(futures::finished(Some(Output::invalid_request(id, self.compatibility.default_version())))) - }, + } + Call::Invalid { id } => B(futures::finished(Some(Output::invalid_request( + id, + self.compatibility.default_version(), + )))), }) } } @@ -371,17 +361,15 @@ fn write_response(response: Response) -> String { #[cfg(test)] mod tests { + use super::{Compatibility, IoHandler}; + use crate::types::Value; use futures; - use crate::types::{Value}; - use super::{IoHandler, Compatibility}; #[test] fn test_io_handler() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_| Ok(Value::String("hello".to_string()))); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; @@ -393,9 +381,7 @@ mod tests { fn test_io_handler_1dot0() { let mut io = IoHandler::with_compatibility(Compatibility::Both); - io.add_method("say_hello", |_| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_| Ok(Value::String("hello".to_string()))); let request = r#"{"method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"result":"hello","id":1}"#; @@ -407,9 +393,7 @@ mod tests { fn test_async_io_handler() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| { - futures::finished(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_| futures::finished(Value::String("hello".to_string()))); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; @@ -419,8 +403,8 @@ mod tests { #[test] fn test_notification() { - use std::sync::Arc; use std::sync::atomic; + use std::sync::Arc; let mut io = IoHandler::new(); @@ -448,12 +432,9 @@ mod tests { #[test] fn test_method_alias() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_| Ok(Value::String("hello".to_string()))); io.add_alias("say_hello_alias", "say_hello"); - let request = r#"{"jsonrpc": "2.0", "method": "say_hello_alias", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; @@ -462,8 +443,8 @@ mod tests { #[test] fn test_notification_alias() { - use std::sync::Arc; use std::sync::atomic; + use std::sync::Arc; let mut io = IoHandler::new(); @@ -481,8 +462,9 @@ mod tests { #[test] fn test_send_sync() { - fn is_send_sync(_obj: T) -> bool where - T: Send + Sync + fn is_send_sync(_obj: T) -> bool + where + T: Send + Sync, { true } diff --git a/core/src/lib.rs b/core/src/lib.rs index 0bdeb2e92..772be921a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -21,8 +21,10 @@ #![warn(missing_docs)] -#[macro_use] extern crate log; -#[macro_use] extern crate serde_derive; +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_derive; pub use futures; @@ -32,9 +34,9 @@ pub extern crate serde_json; mod calls; mod io; +pub mod delegates; pub mod middleware; pub mod types; -pub mod delegates; /// A `Future` trait object. pub type BoxFuture = Box + Send>; @@ -42,8 +44,10 @@ pub type BoxFuture = Box + Send>; /// A Result type. pub type Result = ::std::result::Result; -pub use crate::calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification}; +pub use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple}; pub use crate::delegates::IoDelegate; -pub use crate::io::{Compatibility, IoHandler, MetaIoHandler, FutureOutput, FutureResult, FutureResponse, FutureRpcResult}; +pub use crate::io::{ + Compatibility, FutureOutput, FutureResponse, FutureResult, FutureRpcResult, IoHandler, MetaIoHandler, +}; pub use crate::middleware::{Middleware, Noop as NoopMiddleware}; pub use crate::types::*; diff --git a/core/src/middleware.rs b/core/src/middleware.rs index 9bc17f388..36a916014 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -1,23 +1,24 @@ //! `IoHandler` middlewares use crate::calls::Metadata; -use crate::types::{Request, Response, Call, Output}; +use crate::types::{Call, Output, Request, Response}; use futures::{future::Either, Future}; /// RPC middleware pub trait Middleware: Send + Sync + 'static { /// A returned request future. - type Future: Future, Error=()> + Send + 'static; + type Future: Future, Error = ()> + Send + 'static; /// A returned call future. - type CallFuture: Future, Error=()> + Send + 'static; + type CallFuture: Future, Error = ()> + Send + 'static; /// Method invoked on each request. /// Allows you to either respond directly (without executing RPC call) /// or do any additional work before and/or after processing the request. - fn on_request(&self, request: Request, meta: M, next: F) -> Either where + fn on_request(&self, request: Request, meta: M, next: F) -> Either + where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { Either::B(next(request, meta)) } @@ -25,18 +26,19 @@ pub trait Middleware: Send + Sync + 'static { /// Method invoked on each call inside a request. /// /// Allows you to either handle the call directly (without executing RPC call). - fn on_call(&self, call: Call, meta: M, next: F) -> Either where + fn on_call(&self, call: Call, meta: M, next: F) -> Either + where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { Either::B(next(call, meta)) } } /// Dummy future used as a noop result of middleware. -pub type NoopFuture = Box, Error=()> + Send>; +pub type NoopFuture = Box, Error = ()> + Send>; /// Dummy future used as a noop call result of middleware. -pub type NoopCallFuture = Box, Error=()> + Send>; +pub type NoopCallFuture = Box, Error = ()> + Send>; /// No-op middleware implementation #[derive(Debug, Default)] @@ -46,40 +48,40 @@ impl Middleware for Noop { type CallFuture = NoopCallFuture; } -impl, B: Middleware> - Middleware for (A, B) -{ +impl, B: Middleware> Middleware for (A, B) { type Future = Either; type CallFuture = Either; - fn on_request(&self, request: Request, meta: M, process: F) -> Either where + fn on_request(&self, request: Request, meta: M, process: F) -> Either + where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { repack(self.0.on_request(request, meta, |request, meta| { self.1.on_request(request, meta, &process) })) } - fn on_call(&self, call: Call, meta: M, process: F) -> Either where + fn on_call(&self, call: Call, meta: M, process: F) -> Either + where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { - repack(self.0.on_call(call, meta, |call, meta| { - self.1.on_call(call, meta, &process) - })) + repack( + self.0 + .on_call(call, meta, |call, meta| self.1.on_call(call, meta, &process)), + ) } } -impl, B: Middleware, C: Middleware> - Middleware for (A, B, C) -{ +impl, B: Middleware, C: Middleware> Middleware for (A, B, C) { type Future = Either>; type CallFuture = Either>; - fn on_request(&self, request: Request, meta: M, process: F) -> Either where + fn on_request(&self, request: Request, meta: M, process: F) -> Either + where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { repack(self.0.on_request(request, meta, |request, meta| { repack(self.1.on_request(request, meta, |request, meta| { @@ -88,27 +90,30 @@ impl, B: Middleware, C: Middleware> })) } - fn on_call(&self, call: Call, meta: M, process: F) -> Either where + fn on_call(&self, call: Call, meta: M, process: F) -> Either + where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { repack(self.0.on_call(call, meta, |call, meta| { - repack(self.1.on_call(call, meta, |call, meta| { - self.2.on_call(call, meta, &process) - })) + repack( + self.1 + .on_call(call, meta, |call, meta| self.2.on_call(call, meta, &process)), + ) })) } } -impl, B: Middleware, C: Middleware, D: Middleware> - Middleware for (A, B, C, D) +impl, B: Middleware, C: Middleware, D: Middleware> Middleware + for (A, B, C, D) { type Future = Either>>; type CallFuture = Either>>; - fn on_request(&self, request: Request, meta: M, process: F) -> Either where + fn on_request(&self, request: Request, meta: M, process: F) -> Either + where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { repack(self.0.on_request(request, meta, |request, meta| { repack(self.1.on_request(request, meta, |request, meta| { @@ -119,15 +124,17 @@ impl, B: Middleware, C: Middleware, D: Middl })) } - fn on_call(&self, call: Call, meta: M, process: F) -> Either where + fn on_call(&self, call: Call, meta: M, process: F) -> Either + where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error=()> + Send + 'static, + X: Future, Error = ()> + Send + 'static, { repack(self.0.on_call(call, meta, |call, meta| { repack(self.1.on_call(call, meta, |call, meta| { - repack(self.2.on_call(call, meta, |call, meta| { - self.3.on_call(call, meta, &process) - })) + repack( + self.2 + .on_call(call, meta, |call, meta| self.3.on_call(call, meta, &process)), + ) })) })) } diff --git a/core/src/types/error.rs b/core/src/types/error.rs index dea0866f6..8a6b11046 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -1,8 +1,8 @@ //! jsonrpc errors -use std::fmt; +use super::Value; use serde::de::{Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; -use super::Value; +use std::fmt; /// JSONRPC error code #[derive(Debug, PartialEq, Clone)] @@ -19,7 +19,7 @@ pub enum ErrorCode { /// Internal JSON-RPC error. InternalError, /// Reserved for implementation-defined server-errors. - ServerError(i64) + ServerError(i64), } impl ErrorCode { @@ -31,7 +31,7 @@ impl ErrorCode { ErrorCode::MethodNotFound => -32601, ErrorCode::InvalidParams => -32602, ErrorCode::InternalError => -32603, - ErrorCode::ServerError(code) => code + ErrorCode::ServerError(code) => code, } } @@ -64,7 +64,9 @@ impl From for ErrorCode { impl<'a> Deserialize<'a> for ErrorCode { fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { + where + D: Deserializer<'a>, + { let code: i64 = Deserialize::deserialize(deserializer)?; Ok(ErrorCode::from(code)) } @@ -72,7 +74,9 @@ impl<'a> Deserialize<'a> for ErrorCode { impl Serialize for ErrorCode { fn serialize(&self, serializer: S) -> Result - where S: Serializer { + where + S: Serializer, + { serializer.serialize_i64(self.code()) } } @@ -85,8 +89,8 @@ pub struct Error { /// Message pub message: String, /// Optional data - #[serde(skip_serializing_if = "Option::is_none")] - pub data: Option + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, } impl Error { @@ -95,7 +99,7 @@ impl Error { Error { message: code.description(), code, - data: None + data: None, } } @@ -115,7 +119,8 @@ impl Error { } /// Creates new `InvalidParams` - pub fn invalid_params(message: M) -> Self where + pub fn invalid_params(message: M) -> Self + where M: Into, { Error { @@ -126,9 +131,10 @@ impl Error { } /// Creates `InvalidParams` for given parameter, with details. - pub fn invalid_params_with_details(message: M, details: T) -> Error where + pub fn invalid_params_with_details(message: M, details: T) -> Error + where M: Into, - T: fmt::Debug + T: fmt::Debug, { Error { code: ErrorCode::InvalidParams, diff --git a/core/src/types/id.rs b/core/src/types/id.rs index b2abd3d1d..603c93be9 100644 --- a/core/src/types/id.rs +++ b/core/src/types/id.rs @@ -33,12 +33,22 @@ mod tests { let s = r#"[null, 0, 2, "3"]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![Id::Null, Id::Num(0), Id::Num(2), Id::Str("3".into())]); + assert_eq!( + deserialized, + vec![Id::Null, Id::Num(0), Id::Num(2), Id::Str("3".into())] + ); } #[test] fn id_serialization() { - let d = vec![Id::Null, Id::Num(0), Id::Num(2), Id::Num(3), Id::Str("3".to_owned()), Id::Str("test".to_owned())]; + let d = vec![ + Id::Null, + Id::Num(0), + Id::Num(2), + Id::Num(3), + Id::Str("3".to_owned()), + Id::Str("test".to_owned()), + ]; let serialized = serde_json::to_string(&d).unwrap(); assert_eq!(serialized, r#"[null,0,2,3,"3","test"]"#); } diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index d50c989fc..cb4a28e95 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -7,13 +7,13 @@ pub mod request; pub mod response; pub mod version; -pub use serde_json::Value; -pub use serde_json::value::to_value; pub use serde_json::to_string; +pub use serde_json::value::to_value; +pub use serde_json::Value; -pub use self::error::{ErrorCode, Error}; +pub use self::error::{Error, ErrorCode}; pub use self::id::Id; pub use self::params::Params; -pub use self::request::{Request, Call, MethodCall, Notification}; -pub use self::response::{Output, Response, Success, Failure}; +pub use self::request::{Call, MethodCall, Notification, Request}; +pub use self::response::{Failure, Output, Response, Success}; pub use self::version::Version; diff --git a/core/src/types/params.rs b/core/src/types/params.rs index 27bf4b4af..0e40eb6a9 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -1,10 +1,10 @@ //! jsonrpc params field -use serde::de::{DeserializeOwned}; +use serde::de::DeserializeOwned; use serde_json; use serde_json::value::from_value; -use super::{Value, Error}; +use super::{Error, Value}; /// Request parameters #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] @@ -20,16 +20,17 @@ pub enum Params { impl Params { /// Parse incoming `Params` into expected types. - pub fn parse(self) -> Result where D: DeserializeOwned { + pub fn parse(self) -> Result + where + D: DeserializeOwned, + { let value = match self { Params::Array(vec) => Value::Array(vec), Params::Map(map) => Value::Object(map), - Params::None => Value::Null + Params::None => Value::Null, }; - from_value(value).map_err(|e| { - Error::invalid_params(format!("Invalid params: {}.", e)) - }) + from_value(value).map_err(|e| Error::invalid_params(format!("Invalid params: {}.", e))) } /// Check for no params, returns Err if any params @@ -44,9 +45,9 @@ impl Params { #[cfg(test)] mod tests { - use serde_json; use super::Params; - use crate::types::{Value, Error, ErrorCode}; + use crate::types::{Error, ErrorCode, Value}; + use serde_json; #[test] fn params_deserialization() { @@ -56,12 +57,20 @@ mod tests { let mut map = serde_json::Map::new(); map.insert("key".to_string(), Value::String("value".to_string())); - assert_eq!(Params::Array(vec![ - Value::Null, Value::Bool(true), Value::from(-1), Value::from(4), - Value::from(2.3), Value::String("hello".to_string()), - Value::Array(vec![Value::from(0)]), Value::Object(map), - Value::Array(vec![]), - ]), deserialized); + assert_eq!( + Params::Array(vec![ + Value::Null, + Value::Bool(true), + Value::from(-1), + Value::from(4), + Value::from(2.3), + Value::String("hello".to_string()), + Value::Array(vec![Value::from(0)]), + Value::Object(map), + Value::Array(vec![]), + ]), + deserialized + ); } #[test] @@ -78,10 +87,16 @@ mod tests { // then assert_eq!(err1.code, ErrorCode::InvalidParams); - assert_eq!(err1.message, "Invalid params: invalid type: boolean `true`, expected a string."); + assert_eq!( + err1.message, + "Invalid params: invalid type: boolean `true`, expected a string." + ); assert_eq!(err1.data, None); assert_eq!(err2.code, ErrorCode::InvalidParams); - assert_eq!(err2.message, "Invalid params: invalid length 2, expected a tuple of size 3."); + assert_eq!( + err2.message, + "Invalid params: invalid length 2, expected a tuple of size 3." + ); assert_eq!(err2.data, None); } diff --git a/core/src/types/request.rs b/core/src/types/request.rs index 4cf4e6596..5514f8b7a 100644 --- a/core/src/types/request.rs +++ b/core/src/types/request.rs @@ -77,7 +77,7 @@ pub enum Request { /// Single request (call) Single(Call), /// Batch of requests (calls) - Batch(Vec) + Batch(Vec), } #[cfg(test)] @@ -94,11 +94,14 @@ mod tests { jsonrpc: Some(Version::V2), method: "update".to_owned(), params: Params::Array(vec![Value::from(1), Value::from(2)]), - id: Id::Num(1) + id: Id::Num(1), }; let serialized = serde_json::to_string(&m).unwrap(); - assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1}"#); + assert_eq!( + serialized, + r#"{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1}"# + ); } #[test] @@ -109,7 +112,7 @@ mod tests { let n = Notification { jsonrpc: Some(Version::V2), method: "update".to_owned(), - params: Params::Array(vec![Value::from(1), Value::from(2)]) + params: Params::Array(vec![Value::from(1), Value::from(2)]), }; let serialized = serde_json::to_string(&n).unwrap(); @@ -124,7 +127,7 @@ mod tests { let n = Call::Notification(Notification { jsonrpc: Some(Version::V2), method: "update".to_owned(), - params: Params::Array(vec![Value::from(1)]) + params: Params::Array(vec![Value::from(1)]), }); let serialized = serde_json::to_string(&n).unwrap(); @@ -140,18 +143,17 @@ mod tests { jsonrpc: Some(Version::V2), method: "update".to_owned(), params: Params::Array(vec![Value::from(1), Value::from(2)]), - id: Id::Num(1) + id: Id::Num(1), }), Call::Notification(Notification { jsonrpc: Some(Version::V2), method: "update".to_owned(), - params: Params::Array(vec![Value::from(1)]) - }) + params: Params::Array(vec![Value::from(1)]), + }), ]); let serialized = serde_json::to_string(&batch).unwrap(); assert_eq!(serialized, r#"[{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1},{"jsonrpc":"2.0","method":"update","params":[1]}]"#); - } #[test] @@ -162,20 +164,26 @@ mod tests { let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2]}"#; let deserialized: Notification = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Notification { - jsonrpc: Some(Version::V2), - method: "update".to_owned(), - params: Params::Array(vec![Value::from(1), Value::from(2)]) - }); + assert_eq!( + deserialized, + Notification { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::Array(vec![Value::from(1), Value::from(2)]) + } + ); let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#; let deserialized: Notification = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Notification { - jsonrpc: Some(Version::V2), - method: "foobar".to_owned(), - params: Params::None, - }); + assert_eq!( + deserialized, + Notification { + jsonrpc: Some(Version::V2), + method: "foobar".to_owned(), + params: Params::None, + } + ); let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1}"#; let deserialized: Result = serde_json::from_str(s); @@ -188,48 +196,62 @@ mod tests { 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: Some(Version::V2), - method: "update".to_owned(), - params: Params::Array(vec![Value::from(1)]) - })); + assert_eq!( + deserialized, + Call::Notification(Notification { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::Array(vec![Value::from(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: Some(Version::V2), - method: "update".to_owned(), - params: Params::Array(vec![Value::from(1)]), - id: Id::Num(1) - })); + assert_eq!( + deserialized, + Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::Array(vec![Value::from(1)]), + id: Id::Num(1) + }) + ); let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [], "id": 1}"#; let deserialized: Call = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Call::MethodCall(MethodCall { - jsonrpc: Some(Version::V2), - method: "update".to_owned(), - params: Params::Array(vec![]), - id: Id::Num(1) - })); + assert_eq!( + deserialized, + Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::Array(vec![]), + id: Id::Num(1) + }) + ); let s = r#"{"jsonrpc": "2.0", "method": "update", "params": null, "id": 1}"#; let deserialized: Call = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Call::MethodCall(MethodCall { - jsonrpc: Some(Version::V2), - method: "update".to_owned(), - params: Params::None, - id: Id::Num(1) - })); - + assert_eq!( + deserialized, + Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::None, + id: Id::Num(1) + }) + ); let s = r#"{"jsonrpc": "2.0", "method": "update", "id": 1}"#; let deserialized: Call = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Call::MethodCall(MethodCall { - jsonrpc: Some(Version::V2), - method: "update".to_owned(), - params: Params::None, - id: Id::Num(1) - })); + assert_eq!( + deserialized, + Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::None, + id: Id::Num(1) + }) + ); } #[test] @@ -238,20 +260,23 @@ mod tests { let s = r#"[{}, {"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1},{"jsonrpc": "2.0", "method": "update", "params": [1]}]"#; let deserialized: Request = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, Request::Batch(vec![ - Call::Invalid { id: Id::Null }, - Call::MethodCall(MethodCall { - jsonrpc: Some(Version::V2), - method: "update".to_owned(), - params: Params::Array(vec![Value::from(1), Value::from(2)]), - id: Id::Num(1) - }), - Call::Notification(Notification { - jsonrpc: Some(Version::V2), - method: "update".to_owned(), - params: Params::Array(vec![Value::from(1)]) - }) - ])) + assert_eq!( + deserialized, + Request::Batch(vec![ + Call::Invalid { id: Id::Null }, + Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::Array(vec![Value::from(1), Value::from(2)]), + id: Id::Num(1) + }), + Call::Notification(Notification { + jsonrpc: Some(Version::V2), + method: "update".to_owned(), + params: Params::Array(vec![Value::from(1)]) + }) + ]) + ) } #[test] @@ -261,7 +286,7 @@ mod tests { let s = r#"{"id":120,"method":"my_method","params":["foo", "bar"],"extra_field":[]}"#; let deserialized: Request = serde_json::from_str(s).unwrap(); match deserialized { - Request::Single(Call::Invalid { id: Id::Num(120) }) => {}, + Request::Single(Call::Invalid { id: Id::Num(120) }) => {} _ => panic!("Request wrongly deserialized: {:?}", deserialized), } } diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 60bd80152..484e2d226 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -1,29 +1,29 @@ //! jsonrpc response -use super::{Id, Value, Error, ErrorCode, Version}; -use crate::{Result as CoreResult}; +use super::{Error, ErrorCode, Id, Value, Version}; +use crate::Result as CoreResult; /// Successful response #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Success { /// Protocol version - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub jsonrpc: Option, /// Result pub result: Value, /// Correlation id - pub id: Id + pub id: Id, } /// Unsuccessful response #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Failure { /// Protocol Version - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub jsonrpc: Option, /// Error pub error: Error, /// Correlation id - pub id: Id + pub id: Id, } /// Represents output - failure or success @@ -40,16 +40,8 @@ impl Output { /// Creates new output given `Result`, `Id` and `Version`. pub fn from(result: CoreResult, id: Id, jsonrpc: Option) -> Self { match result { - Ok(result) => Output::Success(Success { - id, - jsonrpc, - result, - }), - Err(error) => Output::Failure(Failure { - id, - jsonrpc, - error, - }), + Ok(result) => Output::Success(Success { id, jsonrpc, result }), + Err(error) => Output::Failure(Failure { id, jsonrpc, error }), } } @@ -96,7 +88,7 @@ pub enum Response { /// Single response Single(Output), /// Response to batch request (batch of responses) - Batch(Vec) + Batch(Vec), } impl Response { @@ -106,7 +98,8 @@ impl Response { id: Id::Null, jsonrpc, error, - }.into() + } + .into() } } @@ -130,7 +123,7 @@ fn success_output_serialize() { let so = Output::Success(Success { jsonrpc: Some(Version::V2), result: Value::from(1), - id: Id::Num(1) + id: Id::Num(1), }); let serialized = serde_json::to_string(&so).unwrap(); @@ -145,11 +138,14 @@ fn success_output_deserialize() { let dso = r#"{"jsonrpc":"2.0","result":1,"id":1}"#; let deserialized: Output = serde_json::from_str(dso).unwrap(); - assert_eq!(deserialized, Output::Success(Success { - jsonrpc: Some(Version::V2), - result: Value::from(1), - id: Id::Num(1) - })); + assert_eq!( + deserialized, + Output::Success(Success { + jsonrpc: Some(Version::V2), + result: Value::from(1), + id: Id::Num(1) + }) + ); } #[test] @@ -159,11 +155,14 @@ fn failure_output_serialize() { let fo = Output::Failure(Failure { jsonrpc: Some(Version::V2), error: Error::parse_error(), - id: Id::Num(1) + id: Id::Num(1), }); let serialized = serde_json::to_string(&fo).unwrap(); - assert_eq!(serialized, r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}"#); + assert_eq!( + serialized, + r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}"# + ); } #[test] @@ -173,11 +172,14 @@ fn failure_output_serialize_jsonrpc_1() { let fo = Output::Failure(Failure { jsonrpc: None, error: Error::parse_error(), - id: Id::Num(1) + id: Id::Num(1), }); let serialized = serde_json::to_string(&fo).unwrap(); - assert_eq!(serialized, r#"{"error":{"code":-32700,"message":"Parse error"},"id":1}"#); + assert_eq!( + serialized, + r#"{"error":{"code":-32700,"message":"Parse error"},"id":1}"# + ); } #[test] @@ -187,11 +189,14 @@ fn failure_output_deserialize() { let dfo = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}"#; let deserialized: Output = serde_json::from_str(dfo).unwrap(); - assert_eq!(deserialized, Output::Failure(Failure { - jsonrpc: Some(Version::V2), - error: Error::parse_error(), - id: Id::Num(1) - })); + assert_eq!( + deserialized, + Output::Failure(Failure { + jsonrpc: Some(Version::V2), + error: Error::parse_error(), + id: Id::Num(1) + }) + ); } #[test] @@ -202,11 +207,14 @@ fn single_response_deserialize() { let dsr = r#"{"jsonrpc":"2.0","result":1,"id":1}"#; let deserialized: Response = serde_json::from_str(dsr).unwrap(); - assert_eq!(deserialized, Response::Single(Output::Success(Success { - jsonrpc: Some(Version::V2), - result: Value::from(1), - id: Id::Num(1) - }))); + assert_eq!( + deserialized, + Response::Single(Output::Success(Success { + jsonrpc: Some(Version::V2), + result: Value::from(1), + id: Id::Num(1) + })) + ); } #[test] @@ -217,16 +225,19 @@ fn batch_response_deserialize() { let dbr = r#"[{"jsonrpc":"2.0","result":1,"id":1},{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}]"#; let deserialized: Response = serde_json::from_str(dbr).unwrap(); - assert_eq!(deserialized, Response::Batch(vec![ - Output::Success(Success { - jsonrpc: Some(Version::V2), - result: Value::from(1), - id: Id::Num(1) - }), - Output::Failure(Failure { - jsonrpc: Some(Version::V2), - error: Error::parse_error(), - id: Id::Num(1) - }) - ])); + assert_eq!( + deserialized, + Response::Batch(vec![ + Output::Success(Success { + jsonrpc: Some(Version::V2), + result: Value::from(1), + id: Id::Num(1) + }), + Output::Failure(Failure { + jsonrpc: Some(Version::V2), + error: Error::parse_error(), + id: Id::Num(1) + }) + ]) + ); } diff --git a/core/src/types/version.rs b/core/src/types/version.rs index c5254949b..e3f51b3cb 100644 --- a/core/src/types/version.rs +++ b/core/src/types/version.rs @@ -1,6 +1,6 @@ //! jsonrpc version field -use serde::{Serialize, Serializer, Deserialize, Deserializer}; use serde::de::{self, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; @@ -8,21 +8,25 @@ use std::fmt; #[derive(Debug, PartialEq, Clone, Copy, Hash, Eq)] pub enum Version { /// JSONRPC 2.0 - V2 + V2, } impl Serialize for Version { fn serialize(&self, serializer: S) -> Result - where S: Serializer { + where + S: Serializer, + { match *self { - Version::V2 => serializer.serialize_str("2.0") + Version::V2 => serializer.serialize_str("2.0"), } } } impl<'a> Deserialize<'a> for Version { fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { + where + D: Deserializer<'a>, + { deserializer.deserialize_identifier(VersionVisitor) } } @@ -36,10 +40,13 @@ impl<'a> Visitor<'a> for VersionVisitor { formatter.write_str("a string") } - fn visit_str(self, value: &str) -> Result where E: de::Error { + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { match value { "2.0" => Ok(Version::V2), - _ => Err(de::Error::custom("invalid version")) + _ => Err(de::Error::custom("invalid version")), } } } diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs index ca2c39a71..41d0b0b84 100644 --- a/derive/examples/generic-trait-bounds.rs +++ b/derive/examples/generic-trait-bounds.rs @@ -1,15 +1,14 @@ -use serde_derive::{Serialize, Deserialize}; +use serde_derive::{Deserialize, Serialize}; -use jsonrpc_core::{IoHandler, Error, Result}; use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; // One is both parameter and a result so requires both Serialize and DeserializeOwned // Two is only a parameter so only requires DeserializeOwned // Three is only a result so only requires Serialize #[rpc] -pub trait Rpc -{ +pub trait Rpc { /// Get One type. #[rpc(name = "getOne")] fn one(&self) -> Result; @@ -29,7 +28,9 @@ pub trait Rpc struct RpcImpl; #[derive(Serialize, Deserialize)] -struct InAndOut { foo: u64 } +struct InAndOut { + foo: u64, +} #[derive(Deserialize)] struct In {} #[derive(Serialize)] @@ -49,7 +50,7 @@ impl Rpc for RpcImpl { } fn call(&self, num: InAndOut) -> FutureResult<(InAndOut, u64), Error> { - crate::future::finished((InAndOut {foo: num.foo + 999}, num.foo)) + crate::future::finished((InAndOut { foo: num.foo + 999 }, num.foo)) } } @@ -58,4 +59,3 @@ fn main() { io.extend_with(Rpc::to_delegate(RpcImpl)); } - diff --git a/derive/examples/generic-trait.rs b/derive/examples/generic-trait.rs index 89504d56f..ec98cb78f 100644 --- a/derive/examples/generic-trait.rs +++ b/derive/examples/generic-trait.rs @@ -1,7 +1,7 @@ use jsonrpc_core; -use jsonrpc_core::{IoHandler, Error, Result}; use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; #[rpc] @@ -36,7 +36,6 @@ impl Rpc for RpcImpl { } } - fn main() { let mut io = IoHandler::new(); let rpc = RpcImpl; diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 96dd0647c..0c3279981 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; -use jsonrpc_core::{futures, MetaIoHandler, Metadata, Error, Value, Result}; use jsonrpc_core::futures::future::FutureResult; +use jsonrpc_core::{futures, Error, MetaIoHandler, Metadata, Result, Value}; use jsonrpc_derive::rpc; #[derive(Clone)] @@ -37,7 +37,9 @@ struct RpcImpl; impl Rpc for RpcImpl { type Metadata = Meta; - fn one(&self) -> Result { Ok(100) } + fn one(&self) -> Result { + Ok(100) + } fn add(&self, a: u64, b: u64) -> Result { Ok(a + b) @@ -56,15 +58,14 @@ impl Rpc for RpcImpl { } } - fn main() { let mut io = MetaIoHandler::default(); let rpc = RpcImpl; io.extend_with(rpc.to_delegate()); - let server = jsonrpc_tcp_server::ServerBuilder - ::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| { + let server = + jsonrpc_tcp_server::ServerBuilder::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| { Meta(format!("{}", context.peer_addr)) }) .start(&"0.0.0.0:3030".parse().unwrap()) diff --git a/derive/examples/pubsub-macros.rs b/derive/examples/pubsub-macros.rs index 4104b8bed..65b650516 100644 --- a/derive/examples/pubsub-macros.rs +++ b/derive/examples/pubsub-macros.rs @@ -1,12 +1,12 @@ -use std::thread; -use std::sync::{atomic, Arc, RwLock}; use std::collections::HashMap; +use std::sync::{atomic, Arc, RwLock}; +use std::thread; -use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_core::futures::Future; +use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_derive::rpc; -use jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId}; use jsonrpc_pubsub::typed; +use jsonrpc_pubsub::{PubSubHandler, Session, SubscriptionId}; #[rpc] pub trait Rpc { @@ -31,11 +31,13 @@ impl Rpc for RpcImpl { fn subscribe(&self, _meta: Self::Metadata, subscriber: typed::Subscriber, param: u64) { if param != 10 { - subscriber.reject(Error { - code: ErrorCode::InvalidParams, - message: "Rejecting subscription - invalid parameters provided.".into(), - data: None, - }).unwrap(); + subscriber + .reject(Error { + code: ErrorCode::InvalidParams, + message: "Rejecting subscription - invalid parameters provided.".into(), + data: None, + }) + .unwrap(); return; } @@ -59,28 +61,27 @@ impl Rpc for RpcImpl { } } - fn main() { let mut io = PubSubHandler::default(); let rpc = RpcImpl::default(); let active_subscriptions = rpc.active.clone(); - thread::spawn(move || { - loop { - { - let subscribers = active_subscriptions.read().unwrap(); - for sink in subscribers.values() { - let _ = sink.notify(Ok("Hello World!".into())).wait(); - } + thread::spawn(move || loop { + { + let subscribers = active_subscriptions.read().unwrap(); + for sink in subscribers.values() { + let _ = sink.notify(Ok("Hello World!".into())).wait(); } - thread::sleep(::std::time::Duration::from_secs(1)); } + thread::sleep(::std::time::Duration::from_secs(1)); }); io.extend_with(rpc.to_delegate()); - let server = jsonrpc_tcp_server::ServerBuilder - ::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(context.sender.clone()))) + let server = + jsonrpc_tcp_server::ServerBuilder::with_meta_extractor(io, |context: &jsonrpc_tcp_server::RequestContext| { + Arc::new(Session::new(context.sender.clone())) + }) .start(&"0.0.0.0:3030".parse().unwrap()) .expect("Server must start with no issues"); diff --git a/derive/examples/std.rs b/derive/examples/std.rs index dcab94e00..da12d2a1b 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -1,5 +1,5 @@ -use jsonrpc_core::{IoHandler, Error, Result}; use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; #[rpc] diff --git a/derive/src/lib.rs b/derive/src/lib.rs index c5d26f706..406721a13 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -144,7 +144,7 @@ mod to_delegate; pub fn rpc(_args: TokenStream, input: TokenStream) -> TokenStream { let input_toks = parse_macro_input!(input as syn::Item); - match rpc_trait::rpc_impl( input_toks) { + match rpc_trait::rpc_impl(input_toks) { Ok(output) => output.into(), Err(err) => err.to_compile_error().into(), } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 09286e049..c34b46cdc 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -1,4 +1,7 @@ -use syn::{Error, Result, visit::{self, Visit}}; +use syn::{ + visit::{self, Visit}, + Error, Result, +}; #[derive(Clone, Debug)] pub struct RpcMethodAttribute { @@ -10,8 +13,13 @@ pub struct RpcMethodAttribute { #[derive(Clone, Debug)] pub enum AttributeKind { - Rpc { has_metadata: bool }, - PubSub { subscription_name: String, kind: PubSubMethodKind } + Rpc { + has_metadata: bool, + }, + PubSub { + subscription_name: String, + kind: PubSubMethodKind, + }, } #[derive(Clone, Debug)] @@ -38,7 +46,8 @@ const NEITHER_SUB_OR_UNSUB_ERR: &str = "pubsub attribute not annotated with eith impl RpcMethodAttribute { pub fn parse_attr(method: &syn::TraitItemMethod) -> Result> { - let attrs = method.attrs + let attrs = method + .attrs .iter() .filter_map(Self::parse_meta) .collect::>>()?; @@ -53,63 +62,53 @@ impl RpcMethodAttribute { fn parse_meta(attr: &syn::Attribute) -> Option> { match attr.parse_meta().and_then(validate_attribute_meta) { Ok(ref meta) => { - let attr_kind = - match meta.name().to_string().as_ref() { - RPC_ATTR_NAME => { - let has_metadata = get_meta_list(meta) - .map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); - Some(Ok(AttributeKind::Rpc { has_metadata })) - }, - PUB_SUB_ATTR_NAME => Some(Self::parse_pubsub(meta)), - _ => None, - }; - attr_kind.map(|kind| kind.and_then(|kind| { - get_meta_list(meta) - .and_then(|ml| get_name_value(RPC_NAME_KEY, ml)) - .map_or( - Err(Error::new_spanned(attr, MISSING_NAME_ERR)), - |name| { - let aliases = get_meta_list(meta) - .map_or(Vec::new(), |ml| get_aliases(ml)); + let attr_kind = match meta.name().to_string().as_ref() { + RPC_ATTR_NAME => { + let has_metadata = + get_meta_list(meta).map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); + Some(Ok(AttributeKind::Rpc { has_metadata })) + } + PUB_SUB_ATTR_NAME => Some(Self::parse_pubsub(meta)), + _ => None, + }; + attr_kind.map(|kind| { + kind.and_then(|kind| { + get_meta_list(meta) + .and_then(|ml| get_name_value(RPC_NAME_KEY, ml)) + .map_or(Err(Error::new_spanned(attr, MISSING_NAME_ERR)), |name| { + let aliases = get_meta_list(meta).map_or(Vec::new(), |ml| get_aliases(ml)); Ok(RpcMethodAttribute { attr: attr.clone(), name, aliases, - kind + kind, }) }) - })) - }, + }) + }) + } Err(err) => Some(Err(err)), } } fn parse_pubsub(meta: &syn::Meta) -> Result { - let name_and_list = get_meta_list(meta) - .and_then(|ml| - get_name_value(SUBSCRIPTION_NAME_KEY, ml).map(|name| (name, ml)) - ); - - name_and_list.map_or( - Err(Error::new_spanned(meta, MISSING_SUB_NAME_ERR)), - |(sub_name, ml)| { - let is_subscribe = has_meta_word(SUBSCRIBE_META_WORD, ml); - let is_unsubscribe = has_meta_word(UNSUBSCRIBE_META_WORD, ml); - let kind = match (is_subscribe, is_unsubscribe) { - (true, false) => - Ok(PubSubMethodKind::Subscribe), - (false, true) => - Ok(PubSubMethodKind::Unsubscribe), - (true, true) => - Err(Error::new_spanned(meta, BOTH_SUB_AND_UNSUB_ERR)), - (false, false) => - Err(Error::new_spanned(meta, NEITHER_SUB_OR_UNSUB_ERR)), - }; - kind.map(|kind| AttributeKind::PubSub { - subscription_name: sub_name, - kind, - }) + let name_and_list = + get_meta_list(meta).and_then(|ml| get_name_value(SUBSCRIPTION_NAME_KEY, ml).map(|name| (name, ml))); + + name_and_list.map_or(Err(Error::new_spanned(meta, MISSING_SUB_NAME_ERR)), |(sub_name, ml)| { + let is_subscribe = has_meta_word(SUBSCRIBE_META_WORD, ml); + let is_unsubscribe = has_meta_word(UNSUBSCRIBE_META_WORD, ml); + let kind = match (is_subscribe, is_unsubscribe) { + (true, false) => Ok(PubSubMethodKind::Subscribe), + (false, true) => Ok(PubSubMethodKind::Unsubscribe), + (true, true) => Err(Error::new_spanned(meta, BOTH_SUB_AND_UNSUB_ERR)), + (false, false) => Err(Error::new_spanned(meta, NEITHER_SUB_OR_UNSUB_ERR)), + }; + kind.map(|kind| AttributeKind::PubSub { + subscription_name: sub_name, + kind, }) + }) } pub fn is_pubsub(&self) -> bool { @@ -132,7 +131,7 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { match meta { syn::Meta::List(list) => self.meta_list_names.push(list.ident.to_string()), syn::Meta::Word(ident) => self.meta_words.push(ident.to_string()), - syn::Meta::NameValue(nv) => self.name_value_names.push(nv.ident.to_string()) + syn::Meta::NameValue(nv) => self.name_value_names.push(nv.ident.to_string()), } } } @@ -145,12 +144,16 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD])?; validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) - }, + } PUB_SUB_ATTR_NAME => { - validate_idents(&meta, &visitor.meta_words, &[SUBSCRIBE_META_WORD, UNSUBSCRIBE_META_WORD])?; + validate_idents( + &meta, + &visitor.meta_words, + &[SUBSCRIBE_META_WORD, UNSUBSCRIBE_META_WORD], + )?; validate_idents(&meta, &visitor.name_value_names, &[SUBSCRIPTION_NAME_KEY, RPC_NAME_KEY])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) - }, + } _ => Ok(meta), // ignore other attributes - compiler will catch unknown ones } } @@ -165,7 +168,12 @@ fn validate_idents(meta: &syn::Meta, attr_idents: &[String], valid: &[&str]) -> Ok(meta.clone()) } else { let expected = format!("Expected '{}'", valid.join(", ")); - let msg = format!("{} '{}'. {}", INVALID_ATTR_PARAM_NAMES_ERR, invalid_meta_words.join(", "), expected); + let msg = format!( + "{} '{}'. {}", + INVALID_ATTR_PARAM_NAMES_ERR, + invalid_meta_words.join(", "), + expected + ); Err(Error::new_spanned(meta, msg)) } } @@ -179,41 +187,37 @@ fn get_meta_list(meta: &syn::Meta) -> Option<&syn::MetaList> { } fn get_name_value(key: &str, ml: &syn::MetaList) -> Option { - ml.nested - .iter() - .find_map(|nested| - if let syn::NestedMeta::Meta(syn::Meta::NameValue(mnv)) = nested { - if mnv.ident == key { - if let syn::Lit::Str(ref lit) = mnv.lit { - Some(lit.value()) - } else { - None - } + ml.nested.iter().find_map(|nested| { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(mnv)) = nested { + if mnv.ident == key { + if let syn::Lit::Str(ref lit) = mnv.lit { + Some(lit.value()) } else { None } } else { None } - ) + } else { + None + } + }) } fn has_meta_word(word: &str, ml: &syn::MetaList) -> bool { - ml.nested - .iter() - .any(|nested| - if let syn::NestedMeta::Meta(syn::Meta::Word(w)) = nested { - w == word - } else { - false - } - ) + ml.nested.iter().any(|nested| { + if let syn::NestedMeta::Meta(syn::Meta::Word(w)) = nested { + w == word + } else { + false + } + }) } fn get_aliases(ml: &syn::MetaList) -> Vec { ml.nested .iter() - .find_map(|nested| + .find_map(|nested| { if let syn::NestedMeta::Meta(syn::Meta::List(list)) = nested { if list.ident == ALIASES_KEY { Some(list) @@ -223,8 +227,8 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { } else { None } - ) - .map_or(Vec::new(), |list| + }) + .map_or(Vec::new(), |list| { list.nested .iter() .filter_map(|nm| { @@ -235,5 +239,5 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { } }) .collect() - ) + }) } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 630b54389..ea357d03f 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -1,22 +1,24 @@ -use std::collections::HashMap; +use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute}; +use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod}; use proc_macro2::Span; use quote::quote; +use std::collections::HashMap; use syn::{ - parse_quote, Token, punctuated::Punctuated, - fold::{self, Fold}, Result, Error, Ident + fold::{self, Fold}, + parse_quote, + punctuated::Punctuated, + Error, Ident, Result, Token, }; -use crate::rpc_attr::{RpcMethodAttribute, PubSubMethodKind, AttributeKind}; -use crate::to_delegate::{RpcMethod, MethodRegistration, generate_trait_item_method}; const METADATA_TYPE: &str = "Metadata"; const MISSING_SUBSCRIBE_METHOD_ERR: &str = "Can't find subscribe method, expected a method annotated with `subscribe` \ - e.g. `#[pubsub(subscription = \"hello\", subscribe, name = \"hello_subscribe\")]`"; + e.g. `#[pubsub(subscription = \"hello\", subscribe, name = \"hello_subscribe\")]`"; const MISSING_UNSUBSCRIBE_METHOD_ERR: &str = "Can't find unsubscribe method, expected a method annotated with `unsubscribe` \ - e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`"; + e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`"; const RPC_MOD_NAME_PREFIX: &str = "rpc_impl_"; @@ -31,8 +33,7 @@ impl<'a> Fold for RpcTrait { let mut foldable_method = method.clone(); // strip rpc attributes foldable_method.attrs.retain(|a| { - let rpc_method = - self.methods.iter().find(|m| m.trait_item == method); + let rpc_method = self.methods.iter().find(|m| m.trait_item == method); rpc_method.map_or(true, |rpc| rpc.attr.attr != *a) }); fold::fold_trait_item_method(self, foldable_method) @@ -54,16 +55,13 @@ impl<'a> Fold for RpcTrait { } fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrait, bool)> { - let methods_result: Result> = item_trait.items + let methods_result: Result> = item_trait + .items .iter() .filter_map(|trait_item| { if let syn::TraitItem::Method(method) = trait_item { match RpcMethodAttribute::parse_attr(method) { - Ok(Some(attr)) => - Some(Ok(RpcMethod::new( - attr, - method.clone(), - ))), + Ok(Some(attr)) => Some(Ok(RpcMethod::new(attr, method.clone()))), Ok(None) => None, // non rpc annotated trait method Err(err) => Some(Err(syn::Error::new_spanned(method, err))), } @@ -74,7 +72,11 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai .collect(); let methods = methods_result?; let has_pubsub_methods = methods.iter().any(RpcMethod::is_pubsub); - let mut rpc_trait = RpcTrait { methods: methods.clone(), has_pubsub_methods, has_metadata: false }; + let mut rpc_trait = RpcTrait { + methods: methods.clone(), + has_pubsub_methods, + has_metadata: false, + }; let mut item_trait = fold::fold_item_trait(&mut rpc_trait, item_trait.clone()); let mut pubsub_method_pairs: HashMap, Option)> = HashMap::new(); @@ -82,12 +84,14 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai for method in methods.iter() { match &method.attr().kind { - AttributeKind::Rpc { has_metadata } => - method_registrations.push(MethodRegistration::Standard { - method: method.clone(), - has_metadata: *has_metadata - }), - AttributeKind::PubSub { subscription_name, kind } => { + AttributeKind::Rpc { has_metadata } => method_registrations.push(MethodRegistration::Standard { + method: method.clone(), + has_metadata: *has_metadata, + }), + AttributeKind::PubSub { + subscription_name, + kind, + } => { let (ref mut sub, ref mut unsub) = pubsub_method_pairs .entry(subscription_name.clone()) .or_insert((None, None)); @@ -96,45 +100,65 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai if sub.is_none() { *sub = Some(method.clone()) } else { - return Err(syn::Error::new_spanned(&method.trait_item, - format!("Subscription '{}' subscribe method is already defined", subscription_name))) + return Err(syn::Error::new_spanned( + &method.trait_item, + format!( + "Subscription '{}' subscribe method is already defined", + subscription_name + ), + )); } - }, + } PubSubMethodKind::Unsubscribe => { if unsub.is_none() { *unsub = Some(method.clone()) } else { - return Err(syn::Error::new_spanned(&method.trait_item, - format!("Subscription '{}' unsubscribe method is already defined", subscription_name))) + return Err(syn::Error::new_spanned( + &method.trait_item, + format!( + "Subscription '{}' unsubscribe method is already defined", + subscription_name + ), + )); } - }, + } } - }, + } } } for (name, pair) in pubsub_method_pairs { match pair { - (Some(subscribe), Some(unsubscribe)) => - method_registrations.push(MethodRegistration::PubSub { - name: name.clone(), - subscribe: subscribe.clone(), - unsubscribe: unsubscribe.clone() - }), - (Some(method), None) => return Err(syn::Error::new_spanned(&method.trait_item, - format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR))), - (None, Some(method)) => return Err(syn::Error::new_spanned(&method.trait_item, - format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR))), + (Some(subscribe), Some(unsubscribe)) => method_registrations.push(MethodRegistration::PubSub { + name: name.clone(), + subscribe: subscribe.clone(), + unsubscribe: unsubscribe.clone(), + }), + (Some(method), None) => { + return Err(syn::Error::new_spanned( + &method.trait_item, + format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR), + )); + } + (None, Some(method)) => { + return Err(syn::Error::new_spanned( + &method.trait_item, + format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR), + )); + } (None, None) => unreachable!(), } } - let to_delegate_method = - generate_trait_item_method(&method_registrations, &item_trait, rpc_trait.has_metadata, has_pubsub_methods)?; + let to_delegate_method = generate_trait_item_method( + &method_registrations, + &item_trait, + rpc_trait.has_metadata, + has_pubsub_methods, + )?; item_trait.items.push(syn::TraitItem::Method(to_delegate_method)); - let trait_bounds: Punctuated = - parse_quote!(Sized + Send + Sync + 'static); + let trait_bounds: Punctuated = parse_quote!(Sized + Send + Sync + 'static); item_trait.supertraits.extend(trait_bounds); Ok((item_trait, has_pubsub_methods)) @@ -149,7 +173,12 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { pub fn rpc_impl(input: syn::Item) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, - item => return Err(syn::Error::new_spanned(item, "The #[rpc] custom attribute only works with trait declarations")), + item => { + return Err(syn::Error::new_spanned( + item, + "The #[rpc] custom attribute only works with trait declarations", + )); + } }; let (rpc_trait, has_pubsub_methods) = generate_rpc_item_trait(&rpc_trait)?; @@ -163,13 +192,11 @@ pub fn rpc_impl(input: syn::Item) -> Result { .map_err(|e| Error::new(Span::call_site(), &e)) }; - let optional_pubsub_import = - if has_pubsub_methods { - crate_name("jsonrpc-pubsub") - .map(|pubsub_name| quote!(use #pubsub_name as _jsonrpc_pubsub;)) - } else { - Ok(quote!()) - }?; + let optional_pubsub_import = if has_pubsub_methods { + crate_name("jsonrpc-pubsub").map(|pubsub_name| quote!(use #pubsub_name as _jsonrpc_pubsub;)) + } else { + Ok(quote!()) + }?; let core_name = crate_name("jsonrpc-core")?; let serde_name = crate_name("serde")?; diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 6cc08aff5..fa1b0043c 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -1,22 +1,24 @@ use std::collections::HashSet; +use crate::rpc_attr::RpcMethodAttribute; use quote::quote; use syn::{ - parse_quote, Token, punctuated::Punctuated, - visit::{self, Visit}, Result, + parse_quote, + punctuated::Punctuated, + visit::{self, Visit}, + Result, Token, }; -use crate::rpc_attr::RpcMethodAttribute; pub enum MethodRegistration { Standard { method: RpcMethod, - has_metadata: bool + has_metadata: bool, }, PubSub { name: String, subscribe: RpcMethod, unsubscribe: RpcMethod, - } + }, } impl MethodRegistration { @@ -24,12 +26,11 @@ impl MethodRegistration { match self { MethodRegistration::Standard { method, has_metadata } => { let rpc_name = &method.name(); - let add_method = - if *has_metadata { - quote!(add_method_with_meta) - } else { - quote!(add_method) - }; + let add_method = if *has_metadata { + quote!(add_method_with_meta) + } else { + quote!(add_method) + }; let closure = method.generate_delegate_closure(false)?; let add_aliases = method.generate_add_aliases(); @@ -37,24 +38,27 @@ impl MethodRegistration { del.#add_method(#rpc_name, #closure); #add_aliases }) - }, - MethodRegistration::PubSub { name, subscribe, unsubscribe } => { + } + MethodRegistration::PubSub { + name, + subscribe, + unsubscribe, + } => { let sub_name = subscribe.name(); let sub_closure = subscribe.generate_delegate_closure(true)?; let sub_aliases = subscribe.generate_add_aliases(); let unsub_name = unsubscribe.name(); let unsub_method_ident = unsubscribe.ident(); - let unsub_closure = - quote! { - move |base, id, meta| { - use self::_futures::{Future, IntoFuture}; - Self::#unsub_method_ident(base, meta, id).into_future() - .map(|value| _jsonrpc_core::to_value(value) - .expect("Expected always-serializable type; qed")) - .map_err(Into::into) - } - }; + let unsub_closure = quote! { + move |base, id, meta| { + use self::_futures::{Future, IntoFuture}; + Self::#unsub_method_ident(base, meta, id).into_future() + .map(|value| _jsonrpc_core::to_value(value) + .expect("Expected always-serializable type; qed")) + .map_err(Into::into) + } + }; let unsub_aliases = unsubscribe.generate_add_aliases(); Ok(quote! { @@ -66,7 +70,7 @@ impl MethodRegistration { #sub_aliases #unsub_aliases }) - }, + } } } } @@ -76,7 +80,9 @@ const METADATA_CLOSURE_ARG: &str = "meta"; const SUBSCRIBER_CLOSURE_ARG: &str = "subscriber"; // tuples are limited to 16 fields: the maximum supported by `serde::Deserialize` -const TUPLE_FIELD_NAMES: [&str; 16] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"]; +const TUPLE_FIELD_NAMES: [&str; 16] = [ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", +]; pub fn generate_trait_item_method( methods: &[MethodRegistration], @@ -84,43 +90,43 @@ pub fn generate_trait_item_method( has_metadata: bool, has_pubsub_methods: bool, ) -> Result { - let io_delegate_type = - if has_pubsub_methods { - quote!(_jsonrpc_pubsub::IoDelegate) - } else { - quote!(_jsonrpc_core::IoDelegate) - }; + let io_delegate_type = if has_pubsub_methods { + quote!(_jsonrpc_pubsub::IoDelegate) + } else { + quote!(_jsonrpc_core::IoDelegate) + }; let add_methods = methods .iter() .map(MethodRegistration::generate) .collect::>>()?; - let to_delegate_body = - quote! { - let mut del = #io_delegate_type::new(self.into()); - #(#add_methods)* - del - }; - - let method: syn::TraitItemMethod = - if has_metadata { - parse_quote! { - /// Create an `IoDelegate`, wiring rpc calls to the trait methods. - fn to_delegate(self) -> #io_delegate_type { - #to_delegate_body - } + let to_delegate_body = quote! { + let mut del = #io_delegate_type::new(self.into()); + #(#add_methods)* + del + }; + + let method: syn::TraitItemMethod = if has_metadata { + parse_quote! { + /// Create an `IoDelegate`, wiring rpc calls to the trait methods. + fn to_delegate(self) -> #io_delegate_type { + #to_delegate_body } - } else { - parse_quote! { - /// Create an `IoDelegate`, wiring rpc calls to the trait methods. - fn to_delegate(self) -> #io_delegate_type { - #to_delegate_body - } + } + } else { + parse_quote! { + /// Create an `IoDelegate`, wiring rpc calls to the trait methods. + fn to_delegate(self) -> #io_delegate_type { + #to_delegate_body } - }; + } + }; let predicates = generate_where_clause_serialization_predicates(&trait_item); let mut method = method.clone(); - method.sig.decl.generics + method + .sig + .decl + .generics .make_where_clause() .predicates .extend(predicates); @@ -138,7 +144,9 @@ impl RpcMethod { RpcMethod { attr, trait_item } } - pub fn attr(&self) -> &RpcMethodAttribute { &self.attr } + pub fn attr(&self) -> &RpcMethodAttribute { + &self.attr + } pub fn name(&self) -> &str { &self.attr.name @@ -153,32 +161,35 @@ impl RpcMethod { } fn generate_delegate_closure(&self, is_subscribe: bool) -> Result { - let mut param_types: Vec<_> = - self.trait_item.sig.decl.inputs - .iter() - .cloned() - .filter_map(|arg| { - match arg { - syn::FnArg::Captured(arg_captured) => Some(arg_captured.ty), - syn::FnArg::Ignored(ty) => Some(ty), - _ => None, - } - }) - .collect(); + let mut param_types: Vec<_> = self + .trait_item + .sig + .decl + .inputs + .iter() + .cloned() + .filter_map(|arg| match arg { + syn::FnArg::Captured(arg_captured) => Some(arg_captured.ty), + syn::FnArg::Ignored(ty) => Some(ty), + _ => None, + }) + .collect(); // special args are those which are not passed directly via rpc params: metadata, subscriber let special_args = Self::special_args(¶m_types); - param_types.retain(|ty| - special_args.iter().find(|(_,sty)| sty == ty).is_none()); + param_types.retain(|ty| special_args.iter().find(|(_, sty)| sty == ty).is_none()); if param_types.len() > TUPLE_FIELD_NAMES.len() { return Err(syn::Error::new_spanned( &self.trait_item, - &format!("Maximum supported number of params is {}", TUPLE_FIELD_NAMES.len()) + &format!("Maximum supported number of params is {}", TUPLE_FIELD_NAMES.len()), )); } - let tuple_fields : &Vec<_> = - &(TUPLE_FIELD_NAMES.iter().take(param_types.len()).map(|name| ident(name)).collect()); + let tuple_fields: &Vec<_> = &(TUPLE_FIELD_NAMES + .iter() + .take(param_types.len()) + .map(|name| ident(name)) + .collect()); let param_types = ¶m_types; let parse_params = { // last arguments that are `Option`-s are optional 'trailing' arguments @@ -200,32 +211,31 @@ impl RpcMethod { let closure_args = quote! { base, params, #(#extra_closure_args), * }; let method_sig = quote! { fn(&Self, #(#extra_method_types, ) * #(#param_types), *) #result }; let method_call = quote! { (base, #(#extra_closure_args, )* #(#tuple_fields), *) }; - let match_params = - if is_subscribe { - quote! { - Ok((#(#tuple_fields, )*)) => { - let subscriber = _jsonrpc_pubsub::typed::Subscriber::new(subscriber); - (method)#method_call - }, - Err(e) => { - let _ = subscriber.reject(e); - return - } - } - } else { - quote! { - Ok((#(#tuple_fields, )*)) => { - use self::_futures::{Future, IntoFuture}; - let fut = (method)#method_call - .into_future() - .map(|value| _jsonrpc_core::to_value(value) - .expect("Expected always-serializable type; qed")) - .map_err(Into::into as fn(_) -> _jsonrpc_core::Error); - _futures::future::Either::A(fut) - }, - Err(e) => _futures::future::Either::B(_futures::failed(e)), + let match_params = if is_subscribe { + quote! { + Ok((#(#tuple_fields, )*)) => { + let subscriber = _jsonrpc_pubsub::typed::Subscriber::new(subscriber); + (method)#method_call + }, + Err(e) => { + let _ = subscriber.reject(e); + return } - }; + } + } else { + quote! { + Ok((#(#tuple_fields, )*)) => { + use self::_futures::{Future, IntoFuture}; + let fut = (method)#method_call + .into_future() + .map(|value| _jsonrpc_core::to_value(value) + .expect("Expected always-serializable type; qed")) + .map_err(Into::into as fn(_) -> _jsonrpc_core::Error); + _futures::future::Either::A(fut) + }, + Err(e) => _futures::future::Either::B(_futures::failed(e)), + } + }; Ok(quote! { move |#closure_args| { @@ -239,9 +249,13 @@ impl RpcMethod { } fn special_args(param_types: &[syn::Type]) -> Vec<(syn::Ident, syn::Type)> { - let meta_arg = - param_types.first().and_then(|ty| - if *ty == parse_quote!(Self::Metadata) { Some(ty.clone()) } else { None }); + let meta_arg = param_types.first().and_then(|ty| { + if *ty == parse_quote!(Self::Metadata) { + Some(ty.clone()) + } else { + None + } + }); let subscriber_arg = param_types.get(1).and_then(|ty| { if let syn::Type::Path(path) = ty { if path.path.segments.iter().any(|s| s.ident == SUBCRIBER_TYPE_IDENT) { @@ -279,7 +293,9 @@ impl RpcMethod { let passed_param_types = ¶m_types[..passed_args_num]; let passed_tuple_fields = &tuple_fields[..passed_args_num]; let missed_args_num = total_args_num - passed_args_num; - let missed_params_values = ::std::iter::repeat(quote! { None }).take(missed_args_num).collect::>(); + let missed_params_values = ::std::iter::repeat(quote! { None }) + .take(missed_args_num) + .collect::>(); if passed_args_num == 0 { quote! { @@ -295,7 +311,8 @@ impl RpcMethod { .map_err(Into::into) } } - }).collect::>(); + }) + .collect::>(); quote! { let passed_args_num = match params { @@ -319,11 +336,13 @@ impl RpcMethod { fn generate_add_aliases(&self) -> proc_macro2::TokenStream { let name = self.name(); - let add_aliases: Vec<_> = self.attr.aliases + let add_aliases: Vec<_> = self + .attr + .aliases .iter() .map(|alias| quote! { del.add_alias(#alias, #name); }) .collect(); - quote!{ #(#add_aliases)* } + quote! { #(#add_aliases)* } } } @@ -333,7 +352,8 @@ fn ident(s: &str) -> syn::Ident { fn is_option_type(ty: &syn::Type) -> bool { if let syn::Type::Path(path) = ty { - path.path.segments + path.path + .segments .first() .map(|t| t.value().ident == "Option") .unwrap_or(false) @@ -365,9 +385,7 @@ fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) - if self.visiting_return_type && self.trait_generics.contains(&segment.ident) { self.serialize_type_params.insert(segment.ident.clone()); } - if self.visiting_fn_arg && - self.trait_generics.contains(&segment.ident) && - !self.visiting_subscriber_arg { + if self.visiting_fn_arg && self.trait_generics.contains(&segment.ident) && !self.visiting_subscriber_arg { self.deserialize_type_params.insert(segment.ident.clone()); } self.visiting_subscriber_arg = self.visiting_fn_arg && segment.ident == SUBCRIBER_TYPE_IDENT; @@ -383,12 +401,15 @@ fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) - let mut visitor = FindTyParams::default(); visitor.visit_item_trait(item_trait); - item_trait.generics + item_trait + .generics .type_params() .map(|ty| { - let ty_path = syn::TypePath { qself: None, path: ty.ident.clone().into() }; - let mut bounds: Punctuated = - parse_quote!(Send + Sync + 'static); + let ty_path = syn::TypePath { + qself: None, + path: ty.ident.clone().into(), + }; + let mut bounds: Punctuated = parse_quote!(Send + Sync + 'static); // add json serialization trait bounds if visitor.serialize_type_params.contains(&ty.ident) { bounds.push(parse_quote!(_serde::Serialize)) diff --git a/derive/tests/compiletests.rs b/derive/tests/compiletests.rs index 00ca38cf9..ae7f4fbe3 100644 --- a/derive/tests/compiletests.rs +++ b/derive/tests/compiletests.rs @@ -9,9 +9,10 @@ fn run_mode(mode: &'static str) { config.src_base = PathBuf::from(format!("tests/{}", mode)); config.target_rustcflags = Some(String::from( "\ - -L ../target/debug/ \ - -L ../target/debug/deps/ \ - ")); + -L ../target/debug/ \ + -L ../target/debug/deps/ \ + ", + )); config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 compiletest::run_tests(&config); diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index 0c119e5af..501704d8c 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -1,6 +1,6 @@ -use serde_json; -use jsonrpc_derive::rpc; use jsonrpc_core::{IoHandler, Response}; +use jsonrpc_derive::rpc; +use serde_json; pub enum MyError {} impl From for jsonrpc_core::Error { @@ -88,11 +88,17 @@ fn should_accept_single_param() { // then let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ + assert_eq!( + result1, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": -1, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } #[test] @@ -108,11 +114,17 @@ fn should_accept_multiple_params() { // then let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ + assert_eq!( + result1, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 3, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } #[test] @@ -130,17 +142,28 @@ fn should_use_method_name_aliases() { // then let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ + assert_eq!( + result1, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 3, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); - assert_eq!(result2, serde_json::from_str(r#"{ + assert_eq!( + result2, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 3, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } - diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index 4f1f05e6e..5589f0833 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -1,13 +1,13 @@ -use serde_json; use jsonrpc_core; use jsonrpc_pubsub; +use serde_json; #[macro_use] extern crate jsonrpc_derive; -use std::sync::Arc; use jsonrpc_core::futures::sync::mpsc; -use jsonrpc_pubsub::{PubSubHandler, SubscriptionId, Session, PubSubMetadata}; use jsonrpc_pubsub::typed::Subscriber; +use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; +use std::sync::Arc; pub enum MyError {} impl From for jsonrpc_core::Error { @@ -108,4 +108,3 @@ fn test_subscribe_with_alias() { let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); assert_eq!(expected, result); } - diff --git a/derive/tests/trailing.rs b/derive/tests/trailing.rs index 232cc8709..605525360 100644 --- a/derive/tests/trailing.rs +++ b/derive/tests/trailing.rs @@ -1,6 +1,6 @@ -use serde_json; -use jsonrpc_derive::rpc; use jsonrpc_core::{IoHandler, Response, Result}; +use jsonrpc_derive::rpc; +use serde_json; #[rpc] pub trait Rpc { @@ -46,11 +46,17 @@ fn should_accept_trailing_param() { // then let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(result, serde_json::from_str(r#"{ + assert_eq!( + result, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 4, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } #[test] @@ -65,11 +71,17 @@ fn should_accept_missing_trailing_param() { // then let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(result, serde_json::from_str(r#"{ + assert_eq!( + result, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 2, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } #[test] @@ -87,18 +99,30 @@ fn should_accept_single_trailing_param() { // then let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ + assert_eq!( + result1, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": "hello", "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); - assert_eq!(result2, serde_json::from_str(r#"{ + assert_eq!( + result2, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": "", "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } #[test] @@ -120,30 +144,54 @@ fn should_accept_multiple_trailing_params() { // then let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); - assert_eq!(result1, serde_json::from_str(r#"{ + assert_eq!( + result1, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 0, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); - assert_eq!(result2, serde_json::from_str(r#"{ + assert_eq!( + result2, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 1, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); - assert_eq!(result3, serde_json::from_str(r#"{ + assert_eq!( + result3, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 3, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); let result4: Response = serde_json::from_str(&res4.unwrap()).unwrap(); - assert_eq!(result4, serde_json::from_str(r#"{ + assert_eq!( + result4, + serde_json::from_str( + r#"{ "jsonrpc": "2.0", "result": 6, "id": 1 - }"#).unwrap()); + }"# + ) + .unwrap() + ); } diff --git a/http/examples/http_async.rs b/http/examples/http_async.rs index f0da3cd32..44704f5ea 100644 --- a/http/examples/http_async.rs +++ b/http/examples/http_async.rs @@ -1,5 +1,5 @@ -use jsonrpc_http_server::{ServerBuilder, DomainsValidation, AccessControlAllowOrigin}; use jsonrpc_http_server::jsonrpc_core::*; +use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; fn main() { let mut io = IoHandler::default(); @@ -14,4 +14,3 @@ fn main() { server.wait(); } - diff --git a/http/examples/http_meta.rs b/http/examples/http_meta.rs index a87df82bb..b3b40a407 100644 --- a/http/examples/http_meta.rs +++ b/http/examples/http_meta.rs @@ -1,5 +1,5 @@ -use jsonrpc_http_server::{ServerBuilder, hyper, RestApi, cors::AccessControlAllowHeaders}; use jsonrpc_http_server::jsonrpc_core::*; +use jsonrpc_http_server::{cors::AccessControlAllowHeaders, hyper, RestApi, ServerBuilder}; #[derive(Default, Clone)] struct Meta { @@ -16,20 +16,20 @@ fn main() { if auth.as_str() == "let-me-in" { Ok(Value::String("Hello World!".to_owned())) } else { - Ok(Value::String("Please send a valid Bearer token in Authorization header.".to_owned())) + Ok(Value::String( + "Please send a valid Bearer token in Authorization header.".to_owned(), + )) } }); let server = ServerBuilder::new(io) - .cors_allow_headers(AccessControlAllowHeaders::Only( - vec![ - "Authorization".to_owned(), - ]) - ) + .cors_allow_headers(AccessControlAllowHeaders::Only(vec!["Authorization".to_owned()])) .rest_api(RestApi::Unsecure) // You can also implement `MetaExtractor` trait and pass a struct here. .meta_extractor(|req: &hyper::Request| { - let auth = req.headers().get(hyper::header::AUTHORIZATION) + let auth = req + .headers() + .get(hyper::header::AUTHORIZATION) .map(|h| h.to_str().unwrap_or("").to_owned()); Meta { auth } @@ -39,4 +39,3 @@ fn main() { server.wait(); } - diff --git a/http/examples/http_middleware.rs b/http/examples/http_middleware.rs index d2ca5bee1..47d03cd06 100644 --- a/http/examples/http_middleware.rs +++ b/http/examples/http_middleware.rs @@ -1,8 +1,6 @@ -use jsonrpc_http_server::{ - hyper, ServerBuilder, DomainsValidation, AccessControlAllowOrigin, Response, RestApi -}; -use jsonrpc_http_server::jsonrpc_core::{IoHandler, Value}; use jsonrpc_http_server::jsonrpc_core::futures; +use jsonrpc_http_server::jsonrpc_core::{IoHandler, Value}; +use jsonrpc_http_server::{hyper, AccessControlAllowOrigin, DomainsValidation, Response, RestApi, ServerBuilder}; fn main() { let mut io = IoHandler::default(); @@ -25,4 +23,3 @@ fn main() { server.wait(); } - diff --git a/http/examples/server.rs b/http/examples/server.rs index 011d99abc..2b25ee0bc 100644 --- a/http/examples/server.rs +++ b/http/examples/server.rs @@ -1,11 +1,9 @@ -use jsonrpc_http_server::{ServerBuilder, DomainsValidation, AccessControlAllowOrigin, RestApi}; use jsonrpc_http_server::jsonrpc_core::*; +use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, RestApi, ServerBuilder}; fn main() { let mut io = IoHandler::default(); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); let server = ServerBuilder::new(io) .threads(3) @@ -16,4 +14,3 @@ fn main() { server.wait(); } - diff --git a/http/src/handler.rs b/http/src/handler.rs index 8db5f4f2b..02a329406 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -1,18 +1,18 @@ use crate::Rpc; -use std::{fmt, mem, str}; use std::sync::Arc; +use std::{fmt, mem, str}; -use hyper::{self, service::Service, Body, Method}; use hyper::header::{self, HeaderMap, HeaderValue}; +use hyper::{self, service::Service, Body, Method}; -use crate::jsonrpc::{self as core, middleware, FutureResult, Metadata, Middleware}; -use crate::jsonrpc::futures::{Future, Poll, Async, Stream, future}; +use crate::jsonrpc::futures::{future, Async, Future, Poll, Stream}; use crate::jsonrpc::serde_json; +use crate::jsonrpc::{self as core, middleware, FutureResult, Metadata, Middleware}; use crate::response::Response; use crate::server_utils::cors; -use crate::{utils, RequestMiddleware, RequestMiddlewareAction, CorsDomains, AllowedHosts, RestApi}; +use crate::{utils, AllowedHosts, CorsDomains, RequestMiddleware, RequestMiddlewareAction, RestApi}; /// jsonrpc http request handler. pub struct ServerHandler = middleware::Noop> { @@ -68,12 +68,14 @@ impl> Service for ServerHandler { let action = self.middleware.on_request(request); let (should_validate_hosts, should_continue_on_invalid_cors, response) = match action { - RequestMiddlewareAction::Proceed { should_continue_on_invalid_cors, request }=> ( - true, should_continue_on_invalid_cors, Err(request) - ), - RequestMiddlewareAction::Respond { should_validate_hosts, response } => ( - should_validate_hosts, false, Ok(response) - ), + RequestMiddlewareAction::Proceed { + should_continue_on_invalid_cors, + request, + } => (true, should_continue_on_invalid_cors, Err(request)), + RequestMiddlewareAction::Respond { + should_validate_hosts, + response, + } => (should_validate_hosts, false, Ok(response)), }; // Validate host @@ -124,13 +126,17 @@ impl> Future for Handler { Handler::Rpc(ref mut handler) => handler.poll(), Handler::Middleware(ref mut middleware) => middleware.poll(), Handler::Error(ref mut response) => Ok(Async::Ready( - response.take().expect("Response always Some initialy. Returning `Ready` so will never be polled again; qed").into() + response + .take() + .expect("Response always Some initialy. Returning `Ready` so will never be polled again; qed") + .into(), )), } } } -enum RpcPollState where +enum RpcPollState +where F: Future, Error = ()>, G: Future, Error = ()>, { @@ -138,7 +144,8 @@ enum RpcPollState where NotReady(RpcHandlerState), } -impl RpcPollState where +impl RpcPollState +where F: Future, Error = ()>, G: Future, Error = ()>, { @@ -156,7 +163,8 @@ type FutureResponse = future::Map< fn(Option) -> Response, >; -enum RpcHandlerState where +enum RpcHandlerState +where F: Future, Error = ()>, G: Future, Error = ()>, { @@ -187,7 +195,8 @@ enum RpcHandlerState where Done, } -impl fmt::Debug for RpcHandlerState where +impl fmt::Debug for RpcHandlerState +where F: Future, Error = ()>, G: Future, Error = ()>, { @@ -195,10 +204,10 @@ impl fmt::Debug for RpcHandlerState where use self::RpcHandlerState::*; match *self { - ReadingHeaders {..} => write!(fmt, "ReadingHeaders"), - ReadingBody {..} => write!(fmt, "ReadingBody"), - ProcessRest {..} => write!(fmt, "ProcessRest"), - ProcessHealth {..} => write!(fmt, "ProcessHealth"), + ReadingHeaders { .. } => write!(fmt, "ReadingHeaders"), + ReadingBody { .. } => write!(fmt, "ReadingBody"), + ProcessRest { .. } => write!(fmt, "ProcessRest"), + ProcessHealth { .. } => write!(fmt, "ProcessHealth"), Writing(ref res) => write!(fmt, "Writing({:?})", res), WaitingForResponse(_) => write!(fmt, "WaitingForResponse"), Waiting(_) => write!(fmt, "Waiting"), @@ -227,7 +236,11 @@ impl> Future for RpcHandler { fn poll(&mut self) -> Poll { let new_state = match mem::replace(&mut self.state, RpcHandlerState::Done) { RpcHandlerState::ReadingHeaders { - request, cors_domains, cors_headers, continue_on_invalid_cors, keep_alive, + request, + cors_domains, + cors_headers, + continue_on_invalid_cors, + keep_alive, } => { // Read cors header self.cors_allow_origin = utils::cors_allow_origin(&request, &cors_domains); @@ -236,36 +249,31 @@ impl> Future for RpcHandler { self.is_options = *request.method() == Method::OPTIONS; // Read other headers RpcPollState::Ready(self.read_headers(request, continue_on_invalid_cors)) - }, - RpcHandlerState::ReadingBody { body, request, metadata, uri, } => { - match self.process_body(body, request, uri, metadata) { - Err(BodyError::Utf8(ref e)) => { - let mesg = format!("utf-8 encoding error at byte {} in request body", e.valid_up_to()); - let resp = Response::bad_request(mesg); - RpcPollState::Ready(RpcHandlerState::Writing(resp)) - } - Err(BodyError::TooLarge) => { - let resp = Response::too_large("request body size exceeds allowed maximum"); - RpcPollState::Ready(RpcHandlerState::Writing(resp)) - } - Err(BodyError::Hyper(e)) => return Err(e), - Ok(state) => state, + } + RpcHandlerState::ReadingBody { + body, + request, + metadata, + uri, + } => match self.process_body(body, request, uri, metadata) { + Err(BodyError::Utf8(ref e)) => { + let mesg = format!("utf-8 encoding error at byte {} in request body", e.valid_up_to()); + let resp = Response::bad_request(mesg); + RpcPollState::Ready(RpcHandlerState::Writing(resp)) } - }, - RpcHandlerState::ProcessRest { uri, metadata } => { - self.process_rest(uri, metadata)? - }, - RpcHandlerState::ProcessHealth { method, metadata } => { - self.process_health(method, metadata)? - }, - RpcHandlerState::WaitingForResponse(mut waiting) => { - match waiting.poll() { - Ok(Async::Ready(response)) => RpcPollState::Ready(RpcHandlerState::Writing(response)), - Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::WaitingForResponse(waiting)), - Err(e) => RpcPollState::Ready(RpcHandlerState::Writing( - Response::internal_error(format!("{:?}", e)) - )), + Err(BodyError::TooLarge) => { + let resp = Response::too_large("request body size exceeds allowed maximum"); + RpcPollState::Ready(RpcHandlerState::Writing(resp)) } + Err(BodyError::Hyper(e)) => return Err(e), + Ok(state) => state, + }, + RpcHandlerState::ProcessRest { uri, metadata } => self.process_rest(uri, metadata)?, + RpcHandlerState::ProcessHealth { method, metadata } => self.process_health(method, metadata)?, + RpcHandlerState::WaitingForResponse(mut waiting) => match waiting.poll() { + Ok(Async::Ready(response)) => RpcPollState::Ready(RpcHandlerState::Writing(response)), + Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::WaitingForResponse(waiting)), + Err(e) => RpcPollState::Ready(RpcHandlerState::Writing(Response::internal_error(format!("{:?}", e)))), }, RpcHandlerState::Waiting(mut waiting) => { match waiting.poll() { @@ -276,13 +284,13 @@ impl> Future for RpcHandler { // Add new line to have nice output when using CLI clients (curl) Some(result) => Response::ok(format!("{}\n", result)), })) - }, + } Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::Waiting(waiting)), - Err(e) => RpcPollState::Ready(RpcHandlerState::Writing( - Response::internal_error(format!("{:?}", e)) - )), + Err(e) => { + RpcPollState::Ready(RpcHandlerState::Writing(Response::internal_error(format!("{:?}", e)))) + } } - }, + } state => RpcPollState::NotReady(state), }; @@ -302,7 +310,7 @@ impl> Future for RpcHandler { self.keep_alive, ); Ok(Async::Ready(response)) - }, + } state => { self.state = state; if is_ready { @@ -310,7 +318,7 @@ impl> Future for RpcHandler { } else { Ok(Async::NotReady) } - }, + } } } } @@ -351,41 +359,41 @@ impl> RpcHandler { // Validate the ContentType header // to prevent Cross-Origin XHRs with text/plain Method::POST if Self::is_json(request.headers().get("content-type")) => { - let uri = if self.rest_api != RestApi::Disabled { Some(request.uri().clone()) } else { None }; + let uri = if self.rest_api != RestApi::Disabled { + Some(request.uri().clone()) + } else { + None + }; RpcHandlerState::ReadingBody { metadata, request: Default::default(), uri, body: request.into_body(), } - }, + } Method::POST if self.rest_api == RestApi::Unsecure && request.uri().path().split('/').count() > 2 => { RpcHandlerState::ProcessRest { metadata, uri: request.uri().clone(), } - }, + } // Just return error for unsupported content type - Method::POST => { - RpcHandlerState::Writing(Response::unsupported_content_type()) - }, + Method::POST => RpcHandlerState::Writing(Response::unsupported_content_type()), // Don't validate content type on options - Method::OPTIONS => { - RpcHandlerState::Writing(Response::empty()) - }, + Method::OPTIONS => RpcHandlerState::Writing(Response::empty()), // Respond to health API request if there is one configured. Method::GET if self.health_api.as_ref().map(|x| &*x.0) == Some(request.uri().path()) => { RpcHandlerState::ProcessHealth { metadata, - method: self.health_api.as_ref() - .map(|x| x.1.clone()) - .expect("Health api is defined since the URI matched."), + method: self + .health_api + .as_ref() + .map(|x| x.1.clone()) + .expect("Health api is defined since the URI matched."), } - }, + } // Disallow other methods. - _ => { - RpcHandlerState::Writing(Response::method_not_allowed()) - }, + _ => RpcHandlerState::Writing(Response::method_not_allowed()), } } @@ -394,7 +402,7 @@ impl> RpcHandler { method: String, metadata: M, ) -> Result, hyper::Error> { - use self::core::types::{Call, MethodCall, Version, Params, Request, Id, Output, Success, Failure}; + use self::core::types::{Call, Failure, Id, MethodCall, Output, Params, Request, Success, Version}; // Create a request let call = Request::Single(Call::MethodCall(MethodCall { @@ -405,22 +413,19 @@ impl> RpcHandler { })); Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse( - future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)) - .map(|res| match res { - Some(core::Response::Single(Output::Success(Success { result, .. }))) => { - let result = serde_json::to_string(&result) - .expect("Serialization of result is infallible;qed"); + future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)).map(|res| match res { + Some(core::Response::Single(Output::Success(Success { result, .. }))) => { + let result = serde_json::to_string(&result).expect("Serialization of result is infallible;qed"); - Response::ok(result) - }, - Some(core::Response::Single(Output::Failure(Failure { error, .. }))) => { - let result = serde_json::to_string(&error) - .expect("Serialization of error is infallible;qed"); + Response::ok(result) + } + Some(core::Response::Single(Output::Failure(Failure { error, .. }))) => { + let result = serde_json::to_string(&error).expect("Serialization of error is infallible;qed"); - Response::service_unavailable(result) - }, - e => Response::internal_error(format!("Invalid response for health request: {:?}", e)), - }) + Response::service_unavailable(result) + } + e => Response::internal_error(format!("Invalid response for health request: {:?}", e)), + }), ))) } @@ -429,7 +434,7 @@ impl> RpcHandler { uri: hyper::Uri, metadata: M, ) -> Result, hyper::Error> { - use self::core::types::{Call, MethodCall, Version, Params, Request, Id, Value}; + use self::core::types::{Call, Id, MethodCall, Params, Request, Value, Version}; // skip the initial / let mut it = uri.path().split('/').skip(1); @@ -453,10 +458,9 @@ impl> RpcHandler { })); Ok(RpcPollState::Ready(RpcHandlerState::Waiting( - future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)) - .map(|res| res.map(|x| serde_json::to_string(&x) - .expect("Serialization of response is infallible;qed") - )) + future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)).map(|res| { + res.map(|x| serde_json::to_string(&x).expect("Serialization of response is infallible;qed")) + }), ))) } @@ -470,17 +474,19 @@ impl> RpcHandler { loop { match body.poll()? { Async::Ready(Some(chunk)) => { - if request.len().checked_add(chunk.len()).map(|n| n > self.max_request_body_size).unwrap_or(true) { - return Err(BodyError::TooLarge) + if request + .len() + .checked_add(chunk.len()) + .map(|n| n > self.max_request_body_size) + .unwrap_or(true) + { + return Err(BodyError::TooLarge); } request.extend_from_slice(&*chunk) - }, + } Async::Ready(None) => { if let (Some(uri), true) = (uri, request.is_empty()) { - return Ok(RpcPollState::Ready(RpcHandlerState::ProcessRest { - uri, - metadata, - })); + return Ok(RpcPollState::Ready(RpcHandlerState::ProcessRest { uri, metadata })); } let content = match str::from_utf8(&request) { @@ -488,14 +494,14 @@ impl> RpcHandler { Err(err) => { // Return utf error. return Err(BodyError::Utf8(err)); - }, + } }; // Content is ready return Ok(RpcPollState::Ready(RpcHandlerState::Waiting( - self.jsonrpc_handler.handler.handle_request(content, metadata) + self.jsonrpc_handler.handler.handle_request(content, metadata), ))); - }, + } Async::NotReady => { return Ok(RpcPollState::NotReady(RpcHandlerState::ReadingBody { body, @@ -503,7 +509,7 @@ impl> RpcHandler { metadata, uri, })); - }, + } } } } @@ -525,7 +531,8 @@ impl> RpcHandler { .cloned() .collect::>(); let max_len = if val.is_empty() { 0 } else { val.len() - 2 }; - HeaderValue::from_bytes(&val[..max_len]).expect("Concatenation of valid headers with `, ` is still valid; qed") + HeaderValue::from_bytes(&val[..max_len]) + .expect("Concatenation of valid headers with `, ` is still valid; qed") }; let allowed = concat(&[as_header(Method::OPTIONS), as_header(Method::POST)]); @@ -543,7 +550,7 @@ impl> RpcHandler { if let Some(cma) = cors_max_age { headers.append( header::ACCESS_CONTROL_MAX_AGE, - HeaderValue::from_str(&cma.to_string()).expect("`u32` will always parse; qed") + HeaderValue::from_str(&cma.to_string()).expect("`u32` will always parse; qed"), ); } @@ -569,7 +576,7 @@ impl> RpcHandler { || content.eq_ignore_ascii_case("application/json;charset=utf-8") => { true - }, + } _ => false, } } @@ -592,7 +599,6 @@ mod test { .body(()) .unwrap(); - assert_eq!( request.headers().get("content-type").unwrap(), &"Application/Json; charset=UTF-8" diff --git a/http/src/lib.rs b/http/src/lib.rs index e73fb45a2..0848b6f77 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -20,40 +20,39 @@ #![warn(missing_docs)] - use jsonrpc_server_utils as server_utils; use net2; -pub use jsonrpc_core; pub use hyper; +pub use jsonrpc_core; #[macro_use] extern crate log; mod handler; mod response; -mod utils; #[cfg(test)] mod tests; +mod utils; use std::io; -use std::sync::{mpsc, Arc}; use std::net::SocketAddr; +use std::sync::{mpsc, Arc}; use std::thread; -use hyper::{server, Body}; -use jsonrpc_core as jsonrpc; -use crate::jsonrpc::MetaIoHandler; -use crate::jsonrpc::futures::{self, Future, Stream, future}; use crate::jsonrpc::futures::sync::oneshot; +use crate::jsonrpc::futures::{self, future, Future, Stream}; +use crate::jsonrpc::MetaIoHandler; use crate::server_utils::reactor::{Executor, UninitializedExecutor}; +use hyper::{server, Body}; +use jsonrpc_core as jsonrpc; -pub use crate::server_utils::hosts::{Host, DomainsValidation}; -pub use crate::server_utils::cors::{self, AccessControlAllowOrigin, Origin, AllowCors}; -pub use crate::server_utils::{tokio, SuspendableStream}; pub use crate::handler::ServerHandler; -pub use crate::utils::{is_host_allowed, cors_allow_origin, cors_allow_headers}; pub use crate::response::Response; +pub use crate::server_utils::cors::{self, AccessControlAllowOrigin, AllowCors, Origin}; +pub use crate::server_utils::hosts::{DomainsValidation, Host}; +pub use crate::server_utils::{tokio, SuspendableStream}; +pub use crate::utils::{cors_allow_headers, cors_allow_origin, is_host_allowed}; /// Action undertaken by a middleware. pub enum RequestMiddlewareAction { @@ -70,8 +69,8 @@ pub enum RequestMiddlewareAction { /// Should standard hosts validation be performed? should_validate_hosts: bool, /// a future for server response - response: Box, Error=hyper::Error> + Send>, - } + response: Box, Error = hyper::Error> + Send>, + }, } impl From for RequestMiddlewareAction { @@ -107,7 +106,8 @@ pub trait RequestMiddleware: Send + Sync + 'static { fn on_request(&self, request: hyper::Request) -> RequestMiddlewareAction; } -impl RequestMiddleware for F where +impl RequestMiddleware for F +where F: Fn(hyper::Request) -> RequestMiddlewareAction + Sync + Send + 'static, { fn on_request(&self, request: hyper::Request) -> RequestMiddlewareAction { @@ -132,7 +132,8 @@ pub trait MetaExtractor: Sync + Send + 'static { fn read_metadata(&self, _: &hyper::Request) -> M; } -impl MetaExtractor for F where +impl MetaExtractor for F +where M: jsonrpc::Metadata, F: Fn(&hyper::Request) -> M + Sync + Send + 'static, { @@ -210,8 +211,9 @@ impl> ServerBuilder(handler: T) -> Self where - T: Into> + pub fn new(handler: T) -> Self + where + T: Into>, { Self::with_meta_extractor(handler, NoopExtractor) } @@ -223,7 +225,8 @@ impl> ServerBuilder { /// By default: /// 1. Server is not sending any CORS headers. /// 2. Server is validating `Host` header. - pub fn with_meta_extractor(handler: T, extractor: E) -> Self where + pub fn with_meta_extractor(handler: T, extractor: E) -> Self + where T: Into>, E: MetaExtractor, { @@ -268,7 +271,8 @@ impl> ServerBuilder { /// Error returned from the method will be converted to status `500` response. /// /// Expects a tuple with `(, )`. - pub fn health_api(mut self, health_api: T) -> Self where + pub fn health_api(mut self, health_api: T) -> Self + where T: Into>, A: Into, B: Into, @@ -281,7 +285,7 @@ impl> ServerBuilder { /// /// Default is true. pub fn keep_alive(mut self, val: bool) -> Self { - self.keep_alive = val; + self.keep_alive = val; self } @@ -391,36 +395,41 @@ impl> ServerBuilder { reuse_port, req_max_size, ); - let handles = (0..self.threads - 1).map(|i| { - let (local_addr_tx, local_addr_rx) = mpsc::channel(); - let (close, shutdown_signal) = oneshot::channel(); - let eloop = UninitializedExecutor::Unspawned.init_with_name(format!("http.worker{}", i + 1))?; - serve( - (shutdown_signal, local_addr_tx), - eloop.executor(), - addr.to_owned(), - cors_domains.clone(), - cors_max_age, - allowed_headers.clone(), - request_middleware.clone(), - allowed_hosts.clone(), - jsonrpc_handler.clone(), - rest_api, - health_api.clone(), - keep_alive, - reuse_port, - req_max_size, - ); - Ok((eloop, close, local_addr_rx)) - }).collect::>>()?; + let handles = (0..self.threads - 1) + .map(|i| { + let (local_addr_tx, local_addr_rx) = mpsc::channel(); + let (close, shutdown_signal) = oneshot::channel(); + let eloop = UninitializedExecutor::Unspawned.init_with_name(format!("http.worker{}", i + 1))?; + serve( + (shutdown_signal, local_addr_tx), + eloop.executor(), + addr.to_owned(), + cors_domains.clone(), + cors_max_age, + allowed_headers.clone(), + request_middleware.clone(), + allowed_hosts.clone(), + jsonrpc_handler.clone(), + rest_api, + health_api.clone(), + keep_alive, + reuse_port, + req_max_size, + ); + Ok((eloop, close, local_addr_rx)) + }) + .collect::>>()?; // Wait for server initialization let local_addr = recv_address(local_addr_rx); // Wait for other threads as well. - let mut handles = handles.into_iter().map(|(eloop, close, local_addr_rx)| { - let _ = recv_address(local_addr_rx)?; - Ok((eloop, close)) - }).collect::)>>()?; + let mut handles = handles + .into_iter() + .map(|(eloop, close, local_addr_rx)| { + let _ = recv_address(local_addr_rx)?; + Ok((eloop, close)) + }) + .collect::)>>()?; handles.push((eloop, close)); let (executors, close) = handles.into_iter().unzip(); @@ -433,9 +442,9 @@ impl> ServerBuilder { } fn recv_address(local_addr_rx: mpsc::Receiver>) -> io::Result { - local_addr_rx.recv().map_err(|_| { - io::Error::new(io::ErrorKind::Interrupted, "") - })? + local_addr_rx + .recv() + .map_err(|_| io::Error::new(io::ErrorKind::Interrupted, ""))? } fn serve>( @@ -482,11 +491,14 @@ fn serve>( match local_addr_tx.send(Ok(local_addr)) { Ok(_) => futures::future::ok((listener, local_addr)), Err(_) => { - warn!("Thread {:?} unable to reach receiver, closing server", thread::current().name()); + warn!( + "Thread {:?} unable to reach receiver, closing server", + thread::current().name() + ); futures::future::err(()) - }, + } } - }, + } Err(err) => { // Send error let _send_result = local_addr_tx.send(Err(err)); @@ -516,8 +528,10 @@ fn serve>( max_request_body_size, keep_alive, ); - tokio::spawn(http.serve_connection(socket, service) - .map_err(|e| error!("Error serving connection: {:?}", e))); + tokio::spawn( + http.serve_connection(socket, service) + .map_err(|e| error!("Error serving connection: {:?}", e)), + ); Ok(()) }) .map_err(|e| { @@ -545,7 +559,7 @@ fn configure_port(reuse: bool, tcp: &net2::TcpBuilder) -> io::Result<()> { #[cfg(not(unix))] fn configure_port(_reuse: bool, _tcp: &net2::TcpBuilder) -> io::Result<()> { - Ok(()) + Ok(()) } /// jsonrpc http server instance @@ -584,7 +598,9 @@ impl Server { impl Drop for Server { fn drop(&mut self) { if let Some(executors) = self.executor.take() { - for executor in executors { executor.close(); } + for executor in executors { + executor.close(); + } }; } } diff --git a/http/src/response.rs b/http/src/response.rs index 7d94a908d..34b8f76b2 100644 --- a/http/src/response.rs +++ b/http/src/response.rs @@ -1,6 +1,6 @@ //! Basic Request/Response structures used internally. -pub use hyper::{self, Method, Body, StatusCode, header::HeaderValue}; +pub use hyper::{self, header::HeaderValue, Body, Method, StatusCode}; /// Simple server response structure #[derive(Debug)] @@ -96,7 +96,7 @@ impl Response { Response { code: StatusCode::BAD_REQUEST, content_type: plain_text(), - content: msg.into() + content: msg.into(), } } @@ -105,7 +105,7 @@ impl Response { Response { code: StatusCode::PAYLOAD_TOO_LARGE, content_type: plain_text(), - content: msg.into() + content: msg.into(), } } } diff --git a/http/src/tests.rs b/http/src/tests.rs index 0fd325722..28cd772ee 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -1,22 +1,26 @@ use jsonrpc_core; -use std::str::Lines; -use std::net::TcpStream; +use self::jsonrpc_core::{Error, ErrorCode, IoHandler, Params, Value}; use std::io::{Read, Write}; -use self::jsonrpc_core::{IoHandler, Params, Value, Error, ErrorCode}; +use std::net::TcpStream; +use std::str::Lines; use self::jsonrpc_core::futures::{self, Future}; use super::*; fn serve_hosts(hosts: Vec) -> Server { ServerBuilder::new(IoHandler::default()) - .cors(DomainsValidation::AllowOnly(vec![AccessControlAllowOrigin::Value("parity.io".into())])) + .cors(DomainsValidation::AllowOnly(vec![AccessControlAllowOrigin::Value( + "parity.io".into(), + )])) .allowed_hosts(DomainsValidation::AllowOnly(hosts)) .start_http(&"127.0.0.1:0".parse().unwrap()) .unwrap() } -fn id(t: T) -> T { t } +fn id(t: T) -> T { + t +} fn serve ServerBuilder>(alter: F) -> Server { let builder = ServerBuilder::new(io()) @@ -28,26 +32,20 @@ fn serve ServerBuilder>(alter: F) -> Server { .rest_api(RestApi::Secure) .health_api(("/health", "hello_async")); - alter(builder) - .start_http(&"127.0.0.1:0".parse().unwrap()) - .unwrap() + alter(builder).start_http(&"127.0.0.1:0".parse().unwrap()).unwrap() } fn serve_allow_headers(cors_allow_headers: cors::AccessControlAllowHeaders) -> Server { let mut io = IoHandler::default(); - io.add_method("hello", |params: Params| { - match params.parse::<(u64, )>() { - Ok((num, )) => Ok(Value::String(format!("world: {}", num))), - _ => Ok(Value::String("world".into())), - } + io.add_method("hello", |params: Params| match params.parse::<(u64,)>() { + Ok((num,)) => Ok(Value::String(format!("world: {}", num))), + _ => Ok(Value::String("world".into())), }); ServerBuilder::new(io) - .cors( - DomainsValidation::AllowOnly(vec![ - AccessControlAllowOrigin::Value("parity.io".into()), - AccessControlAllowOrigin::Null, - ]) - ) + .cors(DomainsValidation::AllowOnly(vec![ + AccessControlAllowOrigin::Value("parity.io".into()), + AccessControlAllowOrigin::Null, + ])) .cors_allow_headers(cors_allow_headers) .start_http(&"127.0.0.1:0".parse().unwrap()) .unwrap() @@ -57,11 +55,9 @@ fn io() -> IoHandler { use std::{thread, time}; let mut io = IoHandler::default(); - io.add_method("hello", |params: Params| { - match params.parse::<(u64, )>() { - Ok((num, )) => Ok(Value::String(format!("world: {}", num))), - _ => Ok(Value::String("world".into())), - } + io.add_method("hello", |params: Params| match params.parse::<(u64,)>() { + Ok((num,)) => Ok(Value::String(format!("world: {}", num))), + _ => Ok(Value::String("world".into())), }); io.add_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); io.add_method("hello_async", |_params: Params| { @@ -94,7 +90,7 @@ fn read_block(lines: &mut Lines) -> String { Some(v) => { block.push_str(v); block.push_str("\n"); - }, + } } } block @@ -112,11 +108,7 @@ fn request(server: Server, request: &str) -> Response { let headers = read_block(&mut lines); let body = read_block(&mut lines); - Response { - status, - headers, - body, - } + Response { status, headers, body } } #[test] @@ -125,19 +117,23 @@ fn should_return_method_not_allowed_for_get() { let server = serve(id); // when - let response = request(server, + let response = request( + server, "\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - I shouldn't be read.\r\n\ - " + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + \r\n\ + I shouldn't be read.\r\n\ + ", ); // then assert_eq!(response.status, "HTTP/1.1 405 Method Not Allowed".to_owned()); - assert_eq!(response.body, "Used HTTP Method is not allowed. POST or OPTIONS is required\n".to_owned()); + assert_eq!( + response.body, + "Used HTTP Method is not allowed. POST or OPTIONS is required\n".to_owned() + ); } #[test] @@ -146,14 +142,15 @@ fn should_handle_health_endpoint() { let server = serve(id); // when - let response = request(server, + let response = request( + server, "\ - GET /health HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - I shouldn't be read.\r\n\ - " + GET /health HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + \r\n\ + I shouldn't be read.\r\n\ + ", ); // then @@ -167,14 +164,15 @@ fn should_handle_health_endpoint_failure() { let server = serve(|builder| builder.health_api(("/api/health", "fail"))); // when - let response = request(server, + let response = request( + server, "\ - GET /api/health HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - I shouldn't be read.\r\n\ - " + GET /api/health HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + \r\n\ + I shouldn't be read.\r\n\ + ", ); // then @@ -188,19 +186,23 @@ fn should_return_unsupported_media_type_if_not_json() { let server = serve(id); // when - let response = request(server, + let response = request( + server, "\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - \r\n\ - {}\r\n\ - " + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + \r\n\ + {}\r\n\ + ", ); // then assert_eq!(response.status, "HTTP/1.1 415 Unsupported Media Type".to_owned()); - assert_eq!(response.body, "Supplied content type is not allowed. Content-Type: application/json is required\n".to_owned()); + assert_eq!( + response.body, + "Supplied content type is not allowed. Content-Type: application/json is required\n".to_owned() + ); } #[test] @@ -210,16 +212,21 @@ fn should_return_error_for_malformed_request() { // when let req = r#"{"jsonrpc":"3.0","method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -234,16 +241,21 @@ fn should_return_error_for_malformed_request2() { // when let req = r#"{"jsonrpc":"2.0","metho1d":""}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -258,16 +270,21 @@ fn should_return_empty_response_for_notification() { // when let req = r#"{"jsonrpc":"2.0","method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -275,7 +292,6 @@ fn should_return_empty_response_for_notification() { assert_eq!(response.body, "".to_owned()); } - #[test] fn should_return_method_not_found() { // given @@ -283,16 +299,21 @@ fn should_return_method_not_found() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -307,23 +328,34 @@ fn should_add_cors_allow_origins() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, method_not_found()); - assert!(response.headers.contains("access-control-allow-origin: http://parity.io"), "Headers missing in {}", response.headers); + assert!( + response + .headers + .contains("access-control-allow-origin: http://parity.io"), + "Headers missing in {}", + response.headers + ); } #[test] @@ -333,24 +365,39 @@ fn should_add_cors_max_age_headers() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, method_not_found()); - assert!(response.headers.contains("access-control-allow-origin: http://parity.io"), "Headers missing in {}", response.headers); - assert!(response.headers.contains("access-control-max-age: 1000"), "Headers missing in {}", response.headers); + assert!( + response + .headers + .contains("access-control-allow-origin: http://parity.io"), + "Headers missing in {}", + response.headers + ); + assert!( + response.headers.contains("access-control-max-age: 1000"), + "Headers missing in {}", + response.headers + ); } #[test] @@ -360,17 +407,22 @@ fn should_not_add_cors_allow_origins() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: fake.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: fake.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -378,8 +430,6 @@ fn should_not_add_cors_allow_origins() { assert_eq!(response.body, cors_invalid_allow_origin()); } - - #[test] fn should_not_process_the_request_in_case_of_invalid_allow_origin() { // given @@ -387,17 +437,22 @@ fn should_not_process_the_request_in_case_of_invalid_allow_origin() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello"}"#; - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: fake.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: fake.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -405,27 +460,35 @@ fn should_not_process_the_request_in_case_of_invalid_allow_origin() { assert_eq!(response.body, cors_invalid_allow_origin()); } - #[test] fn should_return_proper_headers_on_options() { // given let server = serve(id); // when - let response = request(server, + let response = request( + server, "\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Length: 0\r\n\ - \r\n\ - " + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Length: 0\r\n\ + \r\n\ + ", ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!(response.headers.contains("allow: OPTIONS, POST"), "Headers missing in {}", response.headers); - assert!(response.headers.contains("accept: application/json"), "Headers missing in {}", response.headers); + assert!( + response.headers.contains("allow: OPTIONS, POST"), + "Headers missing in {}", + response.headers + ); + assert!( + response.headers.contains("accept: application/json"), + "Headers missing in {}", + response.headers + ); assert_eq!(response.body, ""); } @@ -436,23 +499,32 @@ fn should_add_cors_allow_origin_for_null_origin() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: null\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: null\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, method_not_found()); - assert!(response.headers.contains("access-control-allow-origin: null"), "Headers missing in {}", response.headers); + assert!( + response.headers.contains("access-control-allow-origin: null"), + "Headers missing in {}", + response.headers + ); } #[test] @@ -462,23 +534,32 @@ fn should_add_cors_allow_origin_for_null_origin_when_all() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: null\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: null\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, method_not_found()); - assert!(response.headers.contains("access-control-allow-origin: null"), "Headers missing in {}", response.headers); + assert!( + response.headers.contains("access-control-allow-origin: null"), + "Headers missing in {}", + response.headers + ); } #[test] @@ -488,16 +569,17 @@ fn should_not_allow_request_larger_than_max() { .start_http(&"127.0.0.1:0".parse().unwrap()) .unwrap(); - let response = request(server, + let response = request( + server, "\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Length: 8\r\n\ - Content-Type: application/json\r\n\ - \r\n\ - 12345678\r\n\ - " + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Length: 8\r\n\ + Content-Type: application/json\r\n\ + \r\n\ + 12345678\r\n\ + ", ); assert_eq!(response.status, "HTTP/1.1 413 Payload Too Large".to_owned()); } @@ -509,16 +591,21 @@ fn should_reject_invalid_hosts() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -533,15 +620,20 @@ fn should_reject_missing_host() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -556,16 +648,21 @@ fn should_allow_if_host_is_valid() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: parity.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: parity.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -576,31 +673,36 @@ fn should_allow_if_host_is_valid() { #[test] fn should_respond_configured_allowed_hosts_to_options() { // given - let allowed = vec![ - "X-Allowed".to_owned(), - "X-AlsoAllowed".to_owned(), - ]; + let allowed = vec!["X-Allowed".to_owned(), "X-AlsoAllowed".to_owned()]; let custom = cors::AccessControlAllowHeaders::Only(allowed.clone()); let server = serve_allow_headers(custom); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Access-Control-Request-Headers: {}\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - \r\n\ - ", &allowed.join(", ")) + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Access-Control-Request-Headers: {}\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + \r\n\ + ", + &allowed.join(", ") + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); let expected = format!("access-control-allow-headers: {}", &allowed.join(", ")); - assert!(response.headers.contains(&expected), "Headers missing in {}", response.headers); + assert!( + response.headers.contains(&expected), + "Headers missing in {}", + response.headers + ); } #[test] @@ -609,22 +711,28 @@ fn should_not_contain_default_cors_allow_headers() { let server = serve(id); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: 0\r\n\ - \r\n\ - ") + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: 0\r\n\ + \r\n\ + " + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!(!response.headers.contains("access-control-allow-headers:"), - "Header should not be in {}", response.headers); + assert!( + !response.headers.contains("access-control-allow-headers:"), + "Header should not be in {}", + response.headers + ); } #[test] @@ -633,106 +741,123 @@ fn should_respond_valid_to_default_allowed_headers() { let server = serve(id); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: Accept, Content-Type, Origin\r\n\ - \r\n\ - ") + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: Accept, Content-Type, Origin\r\n\ + \r\n\ + " + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); let expected = "access-control-allow-headers: Accept, Content-Type, Origin"; - assert!(response.headers.contains(expected), "Headers missing in {}", response.headers); + assert!( + response.headers.contains(expected), + "Headers missing in {}", + response.headers + ); } #[test] fn should_by_default_respond_valid_to_any_request_headers() { // given - let allowed = vec![ - "X-Abc".to_owned(), - "X-123".to_owned(), - ]; + let allowed = vec!["X-Abc".to_owned(), "X-123".to_owned()]; let custom = cors::AccessControlAllowHeaders::Only(allowed.clone()); let server = serve_allow_headers(custom); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: {}\r\n\ - \r\n\ - ", &allowed.join(", ")) + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: {}\r\n\ + \r\n\ + ", + &allowed.join(", ") + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); let expected = format!("access-control-allow-headers: {}", &allowed.join(", ")); - assert!(response.headers.contains(&expected), "Headers missing in {}", response.headers); + assert!( + response.headers.contains(&expected), + "Headers missing in {}", + response.headers + ); } #[test] fn should_respond_valid_to_configured_allow_headers() { // given - let allowed = vec![ - "X-Allowed".to_owned(), - "X-AlsoAllowed".to_owned(), - ]; + let allowed = vec!["X-Allowed".to_owned(), "X-AlsoAllowed".to_owned()]; let custom = cors::AccessControlAllowHeaders::Only(allowed.clone()); let server = serve_allow_headers(custom); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: {}\r\n\ - \r\n\ - ", &allowed.join(", ")) + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: {}\r\n\ + \r\n\ + ", + &allowed.join(", ") + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); let expected = format!("access-control-allow-headers: {}", &allowed.join(", ")); - assert!(response.headers.contains(&expected), "Headers missing in {}", response.headers); + assert!( + response.headers.contains(&expected), + "Headers missing in {}", + response.headers + ); } #[test] fn should_respond_invalid_if_non_allowed_header_used() { // given - let custom = cors::AccessControlAllowHeaders::Only( - vec![ - "X-Allowed".to_owned(), - ]); + let custom = cors::AccessControlAllowHeaders::Only(vec!["X-Allowed".to_owned()]); let server = serve_allow_headers(custom); // when - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - X-Not-Allowed: not allowed\r\n\ - \r\n\ - ") + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + X-Not-Allowed: not allowed\r\n\ + \r\n\ + " + ), ); // then @@ -743,26 +868,29 @@ fn should_respond_invalid_if_non_allowed_header_used() { #[test] fn should_respond_valid_if_allowed_header_used() { // given - let custom = cors::AccessControlAllowHeaders::Only( - vec![ - "X-Allowed".to_owned(), - ]); + let custom = cors::AccessControlAllowHeaders::Only(vec!["X-Allowed".to_owned()]); let server = serve_allow_headers(custom); let addr = server.address().clone(); // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - X-Allowed: Foobar\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + X-Allowed: Foobar\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -773,26 +901,29 @@ fn should_respond_valid_if_allowed_header_used() { #[test] fn should_respond_valid_if_case_insensitive_allowed_header_used() { // given - let custom = cors::AccessControlAllowHeaders::Only( - vec![ - "X-Allowed".to_owned(), - ]); + let custom = cors::AccessControlAllowHeaders::Only(vec!["X-Allowed".to_owned()]); let server = serve_allow_headers(custom); let addr = server.address().clone(); // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - X-AlLoWed: Foobar\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + X-AlLoWed: Foobar\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -803,32 +934,32 @@ fn should_respond_valid_if_case_insensitive_allowed_header_used() { #[test] fn should_respond_valid_on_case_mismatches_in_allowed_headers() { // given - let allowed = vec![ - "X-Allowed".to_owned(), - "X-AlsoAllowed".to_owned(), - ]; + let allowed = vec!["X-Allowed".to_owned(), "X-AlsoAllowed".to_owned()]; let custom = cors::AccessControlAllowHeaders::Only(allowed.clone()); let server = serve_allow_headers(custom); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: x-ALLoweD, x-alSOaLloWeD\r\n\ - \r\n\ - ") + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: x-ALLoweD, x-alSOaLloWeD\r\n\ + \r\n\ + " + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - let contained = response.headers.contains( - "access-control-allow-headers: x-ALLoweD, x-alSOaLloWeD" - ); + let contained = response + .headers + .contains("access-control-allow-headers: x-ALLoweD, x-alSOaLloWeD"); assert!(contained, "Headers missing in {}", response.headers); } @@ -840,46 +971,54 @@ fn should_respond_valid_to_any_requested_header() { let headers = "Something, Anything, Xyz, 123, _?"; // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: {}\r\n\ - \r\n\ - ", headers) + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: {}\r\n\ + \r\n\ + ", + headers + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); let expected = format!("access-control-allow-headers: {}", headers); - assert!(response.headers.contains(&expected), "Headers missing in {}", response.headers); + assert!( + response.headers.contains(&expected), + "Headers missing in {}", + response.headers + ); } #[test] fn should_forbid_invalid_request_headers() { // given - let custom = cors::AccessControlAllowHeaders::Only( - vec![ - "X-Allowed".to_owned(), - ]); + let custom = cors::AccessControlAllowHeaders::Only(vec!["X-Allowed".to_owned()]); let server = serve_allow_headers(custom); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: *\r\n\ - \r\n\ - ") + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: *\r\n\ + \r\n\ + " + ), ); // then @@ -896,23 +1035,29 @@ fn should_respond_valid_to_wildcard_if_any_header_allowed() { let server = serve_allow_headers(cors::AccessControlAllowHeaders::Any); // when - let response = request(server, - &format!("\ - OPTIONS / HTTP/1.1\r\n\ - Host: 127.0.0.1:8080\r\n\ - Origin: http://parity.io\r\n\ - Content-Length: 0\r\n\ - Content-Type: application/json\r\n\ - Connection: close\r\n\ - Access-Control-Request-Headers: *\r\n\ - \r\n\ - ") + let response = request( + server, + &format!( + "\ + OPTIONS / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Origin: http://parity.io\r\n\ + Content-Length: 0\r\n\ + Content-Type: application/json\r\n\ + Connection: close\r\n\ + Access-Control-Request-Headers: *\r\n\ + \r\n\ + " + ), ); // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!(response.headers.contains("access-control-allow-headers: *"), - "Headers missing in {}", response.headers); + assert!( + response.headers.contains("access-control-allow-headers: *"), + "Headers missing in {}", + response.headers + ); } #[test] @@ -922,16 +1067,21 @@ fn should_allow_application_json_utf8() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: parity.io\r\n\ - Connection: close\r\n\ - Content-Type: application/json; charset=utf-8\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: parity.io\r\n\ + Connection: close\r\n\ + Content-Type: application/json; charset=utf-8\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + req.as_bytes().len(), + req + ), ); // then @@ -947,16 +1097,22 @@ fn should_always_allow_the_bind_address() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: {}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr, req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: {}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr, + req.as_bytes().len(), + req + ), ); // then @@ -972,16 +1128,22 @@ fn should_always_allow_the_bind_address_as_localhost() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"x"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -997,16 +1159,22 @@ fn should_handle_sync_requests_correctly() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -1022,16 +1190,22 @@ fn should_handle_async_requests_with_immediate_response_correctly() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello_async"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -1047,16 +1221,22 @@ fn should_handle_async_requests_correctly() { // when let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello_async2"}"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -1072,16 +1252,22 @@ fn should_handle_sync_batch_requests_correctly() { // when let req = r#"[{"jsonrpc":"2.0","id":1,"method":"hello"}]"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -1097,16 +1283,22 @@ fn should_handle_rest_request_with_params() { // when let req = ""; - let response = request(server, - &format!("\ - POST /hello/5 HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST /hello/5 HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -1122,16 +1314,22 @@ fn should_handle_rest_request_with_case_insensitive_content_type() { // when let req = ""; - let response = request(server, - &format!("\ - POST /hello/5 HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: Application/JSON; charset=UTF-8\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST /hello/5 HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: Application/JSON; charset=UTF-8\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then @@ -1147,20 +1345,29 @@ fn should_return_error_in_case_of_unsecure_rest_and_no_method() { // when let req = ""; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then assert_eq!(response.status, "HTTP/1.1 415 Unsupported Media Type".to_owned()); - assert_eq!(&response.body, "Supplied content type is not allowed. Content-Type: application/json is required\n"); + assert_eq!( + &response.body, + "Supplied content type is not allowed. Content-Type: application/json is required\n" + ); } #[test] @@ -1171,21 +1378,30 @@ fn should_return_connection_header() { // when let req = r#"[{"jsonrpc":"2.0","id":1,"method":"hello"}]"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: close\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: close\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then - assert!(response.headers.contains("connection: close"), - "Headers missing in {}", response.headers); + assert!( + response.headers.contains("connection: close"), + "Headers missing in {}", + response.headers + ); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, world_batch()); } @@ -1198,20 +1414,29 @@ fn should_close_connection_without_keep_alive() { // when let req = r#"[{"jsonrpc":"2.0","id":1,"method":"hello"}]"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then - assert!(response.headers.contains("connection: close"), - "Header missing in {}", response.headers); + assert!( + response.headers.contains("connection: close"), + "Header missing in {}", + response.headers + ); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, world_batch()); } @@ -1224,27 +1449,34 @@ fn should_respond_with_close_even_if_client_wants_to_keep_alive() { // when let req = r#"[{"jsonrpc":"2.0","id":1,"method":"hello"}]"#; - let response = request(server, - &format!("\ - POST / HTTP/1.1\r\n\ - Host: localhost:{}\r\n\ - Connection: keep-alive\r\n\ - Content-Type: application/json\r\n\ - Content-Length: {}\r\n\ - \r\n\ - {}\r\n\ - ", addr.port(), req.as_bytes().len(), req) + let response = request( + server, + &format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Connection: keep-alive\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ), ); // then - assert!(response.headers.contains("connection: close"), - "Headers missing in {}", response.headers); + assert!( + response.headers.contains("connection: close"), + "Headers missing in {}", + response.headers + ); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.body, world_batch()); } - - fn invalid_host() -> String { "Provided Host header is not whitelisted.\n".into() } @@ -1258,18 +1490,18 @@ fn cors_invalid_allow_headers() -> String { } fn method_not_found() -> String { - "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":1}\n".into() + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":1}\n".into() } fn invalid_request() -> String { - "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":null}\n".into() + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32600,\"message\":\"Invalid request\"},\"id\":null}\n".into() } fn world() -> String { - "{\"jsonrpc\":\"2.0\",\"result\":\"world\",\"id\":1}\n".into() + "{\"jsonrpc\":\"2.0\",\"result\":\"world\",\"id\":1}\n".into() } fn world_5() -> String { - "{\"jsonrpc\":\"2.0\",\"result\":\"world: 5\",\"id\":1}\n".into() + "{\"jsonrpc\":\"2.0\",\"result\":\"world: 5\",\"id\":1}\n".into() } fn world_batch() -> String { - "[{\"jsonrpc\":\"2.0\",\"result\":\"world\",\"id\":1}]\n".into() + "[{\"jsonrpc\":\"2.0\",\"result\":\"world\",\"id\":1}]\n".into() } diff --git a/http/src/utils.rs b/http/src/utils.rs index 5d3214747..22775e24f 100644 --- a/http/src/utils.rs +++ b/http/src/utils.rs @@ -8,22 +8,26 @@ fn read_header<'a>(req: &'a hyper::Request, header_name: &str) -> O } /// Returns `true` if Host header in request matches a list of allowed hosts. -pub fn is_host_allowed( - request: &hyper::Request, - allowed_hosts: &Option>, -) -> bool { +pub fn is_host_allowed(request: &hyper::Request, allowed_hosts: &Option>) -> bool { hosts::is_host_valid(read_header(request, "host"), allowed_hosts) } /// Returns a CORS AllowOrigin header that should be returned with that request. pub fn cors_allow_origin( request: &hyper::Request, - cors_domains: &Option> + cors_domains: &Option>, ) -> cors::AllowCors { - cors::get_cors_allow_origin(read_header(request, "origin"), read_header(request, "host"), cors_domains).map(|origin| { + cors::get_cors_allow_origin( + read_header(request, "origin"), + read_header(request, "host"), + cors_domains, + ) + .map(|origin| { use self::cors::AccessControlAllowOrigin::*; match origin { - Value(ref val) => header::HeaderValue::from_str(val).unwrap_or_else(|_| header::HeaderValue::from_static("null")), + Value(ref val) => { + header::HeaderValue::from_str(val).unwrap_or_else(|_| header::HeaderValue::from_static("null")) + } Null => header::HeaderValue::from_static("null"), Any => header::HeaderValue::from_static("*"), } @@ -33,34 +37,27 @@ pub fn cors_allow_origin( /// Returns the CORS AllowHeaders header that should be returned with that request. pub fn cors_allow_headers( request: &hyper::Request, - cors_allow_headers: &cors::AccessControlAllowHeaders + cors_allow_headers: &cors::AccessControlAllowHeaders, ) -> cors::AllowCors> { - let headers = request.headers().keys() - .map(|name| name.as_str()); - let requested_headers = request.headers() + let headers = request.headers().keys().map(|name| name.as_str()); + let requested_headers = request + .headers() .get_all("access-control-request-headers") .iter() .filter_map(|val| val.to_str().ok()) .flat_map(|val| val.split(", ")) .flat_map(|val| val.split(',')); - cors::get_cors_allow_headers( - headers, - requested_headers, - cors_allow_headers, - |name| header::HeaderValue::from_str(name) - .unwrap_or_else(|_| header::HeaderValue::from_static("unknown")) - ) + cors::get_cors_allow_headers(headers, requested_headers, cors_allow_headers, |name| { + header::HeaderValue::from_str(name).unwrap_or_else(|_| header::HeaderValue::from_static("unknown")) + }) } /// Returns an optional value of `Connection` header that should be included in the response. /// The second parameter defines if server is configured with keep-alive option. /// Return value of `true` indicates that no `Connection` header should be returned, /// `false` indicates `Connection: close`. -pub fn keep_alive( - request: &hyper::Request, - keep_alive: bool, -) -> bool { +pub fn keep_alive(request: &hyper::Request, keep_alive: bool) -> bool { read_header(request, "connection") .map(|val| match (keep_alive, val) { // indicate that connection should be closed diff --git a/ipc/examples/ipc.rs b/ipc/examples/ipc.rs index 8fb375b79..5a94bce0f 100644 --- a/ipc/examples/ipc.rs +++ b/ipc/examples/ipc.rs @@ -4,9 +4,8 @@ use jsonrpc_ipc_server::jsonrpc_core::*; fn main() { let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); let _server = jsonrpc_ipc_server::ServerBuilder::new(io) - .start("/tmp/parity-example.ipc").expect("Server should start ok"); + .start("/tmp/parity-example.ipc") + .expect("Server should start ok"); } diff --git a/ipc/src/lib.rs b/ipc/src/lib.rs index f7975ba2e..89a8ed54a 100644 --- a/ipc/src/lib.rs +++ b/ipc/src/lib.rs @@ -6,20 +6,24 @@ use jsonrpc_server_utils as server_utils; pub use jsonrpc_core; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; -#[cfg(test)] #[macro_use] extern crate lazy_static; +#[cfg(test)] +#[macro_use] +extern crate lazy_static; -#[cfg(test)] mod logger; +#[cfg(test)] +mod logger; -mod server; -mod select_with_weak; mod meta; +mod select_with_weak; +mod server; use jsonrpc_core as jsonrpc; pub use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; -pub use crate::server::{Server, ServerBuilder, CloseHandle,SecurityAttributes}; +pub use crate::server::{CloseHandle, SecurityAttributes, Server, ServerBuilder}; -pub use self::server_utils::{tokio, codecs::Separator}; -pub use self::server_utils::session::{SessionStats, SessionId}; +pub use self::server_utils::session::{SessionId, SessionStats}; +pub use self::server_utils::{codecs::Separator, tokio}; diff --git a/ipc/src/logger.rs b/ipc/src/logger.rs index 02d5a8ce2..c599dc34b 100644 --- a/ipc/src/logger.rs +++ b/ipc/src/logger.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] -use std::env; -use log::LevelFilter; use env_logger::Builder; +use log::LevelFilter; +use std::env; lazy_static! { static ref LOG_DUMMY: bool = { diff --git a/ipc/src/meta.rs b/ipc/src/meta.rs index ed3ac6ae1..8eff47a0e 100644 --- a/ipc/src/meta.rs +++ b/ipc/src/meta.rs @@ -13,12 +13,13 @@ pub struct RequestContext<'a> { } /// Metadata extractor (per session) -pub trait MetaExtractor : Send + Sync + 'static { +pub trait MetaExtractor: Send + Sync + 'static { /// Extracts metadata from request context fn extract(&self, context: &RequestContext) -> M; } -impl MetaExtractor for F where +impl MetaExtractor for F +where M: Metadata, F: Fn(&RequestContext) -> M + Send + Sync + 'static, { @@ -30,5 +31,7 @@ impl MetaExtractor for F where /// Noop-extractor pub struct NoopExtractor; impl MetaExtractor for NoopExtractor { - fn extract(&self, _context: &RequestContext) -> M { M::default() } + fn extract(&self, _context: &RequestContext) -> M { + M::default() + } } diff --git a/ipc/src/select_with_weak.rs b/ipc/src/select_with_weak.rs index bc96bff45..7bfec7c7c 100644 --- a/ipc/src/select_with_weak.rs +++ b/ipc/src/select_with_weak.rs @@ -1,14 +1,22 @@ -use crate::jsonrpc::futures::{Poll, Async}; -use crate::jsonrpc::futures::stream::{Stream, Fuse}; +use crate::jsonrpc::futures::stream::{Fuse, Stream}; +use crate::jsonrpc::futures::{Async, Poll}; pub trait SelectWithWeakExt: Stream { fn select_with_weak(self, other: S) -> SelectWithWeak - where S: Stream, Self: Sized; + where + S: Stream, + Self: Sized; } -impl SelectWithWeakExt for T where T: Stream { +impl SelectWithWeakExt for T +where + T: Stream, +{ fn select_with_weak(self, other: S) -> SelectWithWeak - where S: Stream, Self: Sized { + where + S: Stream, + Self: Sized, + { new(self, other) } } @@ -29,8 +37,9 @@ pub struct SelectWithWeak { } fn new(stream1: S1, stream2: S2) -> SelectWithWeak - where S1: Stream, - S2: Stream +where + S1: Stream, + S2: Stream, { SelectWithWeak { strong: stream1.fuse(), @@ -40,8 +49,9 @@ fn new(stream1: S1, stream2: S2) -> SelectWithWeak } impl Stream for SelectWithWeak - where S1: Stream, - S2: Stream +where + S1: Stream, + S2: Stream, { type Item = S1::Item; type Error = S1::Error; @@ -53,14 +63,14 @@ impl Stream for SelectWithWeak match self.strong.poll()? { Async::Ready(Some(item)) => { self.use_strong = false; - return Ok(Some(item).into()) - }, + return Ok(Some(item).into()); + } Async::Ready(None) => return Ok(None.into()), Async::NotReady => { if !checked_strong { self.use_strong = false; } else { - return Ok(Async::NotReady) + return Ok(Async::NotReady); } } } diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 6128e525f..3ed1b88be 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -3,15 +3,15 @@ use std; use std::sync::Arc; -use tokio_service::{self, Service as TokioService}; -use crate::jsonrpc::futures::{future, Future, Stream, Sink}; use crate::jsonrpc::futures::sync::{mpsc, oneshot}; -use crate::jsonrpc::{middleware, FutureResult, Metadata, MetaIoHandler, Middleware}; +use crate::jsonrpc::futures::{future, Future, Sink, Stream}; +use crate::jsonrpc::{middleware, FutureResult, MetaIoHandler, Metadata, Middleware}; +use tokio_service::{self, Service as TokioService}; use crate::server_utils::{ + codecs, reactor, session, + tokio::{self, reactor::Handle, runtime::TaskExecutor}, tokio_codec::Framed, - tokio::{self, runtime::TaskExecutor, reactor::Handle}, - reactor, session, codecs, }; use parking_lot::Mutex; @@ -61,7 +61,8 @@ pub struct ServerBuilder = middleware::Noop> impl> ServerBuilder { /// Creates new IPC server build given the `IoHandler`. - pub fn new(io_handler: T) -> ServerBuilder where + pub fn new(io_handler: T) -> ServerBuilder + where T: Into>, { Self::with_meta_extractor(io_handler, NoopExtractor) @@ -70,7 +71,8 @@ impl> ServerBuilder { impl> ServerBuilder { /// Creates new IPC server build given the `IoHandler` and metadata extractor. - pub fn with_meta_extractor(io_handler: T, extractor: E) -> ServerBuilder where + pub fn with_meta_extractor(io_handler: T, extractor: E) -> ServerBuilder + where T: Into>, E: MetaExtractor, { @@ -93,7 +95,8 @@ impl> ServerBuilder { } /// Sets session metadata extractor. - pub fn session_meta_extractor(mut self, meta_extractor: X) -> Self where + pub fn session_meta_extractor(mut self, meta_extractor: X) -> Self + where X: MetaExtractor, { self.meta_extractor = Arc::new(meta_extractor); @@ -155,7 +158,9 @@ impl> ServerBuilder { let connections = match endpoint.incoming(&endpoint_handle) { Ok(connections) => connections, Err(e) => { - start_signal.send(Err(e)).expect("Cannot fail since receiver never dropped before receiving"); + start_signal + .send(Err(e)) + .expect("Cannot fail since receiver never dropped before receiving"); return future::Either::A(future::ok(())); } }; @@ -167,7 +172,9 @@ impl> ServerBuilder { let session_id = id; let session_stats = session_stats.clone(); trace!(target: "ipc", "Accepted incoming IPC connection: {}", session_id); - if let Some(stats) = session_stats.as_ref() { stats.open_session(session_id) } + if let Some(stats) = session_stats.as_ref() { + stats.open_session(session_id) + } let (sender, receiver) = mpsc::channel(16); let meta = meta_extractor.extract(&RequestContext { @@ -178,23 +185,18 @@ impl> ServerBuilder { let service = Service::new(rpc_handler.clone(), meta); let (writer, reader) = Framed::new( io_stream, - codecs::StreamCodec::new( - incoming_separator.clone(), - outgoing_separator.clone(), - ), - ).split(); + codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()), + ) + .split(); let responses = reader .map(move |req| { - service.call(req) - .then(|result| { - match result { - Err(_) => { - future::ok(None) - } - Ok(some_result) => future::ok(some_result), - } + service + .call(req) + .then(|result| match result { + Err(_) => future::ok(None), + Ok(some_result) => future::ok(some_result), }) - .map_err(|_:()| std::io::ErrorKind::Other.into()) + .map_err(|_: ()| std::io::ErrorKind::Other.into()) }) .buffer_unordered(client_buffer_size) .filter_map(|x| x) @@ -203,11 +205,13 @@ impl> ServerBuilder { .select_with_weak(receiver.map_err(|e| { warn!(target: "ipc", "Notification error: {:?}", e); std::io::ErrorKind::Other.into() - })); + })); let writer = writer.send_all(responses).then(move |_| { trace!(target: "ipc", "Peer: service finished"); - if let Some(stats) = session_stats.as_ref() { stats.close_session(session_id) } + if let Some(stats) = session_stats.as_ref() { + stats.close_session(session_id) + } Ok(()) }); @@ -215,15 +219,18 @@ impl> ServerBuilder { Ok(()) }); - start_signal.send(Ok(())).expect("Cannot fail since receiver never dropped before receiving"); + start_signal + .send(Ok(())) + .expect("Cannot fail since receiver never dropped before receiving"); let stop = stop_receiver.map_err(|_| std::io::ErrorKind::Interrupted.into()); future::Either::B( - server.select(stop) + server + .select(stop) .map(|_| { let _ = wait_signal.send(()); }) - .map_err(|_| ()) + .map_err(|_| ()), ) })); @@ -238,12 +245,11 @@ impl> ServerBuilder { handles: Arc::new(Mutex::new(handle)), wait_handle: Some(wait_receiver), }), - Err(e) => Err(e) + Err(e) => Err(e), } } } - /// IPC Server handle #[derive(Debug)] pub struct Server { @@ -268,10 +274,8 @@ impl Server { pub fn wait(mut self) { self.wait_handle.take().map(|wait_receiver| wait_receiver.wait()); } - } - #[derive(Debug)] struct InnerHandles { executor: Option, @@ -282,7 +286,9 @@ struct InnerHandles { impl InnerHandles { pub fn close(&mut self) { let _ = self.stop.take().map(|stop| stop.send(())); - if let Some(executor) = self.executor.take() { executor.close() } + if let Some(executor) = self.executor.take() { + executor.close() + } let _ = ::std::fs::remove_file(&self.path); // ignore error, file could have been gone somewhere } } @@ -310,29 +316,27 @@ impl CloseHandle { mod tests { use tokio_uds; - use std::thread; - use std::sync::Arc; - use std::time; - use std::time::{Instant, Duration}; - use super::{ServerBuilder, Server}; - use crate::jsonrpc::{MetaIoHandler, Value}; - use crate::jsonrpc::futures::{Future, future, Stream, Sink}; - use crate::jsonrpc::futures::sync::{mpsc, oneshot}; use self::tokio_uds::UnixStream; - use parking_lot::Mutex; + use super::SecurityAttributes; + use super::{Server, ServerBuilder}; + use crate::jsonrpc::futures::sync::{mpsc, oneshot}; + use crate::jsonrpc::futures::{future, Future, Sink, Stream}; + use crate::jsonrpc::{MetaIoHandler, Value}; + use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; + use crate::server_utils::codecs; use crate::server_utils::{ + tokio::{self, timer::Delay}, tokio_codec::Decoder, - tokio::{self, timer::Delay} }; - use crate::server_utils::codecs; - use crate::meta::{MetaExtractor, RequestContext, NoopExtractor}; - use super::SecurityAttributes; + use parking_lot::Mutex; + use std::sync::Arc; + use std::thread; + use std::time; + use std::time::{Duration, Instant}; fn server_builder() -> ServerBuilder { let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); ServerBuilder::new(io) } @@ -345,16 +349,11 @@ mod tests { fn dummy_request_str(path: &str, data: &str) -> String { let stream_future = UnixStream::connect(path); let reply = stream_future.and_then(|stream| { - let stream = codecs::StreamCodec::stream_incoming() - .framed(stream); + let stream = codecs::StreamCodec::stream_incoming().framed(stream); let reply = stream .send(data.to_owned()) - .and_then(move |stream| { - stream.into_future().map_err(|(err, _)| err) - }) - .and_then(|(reply, _)| { - future::ok(reply.expect("there should be one reply")) - }); + .and_then(move |stream| stream.into_future().map_err(|(err, _)| err)) + .and_then(|(reply, _)| future::ok(reply.expect("there should be one reply"))); reply }); @@ -366,12 +365,11 @@ mod tests { crate::logger::init_log(); let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); let server = ServerBuilder::new(io); - let _server = server.start("/tmp/test-ipc-20000") + let _server = server + .start("/tmp/test-ipc-20000") .expect("Server must run with no issues"); } @@ -395,19 +393,20 @@ mod tests { let result = dummy_request_str( path, "{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}", - ); + ); stop_signal.send(result).unwrap(); }); t.join().unwrap(); - let _ = stop_receiver.map(move |result: String| { - assert_eq!( - result, - "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", - "Response does not exactly match the expected response", - ); - server.close(); - }).wait(); + let _ = stop_receiver + .map(move |result: String| { + assert_eq!( + result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", + "Response does not exactly match the expected response", + ); + server.close(); + }) + .wait(); } #[test] @@ -421,30 +420,31 @@ mod tests { for _ in 0..4 { let path = path.clone(); let mut stop_signal = stop_signal.clone(); - handles.push( - thread::spawn(move || { - for _ in 0..100 { - let result = dummy_request_str( - &path, - "{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}", - ); - stop_signal.try_send(result).unwrap(); - } - }) - ); + handles.push(thread::spawn(move || { + for _ in 0..100 { + let result = dummy_request_str( + &path, + "{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}", + ); + stop_signal.try_send(result).unwrap(); + } + })); } for handle in handles.drain(..) { handle.join().unwrap(); } - let _ = stop_receiver.map(|result| { - assert_eq!( - result, - "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", - "Response does not exactly match the expected response", + let _ = stop_receiver + .map(|result| { + assert_eq!( + result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", + "Response does not exactly match the expected response", ); - }).take(400).collect().wait(); + }) + .take(400) + .collect() + .wait(); server.close(); } @@ -455,14 +455,22 @@ mod tests { let server = run(path); server.close(); - assert!(::std::fs::metadata(path).is_err(), "There should be no socket file left"); - assert!(UnixStream::connect(path).wait().is_err(), "Connection to the closed socket should fail"); + assert!( + ::std::fs::metadata(path).is_err(), + "There should be no socket file left" + ); + assert!( + UnixStream::connect(path).wait().is_err(), + "Connection to the closed socket should fail" + ); } fn huge_response_test_str() -> String { let mut result = String::from("begin_hello"); result.push_str("begin_hello"); - for _ in 0..16384 { result.push(' '); } + for _ in 0..16384 { + result.push(' '); + } result.push_str("end_hello"); result } @@ -481,9 +489,7 @@ mod tests { let path = "/tmp/test-ipc-60000"; let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_huge_hello", |_params| { - Ok(Value::String(huge_response_test_str())) - }); + io.add_method("say_huge_hello", |_params| Ok(Value::String(huge_response_test_str()))); let builder = ServerBuilder::new(io); let server = builder.start(path).expect("Server must run with no issues"); @@ -499,14 +505,16 @@ mod tests { }); t.join().unwrap(); - let _ = stop_receiver.map(move |result: String| { - assert_eq!( - result, - huge_response_test_json(), - "Response does not exactly match the expected response", - ); - server.close(); - }).wait(); + let _ = stop_receiver + .map(move |result: String| { + assert_eq!( + result, + huge_response_test_json(), + "Response does not exactly match the expected response", + ); + server.close(); + }) + .wait(); } #[test] @@ -541,7 +549,7 @@ mod tests { let path = "/tmp/test-ipc-30009"; let (signal, receiver) = mpsc::channel(16); let session_metadata_extractor = SessionEndExtractor { - drop_receivers: Arc::new(Mutex::new(signal)) + drop_receivers: Arc::new(Mutex::new(signal)), }; let io = MetaIoHandler::>::default(); @@ -551,10 +559,12 @@ mod tests { let _ = UnixStream::connect(path).wait().expect("Socket should connect"); } - receiver.into_future() + receiver + .into_future() .map_err(|_| ()) .and_then(|drop_receiver| drop_receiver.0.unwrap().map_err(|_| ())) - .wait().unwrap(); + .wait() + .unwrap(); server.close(); } @@ -565,7 +575,10 @@ mod tests { let server = run(path); let handle = server.close_handle(); handle.close(); - assert!(UnixStream::connect(path).wait().is_err(), "Connection to the closed socket should fail"); + assert!( + UnixStream::connect(path).wait().is_err(), + "Connection to the closed socket should fail" + ); } #[test] @@ -589,20 +602,17 @@ mod tests { .map(|_| false) .map_err(|err| panic!("{:?}", err)); - let result_fut = rx - .map_err(|_| ()) - .select(delay) - .then(move |result| { - match result { - Ok((result, _)) => { - assert_eq!(result, true, "Wait timeout exceeded"); - assert!(UnixStream::connect(path).wait().is_err(), - "Connection to the closed socket should fail"); - Ok(()) - }, - Err(_) => Err(()), - } - }); + let result_fut = rx.map_err(|_| ()).select(delay).then(move |result| match result { + Ok((result, _)) => { + assert_eq!(result, true, "Wait timeout exceeded"); + assert!( + UnixStream::connect(path).wait().is_err(), + "Connection to the closed socket should fail" + ); + Ok(()) + } + Err(_) => Err(()), + }); tokio::run(result_fut); } diff --git a/pubsub/examples/pubsub.rs b/pubsub/examples/pubsub.rs index c17169d4d..6e78a20ce 100644 --- a/pubsub/examples/pubsub.rs +++ b/pubsub/examples/pubsub.rs @@ -1,9 +1,9 @@ -use std::{time, thread}; -use std::sync::{Arc, atomic}; +use std::sync::{atomic, Arc}; +use std::{thread, time}; use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; -use jsonrpc_tcp_server::{ServerBuilder, RequestContext}; +use jsonrpc_tcp_server::{RequestContext, ServerBuilder}; use jsonrpc_core::futures::Future; @@ -16,9 +16,7 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); let is_done = Arc::new(atomic::AtomicBool::default()); let is_done2 = is_done.clone(); @@ -26,11 +24,13 @@ fn main() { "hello", ("subscribe_hello", move |params: Params, _, subscriber: Subscriber| { if params != Params::None { - subscriber.reject(Error { - code: ErrorCode::ParseError, - message: "Invalid parameters. Subscription rejected.".into(), - data: None, - }).unwrap(); + subscriber + .reject(Error { + code: ErrorCode::ParseError, + message: "Invalid parameters. Subscription rejected.".into(), + data: None, + }) + .unwrap(); return; } @@ -47,7 +47,7 @@ fn main() { thread::sleep(time::Duration::from_millis(100)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { - Ok(_) => {}, + Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); break; @@ -70,4 +70,3 @@ fn main() { server.wait(); } - diff --git a/pubsub/examples/pubsub_simple.rs b/pubsub/examples/pubsub_simple.rs index 991a17bf5..cc9b30b54 100644 --- a/pubsub/examples/pubsub_simple.rs +++ b/pubsub/examples/pubsub_simple.rs @@ -1,9 +1,9 @@ -use std::{time, thread}; use std::sync::Arc; +use std::{thread, time}; use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; -use jsonrpc_tcp_server::{ServerBuilder, RequestContext}; +use jsonrpc_tcp_server::{RequestContext, ServerBuilder}; use jsonrpc_core::futures::Future; @@ -16,19 +16,19 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); io.add_subscription( "hello", ("subscribe_hello", |params: Params, _, subscriber: Subscriber| { if params != Params::None { - subscriber.reject(Error { - code: ErrorCode::ParseError, - message: "Invalid parameters. Subscription rejected.".into(), - data: None, - }).unwrap(); + subscriber + .reject(Error { + code: ErrorCode::ParseError, + message: "Invalid parameters. Subscription rejected.".into(), + data: None, + }) + .unwrap(); return; } @@ -40,7 +40,7 @@ fn main() { loop { thread::sleep(time::Duration::from_millis(100)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { - Ok(_) => {}, + Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); break; @@ -55,10 +55,11 @@ fn main() { }), ); - let server = ServerBuilder::with_meta_extractor(io, |context: &RequestContext| Arc::new(Session::new(context.sender.clone()))) - .start(&"127.0.0.1:3030".parse().unwrap()) - .expect("Unable to start RPC server"); + let server = ServerBuilder::with_meta_extractor(io, |context: &RequestContext| { + Arc::new(Session::new(context.sender.clone())) + }) + .start(&"127.0.0.1:3030".parse().unwrap()) + .expect("Unable to start RPC server"); server.wait(); } - diff --git a/pubsub/more-examples/examples/pubsub_ipc.rs b/pubsub/more-examples/examples/pubsub_ipc.rs index 05dca8ce7..bdd805287 100644 --- a/pubsub/more-examples/examples/pubsub_ipc.rs +++ b/pubsub/more-examples/examples/pubsub_ipc.rs @@ -1,13 +1,13 @@ extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; extern crate jsonrpc_ipc_server; +extern crate jsonrpc_pubsub; -use std::{time, thread}; use std::sync::Arc; +use std::{thread, time}; use jsonrpc_core::*; +use jsonrpc_ipc_server::{RequestContext, ServerBuilder, SessionId, SessionStats}; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; -use jsonrpc_ipc_server::{ServerBuilder, RequestContext, SessionStats, SessionId}; use jsonrpc_core::futures::Future; @@ -20,19 +20,19 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); io.add_subscription( "hello", ("subscribe_hello", |params: Params, _, subscriber: Subscriber| { if params != Params::None { - subscriber.reject(Error { - code: ErrorCode::ParseError, - message: "Invalid parameters. Subscription rejected.".into(), - data: None, - }).unwrap(); + subscriber + .reject(Error { + code: ErrorCode::ParseError, + message: "Invalid parameters. Subscription rejected.".into(), + data: None, + }) + .unwrap(); return; } @@ -44,7 +44,7 @@ fn main() { loop { thread::sleep(time::Duration::from_millis(100)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { - Ok(_) => {}, + Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); break; @@ -59,10 +59,12 @@ fn main() { }), ); - let server = ServerBuilder::with_meta_extractor(io, |context: &RequestContext| Arc::new(Session::new(context.sender.clone()))) - .session_stats(Stats) - .start("./test.ipc") - .expect("Unable to start RPC server"); + let server = ServerBuilder::with_meta_extractor(io, |context: &RequestContext| { + Arc::new(Session::new(context.sender.clone())) + }) + .session_stats(Stats) + .start("./test.ipc") + .expect("Unable to start RPC server"); server.wait(); } @@ -77,4 +79,3 @@ impl SessionStats for Stats { println!("Closing session: {}", id); } } - diff --git a/pubsub/more-examples/examples/pubsub_ws.rs b/pubsub/more-examples/examples/pubsub_ws.rs index 7dc87ddf9..5b0de4405 100644 --- a/pubsub/more-examples/examples/pubsub_ws.rs +++ b/pubsub/more-examples/examples/pubsub_ws.rs @@ -2,12 +2,12 @@ extern crate jsonrpc_core; extern crate jsonrpc_pubsub; extern crate jsonrpc_ws_server; -use std::{time, thread}; use std::sync::Arc; +use std::{thread, time}; use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; -use jsonrpc_ws_server::{ServerBuilder, RequestContext}; +use jsonrpc_ws_server::{RequestContext, ServerBuilder}; use jsonrpc_core::futures::Future; @@ -36,19 +36,19 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); io.add_subscription( "hello", ("subscribe_hello", |params: Params, _, subscriber: Subscriber| { if params != Params::None { - subscriber.reject(Error { - code: ErrorCode::ParseError, - message: "Invalid parameters. Subscription rejected.".into(), - data: None, - }).unwrap(); + subscriber + .reject(Error { + code: ErrorCode::ParseError, + message: "Invalid parameters. Subscription rejected.".into(), + data: None, + }) + .unwrap(); return; } @@ -60,7 +60,7 @@ fn main() { loop { thread::sleep(time::Duration::from_millis(1000)); match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { - Ok(_) => {}, + Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); break; @@ -75,9 +75,10 @@ fn main() { }), ); - let server = ServerBuilder::with_meta_extractor(io, |context: &RequestContext| Arc::new(Session::new(context.sender()))) - .start(&"127.0.0.1:3030".parse().unwrap()) - .expect("Unable to start RPC server"); + let server = + ServerBuilder::with_meta_extractor(io, |context: &RequestContext| Arc::new(Session::new(context.sender()))) + .start(&"127.0.0.1:3030".parse().unwrap()) + .expect("Unable to start RPC server"); let _ = server.wait(); } diff --git a/pubsub/src/delegates.rs b/pubsub/src/delegates.rs index 988cc6e86..008a314c9 100644 --- a/pubsub/src/delegates.rs +++ b/pubsub/src/delegates.rs @@ -1,19 +1,20 @@ +use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; -use std::collections::HashMap; -use crate::subscription::{Subscriber, new_subscription}; -use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; -use crate::types::{SubscriptionId, PubSubMetadata}; -use crate::core::{self, Params, Value, Error, Metadata, RemoteProcedure, RpcMethod}; use crate::core::futures::IntoFuture; +use crate::core::{self, Error, Metadata, Params, RemoteProcedure, RpcMethod, Value}; +use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; +use crate::subscription::{new_subscription, Subscriber}; +use crate::types::{PubSubMetadata, SubscriptionId}; struct DelegateSubscription { delegate: Arc, closure: F, } -impl SubscribeRpcMethod for DelegateSubscription where +impl SubscribeRpcMethod for DelegateSubscription +where M: PubSubMetadata, F: Fn(&T, Params, M, Subscriber), T: Send + Sync + 'static, @@ -25,7 +26,8 @@ impl SubscribeRpcMethod for DelegateSubscription where } } -impl UnsubscribeRpcMethod for DelegateSubscription where +impl UnsubscribeRpcMethod for DelegateSubscription +where M: PubSubMetadata, F: Fn(&T, SubscriptionId, Option) -> I, I: IntoFuture, @@ -41,7 +43,8 @@ impl UnsubscribeRpcMethod for DelegateSubscription where } /// Wire up rpc subscriptions to `delegate` struct -pub struct IoDelegate where +pub struct IoDelegate +where T: Send + Sync + 'static, M: Metadata, { @@ -50,7 +53,8 @@ pub struct IoDelegate where _data: PhantomData, } -impl IoDelegate where +impl IoDelegate +where T: Send + Sync + 'static, M: PubSubMetadata, { @@ -64,12 +68,8 @@ impl IoDelegate where } /// Adds subscription to the delegate. - pub fn add_subscription( - &mut self, - name: &str, - subscribe: (&str, Sub), - unsubscribe: (&str, Unsub), - ) where + pub fn add_subscription(&mut self, name: &str, subscribe: (&str, Sub), unsubscribe: (&str, Unsub)) + where Sub: Fn(&T, Params, M, Subscriber), Sub: Send + Sync + 'static, Unsub: Fn(&T, SubscriptionId, Option) -> I, @@ -86,10 +86,12 @@ impl IoDelegate where DelegateSubscription { delegate: self.delegate.clone(), closure: unsubscribe.1, - } + }, ); - self.inner.add_method_with_meta(subscribe.0, move |_, params, meta| sub.call(params, meta)); - self.inner.add_method_with_meta(unsubscribe.0, move |_, params, meta| unsub.call(params, meta)); + self.inner + .add_method_with_meta(subscribe.0, move |_, params, meta| sub.call(params, meta)); + self.inner + .add_method_with_meta(unsubscribe.0, move |_, params, meta| unsub.call(params, meta)); } /// Adds an alias to existing method. @@ -98,7 +100,8 @@ impl IoDelegate where } /// Adds async method to the delegate. - pub fn add_method(&mut self, name: &str, method: F) where + pub fn add_method(&mut self, name: &str, method: F) + where F: Fn(&T, Params) -> I, I: IntoFuture, F: Send + Sync + 'static, @@ -108,7 +111,8 @@ impl IoDelegate where } /// Adds async method with metadata to the delegate. - pub fn add_method_with_meta(&mut self, name: &str, method: F) where + pub fn add_method_with_meta(&mut self, name: &str, method: F) + where F: Fn(&T, Params, M) -> I, I: IntoFuture, F: Send + Sync + 'static, @@ -118,7 +122,8 @@ impl IoDelegate where } /// Adds notification to the delegate. - pub fn add_notification(&mut self, name: &str, notification: F) where + pub fn add_notification(&mut self, name: &str, notification: F) + where F: Fn(&T, Params), F: Send + Sync + 'static, { @@ -126,7 +131,8 @@ impl IoDelegate where } } -impl Into>> for IoDelegate where +impl Into>> for IoDelegate +where T: Send + Sync + 'static, M: Metadata, { diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index fb4a134f7..d23834727 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -1,8 +1,8 @@ use crate::core; use crate::core::futures::{Future, IntoFuture}; +use crate::subscription::{new_subscription, Subscriber}; use crate::types::{PubSubMetadata, SubscriptionId}; -use crate::subscription::{Subscriber, new_subscription}; /// Subscribe handler pub trait SubscribeRpcMethod: Send + Sync + 'static { @@ -10,7 +10,8 @@ pub trait SubscribeRpcMethod: Send + Sync + 'static { fn call(&self, params: core::Params, meta: M, subscriber: Subscriber); } -impl SubscribeRpcMethod for F where +impl SubscribeRpcMethod for F +where F: Fn(core::Params, M, Subscriber) + Send + Sync + 'static, M: PubSubMetadata, { @@ -29,7 +30,8 @@ pub trait UnsubscribeRpcMethod: Send + Sync + 'static { fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out; } -impl UnsubscribeRpcMethod for F where +impl UnsubscribeRpcMethod for F +where F: Fn(SubscriptionId, Option) -> I + Send + Sync + 'static, I: IntoFuture, I::Future: Send + 'static, @@ -56,18 +58,12 @@ impl Default for PubSubHandler { impl> PubSubHandler { /// Creates new `PubSubHandler` pub fn new(handler: core::MetaIoHandler) -> Self { - PubSubHandler { - handler, - } + PubSubHandler { handler } } /// Adds new subscription. - pub fn add_subscription( - &mut self, - notification: &str, - subscribe: (&str, F), - unsubscribe: (&str, G), - ) where + pub fn add_subscription(&mut self, notification: &str, subscribe: (&str, F), unsubscribe: (&str, G)) + where F: SubscribeRpcMethod, G: UnsubscribeRpcMethod, { @@ -99,8 +95,8 @@ impl> Into> #[cfg(test)] mod tests { - use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; use crate::core; use crate::core::futures::future; diff --git a/pubsub/src/lib.rs b/pubsub/src/lib.rs index f16319bb6..18f18071b 100644 --- a/pubsub/src/lib.rs +++ b/pubsub/src/lib.rs @@ -9,12 +9,12 @@ extern crate log; mod delegates; mod handler; -mod subscription; -mod types; pub mod oneshot; +mod subscription; pub mod typed; +mod types; -pub use self::handler::{PubSubHandler, SubscribeRpcMethod, UnsubscribeRpcMethod}; pub use self::delegates::IoDelegate; -pub use self::subscription::{Session, Sink, Subscriber, new_subscription}; -pub use self::types::{PubSubMetadata, SubscriptionId, TransportError, SinkResult}; +pub use self::handler::{PubSubHandler, SubscribeRpcMethod, UnsubscribeRpcMethod}; +pub use self::subscription::{new_subscription, Session, Sink, Subscriber}; +pub use self::types::{PubSubMetadata, SinkResult, SubscriptionId, TransportError}; diff --git a/pubsub/src/oneshot.rs b/pubsub/src/oneshot.rs index dd0eb72ff..2d72f11ef 100644 --- a/pubsub/src/oneshot.rs +++ b/pubsub/src/oneshot.rs @@ -1,7 +1,7 @@ //! A futures oneshot channel that can be used for rendezvous. +use crate::core::futures::{self, future, sync::oneshot, Future}; use std::ops::{Deref, DerefMut}; -use crate::core::futures::{self, Future, future, sync::oneshot}; /// Create a new future-base rendezvous channel. /// @@ -14,8 +14,14 @@ pub fn channel() -> (Sender, Receiver) { let (receipt_tx, receipt_rx) = oneshot::channel(); ( - Sender { sender, receipt: receipt_rx }, - Receiver { receiver, receipt: Some(receipt_tx) } + Sender { + sender, + receipt: receipt_rx, + }, + Receiver { + receiver, + receipt: Some(receipt_tx), + }, ) } @@ -47,7 +53,7 @@ impl Sender { let Self { sender, receipt } = self; if let Err(_) = sender.send(t) { - return future::Either::A(future::err(())) + return future::Either::A(future::err(())); } future::Either::B(receipt.map_err(|_| ())) @@ -92,7 +98,7 @@ impl Future for Receiver { let _ = receipt.send(()); } Ok(futures::Async::Ready(r)) - }, + } e => e, } } diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 7cb2740e5..fa004cf6a 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -1,16 +1,16 @@ //! Subscription primitives. -use std::fmt; +use parking_lot::Mutex; use std::collections::HashMap; +use std::fmt; use std::sync::Arc; -use parking_lot::Mutex; -use crate::core::{self, BoxFuture}; -use crate::core::futures::{self, future, Sink as FuturesSink, Future}; use crate::core::futures::sync::mpsc; +use crate::core::futures::{self, future, Future, Sink as FuturesSink}; +use crate::core::{self, BoxFuture}; use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; -use crate::types::{PubSubMetadata, SubscriptionId, TransportSender, TransportError, SinkResult}; +use crate::types::{PubSubMetadata, SinkResult, SubscriptionId, TransportError, TransportSender}; /// RPC client session /// Keeps track of active subscriptions and unsubscribes from them upon dropping. @@ -56,10 +56,14 @@ impl Session { } /// Adds new active subscription - fn add_subscription(&self, name: &str, id: &SubscriptionId, remove: F) where + fn add_subscription(&self, name: &str, id: &SubscriptionId, remove: F) + where F: Fn(SubscriptionId) + Send + 'static, { - let ret = self.active_subscriptions.lock().insert((id.clone(), name.into()), Box::new(remove)); + let ret = self + .active_subscriptions + .lock() + .insert((id.clone(), name.into()), Box::new(remove)); if let Some(remove) = ret { warn!("SubscriptionId collision. Unsubscribing previous client."); remove(id.clone()); @@ -147,7 +151,9 @@ impl Subscriber { /// Creates new subscriber. /// /// Should only be used for tests. - pub fn new_test>(method: T) -> ( + pub fn new_test>( + method: T, + ) -> ( Self, crate::oneshot::Receiver>, mpsc::Receiver, @@ -168,8 +174,13 @@ impl Subscriber { /// /// Returns `Err` if request has already terminated. pub fn assign_id(self, id: SubscriptionId) -> Result { - let Self { notification, transport, sender } = self; - sender.send(Ok(id)) + let Self { + notification, + transport, + sender, + } = self; + sender + .send(Ok(id)) .map(|_| Sink { notification, transport, @@ -182,8 +193,13 @@ impl Subscriber { /// The returned `Future` resolves when the subscriber receives subscription id. /// Resolves to `Err` if request has already terminated. pub fn assign_id_async(self, id: SubscriptionId) -> impl Future { - let Self { notification, transport, sender } = self; - sender.send_and_wait(Ok(id)) + let Self { + notification, + transport, + sender, + } = self; + sender + .send_and_wait(Ok(id)) .map(|_| Sink { notification, transport, @@ -203,15 +219,13 @@ impl Subscriber { /// The returned `Future` resolves when the rejection is sent to the client. /// Resolves to `Err` if request has already terminated. pub fn reject_async(self, error: core::Error) -> impl Future { - self.sender.send_and_wait(Err(error)) - .map(|_| ()) - .map_err(|_| ()) + self.sender.send_and_wait(Err(error)).map(|_| ()).map_err(|_| ()) } } - /// Creates new subscribe and unsubscribe RPC methods -pub fn new_subscription(notification: &str, subscribe: F, unsubscribe: G) -> (Subscribe, Unsubscribe) where +pub fn new_subscription(notification: &str, subscribe: F, unsubscribe: G) -> (Subscribe, Unsubscribe) +where M: PubSubMetadata, F: SubscribeRpcMethod, G: UnsubscribeRpcMethod, @@ -254,7 +268,8 @@ pub struct Subscribe { unsubscribe: Arc, } -impl core::RpcMethod for Subscribe where +impl core::RpcMethod for Subscribe +where M: PubSubMetadata, F: SubscribeRpcMethod, G: UnsubscribeRpcMethod, @@ -274,21 +289,19 @@ impl core::RpcMethod for Subscribe where let unsub = self.unsubscribe.clone(); let notification = self.notification.clone(); - let subscribe_future = rx - .map_err(|_| subscription_rejected()) - .and_then(move |result| { - futures::done(match result { - Ok(id) => { - session.add_subscription(¬ification, &id, move |id| { - let _ = unsub.call(id, None).wait(); - }); - Ok(id.into()) - }, - Err(e) => Err(e), - }) - }); + let subscribe_future = rx.map_err(|_| subscription_rejected()).and_then(move |result| { + futures::done(match result { + Ok(id) => { + session.add_subscription(¬ification, &id, move |id| { + let _ = unsub.call(id, None).wait(); + }); + Ok(id.into()) + } + Err(e) => Err(e), + }) + }); Box::new(subscribe_future) - }, + } None => Box::new(future::err(subscriptions_unavailable())), } } @@ -300,22 +313,21 @@ pub struct Unsubscribe { unsubscribe: Arc, } -impl core::RpcMethod for Unsubscribe where +impl core::RpcMethod for Unsubscribe +where M: PubSubMetadata, G: UnsubscribeRpcMethod, { fn call(&self, params: core::Params, meta: M) -> BoxFuture { let id = match params { - core::Params::Array(ref vec) if vec.len() == 1 => { - SubscriptionId::parse_value(&vec[0]) - }, + core::Params::Array(ref vec) if vec.len() == 1 => SubscriptionId::parse_value(&vec[0]), _ => None, }; match (meta.session(), id) { (Some(session), Some(id)) => { session.remove_subscription(&self.notification, &id); Box::new(self.unsubscribe.call(id, Some(meta))) - }, + } (Some(_), None) => Box::new(future::err(core::Error::invalid_params("Expected subscription id."))), _ => Box::new(future::err(subscriptions_unavailable())), } @@ -324,15 +336,15 @@ impl core::RpcMethod for Unsubscribe where #[cfg(test)] mod tests { - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, Ordering}; use crate::core; - use crate::core::RpcMethod; - use crate::core::futures::{Async, Future, Stream}; use crate::core::futures::sync::mpsc; - use crate::types::{SubscriptionId, PubSubMetadata}; + use crate::core::futures::{Async, Future, Stream}; + use crate::core::RpcMethod; + use crate::types::{PubSubMetadata, SubscriptionId}; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; - use super::{Session, Sink, Subscriber, new_subscription}; + use super::{new_subscription, Session, Sink, Subscriber}; fn session() -> (Session, mpsc::Receiver) { let (tx, rx) = mpsc::channel(1); @@ -407,7 +419,9 @@ mod tests { }; // when - sink.notify(core::Params::Array(vec![core::Value::Number(10.into())])).wait().unwrap(); + sink.notify(core::Params::Array(vec![core::Value::Number(10.into())])) + .wait() + .unwrap(); // then assert_eq!( @@ -431,10 +445,7 @@ mod tests { let sink = subscriber.assign_id_async(SubscriptionId::Number(5)); // then - assert_eq!( - rx.poll().unwrap(), - Async::Ready(Ok(SubscriptionId::Number(5))) - ); + assert_eq!(rx.poll().unwrap(), Async::Ready(Ok(SubscriptionId::Number(5)))); let sink = sink.wait().unwrap(); assert_eq!(sink.notification, "test".to_owned()); } @@ -459,10 +470,7 @@ mod tests { let reject = subscriber.reject_async(error.clone()); // then - assert_eq!( - rx.poll().unwrap(), - Async::Ready(Err(error)) - ); + assert_eq!(rx.poll().unwrap(), Async::Ready(Err(error))); reject.wait().unwrap(); } @@ -495,10 +503,13 @@ mod tests { // then assert_eq!(called.load(Ordering::SeqCst), true); - assert_eq!(result.wait(), Err(core::Error { - code: core::ErrorCode::ServerError(-32091), - message: "Subscription rejected".into(), - data: None, - })); + assert_eq!( + result.wait(), + Err(core::Error { + code: core::ErrorCode::ServerError(-32091), + message: "Subscription rejected".into(), + data: None, + }) + ); } } diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs index b1c20cf4a..a2c5804a2 100644 --- a/pubsub/src/typed.rs +++ b/pubsub/src/typed.rs @@ -2,12 +2,12 @@ use std::marker::PhantomData; -use serde; use crate::subscription; -use crate::types::{SubscriptionId, TransportError, SinkResult}; +use crate::types::{SinkResult, SubscriptionId, TransportError}; +use serde; -use crate::core::{self, Value, Params, Error}; -use crate::core::futures::{self, Sink as FuturesSink, Future, sync}; +use crate::core::futures::{self, sync, Future, Sink as FuturesSink}; +use crate::core::{self, Error, Params, Value}; /// New PUB-SUB subscriber. #[derive(Debug)] @@ -26,7 +26,9 @@ impl Subscriber { } /// Create new subscriber for tests. - pub fn new_test>(method: M) -> ( + pub fn new_test>( + method: M, + ) -> ( Self, crate::oneshot::Receiver>, sync::mpsc::Receiver, @@ -51,26 +53,24 @@ impl Subscriber { /// This method consumes `Subscriber` and returns `Sink` /// if the connection is still open or error otherwise. pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { - self.subscriber.assign_id(id.clone()) - .map(|sink| Sink { - id, - sink, - buffered: None, - _data: PhantomData, - }) + self.subscriber.assign_id(id.clone()).map(|sink| Sink { + id, + sink, + buffered: None, + _data: PhantomData, + }) } /// Assign id to this subscriber. /// This method consumes `Subscriber` and resolves to `Sink` /// if the connection is still open and the id has been sent or to error otherwise. pub fn assign_id_async(self, id: SubscriptionId) -> impl Future, Error = ()> { - self.subscriber.assign_id_async(id.clone()) - .map(|sink| Sink { - id, - sink, - buffered: None, - _data: PhantomData, - }) + self.subscriber.assign_id_async(id.clone()).map(|sink| Sink { + id, + sink, + buffered: None, + _data: PhantomData, + }) } } @@ -89,22 +89,28 @@ impl Sink { self.sink.notify(self.val_to_params(val)) } - fn to_value(value: V) -> Value where V: serde::Serialize { + fn to_value(value: V) -> Value + where + V: serde::Serialize, + { core::to_value(value).expect("Expected always-serializable type.") } fn val_to_params(&self, val: Result) -> Params { - let id = self.id.clone().into(); let val = val.map(Self::to_value).map_err(Self::to_value); - Params::Map(vec![ - ("subscription".to_owned(), id), - match val { - Ok(val) => ("result".to_owned(), val), - Err(err) => ("error".to_owned(), err), - }, - ].into_iter().collect()) + Params::Map( + vec![ + ("subscription".to_owned(), id), + match val { + Ok(val) => ("result".to_owned(), val), + Err(err) => ("error".to_owned(), err), + }, + ] + .into_iter() + .collect(), + ) } fn poll(&mut self) -> futures::Poll<(), TransportError> { diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index 5b8aa98c1..be5fefc49 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -1,6 +1,6 @@ -use std::sync::Arc; use crate::core; use crate::core::futures::sync::mpsc; +use std::sync::Arc; use crate::subscription::Session; @@ -78,8 +78,8 @@ impl From for core::Value { #[cfg(test)] mod tests { - use crate::core::Value; use super::SubscriptionId; + use crate::core::Value; #[test] fn should_convert_between_value_and_subscription_id() { diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..86fe606e0 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +max_width = 120 +hard_tabs = true \ No newline at end of file diff --git a/server-utils/src/cors.rs b/server-utils/src/cors.rs index 5162b6380..5889bf891 100644 --- a/server-utils/src/cors.rs +++ b/server-utils/src/cors.rs @@ -1,11 +1,11 @@ //! CORS handling utility functions use unicase; -use std::{fmt, ops}; +pub use self::unicase::Ascii; use crate::hosts::{Host, Port}; use crate::matcher::{Matcher, Pattern}; use std::collections::HashSet; -pub use self::unicase::Ascii; +use std::{fmt, ops}; /// Origin Protocol #[derive(Clone, Hash, Debug, PartialEq, Eq)] @@ -116,11 +116,15 @@ pub enum AccessControlAllowOrigin { impl fmt::Display for AccessControlAllowOrigin { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match *self { - AccessControlAllowOrigin::Any => "*", - AccessControlAllowOrigin::Null => "null", - AccessControlAllowOrigin::Value(ref val) => val, - }) + write!( + f, + "{}", + match *self { + AccessControlAllowOrigin::Any => "*", + AccessControlAllowOrigin::Null => "null", + AccessControlAllowOrigin::Value(ref val) => val, + } + ) } } @@ -156,7 +160,8 @@ pub enum AllowCors { impl AllowCors { /// Maps `Ok` variant of `AllowCors`. - pub fn map(self, f: F) -> AllowCors where + pub fn map(self, f: F) -> AllowCors + where F: FnOnce(T) -> O, { use self::AllowCors::*; @@ -184,7 +189,7 @@ impl Into> for AllowCors { pub fn get_cors_allow_origin( origin: Option<&str>, host: Option<&str>, - allowed: &Option> + allowed: &Option>, ) -> AllowCors { match origin { None => AllowCors::NotRequired, @@ -203,44 +208,40 @@ pub fn get_cors_allow_origin( match allowed.as_ref() { None if *origin == "null" => AllowCors::Ok(AccessControlAllowOrigin::Null), None => AllowCors::Ok(AccessControlAllowOrigin::Value(Origin::parse(origin))), - Some(ref allowed) if *origin == "null" => { - allowed.iter().find(|cors| **cors == AccessControlAllowOrigin::Null).cloned() - .map(AllowCors::Ok) - .unwrap_or(AllowCors::Invalid) - }, - Some(ref allowed) => { - allowed.iter().find(|cors| { - match **cors { - AccessControlAllowOrigin::Any => true, - AccessControlAllowOrigin::Value(ref val) if val.matches(origin) => - { - true - }, - _ => false - } + Some(ref allowed) if *origin == "null" => allowed + .iter() + .find(|cors| **cors == AccessControlAllowOrigin::Null) + .cloned() + .map(AllowCors::Ok) + .unwrap_or(AllowCors::Invalid), + Some(ref allowed) => allowed + .iter() + .find(|cors| match **cors { + AccessControlAllowOrigin::Any => true, + AccessControlAllowOrigin::Value(ref val) if val.matches(origin) => true, + _ => false, }) .map(|_| AccessControlAllowOrigin::Value(Origin::parse(origin))) - .map(AllowCors::Ok).unwrap_or(AllowCors::Invalid) - }, + .map(AllowCors::Ok) + .unwrap_or(AllowCors::Invalid), } - }, + } } } /// Validates if the `AccessControlAllowedHeaders` in the request are allowed. pub fn get_cors_allow_headers, O, F: Fn(T) -> O>( - mut headers: impl Iterator, - requested_headers: impl Iterator, + mut headers: impl Iterator, + requested_headers: impl Iterator, cors_allow_headers: &AccessControlAllowHeaders, - to_result: F + to_result: F, ) -> AllowCors> { // Check if the header fields which were sent in the request are allowed if let AccessControlAllowHeaders::Only(only) = cors_allow_headers { - let are_all_allowed = headers - .all(|header| { - let name = &Ascii::new(header.as_ref()); - only.iter().any(|h| Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name) - }); + let are_all_allowed = headers.all(|header| { + let name = &Ascii::new(header.as_ref()); + only.iter().any(|h| Ascii::new(&*h) == name) || ALWAYS_ALLOWED_HEADERS.contains(name) + }); if !are_all_allowed { return AllowCors::Invalid; @@ -252,7 +253,7 @@ pub fn get_cors_allow_headers, O, F: Fn(T) -> O>( AccessControlAllowHeaders::Any => { let headers = requested_headers.map(to_result).collect(); (false, headers) - }, + } AccessControlAllowHeaders::Only(only) => { let mut filtered = false; let headers: Vec<_> = requested_headers @@ -265,7 +266,7 @@ pub fn get_cors_allow_headers, O, F: Fn(T) -> O>( .collect(); (filtered, headers) - }, + } }; if headers.is_empty() { @@ -310,10 +311,22 @@ mod tests { use self::OriginProtocol::*; assert_eq!(Origin::parse("http://parity.io"), Origin::new(Http, "parity.io", None)); - assert_eq!(Origin::parse("https://parity.io:8443"), Origin::new(Https, "parity.io", Some(8443))); - assert_eq!(Origin::parse("chrome-extension://124.0.0.1"), Origin::new(Custom("chrome-extension".into()), "124.0.0.1", None)); - assert_eq!(Origin::parse("parity.io/somepath"), Origin::new(Http, "parity.io", None)); - assert_eq!(Origin::parse("127.0.0.1:8545/somepath"), Origin::new(Http, "127.0.0.1", Some(8545))); + assert_eq!( + Origin::parse("https://parity.io:8443"), + Origin::new(Https, "parity.io", Some(8443)) + ); + assert_eq!( + Origin::parse("chrome-extension://124.0.0.1"), + Origin::new(Custom("chrome-extension".into()), "124.0.0.1", None) + ); + assert_eq!( + Origin::parse("parity.io/somepath"), + Origin::new(Http, "parity.io", None) + ); + assert_eq!( + Origin::parse("127.0.0.1:8545/somepath"), + Origin::new(Http, "127.0.0.1", Some(8545)) + ); } #[test] @@ -435,7 +448,10 @@ mod tests { let res = get_cors_allow_origin(origin, host, &Some(vec![AccessControlAllowOrigin::Any])); // then - assert_eq!(res, AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into()))); + assert_eq!( + res, + AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())) + ); } #[test] @@ -445,11 +461,7 @@ mod tests { let host = None; // when - let res = get_cors_allow_origin( - origin, - host, - &Some(vec![AccessControlAllowOrigin::Null]), - ); + let res = get_cors_allow_origin(origin, host, &Some(vec![AccessControlAllowOrigin::Null])); // then assert_eq!(res, AllowCors::NotRequired); @@ -462,11 +474,7 @@ mod tests { let host = None; // when - let res = get_cors_allow_origin( - origin, - host, - &Some(vec![AccessControlAllowOrigin::Null]), - ); + let res = get_cors_allow_origin(origin, host, &Some(vec![AccessControlAllowOrigin::Null])); // then assert_eq!(res, AllowCors::Ok(AccessControlAllowOrigin::Null)); @@ -482,11 +490,17 @@ mod tests { let res = get_cors_allow_origin( origin, host, - &Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into()), AccessControlAllowOrigin::Value("http://parity.io".into())]), + &Some(vec![ + AccessControlAllowOrigin::Value("http://ethereum.org".into()), + AccessControlAllowOrigin::Value("http://parity.io".into()), + ]), ); // then - assert_eq!(res, AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into()))); + assert_eq!( + res, + AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())) + ); } #[test] @@ -497,8 +511,8 @@ mod tests { let origin3 = Some("chrome-extension://test".into()); let host = None; let allowed = Some(vec![ - AccessControlAllowOrigin::Value("http://*.io".into()), - AccessControlAllowOrigin::Value("chrome-extension://*".into()) + AccessControlAllowOrigin::Value("http://*.io".into()), + AccessControlAllowOrigin::Value("chrome-extension://*".into()), ]); // when @@ -507,17 +521,21 @@ mod tests { let res3 = get_cors_allow_origin(origin3, host, &allowed); // then - assert_eq!(res1, AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into()))); + assert_eq!( + res1, + AllowCors::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())) + ); assert_eq!(res2, AllowCors::Invalid); - assert_eq!(res3, AllowCors::Ok(AccessControlAllowOrigin::Value("chrome-extension://test".into()))); + assert_eq!( + res3, + AllowCors::Ok(AccessControlAllowOrigin::Value("chrome-extension://test".into())) + ); } #[test] fn should_return_invalid_if_header_not_allowed() { // given - let cors_allow_headers = AccessControlAllowHeaders::Only(vec![ - "x-allowed".to_owned(), - ]); + let cors_allow_headers = AccessControlAllowHeaders::Only(vec!["x-allowed".to_owned()]); let headers = vec!["Access-Control-Request-Headers"]; let requested = vec!["x-not-allowed"]; @@ -531,29 +549,25 @@ mod tests { #[test] fn should_return_valid_if_header_allowed() { // given - let allowed = vec![ - "x-allowed".to_owned(), - ]; + let allowed = vec!["x-allowed".to_owned()]; let cors_allow_headers = AccessControlAllowHeaders::Only(allowed.clone()); let headers = vec!["Access-Control-Request-Headers"]; let requested = vec!["x-allowed"]; // when - let res = get_cors_allow_headers(headers.iter(), requested.iter(), &cors_allow_headers.into(), |x| (*x).to_owned()); + let res = get_cors_allow_headers(headers.iter(), requested.iter(), &cors_allow_headers.into(), |x| { + (*x).to_owned() + }); // then - let allowed = vec![ - "x-allowed".to_owned(), - ]; + let allowed = vec!["x-allowed".to_owned()]; assert_eq!(res, AllowCors::Ok(allowed)); } #[test] fn should_return_no_allowed_headers_if_none_in_request() { // given - let allowed = vec![ - "x-allowed".to_owned(), - ]; + let allowed = vec!["x-allowed".to_owned()]; let cors_allow_headers = AccessControlAllowHeaders::Only(allowed.clone()); let headers: Vec = vec![]; diff --git a/server-utils/src/hosts.rs b/server-utils/src/hosts.rs index c02efcb6e..53f7a9702 100644 --- a/server-utils/src/hosts.rs +++ b/server-utils/src/hosts.rs @@ -1,8 +1,8 @@ //! Host header validation. +use crate::matcher::{Matcher, Pattern}; use std::collections::HashSet; use std::net::SocketAddr; -use crate::matcher::{Matcher, Pattern}; const SPLIT_PROOF: &str = "split always returns non-empty iterator."; @@ -14,7 +14,7 @@ pub enum Port { /// Port specified as a wildcard pattern Pattern(String), /// Fixed numeric port - Fixed(u16) + Fixed(u16), } impl From> for Port { @@ -74,7 +74,7 @@ impl Host { Some(port) => match port.parse::().ok() { Some(num) => Port::Fixed(num), None => Port::Pattern(port.into()), - } + }, }; Host::new(host, port) @@ -153,10 +153,8 @@ pub fn is_host_valid(host: Option<&str>, allowed_hosts: &Option>) -> b None => true, Some(ref allowed_hosts) => match host { None => false, - Some(ref host) => { - allowed_hosts.iter().any(|h| h.matches(host)) - } - } + Some(ref host) => allowed_hosts.iter().any(|h| h.matches(host)), + }, } } @@ -173,15 +171,24 @@ pub fn update(hosts: Option>, address: &SocketAddr) -> Option(&self, state: &mut H) where H: hash::Hasher { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { self.1.hash(state) } } diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index d1eb6d8ba..85834e30a 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -1,10 +1,10 @@ //! Event Loop Executor //! Either spawns a new event loop, or re-uses provided one. -use std::{io, thread}; +use num_cpus; use std::sync::mpsc; +use std::{io, thread}; use tokio; -use num_cpus; use crate::core::futures::{self, Future}; @@ -106,32 +106,32 @@ impl RpcEventLoop { tb = tb.name(name); } - let handle = tb.spawn(move || { - let core_threads = match num_cpus::get_physical() { - 1 => 1, - 2..=4 => 2, - _ => 3, - }; - - let runtime = tokio::runtime::Builder::new() - .core_threads(core_threads) - .name_prefix("jsonrpc-eventloop-") - .build(); - - match runtime { - Ok(mut runtime) => { - tx.send(Ok(runtime.executor())).expect("Rx is blocking upper thread."); - let terminate = futures::empty().select(stopped) - .map(|_| ()) - .map_err(|_| ()); - runtime.spawn(terminate); - runtime.shutdown_on_idle().wait().unwrap(); - }, - Err(err) => { - tx.send(Err(err)).expect("Rx is blocking upper thread."); + let handle = tb + .spawn(move || { + let core_threads = match num_cpus::get_physical() { + 1 => 1, + 2..=4 => 2, + _ => 3, + }; + + let runtime = tokio::runtime::Builder::new() + .core_threads(core_threads) + .name_prefix("jsonrpc-eventloop-") + .build(); + + match runtime { + Ok(mut runtime) => { + tx.send(Ok(runtime.executor())).expect("Rx is blocking upper thread."); + let terminate = futures::empty().select(stopped).map(|_| ()).map_err(|_| ()); + runtime.spawn(terminate); + runtime.shutdown_on_idle().wait().unwrap(); + } + Err(err) => { + tx.send(Err(err)).expect("Rx is blocking upper thread."); + } } - } - }).expect("Couldn't spawn a thread."); + }) + .expect("Couldn't spawn a thread."); let exec = rx.recv().expect("tx is transfered to a newly spawned thread."); @@ -149,13 +149,21 @@ impl RpcEventLoop { /// Blocks current thread and waits until the event loop is finished. pub fn wait(mut self) -> thread::Result<()> { - self.handle.take().expect("Handle is always set before self is consumed.").join() + self.handle + .take() + .expect("Handle is always set before self is consumed.") + .join() } /// Finishes this event loop. pub fn close(mut self) { - let _ = self.close.take().expect("Close is always set before self is consumed.").send(()).map_err(|e| { - warn!("Event Loop is already finished. {:?}", e); - }); + let _ = self + .close + .take() + .expect("Close is always set before self is consumed.") + .send(()) + .map_err(|e| { + warn!("Event Loop is already finished. {:?}", e); + }); } } diff --git a/server-utils/src/stream_codec.rs b/server-utils/src/stream_codec.rs index cc18f372d..d7cb268b9 100644 --- a/server-utils/src/stream_codec.rs +++ b/server-utils/src/stream_codec.rs @@ -1,6 +1,6 @@ +use bytes::BytesMut; use std::{io, str}; use tokio_codec::{Decoder, Encoder}; -use bytes::BytesMut; /// Separator for enveloping messages in streaming codecs #[derive(Debug, Clone)] @@ -80,14 +80,11 @@ impl Decoder for StreamCodec { start_idx = idx; } depth += 1; - } - else if (byte == b'}' || byte == b']') && !in_str { + } else if (byte == b'}' || byte == b']') && !in_str { depth -= 1; - } - else if byte == b'"' && !is_escaped { + } else if byte == b'"' && !is_escaped { in_str = !in_str; - } - else if is_whitespace(byte) { + } else if is_whitespace(byte) { whitespaces += 1; } if byte == b'\\' && !is_escaped && in_str { @@ -99,8 +96,10 @@ impl Decoder for StreamCodec { if depth == 0 && idx != start_idx && idx - start_idx + 1 > whitespaces { let bts = buf.split_to(idx + 1); match String::from_utf8(bts.as_ref().to_vec()) { - Ok(val) => { return Ok(Some(val)) }, - Err(_) => { return Ok(None); } // skip non-utf requests (TODO: log error?) + Ok(val) => return Ok(Some(val)), + Err(_) => { + return Ok(None); + } // skip non-utf requests (TODO: log error?) }; } } @@ -127,8 +126,8 @@ impl Encoder for StreamCodec { mod tests { use super::StreamCodec; + use bytes::{BufMut, BytesMut}; use tokio_codec::Decoder; - use bytes::{BytesMut, BufMut}; #[test] fn simple_encode() { @@ -137,7 +136,8 @@ mod tests { let mut codec = StreamCodec::stream_incoming(); - let request = codec.decode(&mut buf) + let request = codec + .decode(&mut buf) .expect("There should be no error in simple test") .expect("There should be at least one request in simple test"); @@ -151,23 +151,27 @@ mod tests { let mut codec = StreamCodec::stream_incoming(); - let request = codec.decode(&mut buf) + let request = codec + .decode(&mut buf) .expect("There should be no error in first escape test") .expect("There should be a request in first escape test"); assert_eq!(request, r#"{ test: "\"\\" }"#); - let request2 = codec.decode(&mut buf) + let request2 = codec + .decode(&mut buf) .expect("There should be no error in 2nd escape test") .expect("There should be a request in 2nd escape test"); assert_eq!(request2, r#"{ test: "\ " }"#); - let request3 = codec.decode(&mut buf) + let request3 = codec + .decode(&mut buf) .expect("There should be no error in 3rd escape test") .expect("There should be a request in 3rd escape test"); assert_eq!(request3, r#"{ test: "\}" }"#); - let request4 = codec.decode(&mut buf) + let request4 = codec + .decode(&mut buf) .expect("There should be no error in 4th escape test") .expect("There should be a request in 4th escape test"); assert_eq!(request4, r#"[ test: "\]" ]"#); @@ -180,26 +184,33 @@ mod tests { let mut codec = StreamCodec::stream_incoming(); - let request = codec.decode(&mut buf) + let request = codec + .decode(&mut buf) .expect("There should be no error in first whitespace test") .expect("There should be a request in first whitespace test"); assert_eq!(request, "{ test: 1 }"); - let request2 = codec.decode(&mut buf) + let request2 = codec + .decode(&mut buf) .expect("There should be no error in first 2nd test") .expect("There should be aa request in 2nd whitespace test"); // TODO: maybe actually trim it out assert_eq!(request2, "\n\n\n\n{ test: 2 }"); - let request3 = codec.decode(&mut buf) + let request3 = codec + .decode(&mut buf) .expect("There should be no error in first 3rd test") .expect("There should be a request in 3rd whitespace test"); assert_eq!(request3, "\n\r{\n test: 3 }"); - let request4 = codec.decode(&mut buf) + let request4 = codec + .decode(&mut buf) .expect("There should be no error in first 4th test"); - assert!(request4.is_none(), "There should be no 4th request because it contains only whitespaces"); + assert!( + request4.is_none(), + "There should be no 4th request because it contains only whitespaces" + ); } #[test] @@ -209,17 +220,20 @@ mod tests { let mut codec = StreamCodec::stream_incoming(); - let request = codec.decode(&mut buf) + let request = codec + .decode(&mut buf) .expect("There should be no error in first fragmented test") .expect("There should be at least one request in first fragmented test"); assert_eq!(request, "{ test: 1 }"); - codec.decode(&mut buf) + codec + .decode(&mut buf) .expect("There should be no error in second fragmented test") .expect("There should be at least one request in second fragmented test"); assert_eq!(String::from_utf8(buf.as_ref().to_vec()).unwrap(), "{ tes"); buf.put_slice(b"t: 3 }"); - let request = codec.decode(&mut buf) + let request = codec + .decode(&mut buf) .expect("There should be no error in third fragmented test") .expect("There should be at least one request in third fragmented test"); assert_eq!(request, "{ test: 3 }"); @@ -247,7 +261,8 @@ mod tests { let mut codec = StreamCodec::stream_incoming(); - let parsed_request = codec.decode(&mut buf) + let parsed_request = codec + .decode(&mut buf) .expect("There should be no error in huge test") .expect("There should be at least one request huge test"); assert_eq!(request, parsed_request); @@ -260,10 +275,12 @@ mod tests { let mut codec = StreamCodec::default(); - let request = codec.decode(&mut buf) + let request = codec + .decode(&mut buf) .expect("There should be no error in simple test") .expect("There should be at least one request in simple test"); - let request2 = codec.decode(&mut buf) + let request2 = codec + .decode(&mut buf) .expect("There should be no error in simple test") .expect("There should be at least one request in simple test"); diff --git a/server-utils/src/suspendable_stream.rs b/server-utils/src/suspendable_stream.rs index 3cdb2f27c..303a18b04 100644 --- a/server-utils/src/suspendable_stream.rs +++ b/server-utils/src/suspendable_stream.rs @@ -1,7 +1,7 @@ -use std::time::{Duration, Instant}; -use tokio::timer::Delay; use std::io; +use std::time::{Duration, Instant}; use tokio::prelude::*; +use tokio::timer::Delay; /// `Incoming` is a stream of incoming sockets /// Polling the stream may return a temporary io::Error (for instance if we can't open the connection because of "too many open files" limit) @@ -34,7 +34,8 @@ impl SuspendableStream { } impl Stream for SuspendableStream - where S: Stream +where + S: Stream, { type Item = I; type Error = (); @@ -61,12 +62,12 @@ impl Stream for SuspendableStream if self.next_delay > self.initial_delay { self.next_delay = self.initial_delay; } - return Ok(item) + return Ok(item); } Err(ref err) => { if connection_error(err) { warn!("Connection Error: {:?}", err); - continue + continue; } self.next_delay = if self.next_delay < self.max_delay { self.next_delay * 2 @@ -82,11 +83,9 @@ impl Stream for SuspendableStream } } - /// assert that the error was a connection error fn connection_error(e: &io::Error) -> bool { - e.kind() == io::ErrorKind::ConnectionRefused || - e.kind() == io::ErrorKind::ConnectionAborted || - e.kind() == io::ErrorKind::ConnectionReset + e.kind() == io::ErrorKind::ConnectionRefused + || e.kind() == io::ErrorKind::ConnectionAborted + || e.kind() == io::ErrorKind::ConnectionReset } - diff --git a/stdio/examples/stdio.rs b/stdio/examples/stdio.rs index cf1ad56ee..5ba4ba31c 100644 --- a/stdio/examples/stdio.rs +++ b/stdio/examples/stdio.rs @@ -1,11 +1,9 @@ -use jsonrpc_stdio_server::ServerBuilder; use jsonrpc_stdio_server::jsonrpc_core::*; +use jsonrpc_stdio_server::ServerBuilder; fn main() { let mut io = IoHandler::default(); - io.add_method("say_hello", |_params| { - Ok(Value::String("hello".to_owned())) - }); + io.add_method("say_hello", |_params| Ok(Value::String("hello".to_owned()))); ServerBuilder::new(io).build(); } diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index 1fd6d9ee8..09753528f 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -19,14 +19,15 @@ use tokio; use tokio_stdin_stdout; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; pub use jsonrpc_core; +use jsonrpc_core::IoHandler; use std::sync::Arc; use tokio::prelude::{Future, Stream}; use tokio_codec::{FramedRead, FramedWrite, LinesCodec}; -use jsonrpc_core::{IoHandler}; /// Stdio server builder pub struct ServerBuilder { @@ -35,8 +36,13 @@ pub struct ServerBuilder { impl ServerBuilder { /// Returns a new server instance - pub fn new(handler: T) -> Self where T: Into { - ServerBuilder { handler: Arc::new(handler.into()) } + pub fn new(handler: T) -> Self + where + T: Into, + { + ServerBuilder { + handler: Arc::new(handler.into()), + } } /// Will block until EOF is read or until an error occurs. diff --git a/tcp/examples/tcp.rs b/tcp/examples/tcp.rs index 1433d1a43..58570cb1f 100644 --- a/tcp/examples/tcp.rs +++ b/tcp/examples/tcp.rs @@ -1,6 +1,6 @@ use env_logger; -use jsonrpc_tcp_server::ServerBuilder; use jsonrpc_tcp_server::jsonrpc_core::*; +use jsonrpc_tcp_server::ServerBuilder; fn main() { env_logger::init(); @@ -16,4 +16,3 @@ fn main() { server.wait() } - diff --git a/tcp/src/dispatch.rs b/tcp/src/dispatch.rs index f7c0e4050..a22e23e6a 100644 --- a/tcp/src/dispatch.rs +++ b/tcp/src/dispatch.rs @@ -1,10 +1,10 @@ use std; use std::collections::HashMap; use std::net::SocketAddr; -use std::sync::{Arc}; +use std::sync::Arc; -use crate::jsonrpc::futures::{Stream, Poll, Async, Sink, Future}; use crate::jsonrpc::futures::sync::mpsc; +use crate::jsonrpc::futures::{Async, Future, Poll, Sink, Stream}; use parking_lot::Mutex; @@ -17,11 +17,7 @@ pub struct PeerMessageQueue { } impl PeerMessageQueue { - pub fn new( - response_stream: S, - receiver: mpsc::Receiver, - addr: SocketAddr, - ) -> Self { + pub fn new(response_stream: S, receiver: mpsc::Receiver, addr: SocketAddr) -> Self { PeerMessageQueue { up: response_stream, receiver, @@ -36,7 +32,7 @@ pub enum PushMessageError { /// Invalid peer NoSuchPeer, /// Send error - Send(mpsc::SendError) + Send(mpsc::SendError), } impl From> for PushMessageError { @@ -54,9 +50,7 @@ pub struct Dispatcher { impl Dispatcher { /// Creates a new dispatcher pub fn new(channels: Arc) -> Self { - Dispatcher { - channels, - } + Dispatcher { channels } } /// Pushes message to given peer @@ -68,10 +62,8 @@ impl Dispatcher { // todo: maybe async here later? channel.send(msg).wait().map_err(PushMessageError::from)?; Ok(()) - }, - None => { - Err(PushMessageError::NoSuchPeer) } + None => Err(PushMessageError::NoSuchPeer), } } @@ -86,8 +78,7 @@ impl Dispatcher { } } -impl> Stream for PeerMessageQueue { - +impl> Stream for PeerMessageQueue { type Item = String; type Error = std::io::Error; @@ -96,11 +87,11 @@ impl> Stream for PeerMessageQueue { return Ok(Async::Ready(Some(val))); - }, + } Ok(Async::Ready(None)) => { // this will ensure that this polling will end when incoming i/o stream ends return Ok(Async::Ready(None)); - }, + } _ => {} } diff --git a/tcp/src/lib.rs b/tcp/src/lib.rs index a90d6ce09..bf0af845c 100644 --- a/tcp/src/lib.rs +++ b/tcp/src/lib.rs @@ -23,21 +23,26 @@ use jsonrpc_server_utils as server_utils; pub use jsonrpc_core; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; -#[cfg(test)] #[macro_use] extern crate lazy_static; +#[cfg(test)] +#[macro_use] +extern crate lazy_static; mod dispatch; mod meta; mod server; mod service; -#[cfg(test)] mod logger; -#[cfg(test)] mod tests; +#[cfg(test)] +mod logger; +#[cfg(test)] +mod tests; use jsonrpc_core as jsonrpc; +pub use self::server_utils::{codecs::Separator, tokio}; pub use crate::dispatch::{Dispatcher, PushMessageError}; pub use crate::meta::{MetaExtractor, RequestContext}; -pub use crate::server::{ServerBuilder, Server}; -pub use self::server_utils::{tokio, codecs::Separator}; +pub use crate::server::{Server, ServerBuilder}; diff --git a/tcp/src/logger.rs b/tcp/src/logger.rs index 74f748364..8502fe870 100644 --- a/tcp/src/logger.rs +++ b/tcp/src/logger.rs @@ -1,6 +1,6 @@ -use std::env; -use log::LevelFilter; use env_logger::Builder; +use log::LevelFilter; +use std::env; lazy_static! { static ref LOG_DUMMY: bool = { diff --git a/tcp/src/meta.rs b/tcp/src/meta.rs index 6aac384c7..c057e733c 100644 --- a/tcp/src/meta.rs +++ b/tcp/src/meta.rs @@ -12,12 +12,13 @@ pub struct RequestContext { } /// Metadata extractor (per session) -pub trait MetaExtractor : Send + Sync { +pub trait MetaExtractor: Send + Sync { /// Extracts metadata from request context fn extract(&self, context: &RequestContext) -> M; } -impl MetaExtractor for F where +impl MetaExtractor for F +where M: Metadata, F: Fn(&RequestContext) -> M + Send + Sync, { @@ -29,5 +30,7 @@ impl MetaExtractor for F where /// Noop-extractor pub struct NoopExtractor; impl MetaExtractor for NoopExtractor { - fn extract(&self, _context: &RequestContext) -> M { M::default() } + fn extract(&self, _context: &RequestContext) -> M { + M::default() + } } diff --git a/tcp/src/server.rs b/tcp/src/server.rs index c094ff0eb..fff4f8327 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -4,17 +4,13 @@ use std::sync::Arc; use tokio_service::Service as TokioService; -use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; -use crate::jsonrpc::futures::{future, Future, Stream, Sink}; use crate::jsonrpc::futures::sync::{mpsc, oneshot}; -use crate::server_utils::{ - tokio_codec::Framed, - tokio, reactor, codecs, - SuspendableStream -}; - -use crate::dispatch::{Dispatcher, SenderChannels, PeerMessageQueue}; -use crate::meta::{MetaExtractor, RequestContext, NoopExtractor}; +use crate::jsonrpc::futures::{future, Future, Sink, Stream}; +use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; +use crate::server_utils::{codecs, reactor, tokio, tokio_codec::Framed, SuspendableStream}; + +use crate::dispatch::{Dispatcher, PeerMessageQueue, SenderChannels}; +use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; use crate::service::Service; /// TCP server builder @@ -29,7 +25,8 @@ pub struct ServerBuilder = middleware::Noop> impl + 'static> ServerBuilder { /// Creates new `ServerBuilder` wih given `IoHandler` - pub fn new(handler: T) -> Self where + pub fn new(handler: T) -> Self + where T: Into>, { Self::with_meta_extractor(handler, NoopExtractor) @@ -38,7 +35,8 @@ impl + 'static> ServerBuilder { impl + 'static> ServerBuilder { /// Creates new `ServerBuilder` wih given `IoHandler` - pub fn with_meta_extractor(handler: T, extractor: E) -> Self where + pub fn with_meta_extractor(handler: T, extractor: E) -> Self + where T: Into>, E: MetaExtractor + 'static, { @@ -102,39 +100,33 @@ impl + 'static> ServerBuilder { let meta = meta_extractor.extract(&context); let service = Service::new(peer_addr, rpc_handler.clone(), meta); let (writer, reader) = Framed::new( - socket, - codecs::StreamCodec::new( - incoming_separator.clone(), - outgoing_separator.clone(), - ), - ).split(); - - let responses = reader.and_then( - move |req| service.call(req).then(|response| match response { + socket, + codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()), + ) + .split(); + + let responses = reader.and_then(move |req| { + service.call(req).then(|response| match response { Err(e) => { warn!(target: "tcp", "Error while processing request: {:?}", e); future::ok(String::new()) - }, + } Ok(None) => { trace!(target: "tcp", "JSON RPC request produced no response"); future::ok(String::new()) - }, + } Ok(Some(response_data)) => { trace!(target: "tcp", "Sent response: {}", &response_data); future::ok(response_data) } }) - ); + }); let peer_message_queue = { let mut channels = channels.lock(); channels.insert(peer_addr, sender.clone()); - PeerMessageQueue::new( - responses, - receiver, - peer_addr, - ) + PeerMessageQueue::new(responses, receiver, peer_addr) }; let shared_channels = channels.clone(); @@ -157,19 +149,16 @@ impl + 'static> ServerBuilder { match start() { Ok(server) => { tx.send(Ok(())).expect("Rx is blocking parent thread."); - future::Either::A(server.select(stop) - .map(|_| ()) - .map_err(|(e, _)| { - error!("Error while executing the server: {:?}", e); - })) - }, + future::Either::A(server.select(stop).map(|_| ()).map_err(|(e, _)| { + error!("Error while executing the server: {:?}", e); + })) + } Err(e) => { tx.send(Err(e)).expect("Rx is blocking parent thread."); - future::Either::B(stop - .map_err(|e| { - error!("Error while executing the server: {:?}", e); - })) - }, + future::Either::B(stop.map_err(|e| { + error!("Error while executing the server: {:?}", e); + })) + } } })); @@ -209,6 +198,8 @@ impl Server { impl Drop for Server { fn drop(&mut self) { let _ = self.stop.take().map(|sg| sg.send(())); - if let Some(executor) = self.executor.take() { executor.close() } + if let Some(executor) = self.executor.take() { + executor.close() + } } } diff --git a/tcp/src/service.rs b/tcp/src/service.rs index d20e6b5c1..9254981fc 100644 --- a/tcp/src/service.rs +++ b/tcp/src/service.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; use std::net::SocketAddr; +use std::sync::Arc; use tokio_service; -use crate::jsonrpc::{middleware, FutureResult, Metadata, MetaIoHandler, Middleware}; +use crate::jsonrpc::{middleware, FutureResult, MetaIoHandler, Metadata, Middleware}; pub struct Service = middleware::Noop> { handler: Arc>, @@ -13,7 +13,11 @@ pub struct Service = middleware::Noop> { impl> Service { pub fn new(peer_addr: SocketAddr, handler: Arc>, meta: M) -> Self { - Service { peer_addr, handler, meta } + Service { + peer_addr, + handler, + meta, + } } } diff --git a/tcp/src/tests.rs b/tcp/src/tests.rs index a063ac3eb..786633f37 100644 --- a/tcp/src/tests.rs +++ b/tcp/src/tests.rs @@ -1,29 +1,22 @@ -use std::net::{SocketAddr, Shutdown}; +use std::net::{Shutdown, SocketAddr}; use std::str::FromStr; use std::sync::Arc; -use std::time::{Instant, Duration}; +use std::time::{Duration, Instant}; -use crate::jsonrpc::{MetaIoHandler, Value, Metadata}; -use crate::jsonrpc::futures::{self, Future, future}; +use crate::jsonrpc::futures::{self, future, Future}; +use crate::jsonrpc::{MetaIoHandler, Metadata, Value}; -use crate::server_utils::tokio::{ - timer::Delay, - net::TcpStream, - io::{self}, - self, -}; +use crate::server_utils::tokio::{self, io, net::TcpStream, timer::Delay}; use parking_lot::Mutex; -use crate::ServerBuilder; use crate::MetaExtractor; use crate::RequestContext; +use crate::ServerBuilder; fn casual_server() -> ServerBuilder { let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); ServerBuilder::new(io) } @@ -32,12 +25,11 @@ fn doc_test() { crate::logger::init_log(); let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| { - Ok(Value::String("hello".to_string())) - }); + io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); let server = ServerBuilder::new(io); - server.start(&SocketAddr::from_str("0.0.0.0:17770").unwrap()) + server + .start(&SocketAddr::from_str("0.0.0.0:17770").unwrap()) .expect("Server must run with no issues") .close() } @@ -50,9 +42,7 @@ fn doc_test_connect() { let _server = server.start(&addr).expect("Server must run with no issues"); let stream = TcpStream::connect(&addr) - .and_then(move |_stream| { - Ok(()) - }) + .and_then(move |_stream| Ok(())) .map_err(|err| panic!("Server connection error: {:?}", err)); tokio::run(stream); @@ -84,16 +74,12 @@ fn dummy_request(addr: &SocketAddr, data: Vec) -> Vec { let (ret_tx, ret_rx) = futures::sync::oneshot::channel(); let stream = TcpStream::connect(addr) - .and_then(move |stream| { - io::write_all(stream, data) - }) + .and_then(move |stream| io::write_all(stream, data)) .and_then(|(stream, _data)| { stream.shutdown(Shutdown::Write).unwrap(); io::read_to_end(stream, vec![]) }) - .and_then(move |(_stream, read_buf)| { - ret_tx.send(read_buf).map_err(|err| panic!("Unable to send {:?}", err)) - }) + .and_then(move |(_stream, read_buf)| ret_tx.send(read_buf).map_err(|err| panic!("Unable to send {:?}", err))) .map_err(|err| panic!("Error connecting or closing connection: {:?}", err));; tokio::run(stream); @@ -115,13 +101,12 @@ fn doc_test_handle() { let result = dummy_request_str( &addr, b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"[..].to_owned(), - ); + ); assert_eq!( - result, - "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", + result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", "Response does not exactly much the expected response", - ); + ); } #[test] @@ -136,22 +121,20 @@ fn req_parallel() { let mut handles = Vec::new(); for _ in 0..6 { let addr = addr.clone(); - handles.push( - thread::spawn(move || { - for _ in 0..100 { - let result = dummy_request_str( - &addr, - b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"[..].to_owned(), - ); - - assert_eq!( - result, - "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", - "Response does not exactly much the expected response", - ); - } - }) - ); + handles.push(thread::spawn(move || { + for _ in 0..100 { + let result = dummy_request_str( + &addr, + b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"[..] + .to_owned(), + ); + + assert_eq!( + result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", + "Response does not exactly much the expected response", + ); + } + })); } for handle in handles.drain(..) { @@ -166,7 +149,9 @@ pub struct SocketMetadata { impl Default for SocketMetadata { fn default() -> Self { - SocketMetadata { addr: "0.0.0.0:0".parse().unwrap() } + SocketMetadata { + addr: "0.0.0.0:0".parse().unwrap(), + } } } @@ -176,7 +161,7 @@ impl SocketMetadata { } } -impl Metadata for SocketMetadata { } +impl Metadata for SocketMetadata {} impl From for SocketMetadata { fn from(addr: SocketAddr) -> SocketMetadata { @@ -209,15 +194,13 @@ fn peer_meta() { let result = dummy_request_str( &addr, - b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"[..].to_owned() - ); + b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"[..].to_owned(), + ); println!("{}", result); // contains random port, so just smoky comparing response length - assert!( - result.len() == 58 || result.len() == 59 - ); + assert!(result.len() == 58 || result.len() == 59); } #[derive(Default)] @@ -244,14 +227,12 @@ fn message() { }); let extractor = PeerListMetaExtractor::default(); let peer_list = extractor.peers.clone(); - let server = ServerBuilder::new(io) - .session_meta_extractor(extractor); + let server = ServerBuilder::new(io).session_meta_extractor(extractor); let dispatcher = server.dispatcher(); let _server = server.start(&addr).expect("Server must run with no issues"); - let delay = Delay::new(Instant::now() + Duration::from_millis(500)) - .map_err(|err| panic!("{:?}", err)); + let delay = Delay::new(Instant::now() + Duration::from_millis(500)).map_err(|err| panic!("{:?}", err)); let message = "ping"; let executed_dispatch = Arc::new(Mutex::new(false)); @@ -261,15 +242,12 @@ fn message() { // CLIENT RUN let stream = TcpStream::connect(&addr) - .and_then(|stream| { - future::ok(stream).join(delay) - }) + .and_then(|stream| future::ok(stream).join(delay)) .and_then(move |stream| { let peer_addr = peer_list.lock()[0].clone(); - dispatcher.push_message( - &peer_addr, - message.to_owned(), - ).expect("Should be sent with no errors"); + dispatcher + .push_message(&peer_addr, message.to_owned()) + .expect("Should be sent with no errors"); trace!(target: "tcp", "Dispatched message for {}", peer_addr); future::ok(stream) }) @@ -285,7 +263,7 @@ fn message() { format!("{}\n", message), String::from_utf8(ping_signal).expect("String should be utf-8"), "Sent request does not match received by the peer", - ); + ); // ensure that the above assert was actually triggered *executed_dispatch_move.lock() = true; @@ -307,7 +285,7 @@ fn message() { "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", String::from_utf8(response_signal).expect("String should be utf-8"), "Response does not match the expected handling", - ); + ); *executed_request_move.lock() = true; future::ok(()) diff --git a/test/src/lib.rs b/test/src/lib.rs index 453306a5b..2c067e387 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -77,13 +77,17 @@ pub enum Encoding { impl From for Rpc { fn from(io: rpc::IoHandler) -> Self { - Rpc { io, ..Default::default() } + Rpc { + io, + ..Default::default() + } } } impl Rpc { /// Create a new RPC instance from a single delegate. - pub fn new(delegate: D) -> Self where + pub fn new(delegate: D) -> Self + where D: Into>>, { let mut io = rpc::IoHandler::new(); @@ -92,19 +96,16 @@ impl Rpc { } /// Perform a single, synchronous method call and return pretty-printed value - pub fn request(&self, method: &str, params: &T) -> String where + pub fn request(&self, method: &str, params: &T) -> String + where T: serde::Serialize, { self.make_request(method, params, Encoding::Pretty) } /// Perform a single, synchronous method call. - pub fn make_request( - &self, - method: &str, - params: &T, - encoding: Encoding, - ) -> String where + pub fn make_request(&self, method: &str, params: &T, encoding: Encoding) -> String + where T: serde::Serialize, { use self::rpc::types::response; @@ -115,25 +116,23 @@ impl Rpc { serde_json::to_string_pretty(params).expect("Serialization should be infallible."), ); - let response = self.io + let response = self + .io .handle_request_sync(&request) .expect("We are sending a method call not notification."); // extract interesting part from the response let extracted = match serde_json::from_str(&response).expect("We will always get a single output.") { - response::Output::Success(response::Success { result, .. }) => { - match encoding { - Encoding::Compact => serde_json::to_string(&result), - Encoding::Pretty => serde_json::to_string_pretty(&result), - } + response::Output::Success(response::Success { result, .. }) => match encoding { + Encoding::Compact => serde_json::to_string(&result), + Encoding::Pretty => serde_json::to_string_pretty(&result), }, - response::Output::Failure(response::Failure { error, .. }) => { - match encoding { - Encoding::Compact => serde_json::to_string(&error), - Encoding::Pretty => serde_json::to_string_pretty(&error), - } + response::Output::Failure(response::Failure { error, .. }) => match encoding { + Encoding::Compact => serde_json::to_string(&error), + Encoding::Pretty => serde_json::to_string_pretty(&error), }, - }.expect("Serialization is infallible; qed"); + } + .expect("Serialization is infallible; qed"); println!("\n{}\n --> {}\n", request, extracted); @@ -150,17 +149,12 @@ mod tests { // given let rpc = { let mut io = rpc::IoHandler::new(); - io.add_method("test_method", |_| { - Ok(rpc::Value::Array(vec![5.into(), 10.into()])) - }); + io.add_method("test_method", |_| Ok(rpc::Value::Array(vec![5.into(), 10.into()]))); Rpc::from(io) }; // when - assert_eq!( - rpc.request("test_method", &[5u64]), - "[\n 5,\n 10\n]" - ); + assert_eq!(rpc.request("test_method", &[5u64]), "[\n 5,\n 10\n]"); } #[test] @@ -168,16 +162,11 @@ mod tests { // given let rpc = { let mut io = rpc::IoHandler::new(); - io.add_method("test_method", |_| { - Ok(rpc::Value::Array(vec![5.into(), 10.into()])) - }); + io.add_method("test_method", |_| Ok(rpc::Value::Array(vec![5.into(), 10.into()]))); Rpc::from(io) }; // when - assert_eq!( - rpc.make_request("test_method", &[5u64], Encoding::Compact), - "[5,10]" - ); + assert_eq!(rpc.make_request("test_method", &[5u64], Encoding::Compact), "[5,10]"); } } diff --git a/ws/examples/ws.rs b/ws/examples/ws.rs index 027fa5421..ea6a9087f 100644 --- a/ws/examples/ws.rs +++ b/ws/examples/ws.rs @@ -1,5 +1,5 @@ -use jsonrpc_ws_server::ServerBuilder; use jsonrpc_ws_server::jsonrpc_core::*; +use jsonrpc_ws_server::ServerBuilder; fn main() { let mut io = IoHandler::default(); diff --git a/ws/src/error.rs b/ws/src/error.rs index 3b01a7b24..1cc589248 100644 --- a/ws/src/error.rs +++ b/ws/src/error.rs @@ -1,4 +1,4 @@ -use std::{io, error, fmt, result}; +use std::{error, fmt, io, result}; use crate::ws; diff --git a/ws/src/lib.rs b/ws/src/lib.rs index 7eb64b09d..fe46ff3ff 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -4,8 +4,8 @@ use jsonrpc_server_utils as server_utils; -pub use ws; pub use jsonrpc_core; +pub use ws; #[macro_use] extern crate log; @@ -21,11 +21,11 @@ mod tests; use jsonrpc_core as core; pub use self::error::{Error, Result}; -pub use self::metadata::{RequestContext, MetaExtractor, NoopExtractor}; -pub use self::session::{RequestMiddleware, MiddlewareAction}; +pub use self::metadata::{MetaExtractor, NoopExtractor, RequestContext}; pub use self::server::{CloseHandle, Server}; pub use self::server_builder::ServerBuilder; pub use self::server_utils::cors::Origin; -pub use self::server_utils::hosts::{Host, DomainsValidation}; -pub use self::server_utils::tokio; +pub use self::server_utils::hosts::{DomainsValidation, Host}; pub use self::server_utils::session::{SessionId, SessionStats}; +pub use self::server_utils::tokio; +pub use self::session::{MiddlewareAction, RequestMiddleware}; diff --git a/ws/src/metadata.rs b/ws/src/metadata.rs index f9c27e503..07befcfe9 100644 --- a/ws/src/metadata.rs +++ b/ws/src/metadata.rs @@ -1,13 +1,13 @@ use std::fmt; use std::sync::{atomic, Arc}; -use crate::core::{self, futures}; use crate::core::futures::sync::mpsc; +use crate::core::{self, futures}; use crate::server_utils::{session, tokio::runtime::TaskExecutor}; use crate::ws; use crate::error; -use crate::{Origin}; +use crate::Origin; /// Output of WebSocket connection. Use this to send messages to the other endpoint. #[derive(Clone)] @@ -19,10 +19,7 @@ pub struct Sender { impl Sender { /// Creates a new `Sender`. pub fn new(out: ws::Sender, active: Arc) -> Self { - Sender { - out, - active, - } + Sender { out, active } } fn check_active(&self) -> error::Result<()> { @@ -36,7 +33,8 @@ impl Sender { /// Sends a message over the connection. /// Will return error if the connection is not active any more. pub fn send(&self, msg: M) -> error::Result<()> - where M: Into + where + M: Into, { self.check_active()?; self.out.send(msg)?; @@ -45,8 +43,9 @@ impl Sender { /// Sends a message over the endpoints of all connections. /// Will return error if the connection is not active any more. - pub fn broadcast(&self, msg: M) -> error::Result<()> where - M: Into + pub fn broadcast(&self, msg: M) -> error::Result<()> + where + M: Into, { self.check_active()?; self.out.broadcast(msg)?; @@ -103,7 +102,8 @@ pub trait MetaExtractor: Send + Sync + 'static { fn extract(&self, _context: &RequestContext) -> M; } -impl MetaExtractor for F where +impl MetaExtractor for F +where M: core::Metadata, F: Fn(&RequestContext) -> M + Send + Sync + 'static, { @@ -116,7 +116,9 @@ impl MetaExtractor for F where #[derive(Debug, Clone)] pub struct NoopExtractor; impl MetaExtractor for NoopExtractor { - fn extract(&self, _context: &RequestContext) -> M { M::default() } + fn extract(&self, _context: &RequestContext) -> M { + M::default() + } } struct SenderFuture(Sender, mpsc::Receiver); @@ -132,16 +134,16 @@ impl futures::Future for SenderFuture { match item { futures::Async::NotReady => { return Ok(futures::Async::NotReady); - }, + } futures::Async::Ready(None) => { return Ok(futures::Async::Ready(())); - }, + } futures::Async::Ready(Some(val)) => { if let Err(e) = self.0.send(val) { warn!("Error sending a subscription update: {:?}", e); return Ok(futures::Async::Ready(())); } - }, + } } } } diff --git a/ws/src/server.rs b/ws/src/server.rs index 1ee89f58f..11585a277 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -1,12 +1,12 @@ -use std::{cmp, fmt}; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; use std::thread; +use std::{cmp, fmt}; use crate::core; use crate::server_utils::cors::Origin; use crate::server_utils::hosts::{self, Host}; -use crate::server_utils::reactor::{UninitializedExecutor, Executor}; +use crate::server_utils::reactor::{Executor, UninitializedExecutor}; use crate::server_utils::session::SessionStats; use crate::ws; @@ -23,13 +23,13 @@ pub struct Server { } impl fmt::Debug for Server { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Server") .field("addr", &self.addr) .field("handle", &self.handle) .field("executor", &self.executor) .finish() - } + } } impl Server { @@ -78,7 +78,13 @@ impl Server { // Create WebSocket let ws = ws::Builder::new().with_settings(config).build(session::Factory::new( - handler, meta_extractor, allowed_origins, allowed_hosts, request_middleware, stats, executor + handler, + meta_extractor, + allowed_origins, + allowed_hosts, + request_middleware, + stats, + executor, ))?; let broadcaster = ws.broadcaster(); @@ -88,14 +94,12 @@ impl Server { debug!("Bound to local address: {}", local_addr); // Spawn a thread with event loop - let handle = thread::spawn(move || { - match ws.run().map_err(Error::from) { - Err(error) => { - error!("Error while running websockets server. Details: {:?}", error); - Err(error) - }, - Ok(_server) => Ok(()), + let handle = thread::spawn(move || match ws.run().map_err(Error::from) { + Err(error) => { + error!("Error while running websockets server. Details: {:?}", error); + Err(error) } + Ok(_server) => Ok(()), }); // Return a handle @@ -111,7 +115,11 @@ impl Server { impl Server { /// Consumes the server and waits for completion pub fn wait(mut self) -> Result<()> { - self.handle.take().expect("Handle is always Some at start.").join().expect("Non-panic exit") + self.handle + .take() + .expect("Handle is always Some at start.") + .join() + .expect("Non-panic exit") } /// Closes the server and waits for it to finish @@ -136,7 +144,6 @@ impl Drop for Server { } } - /// A handle that allows closing of a server even if it owned by a thread blocked in `wait`. #[derive(Clone)] pub struct CloseHandle { @@ -148,6 +155,8 @@ impl CloseHandle { /// Closes the `Server`. pub fn close(self) { let _ = self.broadcaster.shutdown(); - if let Some(executor) = self.executor.lock().unwrap().take() { executor.close() } + if let Some(executor) = self.executor.lock().unwrap().take() { + executor.close() + } } } diff --git a/ws/src/server_builder.rs b/ws/src/server_builder.rs index 1b6a0aed1..bae91a1d7 100644 --- a/ws/src/server_builder.rs +++ b/ws/src/server_builder.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::core; use crate::server_utils; use crate::server_utils::cors::Origin; -use crate::server_utils::hosts::{Host, DomainsValidation}; +use crate::server_utils::hosts::{DomainsValidation, Host}; use crate::server_utils::reactor::UninitializedExecutor; use crate::server_utils::session::SessionStats; @@ -28,7 +28,8 @@ pub struct ServerBuilder> { impl> ServerBuilder { /// Creates new `ServerBuilder` - pub fn new(handler: T) -> Self where + pub fn new(handler: T) -> Self + where T: Into>, { Self::with_meta_extractor(handler, NoopExtractor) @@ -37,7 +38,8 @@ impl> ServerBuilder { impl> ServerBuilder { /// Creates new `ServerBuilder` - pub fn with_meta_extractor(handler: T, extractor: E) -> Self where + pub fn with_meta_extractor(handler: T, extractor: E) -> Self + where T: Into>, E: MetaExtractor, { @@ -121,5 +123,4 @@ impl> ServerBuilder { self.max_payload_bytes, ) } - } diff --git a/ws/src/session.rs b/ws/src/session.rs index 7e3198a3a..ee2ed9082 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -2,17 +2,17 @@ use std; use std::sync::{atomic, Arc}; use crate::core; -use crate::core::futures::{Async, Future, Poll}; use crate::core::futures::sync::oneshot; +use crate::core::futures::{Async, Future, Poll}; use parking_lot::Mutex; use slab::Slab; -use crate::server_utils::Pattern; use crate::server_utils::cors::Origin; use crate::server_utils::hosts::Host; use crate::server_utils::session::{SessionId, SessionStats}; use crate::server_utils::tokio::runtime::TaskExecutor; +use crate::server_utils::Pattern; use crate::ws; use crate::error; @@ -26,7 +26,8 @@ pub trait RequestMiddleware: Send + Sync + 'static { fn process(&self, req: &ws::Request) -> MiddlewareAction; } -impl RequestMiddleware for F where +impl RequestMiddleware for F +where F: Fn(&ws::Request) -> Option + Send + Sync + 'static, { fn process(&self, req: &ws::Request) -> MiddlewareAction { @@ -73,7 +74,11 @@ impl MiddlewareAction { impl From> for MiddlewareAction { fn from(opt: Option) -> Self { match opt { - Some(res) => MiddlewareAction::Respond { response: res, validate_origin: true, validate_hosts: true }, + Some(res) => MiddlewareAction::Respond { + response: res, + validate_origin: true, + validate_hosts: true, + }, None => MiddlewareAction::Proceed, } } @@ -109,7 +114,11 @@ impl LivenessPoll { (index, rx) }; - LivenessPoll { task_slab, slab_handle: index, rx } + LivenessPoll { + task_slab, + slab_handle: index, + rx, + } } } @@ -152,7 +161,9 @@ pub struct Session> { impl> Drop for Session { fn drop(&mut self) { self.active.store(false, atomic::Ordering::SeqCst); - if let Some(stats) = self.stats.as_ref() { stats.close_session(self.context.session_id) } + if let Some(stats) = self.stats.as_ref() { + stats.close_session(self.context.session_id) + } // signal to all still-live tasks that the session has been dropped. for (_index, task) in self.task_slab.lock().iter_mut() { @@ -218,8 +229,12 @@ impl> ws::Handler for Session { } } - self.context.origin = origin.and_then(|origin| ::std::str::from_utf8(origin).ok()).map(Into::into); - self.context.protocols = req.protocols().ok() + self.context.origin = origin + .and_then(|origin| ::std::str::from_utf8(origin).ok()) + .map(Into::into); + self.context.protocols = req + .protocols() + .ok() .map(|protos| protos.into_iter().map(Into::into).collect()) .unwrap_or_else(Vec::new); self.metadata = Some(self.meta_extractor.extract(&self.context)); @@ -238,7 +253,10 @@ impl> ws::Handler for Session { fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { let req = msg.as_text()?; let out = self.context.out.clone(); - let metadata = self.metadata.clone().expect("Metadata is always set in on_request; qed"); + let metadata = self + .metadata + .clone() + .expect("Metadata is always set in on_request; qed"); // TODO: creation requires allocating a `oneshot` channel and acquiring a // mutex. we could alternatively do this lazily upon first poll if @@ -246,7 +264,9 @@ impl> ws::Handler for Session { let poll_liveness = LivenessPoll::create(self.task_slab.clone()); let active_lock = self.active.clone(); - let future = self.handler.handle_request(req, metadata) + let future = self + .handler + .handle_request(req, metadata) .map(move |response| { if !active_lock.load(atomic::Ordering::SeqCst) { return; @@ -256,11 +276,11 @@ impl> ws::Handler for Session { match res { Err(error::Error::ConnectionClosed) => { active_lock.store(false, atomic::Ordering::SeqCst); - }, + } Err(e) => { warn!("Error while sending response: {:?}", e); - }, - _ => {}, + } + _ => {} } } }) @@ -313,7 +333,9 @@ impl> ws::Factory for Factory { fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { self.session_id += 1; - if let Some(executor) = self.stats.as_ref() { executor.open_session(self.session_id) } + if let Some(executor) = self.stats.as_ref() { + executor.open_session(self.session_id) + } let active = Arc::new(atomic::AtomicBool::new(true)); Session { @@ -338,7 +360,8 @@ impl> ws::Factory for Factory { } } -fn header_is_allowed(allowed: &Option>, header: Option<&[u8]>) -> bool where +fn header_is_allowed(allowed: &Option>, header: Option<&[u8]>) -> bool +where T: Pattern, { let header = header.map(std::str::from_utf8); @@ -352,17 +375,16 @@ fn header_is_allowed(allowed: &Option>, header: Option<&[u8]>) -> bool (Some(Ok(val)), Some(values)) => { for v in values { if v.matches(val) { - return true + return true; } } false - }, + } // Disallow in other cases _ => false, } } - fn forbidden(title: &str, message: &str) -> ws::Response { let mut forbidden = ws::Response::new(403, "Forbidden", format!("{}\n{}\n", title, message).into_bytes()); { diff --git a/ws/src/tests.rs b/ws/src/tests.rs index 6195f6552..1f4014637 100644 --- a/ws/src/tests.rs +++ b/ws/src/tests.rs @@ -1,8 +1,8 @@ use std::io::{Read, Write}; -use std::net::{TcpStream, Ipv4Addr}; +use std::net::{Ipv4Addr, TcpStream}; use std::str::Lines; -use std::sync::{mpsc, Arc}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{mpsc, Arc}; use std::thread; use std::time::Duration; @@ -43,7 +43,7 @@ impl Response { Some(v) => { block.push_str(v); block.push_str("\n"); - }, + } } } block @@ -61,8 +61,8 @@ fn request(server: Server, request: &str) -> Response { } fn serve(port: u16) -> (Server, Arc) { - use std::time::Duration; use crate::core::futures::sync::oneshot; + use std::time::Duration; let pending = Arc::new(AtomicUsize::new(0)); @@ -113,15 +113,16 @@ fn should_disallow_not_whitelisted_origins() { let (server, _) = serve(30001); // when - let response = request(server, + let response = request( + server, "\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:30001\r\n\ - Origin: http://test.io\r\n\ - Connection: close\r\n\ - \r\n\ - I shouldn't be read.\r\n\ - " + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:30001\r\n\ + Origin: http://test.io\r\n\ + Connection: close\r\n\ + \r\n\ + I shouldn't be read.\r\n\ + ", ); // then @@ -134,14 +135,15 @@ fn should_disallow_not_whitelisted_hosts() { let (server, _) = serve(30002); // when - let response = request(server, + let response = request( + server, "\ - GET / HTTP/1.1\r\n\ - Host: myhost:30002\r\n\ - Connection: close\r\n\ - \r\n\ - I shouldn't be read.\r\n\ - " + GET / HTTP/1.1\r\n\ + Host: myhost:30002\r\n\ + Connection: close\r\n\ + \r\n\ + I shouldn't be read.\r\n\ + ", ); // then @@ -154,15 +156,16 @@ fn should_allow_whitelisted_origins() { let (server, _) = serve(30003); // when - let response = request(server, + let response = request( + server, "\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:30003\r\n\ - Origin: https://parity.io\r\n\ - Connection: close\r\n\ - \r\n\ - {}\r\n\ - " + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:30003\r\n\ + Origin: https://parity.io\r\n\ + Connection: close\r\n\ + \r\n\ + {}\r\n\ + ", ); // then @@ -175,15 +178,16 @@ fn should_intercept_in_middleware() { let (server, _) = serve(30004); // when - let response = request(server, + let response = request( + server, "\ - GET /intercepted HTTP/1.1\r\n\ - Host: 127.0.0.1:30004\r\n\ - Origin: https://parity.io\r\n\ - Connection: close\r\n\ - \r\n\ - {}\r\n\ - " + GET /intercepted HTTP/1.1\r\n\ + Host: 127.0.0.1:30004\r\n\ + Origin: https://parity.io\r\n\ + Connection: close\r\n\ + \r\n\ + {}\r\n\ + ", ); // then @@ -200,15 +204,18 @@ fn drop_session_should_cancel() { // when connect("ws://127.0.0.1:30005", |out| { - out.send(r#"{"jsonrpc":"2.0", "method":"record_pending", "params": [], "id": 1}"#).unwrap(); + out.send(r#"{"jsonrpc":"2.0", "method":"record_pending", "params": [], "id": 1}"#) + .unwrap(); let incomplete = incomplete.clone(); - move |_| { + move |_| { assert_eq!(incomplete.load(Ordering::SeqCst), 0); - out.send(r#"{"jsonrpc":"2.0", "method":"record_pending", "params": [], "id": 2}"#).unwrap(); + out.send(r#"{"jsonrpc":"2.0", "method":"record_pending", "params": [], "id": 2}"#) + .unwrap(); out.close(CloseCode::Normal) } - }).unwrap(); + }) + .unwrap(); // then let mut i = 0; @@ -217,7 +224,6 @@ fn drop_session_should_cancel() { i += 1; } assert_eq!(incomplete.load(Ordering::SeqCst), 1); - } #[test] @@ -240,6 +246,8 @@ fn close_handle_makes_wait_return() { thread::sleep(Duration::from_secs(1)); close_handle.close(); - let result = rx.recv_timeout(Duration::from_secs(10)).expect("Expected server to close"); + let result = rx + .recv_timeout(Duration::from_secs(10)) + .expect("Expected server to close"); assert!(result.is_ok()); } From eaa5f245b1ef4cd4dac4d98cfa6e1aad5d57810a Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Sat, 30 Mar 2019 14:01:00 -0700 Subject: [PATCH 040/149] ws: expose broadcaster (#408) --- ws/src/lib.rs | 2 +- ws/src/server.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ws/src/lib.rs b/ws/src/lib.rs index fe46ff3ff..a5d58fc55 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -22,7 +22,7 @@ use jsonrpc_core as core; pub use self::error::{Error, Result}; pub use self::metadata::{MetaExtractor, NoopExtractor, RequestContext}; -pub use self::server::{CloseHandle, Server}; +pub use self::server::{Broadcaster, CloseHandle, Server}; pub use self::server_builder::ServerBuilder; pub use self::server_utils::cors::Origin; pub use self::server_utils::hosts::{DomainsValidation, Host}; diff --git a/ws/src/server.rs b/ws/src/server.rs index 11585a277..11a4b68a7 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -38,6 +38,13 @@ impl Server { &self.addr } + /// Returns a Broadcaster that can be used to send messages on all connections. + pub fn broadcaster(&self) -> Broadcaster { + Broadcaster { + broadcaster: self.broadcaster.clone(), + } + } + /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. pub fn start>( @@ -160,3 +167,26 @@ impl CloseHandle { } } } + +/// A Broadcaster that can be used to send messages on all connections. +#[derive(Clone)] +pub struct Broadcaster { + broadcaster: ws::Sender, +} + +impl Broadcaster { + /// Send a message to the endpoints of all connections. + #[inline] + pub fn send(&self, msg: M) -> Result<()> + where + M: Into, + { + match self.broadcaster.send(msg).map_err(Error::from) { + Err(error) => { + error!("Error while running sending. Details: {:?}", error); + Err(error) + } + Ok(_server) => Ok(()), + } + } +} From 9360dc86e9c02e65e858ee7816c5ce6e04f18aef Mon Sep 17 00:00:00 2001 From: David Craven Date: Tue, 2 Apr 2019 10:41:36 +0200 Subject: [PATCH 041/149] Implement client support (#405) * Exclude stderr in editorconfig. * Fix some warnings. * Implement std::error::Error for Error. * Implement jsonrpc client. * Add returns attribute. * Generalize where clause generation. * Generate client. * Make client/server generation optional. * Update examples. * Add websocket client. * Fix tests. * Run rustfmt. * Add local client. * Export RpcChannel and RpcError. * Update client/Cargo.toml Co-Authored-By: dvc94ch * Update ws/client/Cargo.toml Co-Authored-By: dvc94ch * Improve error reporting. * Address review issues. * Fix tests. * Add example. * Add compile tests. --- .editorconfig | 10 +- Cargo.toml | 2 + _automate/publish.sh | 2 +- client/Cargo.toml | 35 ++ client/src/lib.rs | 325 ++++++++++++++++++ core/src/types/error.rs | 8 + derive/Cargo.toml | 2 + derive/examples/generic-trait-bounds.rs | 6 +- derive/examples/generic-trait.rs | 4 +- derive/examples/meta-macros.rs | 8 +- derive/examples/std.rs | 18 +- derive/src/lib.rs | 17 +- derive/src/options.rs | 37 ++ derive/src/rpc_attr.rs | 7 +- derive/src/rpc_trait.rs | 104 ++++-- derive/src/to_client.rs | 290 ++++++++++++++++ derive/src/to_delegate.rs | 26 +- derive/tests/macros.rs | 4 +- derive/tests/pubsub-macros.rs | 6 +- derive/tests/run-pass/client_only.rs | 32 ++ ...dependency-not-required-for-vanilla-rpc.rs | 4 +- ...b-subscription-type-without-deserialize.rs | 4 +- derive/tests/run-pass/server_only.rs | 36 ++ derive/tests/trailing.rs | 6 +- .../tests/ui/attr-invalid-name-values.stderr | 2 +- derive/tests/ui/too-many-params.rs | 4 +- derive/tests/ui/too-many-params.stderr | 4 +- http/src/handler.rs | 6 +- ipc/src/logger.rs | 2 +- server-utils/src/cors.rs | 2 +- tcp/src/logger.rs | 2 +- test/Cargo.toml | 2 + test/src/lib.rs | 14 +- ws/client/Cargo.toml | 31 ++ ws/client/src/lib.rs | 114 ++++++ 35 files changed, 1085 insertions(+), 91 deletions(-) create mode 100644 client/Cargo.toml create mode 100644 client/src/lib.rs create mode 100644 derive/src/options.rs create mode 100644 derive/src/to_client.rs create mode 100644 derive/tests/run-pass/client_only.rs create mode 100644 derive/tests/run-pass/server_only.rs create mode 100644 ws/client/Cargo.toml create mode 100644 ws/client/src/lib.rs diff --git a/.editorconfig b/.editorconfig index 28b112186..5e5b724ee 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,8 +9,16 @@ trim_trailing_whitespace=true max_line_length=120 insert_final_newline=true -[.travis.yml] +[{.travis.yml,appveyor.yml}] indent_style=space indent_size=2 tab_width=8 end_of_line=lf + +[*.stderr] +indent_style=none +indent_size=none +end_of_line=none +charset=none +trim_trailing_whitespace=none +insert_final_newline=none diff --git a/Cargo.toml b/Cargo.toml index b338a53b5..0d735eab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "core", + "client", "http", "ipc", "derive", @@ -11,4 +12,5 @@ members = [ "tcp", "test", "ws", + "ws/client", ] diff --git a/_automate/publish.sh b/_automate/publish.sh index 188f95605..753b62dd1 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -2,7 +2,7 @@ set -exu -ORDER=(core server-utils tcp ws http ipc stdio pubsub macros derive test) +ORDER=(core client server-utils tcp ws ws/client http ipc stdio pubsub macros derive test) for crate in ${ORDER[@]}; do cd $crate diff --git a/client/Cargo.toml b/client/Cargo.toml new file mode 100644 index 000000000..291843f15 --- /dev/null +++ b/client/Cargo.toml @@ -0,0 +1,35 @@ +[package] +authors = ["Parity Technologies "] +description = "Transport agnostic JSON-RPC 2.0 client implementation." +documentation = "https://docs.rs/jsonrpc-client/" +edition = "2018" +homepage = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] +license = "MIT" +name = "jsonrpc-client" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.1.0" + +categories = [ + "asynchronous", + "network-programming", + "web-programming::http-client", + "web-programming::http-server", + "web-programming::websocket", +] + +[dependencies] +failure = "0.1" +futures = "~0.1.6" +jsonrpc-core = { version = "10.1", path = "../core" } +log = "0.4" +serde_json = "1.0" + +[dev-dependencies] +jsonrpc-derive = { version = "10.1", path = "../derive" } +jsonrpc-client = { version = "10.1", path = "." } +serde = "1.0" +tokio = "0.1" + +[badges] +travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/client/src/lib.rs b/client/src/lib.rs new file mode 100644 index 000000000..65b321070 --- /dev/null +++ b/client/src/lib.rs @@ -0,0 +1,325 @@ +//! JSON-RPC client implementation. +#![deny(missing_docs)] + +use failure::{format_err, Fail}; +use futures::prelude::*; +use futures::sync::{mpsc, oneshot}; +use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Params, Request, Response, Version}; +use log::debug; +use serde_json::Value; +use std::collections::HashMap; +use std::collections::VecDeque; + +/// The errors returned by the client. +#[derive(Debug, Fail)] +pub enum RpcError { + /// An error returned by the server. + #[fail(display = "Server returned rpc error {}", _0)] + JsonRpcError(Error), + /// Failure to parse server response. + #[fail(display = "Failed to parse server response as {}: {}", _0, _1)] + ParseError(String, failure::Error), + /// Request timed out. + #[fail(display = "Request timed out")] + Timeout, + /// The server returned a response with an unknown id. + #[fail(display = "Server returned a response with an unknown id")] + UnknownId, + /// Not rpc specific errors. + #[fail(display = "{}", _0)] + Other(failure::Error), +} + +impl From for RpcError { + fn from(error: Error) -> Self { + RpcError::JsonRpcError(error) + } +} + +/// The future retured by the client. +pub struct RpcFuture { + recv: oneshot::Receiver>, +} + +impl RpcFuture { + /// Creates a new `RpcFuture`. + pub fn new(recv: oneshot::Receiver>) -> Self { + RpcFuture { recv } + } +} + +impl Future for RpcFuture { + type Item = Value; + type Error = RpcError; + + fn poll(&mut self) -> Result, Self::Error> { + // TODO should timeout (#410) + match self.recv.poll() { + Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), + Ok(Async::Ready(Err(error))) => Err(RpcError::JsonRpcError(error)), + Ok(Async::NotReady) => Ok(Async::NotReady), + Err(error) => Err(RpcError::Other(error.into())), + } + } +} + +/// A message sent to the `RpcClient`. This is public so that +/// the derive crate can generate a client. +pub struct RpcMessage { + /// The rpc method name. + pub method: String, + /// The rpc method parameters. + pub params: Params, + /// The oneshot channel to send the result of the rpc + /// call to. + pub sender: oneshot::Sender>, +} + +/// A channel to a `RpcClient`. +pub type RpcChannel = mpsc::Sender; + +/// The RpcClient handles sending and receiving asynchronous +/// messages through an underlying transport. +pub struct RpcClient { + id: u64, + queue: HashMap>>, + sink: TSink, + stream: TStream, + channel: Option>, + outgoing: VecDeque, +} + +impl RpcClient { + /// Creates a new `RpcClient`. + pub fn new(sink: TSink, stream: TStream, channel: mpsc::Receiver) -> Self { + RpcClient { + id: 0, + queue: HashMap::new(), + sink, + stream, + channel: Some(channel), + outgoing: VecDeque::new(), + } + } + + fn next_id(&mut self) -> Id { + let id = self.id; + self.id = id + 1; + Id::Num(id) + } +} + +impl Future for RpcClient +where + TSink: Sink, + TStream: Stream, +{ + type Item = (); + type Error = RpcError; + + fn poll(&mut self) -> Result, Self::Error> { + // Handle requests from the client. + loop { + if self.channel.is_none() { + break; + } + let msg = match self.channel.as_mut().expect("channel is some; qed").poll() { + Ok(Async::Ready(Some(msg))) => msg, + Ok(Async::Ready(None)) => { + // When the channel is dropped we still need to finish + // outstanding requests. + self.channel.take(); + break; + } + Ok(Async::NotReady) => break, + Err(()) => continue, + }; + let id = self.next_id(); + let request = Request::Single(Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: msg.method, + params: msg.params, + id: id.clone(), + })); + self.queue.insert(id, msg.sender); + let request_str = serde_json::to_string(&request).map_err(|error| RpcError::Other(error.into()))?; + self.outgoing.push_back(request_str); + } + // Handle outgoing rpc requests. + loop { + match self.outgoing.pop_front() { + Some(request) => match self.sink.start_send(request)? { + AsyncSink::Ready => {} + AsyncSink::NotReady(request) => { + self.outgoing.push_front(request); + break; + } + }, + None => break, + } + } + let done_sending = match self.sink.poll_complete()? { + Async::Ready(()) => true, + Async::NotReady => false, + }; + // Handle incoming rpc requests. + loop { + let response_str = match self.stream.poll() { + Ok(Async::Ready(Some(response_str))) => response_str, + Ok(Async::Ready(None)) => { + // The websocket connection was closed so the client + // can be shutdown. Reopening closed connections must + // be handled by the transport. + debug!("connection closed"); + return Ok(Async::Ready(())); + } + Ok(Async::NotReady) => break, + Err(err) => Err(err)?, + }; + let response = + serde_json::from_str::(&response_str).map_err(|error| RpcError::Other(error.into()))?; + let outputs: Vec = match response { + Response::Single(output) => vec![output], + Response::Batch(outputs) => outputs, + }; + for output in outputs { + let channel = self.queue.remove(output.id()); + let value: Result = output.into(); + match channel { + Some(tx) => tx + .send(value) + .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?, + None => Err(RpcError::UnknownId)?, + }; + } + } + if self.channel.is_none() && self.outgoing.is_empty() && self.queue.is_empty() && done_sending { + debug!("client finished"); + Ok(Async::Ready(())) + } else { + Ok(Async::NotReady) + } + } +} + +/// Rpc client implementation for `Deref>`. +pub mod local { + use super::*; + use jsonrpc_core::{MetaIoHandler, Metadata}; + use std::ops::Deref; + + /// Implements a rpc client for `MetaIoHandler`. + pub struct LocalRpc { + handler: THandler, + queue: VecDeque, + } + + impl LocalRpc + where + TMetadata: Metadata + Default, + THandler: Deref>, + { + /// Creates a new `LocalRpc`. + pub fn new(handler: THandler) -> Self { + Self { + handler, + queue: VecDeque::new(), + } + } + } + + impl Stream for LocalRpc + where + TMetadata: Metadata + Default, + THandler: Deref>, + { + type Item = String; + type Error = RpcError; + + fn poll(&mut self) -> Result>, Self::Error> { + match self.queue.pop_front() { + Some(response) => Ok(Async::Ready(Some(response))), + None => Ok(Async::NotReady), + } + } + } + + impl Sink for LocalRpc + where + TMetadata: Metadata + Default, + THandler: Deref>, + { + type SinkItem = String; + type SinkError = RpcError; + + fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { + match self.handler.handle_request_sync(&request, TMetadata::default()) { + Some(response) => self.queue.push_back(response), + None => {} + }; + Ok(AsyncSink::Ready) + } + + fn poll_complete(&mut self) -> Result, Self::SinkError> { + Ok(Async::Ready(())) + } + } + + /// Connects to a `IoHandler`. + pub fn connect( + handler: THandler, + ) -> (TClient, impl Future) + where + TClient: From, + TMetadata: Metadata + Default, + THandler: Deref>, + { + let (sink, stream) = local::LocalRpc::new(handler).split(); + let (sender, receiver) = mpsc::channel(0); + let rpc_client = RpcClient::new(sink, stream, receiver); + let client = TClient::from(sender); + (client, rpc_client) + } +} + +#[cfg(test)] +mod tests { + use futures::prelude::*; + use jsonrpc_client::local; + use jsonrpc_core::{IoHandler, Result}; + use jsonrpc_derive::rpc; + + #[rpc] + pub trait Rpc { + #[rpc(name = "add")] + fn add(&self, a: u64, b: u64) -> Result; + } + + struct RpcServer; + + impl Rpc for RpcServer { + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } + } + + #[test] + fn test_client_terminates() { + let mut handler = IoHandler::new(); + handler.extend_with(RpcServer.to_delegate()); + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .add(3, 4) + .and_then(move |res| client.add(res, 5)) + .join(rpc_client) + .map(|(res, ())| { + assert_eq!(res, 12); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } +} diff --git a/core/src/types/error.rs b/core/src/types/error.rs index 8a6b11046..12408d86c 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -157,3 +157,11 @@ impl Error { } } } + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}: {}", self.code.description(), self.message) + } +} + +impl std::error::Error for Error {} diff --git a/derive/Cargo.toml b/derive/Cargo.toml index ee95bc732..18c34482f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -20,8 +20,10 @@ proc-macro-crate = "0.1.3" [dev-dependencies] jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-client = { version = "10.1", path = "../client" } jsonrpc-pubsub = { version = "10.1", path = "../pubsub" } jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } +log = "0.4" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs index 41d0b0b84..09f5d3280 100644 --- a/derive/examples/generic-trait-bounds.rs +++ b/derive/examples/generic-trait-bounds.rs @@ -7,7 +7,7 @@ use jsonrpc_derive::rpc; // One is both parameter and a result so requires both Serialize and DeserializeOwned // Two is only a parameter so only requires DeserializeOwned // Three is only a result so only requires Serialize -#[rpc] +#[rpc(server)] pub trait Rpc { /// Get One type. #[rpc(name = "getOne")] @@ -15,14 +15,14 @@ pub trait Rpc { /// Adds two numbers and returns a result #[rpc(name = "setTwo")] - fn set_two(&self, _: Two) -> Result<()>; + fn set_two(&self, a: Two) -> Result<()>; #[rpc(name = "getThree")] fn get_three(&self) -> Result; /// Performs asynchronous operation #[rpc(name = "beFancy")] - fn call(&self, _: One) -> FutureResult<(One, u64), Error>; + fn call(&self, a: One) -> FutureResult<(One, u64), Error>; } struct RpcImpl; diff --git a/derive/examples/generic-trait.rs b/derive/examples/generic-trait.rs index ec98cb78f..6cf867b31 100644 --- a/derive/examples/generic-trait.rs +++ b/derive/examples/generic-trait.rs @@ -12,11 +12,11 @@ pub trait Rpc { /// Adds two numbers and returns a result #[rpc(name = "setTwo")] - fn set_two(&self, _: Two) -> Result<()>; + fn set_two(&self, a: Two) -> Result<()>; /// Performs asynchronous operation #[rpc(name = "beFancy")] - fn call(&self, _: One) -> FutureResult<(One, Two), Error>; + fn call(&self, a: One) -> FutureResult<(One, Two), Error>; } struct RpcImpl; diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 0c3279981..b21fda90e 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -18,19 +18,19 @@ pub trait Rpc { /// Adds two numbers and returns a result #[rpc(name = "add")] - fn add(&self, _: u64, _: u64) -> Result; + fn add(&self, a: u64, b: u64) -> Result; /// Multiplies two numbers. Second number is optional. #[rpc(name = "mul")] - fn mul(&self, _: u64, _: Option) -> Result; + fn mul(&self, a: u64, b: Option) -> Result; /// Performs asynchronous operation #[rpc(name = "callAsync")] - fn call(&self, _: u64) -> FutureResult; + fn call(&self, a: u64) -> FutureResult; /// Performs asynchronous operation with meta #[rpc(meta, name = "callAsyncMeta", alias("callAsyncMetaAlias"))] - fn call_meta(&self, _: Self::Metadata, _: BTreeMap) -> FutureResult; + fn call_meta(&self, a: Self::Metadata, b: BTreeMap) -> FutureResult; } struct RpcImpl; diff --git a/derive/examples/std.rs b/derive/examples/std.rs index da12d2a1b..6924b3e61 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -1,7 +1,11 @@ -use jsonrpc_core::futures::future::{self, FutureResult}; +//! A simple example +#![deny(missing_docs)] +use jsonrpc_client::local; +use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; +/// Rpc trait #[rpc] pub trait Rpc { /// Returns a protocol version @@ -10,11 +14,11 @@ pub trait Rpc { /// Adds two numbers and returns a result #[rpc(name = "add", alias("callAsyncMetaAlias"))] - fn add(&self, _: u64, _: u64) -> Result; + fn add(&self, a: u64, b: u64) -> Result; /// Performs asynchronous operation #[rpc(name = "callAsync")] - fn call(&self, _: u64) -> FutureResult; + fn call(&self, a: u64) -> FutureResult; } struct RpcImpl; @@ -35,7 +39,11 @@ impl Rpc for RpcImpl { fn main() { let mut io = IoHandler::new(); - let rpc = RpcImpl; + io.extend_with(RpcImpl.to_delegate()); - io.extend_with(rpc.to_delegate()) + let fut = { + let (client, server) = local::connect::(io); + client.add(5, 6).map(|res| println!("5 + 6 = {}", res)).join(server) + }; + fut.wait().unwrap(); } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 406721a13..a7cfa2a92 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -15,10 +15,10 @@ //! fn protocol_version(&self) -> Result; //! //! #[rpc(name = "add")] -//! fn add(&self, _: u64, _: u64) -> Result; +//! fn add(&self, a: u64, b: u64) -> Result; //! //! #[rpc(name = "callAsync")] -//! fn call(&self, _: u64) -> FutureResult; +//! fn call(&self, a: u64) -> FutureResult; //! } //! //! struct RpcImpl; @@ -124,7 +124,7 @@ //! # fn main() {} //! ``` -#![recursion_limit = "128"] +#![recursion_limit = "256"] #![warn(missing_docs)] extern crate proc_macro; @@ -132,8 +132,10 @@ extern crate proc_macro; use proc_macro::TokenStream; use syn::parse_macro_input; +mod options; mod rpc_attr; mod rpc_trait; +mod to_client; mod to_delegate; /// Apply `#[rpc]` to a trait, and a `to_delegate` method is generated which @@ -141,10 +143,15 @@ mod to_delegate; /// Attach the delegate to an `IoHandler` and the methods are now callable /// via JSON-RPC. #[proc_macro_attribute] -pub fn rpc(_args: TokenStream, input: TokenStream) -> TokenStream { +pub fn rpc(args: TokenStream, input: TokenStream) -> TokenStream { let input_toks = parse_macro_input!(input as syn::Item); - match rpc_trait::rpc_impl(input_toks) { + let options = match options::DeriveOptions::try_from(args) { + Ok(options) => options, + Err(error) => return error.to_compile_error().into(), + }; + + match rpc_trait::rpc_impl(input_toks, options) { Ok(output) => output.into(), Err(err) => err.to_compile_error().into(), } diff --git a/derive/src/options.rs b/derive/src/options.rs new file mode 100644 index 000000000..a6ba98ec0 --- /dev/null +++ b/derive/src/options.rs @@ -0,0 +1,37 @@ +use proc_macro::TokenStream; + +#[derive(Debug)] +pub struct DeriveOptions { + pub enable_client: bool, + pub enable_server: bool, +} + +impl DeriveOptions { + pub fn new(enable_client: bool, enable_server: bool) -> Self { + DeriveOptions { + enable_client, + enable_server, + } + } + + pub fn try_from(tokens: TokenStream) -> Result { + if tokens.is_empty() { + return Ok(Self::new(true, true)); + } + let ident: syn::Ident = syn::parse::(tokens)?; + let options = { + let ident = ident.to_string(); + if ident == "client" { + Some(Self::new(true, false)) + } else if ident == "server" { + Some(Self::new(false, true)) + } else { + None + } + }; + match options { + Some(options) => Ok(options), + None => Err(syn::Error::new(ident.span(), "Unknown attribute.")), + } + } +} diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index c34b46cdc..9988c6373 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -15,6 +15,7 @@ pub struct RpcMethodAttribute { pub enum AttributeKind { Rpc { has_metadata: bool, + returns: Option, }, PubSub { subscription_name: String, @@ -36,6 +37,7 @@ const PUB_SUB_ATTR_NAME: &str = "pubsub"; const METADATA_META_WORD: &str = "meta"; const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; +const RETURNS_META_WORD: &str = "returns"; const MULTIPLE_RPC_ATTRIBUTES_ERR: &str = "Expected only a single rpc attribute per method"; const INVALID_ATTR_PARAM_NAMES_ERR: &str = "Invalid attribute parameter(s):"; @@ -66,7 +68,8 @@ impl RpcMethodAttribute { RPC_ATTR_NAME => { let has_metadata = get_meta_list(meta).map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); - Some(Ok(AttributeKind::Rpc { has_metadata })) + let returns = get_meta_list(meta).map_or(None, |ml| get_name_value(RETURNS_META_WORD, ml)); + Some(Ok(AttributeKind::Rpc { has_metadata, returns })) } PUB_SUB_ATTR_NAME => Some(Self::parse_pubsub(meta)), _ => None, @@ -142,7 +145,7 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { match meta.name().to_string().as_ref() { RPC_ATTR_NAME => { validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD])?; - validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY])?; + validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } PUB_SUB_ATTR_NAME => { diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index ea357d03f..8a409bf6c 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -1,6 +1,8 @@ +use crate::options::DeriveOptions; use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute}; +use crate::to_client::generate_client_module; use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod}; -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; use quote::quote; use std::collections::HashMap; use syn::{ @@ -54,7 +56,7 @@ impl<'a> Fold for RpcTrait { } } -fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrait, bool)> { +fn compute_method_registrations(item_trait: &syn::ItemTrait) -> Result<(Vec, Vec)> { let methods_result: Result> = item_trait .items .iter() @@ -71,20 +73,13 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai }) .collect(); let methods = methods_result?; - let has_pubsub_methods = methods.iter().any(RpcMethod::is_pubsub); - let mut rpc_trait = RpcTrait { - methods: methods.clone(), - has_pubsub_methods, - has_metadata: false, - }; - let mut item_trait = fold::fold_item_trait(&mut rpc_trait, item_trait.clone()); let mut pubsub_method_pairs: HashMap, Option)> = HashMap::new(); let mut method_registrations: Vec = Vec::new(); for method in methods.iter() { match &method.attr().kind { - AttributeKind::Rpc { has_metadata } => method_registrations.push(MethodRegistration::Standard { + AttributeKind::Rpc { has_metadata, .. } => method_registrations.push(MethodRegistration::Standard { method: method.clone(), has_metadata: *has_metadata, }), @@ -150,18 +145,53 @@ fn generate_rpc_item_trait(item_trait: &syn::ItemTrait) -> Result<(syn::ItemTrai } } + Ok((method_registrations, methods)) +} + +fn generate_server_module( + method_registrations: &[MethodRegistration], + item_trait: &syn::ItemTrait, + methods: &[RpcMethod], +) -> Result { + let has_pubsub_methods = methods.iter().any(RpcMethod::is_pubsub); + + let mut rpc_trait = RpcTrait { + methods: methods.to_owned(), + has_pubsub_methods, + has_metadata: false, + }; + let mut rpc_server_trait = fold::fold_item_trait(&mut rpc_trait, item_trait.clone()); + let to_delegate_method = generate_trait_item_method( &method_registrations, - &item_trait, + &rpc_server_trait, rpc_trait.has_metadata, has_pubsub_methods, )?; - item_trait.items.push(syn::TraitItem::Method(to_delegate_method)); + + rpc_server_trait.items.push(syn::TraitItem::Method(to_delegate_method)); let trait_bounds: Punctuated = parse_quote!(Sized + Send + Sync + 'static); - item_trait.supertraits.extend(trait_bounds); + rpc_server_trait.supertraits.extend(trait_bounds); + + let optional_pubsub_import = if has_pubsub_methods { + crate_name("jsonrpc-pubsub").map(|pubsub_name| quote!(use #pubsub_name as _jsonrpc_pubsub;)) + } else { + Ok(quote!()) + }?; + + let rpc_server_module = quote! { + /// The generated server module. + pub mod gen_server { + #optional_pubsub_import + use self::_jsonrpc_core::futures as _futures; + use super::*; + + #rpc_server_trait + } + }; - Ok((item_trait, has_pubsub_methods)) + Ok(rpc_server_module) } fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { @@ -170,7 +200,13 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { syn::Ident::new(&mod_name, proc_macro2::Span::call_site()) } -pub fn rpc_impl(input: syn::Item) -> Result { +pub fn crate_name(name: &str) -> Result { + proc_macro_crate::crate_name(name) + .map(|name| Ident::new(&name, Span::call_site())) + .map_err(|e| Error::new(Span::call_site(), &e)) +} + +pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, item => { @@ -181,36 +217,38 @@ pub fn rpc_impl(input: syn::Item) -> Result { } }; - let (rpc_trait, has_pubsub_methods) = generate_rpc_item_trait(&rpc_trait)?; + let (method_registrations, methods) = compute_method_registrations(&rpc_trait)?; let name = rpc_trait.ident.clone(); let mod_name_ident = rpc_wrapper_mod_name(&rpc_trait); - let crate_name = |name| { - proc_macro_crate::crate_name(name) - .map(|name| Ident::new(&name, Span::call_site())) - .map_err(|e| Error::new(Span::call_site(), &e)) - }; - - let optional_pubsub_import = if has_pubsub_methods { - crate_name("jsonrpc-pubsub").map(|pubsub_name| quote!(use #pubsub_name as _jsonrpc_pubsub;)) - } else { - Ok(quote!()) - }?; - let core_name = crate_name("jsonrpc-core")?; let serde_name = crate_name("serde")?; + let mut submodules = Vec::new(); + let mut exports = Vec::new(); + if options.enable_client { + let rpc_client_module = generate_client_module(&method_registrations, &rpc_trait)?; + submodules.push(rpc_client_module); + exports.push(quote! { + pub use self::#mod_name_ident::gen_client; + }); + } + if options.enable_server { + let rpc_server_module = generate_server_module(&method_registrations, &rpc_trait, &methods)?; + submodules.push(rpc_server_module); + exports.push(quote! { + pub use self::#mod_name_ident::gen_server::#name; + }); + } Ok(quote!( mod #mod_name_ident { - use #core_name as _jsonrpc_core; - #optional_pubsub_import use #serde_name as _serde; + use #core_name as _jsonrpc_core; use super::*; - use self::_jsonrpc_core::futures as _futures; - #rpc_trait + #(#submodules)* } - pub use self::#mod_name_ident::#name; + #(#exports)* )) } diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs new file mode 100644 index 000000000..f9f4cd156 --- /dev/null +++ b/derive/src/to_client.rs @@ -0,0 +1,290 @@ +use crate::rpc_attr::AttributeKind; +use crate::rpc_trait::crate_name; +use crate::to_delegate::{generate_where_clause_serialization_predicates, MethodRegistration}; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; +use syn::punctuated::Punctuated; +use syn::Result; + +pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn::ItemTrait) -> Result { + let client_methods = generate_client_methods(methods)?; + let generics = &item_trait.generics; + let where_clause = generate_where_clause_serialization_predicates(&item_trait, true); + let where_clause2 = where_clause.clone(); + let markers = generics + .params + .iter() + .filter_map(|param| match param { + syn::GenericParam::Type(syn::TypeParam { ident, .. }) => Some(ident), + _ => None, + }) + .enumerate() + .map(|(i, ty)| { + let field_name = "_".to_string() + &i.to_string(); + let field = Ident::new(&field_name, ty.span()); + (field, ty) + }); + let (markers_decl, markers_impl): (Vec<_>, Vec<_>) = markers + .map(|(field, ty)| { + ( + quote! { + #field: std::marker::PhantomData<#ty> + }, + quote! { + #field: std::marker::PhantomData + }, + ) + }) + .unzip(); + let client_name = crate_name("jsonrpc-client")?; + Ok(quote! { + /// The generated client module. + pub mod gen_client { + use #client_name as _jsonrpc_client; + use super::*; + use _jsonrpc_core::{ + Call, Error, ErrorCode, Id, MethodCall, Params, Request, + Response, Version, + }; + use _jsonrpc_core::futures::{future, Future, Sink}; + use _jsonrpc_core::futures::sync::oneshot; + use _jsonrpc_core::serde_json::{self, Value}; + use _jsonrpc_client::{RpcChannel, RpcError, RpcFuture, RpcMessage}; + + /// The Client. + #[derive(Clone)] + pub struct Client#generics { + sender: RpcChannel, + #(#markers_decl),* + } + + impl#generics Client#generics + where + #(#where_clause),* + { + /// Creates a new `Client`. + pub fn new(sender: RpcChannel) -> Self { + Client { + sender, + #(#markers_impl),* + } + } + + #(#client_methods)* + + fn call_method( + &self, + method: String, + params: Params, + ) -> impl Future { + let (sender, receiver) = oneshot::channel(); + let msg = RpcMessage { + method, + params, + sender, + }; + self.sender + .to_owned() + .send(msg) + .map_err(|error| RpcError::Other(error.into())) + .and_then(|_| RpcFuture::new(receiver)) + } + } + + impl#generics From for Client#generics + where + #(#where_clause2),* + { + fn from(channel: RpcChannel) -> Self { + Client::new(channel) + } + } + } + }) +} + +fn generate_client_methods(methods: &[MethodRegistration]) -> Result> { + let mut client_methods = vec![]; + for method in methods { + match method { + MethodRegistration::Standard { method, .. } => { + let attrs = get_doc_comments(&method.trait_item.attrs); + let rpc_name = method.name(); + let name = &method.trait_item.sig.ident; + let args = compute_args(&method.trait_item); + let arg_names = compute_arg_identifiers(&args)?; + let returns = match &method.attr.kind { + AttributeKind::Rpc { returns, .. } => compute_returns(&method.trait_item, returns)?, + AttributeKind::PubSub { .. } => { + continue; + } + }; + let client_method = generate_client_method(&attrs, rpc_name, name, &args, &arg_names, &returns); + client_methods.push(client_method); + } + MethodRegistration::PubSub { .. } => { + println!("warning: pubsub methods are currently not supported in the generated client.") + } + } + } + Ok(client_methods) +} + +fn generate_client_method( + attrs: &[syn::Attribute], + rpc_name: &str, + name: &syn::Ident, + args: &Punctuated, + arg_names: &[&syn::Ident], + returns: &syn::Type, +) -> syn::ImplItem { + let returns_str = quote!(#returns).to_string(); + syn::parse_quote! { + #(#attrs)* + pub fn #name(&self, #args) -> impl Future { + let args_tuple = (#(#arg_names,)*); + let args = serde_json::to_value(args_tuple) + .expect("Only types with infallible serialisation can be used for JSON-RPC"); + let method = #rpc_name.to_owned(); + let params = match args { + Value::Array(vec) => Some(Params::Array(vec)), + Value::Null => Some(Params::None), + _ => None, + }.expect("should never happen"); + self.call_method(method, params) + .and_then(|value: Value| { + log::debug!("response: {:?}", value); + let result = serde_json::from_value::<#returns>(value) + .map_err(|error| { + RpcError::ParseError( + #returns_str.to_string(), + error.into(), + ) + }); + future::done(result) + }) + } + } +} + +fn get_doc_comments(attrs: &[syn::Attribute]) -> Vec { + let mut doc_comments = vec![]; + for attr in attrs { + match attr { + syn::Attribute { + path: syn::Path { segments, .. }, + .. + } => match &segments[0] { + syn::PathSegment { ident, .. } => { + if ident.to_string() == "doc" { + doc_comments.push(attr.to_owned()); + } + } + }, + } + } + doc_comments +} + +fn compute_args(method: &syn::TraitItemMethod) -> Punctuated { + let mut args = Punctuated::new(); + for arg in &method.sig.decl.inputs { + let ty = match arg { + syn::FnArg::Captured(syn::ArgCaptured { ty, .. }) => ty, + _ => continue, + }; + let segments = match ty { + syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) => segments, + _ => continue, + }; + let ident = match &segments[0] { + syn::PathSegment { ident, .. } => ident, + }; + if ident.to_string() == "Self" { + continue; + } + args.push(arg.to_owned()); + } + args +} + +fn compute_arg_identifiers(args: &Punctuated) -> Result> { + let mut arg_names = vec![]; + for arg in args { + let pat = match arg { + syn::FnArg::Captured(syn::ArgCaptured { pat, .. }) => pat, + _ => continue, + }; + let ident = match pat { + syn::Pat::Ident(syn::PatIdent { ident, .. }) => ident, + syn::Pat::Wild(wild) => { + let span = wild.underscore_token.spans[0]; + let msg = "No wildcard patterns allowed in rpc trait."; + return Err(syn::Error::new(span, msg)); + } + _ => continue, + }; + arg_names.push(ident); + } + Ok(arg_names) +} + +fn compute_returns(method: &syn::TraitItemMethod, returns: &Option) -> Result { + let returns: Option = match returns { + Some(returns) => Some(syn::parse_str(returns)?), + None => None, + }; + let returns = match returns { + None => try_infer_returns(&method.sig.decl.output), + _ => returns, + }; + let returns = match returns { + Some(returns) => returns, + None => { + let span = method.attrs[0].pound_token.spans[0]; + let msg = "Missing returns attribute."; + return Err(syn::Error::new(span, msg)); + } + }; + Ok(returns) +} + +fn try_infer_returns(output: &syn::ReturnType) -> Option { + match output { + syn::ReturnType::Type(_, ty) => match &**ty { + syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) => match &segments[0] { + syn::PathSegment { ident, arguments, .. } => { + if ident.to_string().ends_with("Result") { + get_first_type_argument(arguments) + } else { + None + } + } + }, + _ => None, + }, + _ => None, + } +} + +fn get_first_type_argument(args: &syn::PathArguments) -> Option { + match args { + syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }) => { + if args.len() > 0 { + match &args[0] { + syn::GenericArgument::Type(ty) => Some(ty.to_owned()), + _ => None, + } + } else { + None + } + } + _ => None, + } +} diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index fa1b0043c..e08e49393 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -121,7 +121,7 @@ pub fn generate_trait_item_method( } }; - let predicates = generate_where_clause_serialization_predicates(&trait_item); + let predicates = generate_where_clause_serialization_predicates(&trait_item, false); let mut method = method.clone(); method .sig @@ -362,7 +362,10 @@ fn is_option_type(ty: &syn::Type) -> bool { } } -fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) -> Vec { +pub fn generate_where_clause_serialization_predicates( + item_trait: &syn::ItemTrait, + client: bool, +) -> Vec { #[derive(Default)] struct FindTyParams { trait_generics: HashSet, @@ -411,11 +414,20 @@ fn generate_where_clause_serialization_predicates(item_trait: &syn::ItemTrait) - }; let mut bounds: Punctuated = parse_quote!(Send + Sync + 'static); // add json serialization trait bounds - if visitor.serialize_type_params.contains(&ty.ident) { - bounds.push(parse_quote!(_serde::Serialize)) - } - if visitor.deserialize_type_params.contains(&ty.ident) { - bounds.push(parse_quote!(_serde::de::DeserializeOwned)) + if client { + if visitor.serialize_type_params.contains(&ty.ident) { + bounds.push(parse_quote!(_serde::de::DeserializeOwned)) + } + if visitor.deserialize_type_params.contains(&ty.ident) { + bounds.push(parse_quote!(_serde::Serialize)) + } + } else { + if visitor.serialize_type_params.contains(&ty.ident) { + bounds.push(parse_quote!(_serde::Serialize)) + } + if visitor.deserialize_type_params.contains(&ty.ident) { + bounds.push(parse_quote!(_serde::de::DeserializeOwned)) + } } syn::WherePredicate::Type(syn::PredicateType { lifetimes: None, diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index 501704d8c..5b3310a94 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -19,11 +19,11 @@ pub trait Rpc { /// Negates number and returns a result #[rpc(name = "neg")] - fn neg(&self, _: i64) -> Result; + fn neg(&self, a: i64) -> Result; /// Adds two numbers and returns a result #[rpc(name = "add", alias("add_alias1", "add_alias2"))] - fn add(&self, _: u64, _: u64) -> Result; + fn add(&self, a: u64, b: u64) -> Result; } #[derive(Default)] diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index 5589f0833..e6f1b3ab5 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -24,15 +24,15 @@ pub trait Rpc { /// Hello subscription #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_alias"))] - fn subscribe(&self, _: Self::Metadata, _: Subscriber, _: u32, _: Option); + fn subscribe(&self, a: Self::Metadata, b: Subscriber, c: u32, d: Option); /// Unsubscribe from hello subscription. #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] - fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; + fn unsubscribe(&self, a: Option, b: SubscriptionId) -> Result; /// A regular rpc method alongside pubsub #[rpc(name = "add")] - fn add(&self, _: u64, _: u64) -> Result; + fn add(&self, a: u64, b: u64) -> Result; } #[derive(Default)] diff --git a/derive/tests/run-pass/client_only.rs b/derive/tests/run-pass/client_only.rs new file mode 100644 index 000000000..434709bbc --- /dev/null +++ b/derive/tests/run-pass/client_only.rs @@ -0,0 +1,32 @@ +extern crate serde; +extern crate jsonrpc_core; +extern crate jsonrpc_client; +#[macro_use] +extern crate jsonrpc_derive; +extern crate log; + +use jsonrpc_core::futures::future::Future; +use jsonrpc_core::futures::sync::mpsc; + +#[rpc(client)] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add", alias("callAsyncMetaAlias"))] + fn add(&self, a: u64, b: u64) -> Result; +} + +fn main() { + let fut = { + let (sender, _) = mpsc::channel(0); + gen_client::Client::new(sender) + .add(5, 6) + .map(|res| println!("5 + 6 = {}", res)) + }; + fut + .wait() + .ok(); +} diff --git a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs index 7f277661c..464623914 100644 --- a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs +++ b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs @@ -1,7 +1,9 @@ extern crate serde; extern crate jsonrpc_core; +extern crate jsonrpc_client; #[macro_use] extern crate jsonrpc_derive; +extern crate log; use jsonrpc_core::{Result, IoHandler}; @@ -13,7 +15,7 @@ pub trait Rpc { /// Adds two numbers and returns a result #[rpc(name = "add", alias("callAsyncMetaAlias"))] - fn add(&self, _: u64, _: u64) -> Result; + fn add(&self, a: u64, b: u64) -> Result; } struct RpcImpl; diff --git a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs index af012656d..53daaa294 100644 --- a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs @@ -2,9 +2,11 @@ extern crate serde; #[macro_use] extern crate serde_derive; extern crate jsonrpc_core; +extern crate jsonrpc_client; extern crate jsonrpc_pubsub; #[macro_use] extern crate jsonrpc_derive; +extern crate log; use std::sync::Arc; use jsonrpc_core::Result; @@ -20,7 +22,7 @@ pub trait Rpc { /// Unsubscribe from hello subscription. #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] - fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; + fn unsubscribe(&self, a: Option, b: SubscriptionId) -> Result; } // One way serialization diff --git a/derive/tests/run-pass/server_only.rs b/derive/tests/run-pass/server_only.rs new file mode 100644 index 000000000..dd96193d3 --- /dev/null +++ b/derive/tests/run-pass/server_only.rs @@ -0,0 +1,36 @@ +extern crate serde; +extern crate jsonrpc_core; +#[macro_use] +extern crate jsonrpc_derive; + +use jsonrpc_core::{Result, IoHandler}; + +#[rpc(server)] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add", alias("callAsyncMetaAlias"))] + fn add(&self, a: u64, b: u64) -> Result; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn protocol_version(&self) -> Result { + Ok("version1".into()) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } +} + +fn main() { + let mut io = IoHandler::new(); + let rpc = RpcImpl; + + io.extend_with(rpc.to_delegate()) +} diff --git a/derive/tests/trailing.rs b/derive/tests/trailing.rs index 605525360..b391e3062 100644 --- a/derive/tests/trailing.rs +++ b/derive/tests/trailing.rs @@ -6,15 +6,15 @@ use serde_json; pub trait Rpc { /// Multiplies two numbers. Second number is optional. #[rpc(name = "mul")] - fn mul(&self, _: u64, _: Option) -> Result; + fn mul(&self, a: u64, b: Option) -> Result; /// Echos back the message, example of a single param trailing #[rpc(name = "echo")] - fn echo(&self, _: Option) -> Result; + fn echo(&self, a: Option) -> Result; /// Adds up to three numbers and returns a result #[rpc(name = "add_multi")] - fn add_multi(&self, _: Option, _: Option, _: Option) -> Result; + fn add_multi(&self, a: Option, b: Option, c: Option) -> Result; } #[derive(Default)] diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index cc1f50bd6..f809202a7 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,4 +1,4 @@ -error: Invalid attribute parameter(s): 'Xname'. Expected 'name' +error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns' --> $DIR/attr-invalid-name-values.rs:10:2 | 10 | /// Returns a protocol version diff --git a/derive/tests/ui/too-many-params.rs b/derive/tests/ui/too-many-params.rs index 31ec98b14..40ff9a72b 100644 --- a/derive/tests/ui/too-many-params.rs +++ b/derive/tests/ui/too-many-params.rs @@ -11,8 +11,8 @@ pub trait Rpc { #[rpc(name = "tooManyParams")] fn to_many_params( &self, - _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, - _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, + a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, + k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, ) -> Result; } diff --git a/derive/tests/ui/too-many-params.stderr b/derive/tests/ui/too-many-params.stderr index f12ae8470..d89f4877c 100644 --- a/derive/tests/ui/too-many-params.stderr +++ b/derive/tests/ui/too-many-params.stderr @@ -6,8 +6,8 @@ error: Maximum supported number of params is 16 11 | | #[rpc(name = "tooManyParams")] 12 | | fn to_many_params( 13 | | &self, -14 | | _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, -15 | | _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64, +14 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, +15 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, 16 | | ) -> Result; | |________________________^ diff --git a/http/src/handler.rs b/http/src/handler.rs index 02a329406..aebd2547e 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -80,7 +80,7 @@ impl> Service for ServerHandler { // Validate host if should_validate_hosts && !is_host_allowed { - return Handler::Error(Some(Response::host_not_allowed())); + return Handler::Err(Some(Response::host_not_allowed())); } // Replace response with the one returned by middleware. @@ -113,7 +113,7 @@ impl> Service for ServerHandler { pub enum Handler> { Rpc(RpcHandler), - Error(Option), + Err(Option), Middleware(Box, Error = hyper::Error> + Send>), } @@ -125,7 +125,7 @@ impl> Future for Handler { match *self { Handler::Rpc(ref mut handler) => handler.poll(), Handler::Middleware(ref mut middleware) => middleware.poll(), - Handler::Error(ref mut response) => Ok(Async::Ready( + Handler::Err(ref mut response) => Ok(Async::Ready( response .take() .expect("Response always Some initialy. Returning `Ready` so will never be polled again; qed") diff --git a/ipc/src/logger.rs b/ipc/src/logger.rs index c599dc34b..9b885a72d 100644 --- a/ipc/src/logger.rs +++ b/ipc/src/logger.rs @@ -10,7 +10,7 @@ lazy_static! { builder.filter(None, LevelFilter::Info); if let Ok(log) = env::var("RUST_LOG") { - builder.parse(&log); + builder.parse_filters(&log); } if let Ok(_) = builder.try_init() { diff --git a/server-utils/src/cors.rs b/server-utils/src/cors.rs index 5889bf891..172400c67 100644 --- a/server-utils/src/cors.rs +++ b/server-utils/src/cors.rs @@ -280,8 +280,8 @@ pub fn get_cors_allow_headers, O, F: Fn(T) -> O>( } } -/// Returns headers which are always allowed. lazy_static! { + /// Returns headers which are always allowed. static ref ALWAYS_ALLOWED_HEADERS: HashSet> = { let mut hs = HashSet::new(); hs.insert(Ascii::new("Accept")); diff --git a/tcp/src/logger.rs b/tcp/src/logger.rs index 8502fe870..6edd87759 100644 --- a/tcp/src/logger.rs +++ b/tcp/src/logger.rs @@ -8,7 +8,7 @@ lazy_static! { builder.filter(None, LevelFilter::Info); if let Ok(log) = env::var("RUST_LOG") { - builder.parse(&log); + builder.parse_filters(&log); } if let Ok(_) = builder.try_init() { diff --git a/test/Cargo.toml b/test/Cargo.toml index 1d083f80d..0977d341b 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -11,7 +11,9 @@ edition = "2018" [dependencies] jsonrpc-core = { path = "../core", version = "10.0" } +jsonrpc-client = { path = "../client", version = "10.0" } jsonrpc-pubsub = { path = "../pubsub", version = "10.0" } +log = "0.4" serde = "1.0" serde_json = "1.0" diff --git a/test/src/lib.rs b/test/src/lib.rs index 2c067e387..8d07f4fa9 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -8,16 +8,16 @@ //! //! #[rpc] //! pub trait Test { -//! #[rpc(name = "rpc_some_method")] -//! fn some_method(&self, _: u64) -> Result; +//! #[rpc(name = "rpc_some_method")] +//! fn some_method(&self, a: u64) -> Result; //! } //! //! //! struct Dummy; //! impl Test for Dummy { -//! fn some_method(&self, x: u64) -> Result { -//! Ok(x * 2) -//! } +//! fn some_method(&self, x: u64) -> Result { +//! Ok(x * 2) +//! } //! } //! //! fn main() { @@ -31,8 +31,8 @@ //! let rpc = { //! let mut io = IoHandler::new(); //! io.add_method("rpc_test_method", |_| { -//! Err(Error::internal_error()) -//! }); +//! Err(Error::internal_error()) +//! }); //! test::Rpc::from(io) //! }; //! diff --git a/ws/client/Cargo.toml b/ws/client/Cargo.toml new file mode 100644 index 000000000..c977a785e --- /dev/null +++ b/ws/client/Cargo.toml @@ -0,0 +1,31 @@ +[package] +authors = ["Parity Technologies "] +description = "JSON-RPC 2.0 websocket client implementation." +documentation = "https://docs.rs/jsonrpc-ws-client/" +edition = "2018" +homepage = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] +license = "MIT" +name = "jsonrpc-ws-client" +repository = "https://github.com/paritytech/jsonrpc" +version = "10.1.0" + +categories = [ + "asynchronous", + "network-programming", + "web-programming::http-client", + "web-programming::http-server", + "web-programming::websocket", +] + +[dependencies] +failure = "0.1" +futures = "0.1" +jsonrpc-core = { version = "10.1", path = "../../core" } +jsonrpc-client = { version = "10.1", path = "../../client" } +log = "0.4" +tokio = "0.1" +websocket = "0.22" + +[badges] +travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ws/client/src/lib.rs b/ws/client/src/lib.rs new file mode 100644 index 000000000..5d72792ef --- /dev/null +++ b/ws/client/src/lib.rs @@ -0,0 +1,114 @@ +//! JSON-RPC websocket client implementation. +#![deny(missing_docs)] +use failure::Error; +use futures::prelude::*; +use futures::sync::mpsc; +use jsonrpc_client::RpcClient; +pub use jsonrpc_client::{RpcChannel, RpcError}; +use log::info; +use std::collections::VecDeque; +use websocket::{ClientBuilder, OwnedMessage}; + +/// Connect to a JSON-RPC websocket server. +/// +/// Uses an unbuffered channel to queue outgoing rpc messages. +pub fn connect(url: &str) -> Result, Error> +where + T: From, +{ + let client = ClientBuilder::new(url)? + .async_connect(None) + .map(|(client, _)| { + let (sink, stream) = client.split(); + let (sink, stream) = WebsocketClient::new(sink, stream).split(); + let (sender, receiver) = mpsc::channel(0); + let rpc_client = RpcClient::new(sink, stream, receiver).map_err(|error| eprintln!("{:?}", error)); + tokio::spawn(rpc_client); + sender.into() + }) + .map_err(|error| RpcError::Other(error.into())); + Ok(client) +} + +struct WebsocketClient { + sink: TSink, + stream: TStream, + queue: VecDeque, +} + +impl WebsocketClient +where + TSink: Sink, + TStream: Stream, + TError: Into, +{ + pub fn new(sink: TSink, stream: TStream) -> Self { + Self { + sink, + stream, + queue: VecDeque::new(), + } + } +} + +impl Sink for WebsocketClient +where + TSink: Sink, + TStream: Stream, + TError: Into, +{ + type SinkItem = String; + type SinkError = RpcError; + + fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { + self.queue.push_back(OwnedMessage::Text(request)); + Ok(AsyncSink::Ready) + } + + fn poll_complete(&mut self) -> Result, Self::SinkError> { + loop { + match self.queue.pop_front() { + Some(request) => match self.sink.start_send(request) { + Ok(AsyncSink::Ready) => continue, + Ok(AsyncSink::NotReady(request)) => { + self.queue.push_front(request); + break; + } + Err(error) => return Err(RpcError::Other(error.into())), + }, + None => break, + } + } + self.sink.poll_complete().map_err(|error| RpcError::Other(error.into())) + } +} + +impl Stream for WebsocketClient +where + TSink: Sink, + TStream: Stream, + TError: Into, +{ + type Item = String; + type Error = RpcError; + + fn poll(&mut self) -> Result>, Self::Error> { + loop { + match self.stream.poll() { + Ok(Async::Ready(Some(message))) => match message { + OwnedMessage::Text(data) => return Ok(Async::Ready(Some(data))), + OwnedMessage::Binary(data) => info!("server sent binary data {:?}", data), + OwnedMessage::Ping(p) => self.queue.push_front(OwnedMessage::Pong(p)), + OwnedMessage::Pong(_) => {} + OwnedMessage::Close(c) => self.queue.push_front(OwnedMessage::Close(c)), + }, + Ok(Async::Ready(None)) => { + // TODO try to reconnect (#411). + return Ok(Async::Ready(None)); + } + Ok(Async::NotReady) => return Ok(Async::NotReady), + Err(error) => return Err(RpcError::Other(error.into())), + } + } + } +} From 7b797ae6ef277a207c570e54826883b3013ee28c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 3 Apr 2019 09:33:34 +0100 Subject: [PATCH 042/149] Bump version (11.0) (#413) * Bump version (11.0) * Remove macros from release script * Bump versions in readme * Add simple client examples in docs * Fix doc example * ^^^ try again * Full client/server example * Update README example to use client/server example * Client support notice at top of README --- README.md | 56 +++++++++++++++++++++++++++++++++ _automate/bump_version.sh | 4 +++ _automate/publish.sh | 2 +- client/Cargo.toml | 8 ++--- core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++--- derive/src/lib.rs | 53 +++++++++++++++++++++++++++++++ http/Cargo.toml | 6 ++-- http/README.md | 2 +- ipc/Cargo.toml | 6 ++-- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 ++-- pubsub/more-examples/Cargo.toml | 10 +++--- server-utils/Cargo.toml | 4 +-- stdio/Cargo.toml | 4 +-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 ++-- tcp/README.md | 2 +- test/Cargo.toml | 10 +++--- ws/Cargo.toml | 6 ++-- ws/README.md | 2 +- ws/client/Cargo.toml | 6 ++-- 22 files changed, 161 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 18670ccf3..6014b2b83 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ Rust implementation of JSON-RPC 2.0 Specification. Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` and `tcp`. +**New!** Support for [clients](#Client-support). + [![Build Status][travis-image]][travis-url] [![Build Status][appveyor-image]][appveyor-url] @@ -94,3 +96,57 @@ fn main() { let mut io = jsonrpc_core::IoHandler::new(); io.extend_with(RpcImpl.to_delegate()) } +``` + +### Client support + +```rust +use jsonrpc_client::local; +use jsonrpc_core::futures::future::{self, Future, FutureResult}; +use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_derive::rpc; + +/// Rpc trait +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add", alias("callAsyncMetaAlias"))] + fn add(&self, a: u64, b: u64) -> Result; + + /// Performs asynchronous operation + #[rpc(name = "callAsync")] + fn call(&self, a: u64) -> FutureResult; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn protocol_version(&self) -> Result { + Ok("version1".into()) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } + + fn call(&self, _: u64) -> FutureResult { + future::ok("OK".to_owned()) + } +} + +fn main() { + let mut io = IoHandler::new(); + io.extend_with(RpcImpl.to_delegate()); + + let fut = { + let (client, server) = local::connect::(io); + client.add(5, 6).map(|res| println!("5 + 6 = {}", res)).join(server) + }; + fut.wait().unwrap(); +} + +``` diff --git a/_automate/bump_version.sh b/_automate/bump_version.sh index 6e1d0ec8d..a078520fc 100755 --- a/_automate/bump_version.sh +++ b/_automate/bump_version.sh @@ -14,4 +14,8 @@ ack "{ version = \"$PREV_DEPS" -l | \ grep toml | \ xargs sed -i "s/{ version = \"$PREV_DEPS/{ version = \"$NEW_DEPS/" +ack " = \"$PREV_DEPS" -l | \ + grep md | \ + xargs sed -i "s/ = \"$PREV_DEPS/ = \"$NEW_DEPS/" + cargo check diff --git a/_automate/publish.sh b/_automate/publish.sh index 753b62dd1..433b3641c 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -2,7 +2,7 @@ set -exu -ORDER=(core client server-utils tcp ws ws/client http ipc stdio pubsub macros derive test) +ORDER=(core client server-utils tcp ws ws/client http ipc stdio pubsub derive test) for crate in ${ORDER[@]}; do cd $crate diff --git a/client/Cargo.toml b/client/Cargo.toml index 291843f15..50cc22526 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" categories = [ "asynchronous", @@ -21,13 +21,13 @@ categories = [ [dependencies] failure = "0.1" futures = "~0.1.6" -jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-core = { version = "11.0", path = "../core" } log = "0.4" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { version = "10.1", path = "../derive" } -jsonrpc-client = { version = "10.1", path = "." } +jsonrpc-derive = { version = "11.0", path = "../derive" } +jsonrpc-client = { version = "11.0", path = "." } serde = "1.0" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index 2db2e77ac..73f52ba59 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 18c34482f..de107ed14 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "10.1", path = "../core" } -jsonrpc-client = { version = "10.1", path = "../client" } -jsonrpc-pubsub = { version = "10.1", path = "../pubsub" } -jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } +jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-client = { version = "11.0", path = "../client" } +jsonrpc-pubsub = { version = "11.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } log = "0.4" serde = "1.0" serde_derive = "1.0" diff --git a/derive/src/lib.rs b/derive/src/lib.rs index a7cfa2a92..b2299e09b 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -123,6 +123,59 @@ //! //! # fn main() {} //! ``` +//! +//! Client Example +//! +//! ``` +//! use jsonrpc_client::local; +//! use jsonrpc_core::futures::future::{self, Future, FutureResult}; +//! use jsonrpc_core::{Error, IoHandler, Result}; +//! use jsonrpc_derive::rpc; +//! +//! /// Rpc trait +//! #[rpc] +//! pub trait Rpc { +//! /// Returns a protocol version +//! #[rpc(name = "protocolVersion")] +//! fn protocol_version(&self) -> Result; +//! +//! /// Adds two numbers and returns a result +//! #[rpc(name = "add", alias("callAsyncMetaAlias"))] +//! fn add(&self, a: u64, b: u64) -> Result; +//! +//! /// Performs asynchronous operation +//! #[rpc(name = "callAsync")] +//! fn call(&self, a: u64) -> FutureResult; +//! } +//! +//! struct RpcImpl; +//! +//! impl Rpc for RpcImpl { +//! fn protocol_version(&self) -> Result { +//! Ok("version1".into()) +//! } +//! +//! fn add(&self, a: u64, b: u64) -> Result { +//! Ok(a + b) +//! } +//! +//! fn call(&self, _: u64) -> FutureResult { +//! future::ok("OK".to_owned()) +//! } +//! } +//! +//! fn main() { +//! let mut io = IoHandler::new(); +//! io.extend_with(RpcImpl.to_delegate()); +//! +//! let fut = { +//! let (client, server) = local::connect::(io); +//! client.add(5, 6).map(|res| println!("5 + 6 = {}", res)).join(server) +//! }; +//! fut.wait().unwrap(); +//! } +//! +//! ``` #![recursion_limit = "256"] #![warn(missing_docs)] diff --git a/http/Cargo.toml b/http/Cargo.toml index 5b982cf59..6b18ddaa2 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "10.1", path = "../core" } -jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } +jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index dd4e84ff7..1658fd61a 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "10.0" +jsonrpc-http-server = "11.0" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 001bcbf04..e9074f7bf 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "10.1", path = "../core" } -jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } +jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } parity-tokio-ipc = "0.1" parking_lot = "0.7" diff --git a/ipc/README.md b/ipc/README.md index c0f4c7af6..73f4c402a 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "10.0" +jsonrpc-ipc-server = "11.0" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 795e63951..d59637483 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] log = "0.4" parking_lot = "0.7" -jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-core = { version = "11.0", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "10.1", path = "../tcp" } +jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index e7c5d2cac..58dc5fbd8 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "10.1", path = "../../core" } -jsonrpc-pubsub = { version = "10.1", path = "../" } -jsonrpc-ws-server = { version = "10.1", path = "../../ws" } -jsonrpc-ipc-server = { version = "10.1", path = "../../ipc" } +jsonrpc-core = { version = "11.0", path = "../../core" } +jsonrpc-pubsub = { version = "11.0", path = "../" } +jsonrpc-ws-server = { version = "11.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "11.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 1c3b9a716..e0fc9eb42 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-core = { version = "11.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index ead4ad42c..a8a54da59 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "10.1", path = "../core" } +jsonrpc-core = { version = "11.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index 1df9601f6..1a8be41e0 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "10.0" +jsonrpc-stdio-server = "11.0" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 08a100e58..d03a871e0 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] log = "0.4" parking_lot = "0.7" tokio-service = "0.1" -jsonrpc-core = { version = "10.1", path = "../core" } -jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } +jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index c912220a9..7b9aaf364 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "10.0" +jsonrpc-tcp-server = "11.0" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index 0977d341b..49d026a77 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "10.1.0" +version = "11.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { path = "../core", version = "10.0" } -jsonrpc-client = { path = "../client", version = "10.0" } -jsonrpc-pubsub = { path = "../pubsub", version = "10.0" } +jsonrpc-core = { path = "../core", version = "11.0" } +jsonrpc-client = { path = "../client", version = "11.0" } +jsonrpc-pubsub = { path = "../pubsub", version = "11.0" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { path = "../derive", version = "10.0" } +jsonrpc-derive = { path = "../derive", version = "11.0" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 3c6887455..e36d8ad0b 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" [dependencies] -jsonrpc-core = { version = "10.1", path = "../core" } -jsonrpc-server-utils = { version = "10.1", path = "../server-utils" } +jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } log = "0.4" parking_lot = "0.7" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 307df20f0..177a2ac6a 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "10.0" +jsonrpc-ws-server = "11.0" ``` `main.rs` diff --git a/ws/client/Cargo.toml b/ws/client/Cargo.toml index c977a785e..1d962928a 100644 --- a/ws/client/Cargo.toml +++ b/ws/client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-ws-client" repository = "https://github.com/paritytech/jsonrpc" -version = "10.1.0" +version = "11.0.0" categories = [ "asynchronous", @@ -21,8 +21,8 @@ categories = [ [dependencies] failure = "0.1" futures = "0.1" -jsonrpc-core = { version = "10.1", path = "../../core" } -jsonrpc-client = { version = "10.1", path = "../../client" } +jsonrpc-core = { version = "11.0", path = "../../core" } +jsonrpc-client = { version = "11.0", path = "../../client" } log = "0.4" tokio = "0.1" websocket = "0.22" From a1c3bbe9aa70687bfa5bb85115cbdb473f7e961c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 3 Apr 2019 10:35:14 +0100 Subject: [PATCH 043/149] Rename jsonrpc-client to jsonrpc-core-client (#416) * Fix test build * Rename jsonrpc-client to jsonrpc-core-client Because of crates.io collision with `jsonrpc_client` * Update README --- Cargo.toml | 2 +- README.md | 5 ++++- {client => core-client}/Cargo.toml | 6 +++--- {client => core-client}/src/lib.rs | 2 +- derive/Cargo.toml | 2 +- derive/examples/std.rs | 2 +- derive/src/lib.rs | 2 +- derive/src/to_client.rs | 6 +++--- derive/tests/run-pass/client_only.rs | 2 +- .../pubsub-dependency-not-required-for-vanilla-rpc.rs | 2 +- .../pubsub-subscription-type-without-deserialize.rs | 2 +- ipc/src/logger.rs | 2 +- tcp/src/logger.rs | 2 +- test/Cargo.toml | 2 +- ws/client/Cargo.toml | 2 +- ws/client/src/lib.rs | 4 ++-- 16 files changed, 24 insertions(+), 21 deletions(-) rename {client => core-client}/Cargo.toml (85%) rename {client => core-client}/src/lib.rs (99%) diff --git a/Cargo.toml b/Cargo.toml index 0d735eab8..6d9af1801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ "core", - "client", + "core-client", "http", "ipc", "derive", diff --git a/README.md b/README.md index 6014b2b83..570d8fd61 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` ## Sub-projects - [jsonrpc-core](./core) [![crates.io][core-image]][core-url] +- [jsonrpc-core-client](./core-client) [![crates.io][core-client-image]][core-client-url] - [jsonrpc-http-server](./http) [![crates.io][http-server-image]][http-server-url] - [jsonrpc-ipc-server](./ipc) [![crates.io][ipc-server-image]][ipc-server-url] - [jsonrpc-tcp-server](./tcp) [![crates.io][tcp-server-image]][tcp-server-url] @@ -28,6 +29,8 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` [core-image]: https://img.shields.io/crates/v/jsonrpc-core.svg [core-url]: https://crates.io/crates/jsonrpc-core +[core-client-image]: https://img.shields.io/crates/v/jsonrpc-core-client.svg +[core-client-url]: https://crates.io/crates/jsonrpc-core-client [http-server-image]: https://img.shields.io/crates/v/jsonrpc-http-server.svg [http-server-url]: https://crates.io/crates/jsonrpc-http-server [ipc-server-image]: https://img.shields.io/crates/v/jsonrpc-ipc-server.svg @@ -101,7 +104,7 @@ fn main() { ### Client support ```rust -use jsonrpc_client::local; +use jsonrpc_core_client::local; use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; diff --git a/client/Cargo.toml b/core-client/Cargo.toml similarity index 85% rename from client/Cargo.toml rename to core-client/Cargo.toml index 50cc22526..332240636 100644 --- a/client/Cargo.toml +++ b/core-client/Cargo.toml @@ -1,12 +1,12 @@ [package] authors = ["Parity Technologies "] description = "Transport agnostic JSON-RPC 2.0 client implementation." -documentation = "https://docs.rs/jsonrpc-client/" +documentation = "https://docs.rs/jsonrpc-core-client/" edition = "2018" homepage = "https://github.com/paritytech/jsonrpc" keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" -name = "jsonrpc-client" +name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" version = "11.0.0" @@ -27,7 +27,7 @@ serde_json = "1.0" [dev-dependencies] jsonrpc-derive = { version = "11.0", path = "../derive" } -jsonrpc-client = { version = "11.0", path = "." } +jsonrpc-core-client = { version = "11.0", path = "." } serde = "1.0" tokio = "0.1" diff --git a/client/src/lib.rs b/core-client/src/lib.rs similarity index 99% rename from client/src/lib.rs rename to core-client/src/lib.rs index 65b321070..e73225682 100644 --- a/client/src/lib.rs +++ b/core-client/src/lib.rs @@ -285,7 +285,7 @@ pub mod local { #[cfg(test)] mod tests { use futures::prelude::*; - use jsonrpc_client::local; + use jsonrpc_core_client::local; use jsonrpc_core::{IoHandler, Result}; use jsonrpc_derive::rpc; diff --git a/derive/Cargo.toml b/derive/Cargo.toml index de107ed14..c92d1374b 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -20,7 +20,7 @@ proc-macro-crate = "0.1.3" [dev-dependencies] jsonrpc-core = { version = "11.0", path = "../core" } -jsonrpc-client = { version = "11.0", path = "../client" } +jsonrpc-core-client = { version = "11.0", path = "../core-client" } jsonrpc-pubsub = { version = "11.0", path = "../pubsub" } jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } log = "0.4" diff --git a/derive/examples/std.rs b/derive/examples/std.rs index 6924b3e61..539e5725d 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -1,6 +1,6 @@ //! A simple example #![deny(missing_docs)] -use jsonrpc_client::local; +use jsonrpc_core_client::local; use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; diff --git a/derive/src/lib.rs b/derive/src/lib.rs index b2299e09b..5d28feeda 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -127,7 +127,7 @@ //! Client Example //! //! ``` -//! use jsonrpc_client::local; +//! use jsonrpc_core_client::local; //! use jsonrpc_core::futures::future::{self, Future, FutureResult}; //! use jsonrpc_core::{Error, IoHandler, Result}; //! use jsonrpc_derive::rpc; diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index f9f4cd156..c757615d1 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -36,11 +36,11 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: ) }) .unzip(); - let client_name = crate_name("jsonrpc-client")?; + let client_name = crate_name("jsonrpc-core-client")?; Ok(quote! { /// The generated client module. pub mod gen_client { - use #client_name as _jsonrpc_client; + use #client_name as _jsonrpc_core_client; use super::*; use _jsonrpc_core::{ Call, Error, ErrorCode, Id, MethodCall, Params, Request, @@ -49,7 +49,7 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: use _jsonrpc_core::futures::{future, Future, Sink}; use _jsonrpc_core::futures::sync::oneshot; use _jsonrpc_core::serde_json::{self, Value}; - use _jsonrpc_client::{RpcChannel, RpcError, RpcFuture, RpcMessage}; + use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, RpcMessage}; /// The Client. #[derive(Clone)] diff --git a/derive/tests/run-pass/client_only.rs b/derive/tests/run-pass/client_only.rs index 434709bbc..921f4f574 100644 --- a/derive/tests/run-pass/client_only.rs +++ b/derive/tests/run-pass/client_only.rs @@ -1,6 +1,6 @@ extern crate serde; extern crate jsonrpc_core; -extern crate jsonrpc_client; +extern crate jsonrpc_core_client; #[macro_use] extern crate jsonrpc_derive; extern crate log; diff --git a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs index 464623914..d394905e4 100644 --- a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs +++ b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs @@ -1,6 +1,6 @@ extern crate serde; extern crate jsonrpc_core; -extern crate jsonrpc_client; +extern crate jsonrpc_core_client; #[macro_use] extern crate jsonrpc_derive; extern crate log; diff --git a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs index 53daaa294..0afe9994c 100644 --- a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs @@ -2,7 +2,7 @@ extern crate serde; #[macro_use] extern crate serde_derive; extern crate jsonrpc_core; -extern crate jsonrpc_client; +extern crate jsonrpc_core_client; extern crate jsonrpc_pubsub; #[macro_use] extern crate jsonrpc_derive; diff --git a/ipc/src/logger.rs b/ipc/src/logger.rs index 9b885a72d..c599dc34b 100644 --- a/ipc/src/logger.rs +++ b/ipc/src/logger.rs @@ -10,7 +10,7 @@ lazy_static! { builder.filter(None, LevelFilter::Info); if let Ok(log) = env::var("RUST_LOG") { - builder.parse_filters(&log); + builder.parse(&log); } if let Ok(_) = builder.try_init() { diff --git a/tcp/src/logger.rs b/tcp/src/logger.rs index 6edd87759..8502fe870 100644 --- a/tcp/src/logger.rs +++ b/tcp/src/logger.rs @@ -8,7 +8,7 @@ lazy_static! { builder.filter(None, LevelFilter::Info); if let Ok(log) = env::var("RUST_LOG") { - builder.parse_filters(&log); + builder.parse(&log); } if let Ok(_) = builder.try_init() { diff --git a/test/Cargo.toml b/test/Cargo.toml index 49d026a77..bde095ef1 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" [dependencies] jsonrpc-core = { path = "../core", version = "11.0" } -jsonrpc-client = { path = "../client", version = "11.0" } +jsonrpc-core-client = { path = "../core-client", version = "11.0" } jsonrpc-pubsub = { path = "../pubsub", version = "11.0" } log = "0.4" serde = "1.0" diff --git a/ws/client/Cargo.toml b/ws/client/Cargo.toml index 1d962928a..737f47446 100644 --- a/ws/client/Cargo.toml +++ b/ws/client/Cargo.toml @@ -22,7 +22,7 @@ categories = [ failure = "0.1" futures = "0.1" jsonrpc-core = { version = "11.0", path = "../../core" } -jsonrpc-client = { version = "11.0", path = "../../client" } +jsonrpc-core-client = { version = "11.0", path = "../../core-client" } log = "0.4" tokio = "0.1" websocket = "0.22" diff --git a/ws/client/src/lib.rs b/ws/client/src/lib.rs index 5d72792ef..efc1960b8 100644 --- a/ws/client/src/lib.rs +++ b/ws/client/src/lib.rs @@ -3,8 +3,8 @@ use failure::Error; use futures::prelude::*; use futures::sync::mpsc; -use jsonrpc_client::RpcClient; -pub use jsonrpc_client::{RpcChannel, RpcError}; +use jsonrpc_core_client::RpcClient; +pub use jsonrpc_core_client::{RpcChannel, RpcError}; use log::info; use std::collections::VecDeque; use websocket::{ClientBuilder, OwnedMessage}; From 0d4f49bd8c32b859f1c2d144313843498f558ed6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 Apr 2019 09:49:39 +0100 Subject: [PATCH 044/149] Extract client plumbing from derive, fix circular dependency (#417) * Update core-client name in publish script * Remove circular dependency for client-core test * Use TypedClient in client derive macro * Remove generate_client_method, inline the code * Fix derive client compilation * Don't panic * Remove unused log dependency from derive * Make RpcMessage fields private * Remove redundant intos --- _automate/publish.sh | 2 +- core-client/Cargo.toml | 2 +- core-client/src/lib.rs | 110 +++++++++++++++++++++++++-- derive/Cargo.toml | 1 - derive/src/to_client.rs | 72 +++--------------- derive/tests/run-pass/client_only.rs | 1 - 6 files changed, 116 insertions(+), 72 deletions(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index 433b3641c..ca6b15ae4 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -2,7 +2,7 @@ set -exu -ORDER=(core client server-utils tcp ws ws/client http ipc stdio pubsub derive test) +ORDER=(core core-client server-utils tcp ws ws/client http ipc stdio pubsub derive test) for crate in ${ORDER[@]}; do cd $crate diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 332240636..80d4bc7f1 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -23,11 +23,11 @@ failure = "0.1" futures = "~0.1.6" jsonrpc-core = { version = "11.0", path = "../core" } log = "0.4" +serde = "1.0" serde_json = "1.0" [dev-dependencies] jsonrpc-derive = { version = "11.0", path = "../derive" } -jsonrpc-core-client = { version = "11.0", path = "." } serde = "1.0" tokio = "0.1" diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index e73225682..35e252acf 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -2,13 +2,15 @@ #![deny(missing_docs)] use failure::{format_err, Fail}; -use futures::prelude::*; +use futures::{future, prelude::*}; use futures::sync::{mpsc, oneshot}; use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Params, Request, Response, Version}; use log::debug; use serde_json::Value; use std::collections::HashMap; use std::collections::VecDeque; +use serde::de::DeserializeOwned; +use serde::Serialize; /// The errors returned by the client. #[derive(Debug, Fail)] @@ -67,12 +69,12 @@ impl Future for RpcFuture { /// the derive crate can generate a client. pub struct RpcMessage { /// The rpc method name. - pub method: String, + method: String, /// The rpc method parameters. - pub params: Params, + params: Params, /// The oneshot channel to send the result of the rpc /// call to. - pub sender: oneshot::Sender>, + sender: oneshot::Sender>, } /// A channel to a `RpcClient`. @@ -202,6 +204,83 @@ where } } +/// Client for raw JSON RPC requests +#[derive(Clone)] +pub struct RawClient(RpcChannel); + +impl From for RawClient { + fn from(channel: RpcChannel) -> Self { + RawClient(channel) + } +} + +impl RawClient { + /// Call RPC with raw JSON + pub fn call_method(&self, method: &str, params: Params) -> impl Future { + let (sender, receiver) = oneshot::channel(); + let msg = RpcMessage { + method: method.into(), + params, + sender, + }; + self.0 + .to_owned() + .send(msg) + .map_err(|error| RpcError::Other(error.into())) + .and_then(|_| RpcFuture::new(receiver)) + } +} + +/// Client for typed JSON RPC requests +#[derive(Clone)] +pub struct TypedClient(RawClient); + +impl From for TypedClient { + fn from(channel: RpcChannel) -> Self { + TypedClient(channel.into()) + } +} + +impl TypedClient { + /// Create new TypedClient + pub fn new(raw_cli: RawClient) -> Self { + TypedClient(raw_cli) + } + + /// Call RPC with serialization of request and deserialization of response + pub fn call_method( + &self, + method: &str, + returns: &'static str, + args: T, + ) -> impl Future { + let args = serde_json::to_value(args) + .expect("Only types with infallible serialisation can be used for JSON-RPC"); + let params = match args { + Value::Array(vec) => Params::Array(vec), + Value::Null => Params::None, + _ => return future::Either::A(future::err(RpcError::Other( + format_err!("RPC params should serialize to a JSON array, or null")))), + }; + + future::Either::B( + self.0 + .call_method(method, params) + .and_then(move |value: Value| { + log::debug!("response: {:?}", value); + let result = serde_json::from_value::(value) + .map_err(|error| { + RpcError::ParseError( + returns.into(), + error.into(), + ) + }); + future::done(result) + }) + ) + } +} + /// Rpc client implementation for `Deref>`. pub mod local { use super::*; @@ -284,12 +363,12 @@ pub mod local { #[cfg(test)] mod tests { - use futures::prelude::*; - use jsonrpc_core_client::local; + use super::*; use jsonrpc_core::{IoHandler, Result}; use jsonrpc_derive::rpc; + use crate::{TypedClient, RpcError, RpcChannel}; - #[rpc] + #[rpc(server)] pub trait Rpc { #[rpc(name = "add")] fn add(&self, a: u64, b: u64) -> Result; @@ -303,11 +382,26 @@ mod tests { } } + #[derive(Clone)] + struct AddClient(TypedClient); + + impl From for AddClient { + fn from(channel: RpcChannel) -> Self { + AddClient(channel.into()) + } + } + + impl AddClient { + fn add(&self, a: u64, b: u64) -> impl Future { + self.0.call_method("add", "u64", (a, b)) + } + } + #[test] fn test_client_terminates() { let mut handler = IoHandler::new(); handler.extend_with(RpcServer.to_delegate()); - let (client, rpc_client) = local::connect::(handler); + let (client, rpc_client) = local::connect::(handler); let fut = client .clone() .add(3, 4) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index c92d1374b..bc9b9df7f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -23,7 +23,6 @@ jsonrpc-core = { version = "11.0", path = "../core" } jsonrpc-core-client = { version = "11.0", path = "../core-client" } jsonrpc-pubsub = { version = "11.0", path = "../pubsub" } jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } -log = "0.4" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index c757615d1..471822cd3 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -49,12 +49,12 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: use _jsonrpc_core::futures::{future, Future, Sink}; use _jsonrpc_core::futures::sync::oneshot; use _jsonrpc_core::serde_json::{self, Value}; - use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, RpcMessage}; + use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, RpcMessage, TypedClient}; /// The Client. #[derive(Clone)] pub struct Client#generics { - sender: RpcChannel, + inner: TypedClient, #(#markers_decl),* } @@ -65,30 +65,12 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: /// Creates a new `Client`. pub fn new(sender: RpcChannel) -> Self { Client { - sender, + inner: sender.into(), #(#markers_impl),* } } #(#client_methods)* - - fn call_method( - &self, - method: String, - params: Params, - ) -> impl Future { - let (sender, receiver) = oneshot::channel(); - let msg = RpcMessage { - method, - params, - sender, - }; - self.sender - .to_owned() - .send(msg) - .map_err(|error| RpcError::Other(error.into())) - .and_then(|_| RpcFuture::new(receiver)) - } } impl#generics From for Client#generics @@ -96,7 +78,7 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: #(#where_clause2),* { fn from(channel: RpcChannel) -> Self { - Client::new(channel) + Client::new(channel.into()) } } } @@ -119,7 +101,14 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result impl Future { + let args_tuple = (#(#arg_names,)*); + self.inner.call_method(#rpc_name, #returns_str, args_tuple) + } + }; client_methods.push(client_method); } MethodRegistration::PubSub { .. } => { @@ -130,43 +119,6 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result, - arg_names: &[&syn::Ident], - returns: &syn::Type, -) -> syn::ImplItem { - let returns_str = quote!(#returns).to_string(); - syn::parse_quote! { - #(#attrs)* - pub fn #name(&self, #args) -> impl Future { - let args_tuple = (#(#arg_names,)*); - let args = serde_json::to_value(args_tuple) - .expect("Only types with infallible serialisation can be used for JSON-RPC"); - let method = #rpc_name.to_owned(); - let params = match args { - Value::Array(vec) => Some(Params::Array(vec)), - Value::Null => Some(Params::None), - _ => None, - }.expect("should never happen"); - self.call_method(method, params) - .and_then(|value: Value| { - log::debug!("response: {:?}", value); - let result = serde_json::from_value::<#returns>(value) - .map_err(|error| { - RpcError::ParseError( - #returns_str.to_string(), - error.into(), - ) - }); - future::done(result) - }) - } - } -} - fn get_doc_comments(attrs: &[syn::Attribute]) -> Vec { let mut doc_comments = vec![]; for attr in attrs { diff --git a/derive/tests/run-pass/client_only.rs b/derive/tests/run-pass/client_only.rs index 921f4f574..4ad3c4f2e 100644 --- a/derive/tests/run-pass/client_only.rs +++ b/derive/tests/run-pass/client_only.rs @@ -3,7 +3,6 @@ extern crate jsonrpc_core; extern crate jsonrpc_core_client; #[macro_use] extern crate jsonrpc_derive; -extern crate log; use jsonrpc_core::futures::future::Future; use jsonrpc_core::futures::sync::mpsc; From 1dde12a2dea1e42495633d8c864e586a3b4b0380 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 5 Apr 2019 11:13:00 +0100 Subject: [PATCH 045/149] Remove derive <-> core-client circular dependency (#419) * Remove core-client <-> derive circular dev dependency * Restore original derived cli/server test to derive crate --- core-client/Cargo.toml | 1 - core-client/src/lib.rs | 24 +++++++----------------- derive/Cargo.toml | 2 ++ derive/tests/client.rs | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 derive/tests/client.rs diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 80d4bc7f1..07b844012 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -27,7 +27,6 @@ serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { version = "11.0", path = "../derive" } serde = "1.0" tokio = "0.1" diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index 35e252acf..ebd577920 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -364,24 +364,9 @@ pub mod local { #[cfg(test)] mod tests { use super::*; - use jsonrpc_core::{IoHandler, Result}; - use jsonrpc_derive::rpc; + use jsonrpc_core::{self, IoHandler}; use crate::{TypedClient, RpcError, RpcChannel}; - #[rpc(server)] - pub trait Rpc { - #[rpc(name = "add")] - fn add(&self, a: u64, b: u64) -> Result; - } - - struct RpcServer; - - impl Rpc for RpcServer { - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) - } - } - #[derive(Clone)] struct AddClient(TypedClient); @@ -400,7 +385,12 @@ mod tests { #[test] fn test_client_terminates() { let mut handler = IoHandler::new(); - handler.extend_with(RpcServer.to_delegate()); + handler.add_method("add", |params: Params| { + let (a, b) = params.parse::<(u64, u64)>()?; + let res = a + b; + Ok(jsonrpc_core::to_value(res).unwrap()) + }); + let (client, rpc_client) = local::connect::(handler); let fut = client .clone() diff --git a/derive/Cargo.toml b/derive/Cargo.toml index bc9b9df7f..9148a018a 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -23,7 +23,9 @@ jsonrpc-core = { version = "11.0", path = "../core" } jsonrpc-core-client = { version = "11.0", path = "../core-client" } jsonrpc-pubsub = { version = "11.0", path = "../pubsub" } jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } +futures = "~0.1.6" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +tokio = "0.1" compiletest_rs = { version = "0.3", features = ["stable"] } diff --git a/derive/tests/client.rs b/derive/tests/client.rs new file mode 100644 index 000000000..0f6be630f --- /dev/null +++ b/derive/tests/client.rs @@ -0,0 +1,38 @@ +use futures::prelude::*; +use jsonrpc_core_client::local; +use jsonrpc_core::{IoHandler, Result}; +use jsonrpc_derive::rpc; + +#[rpc] +pub trait Rpc { + #[rpc(name = "add")] + fn add(&self, a: u64, b: u64) -> Result; +} + +struct RpcServer; + +impl Rpc for RpcServer { + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } +} + +#[test] +fn client_server_roundtrip() { + let mut handler = IoHandler::new(); + handler.extend_with(RpcServer.to_delegate()); + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .add(3, 4) + .and_then(move |res| client.add(res, 5)) + .join(rpc_client) + .map(|(res, ())| { + assert_eq!(res, 12); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); +} \ No newline at end of file From 6fa323de53a9ce688b722a42181407ee27f5ced9 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Mon, 6 May 2019 14:04:54 +0200 Subject: [PATCH 046/149] fix(tcp poll): don't drop when receiver value is ready (#421) * fix(tcp poll): don't drop when receiver has items * style(rustfmt) * doc(nit): remove faulty comment --- core-client/src/lib.rs | 46 ++++++++++++++++++---------------------- derive/examples/std.rs | 2 +- derive/src/lib.rs | 6 +++--- derive/tests/client.rs | 4 ++-- tcp/src/dispatch.rs | 48 +++++++++++++++++++++++++++--------------- 5 files changed, 57 insertions(+), 49 deletions(-) diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index ebd577920..b134e6266 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -2,15 +2,15 @@ #![deny(missing_docs)] use failure::{format_err, Fail}; -use futures::{future, prelude::*}; use futures::sync::{mpsc, oneshot}; +use futures::{future, prelude::*}; use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Params, Request, Response, Version}; use log::debug; +use serde::de::DeserializeOwned; +use serde::Serialize; use serde_json::Value; use std::collections::HashMap; use std::collections::VecDeque; -use serde::de::DeserializeOwned; -use serde::Serialize; /// The errors returned by the client. #[derive(Debug, Fail)] @@ -216,7 +216,7 @@ impl From for RawClient { impl RawClient { /// Call RPC with raw JSON - pub fn call_method(&self, method: &str, params: Params) -> impl Future { + pub fn call_method(&self, method: &str, params: Params) -> impl Future { let (sender, receiver) = oneshot::channel(); let msg = RpcMessage { method: method.into(), @@ -253,31 +253,25 @@ impl TypedClient { method: &str, returns: &'static str, args: T, - ) -> impl Future { - let args = serde_json::to_value(args) - .expect("Only types with infallible serialisation can be used for JSON-RPC"); + ) -> impl Future { + let args = + serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC"); let params = match args { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, - _ => return future::Either::A(future::err(RpcError::Other( - format_err!("RPC params should serialize to a JSON array, or null")))), + _ => { + return future::Either::A(future::err(RpcError::Other(format_err!( + "RPC params should serialize to a JSON array, or null" + )))) + } }; - future::Either::B( - self.0 - .call_method(method, params) - .and_then(move |value: Value| { - log::debug!("response: {:?}", value); - let result = serde_json::from_value::(value) - .map_err(|error| { - RpcError::ParseError( - returns.into(), - error.into(), - ) - }); - future::done(result) - }) - ) + future::Either::B(self.0.call_method(method, params).and_then(move |value: Value| { + log::debug!("response: {:?}", value); + let result = + serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns.into(), error.into())); + future::done(result) + })) } } @@ -364,8 +358,8 @@ pub mod local { #[cfg(test)] mod tests { use super::*; + use crate::{RpcChannel, RpcError, TypedClient}; use jsonrpc_core::{self, IoHandler}; - use crate::{TypedClient, RpcError, RpcChannel}; #[derive(Clone)] struct AddClient(TypedClient); @@ -377,7 +371,7 @@ mod tests { } impl AddClient { - fn add(&self, a: u64, b: u64) -> impl Future { + fn add(&self, a: u64, b: u64) -> impl Future { self.0.call_method("add", "u64", (a, b)) } } diff --git a/derive/examples/std.rs b/derive/examples/std.rs index 539e5725d..716b5fd7f 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -1,8 +1,8 @@ //! A simple example #![deny(missing_docs)] -use jsonrpc_core_client::local; use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_core_client::local; use jsonrpc_derive::rpc; /// Rpc trait diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 5d28feeda..644f57657 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -123,9 +123,9 @@ //! //! # fn main() {} //! ``` -//! +//! //! Client Example -//! +//! //! ``` //! use jsonrpc_core_client::local; //! use jsonrpc_core::futures::future::{self, Future, FutureResult}; @@ -174,7 +174,7 @@ //! }; //! fut.wait().unwrap(); //! } -//! +//! //! ``` #![recursion_limit = "256"] diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 0f6be630f..3f1d71554 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -1,6 +1,6 @@ use futures::prelude::*; -use jsonrpc_core_client::local; use jsonrpc_core::{IoHandler, Result}; +use jsonrpc_core_client::local; use jsonrpc_derive::rpc; #[rpc] @@ -35,4 +35,4 @@ fn client_server_roundtrip() { assert!(false); }); tokio::run(fut); -} \ No newline at end of file +} diff --git a/tcp/src/dispatch.rs b/tcp/src/dispatch.rs index a22e23e6a..8af9cc210 100644 --- a/tcp/src/dispatch.rs +++ b/tcp/src/dispatch.rs @@ -12,7 +12,7 @@ pub type SenderChannels = Mutex>>; pub struct PeerMessageQueue { up: S, - receiver: mpsc::Receiver, + receiver: Option>, _addr: SocketAddr, } @@ -20,7 +20,7 @@ impl PeerMessageQueue { pub fn new(response_stream: S, receiver: mpsc::Receiver, addr: SocketAddr) -> Self { PeerMessageQueue { up: response_stream, - receiver, + receiver: Some(receiver), _addr: addr, } } @@ -82,26 +82,40 @@ impl> Stream for PeerMessageQue type Item = String; type Error = std::io::Error; + // The receiver will never return `Ok(Async::Ready(None))` + // Because the sender is kept in `SenderChannels` and it will never be dropped until `the stream` is resolved. + // + // Thus, that is the reason we terminate if `up_closed && receiver == Async::NotReady`. + // + // However, it is possible to have a race between `poll` and `push_work` if the connection is dropped. + // Therefore, the receiver is then dropped when the connection is dropped and an error is propagated when + // a `send` attempt is made on that channel. fn poll(&mut self) -> Poll, std::io::Error> { // check if we have response pending - match self.up.poll() { - Ok(Async::Ready(Some(val))) => { - return Ok(Async::Ready(Some(val))); - } - Ok(Async::Ready(None)) => { - // this will ensure that this polling will end when incoming i/o stream ends + + let up_closed = match self.up.poll() { + Ok(Async::Ready(Some(item))) => return Ok(Async::Ready(Some(item))), + Ok(Async::Ready(None)) => true, + Ok(Async::NotReady) => false, + err => return err, + }; + + let rx = match &mut self.receiver { + None => { + debug_assert!(up_closed); return Ok(Async::Ready(None)); } - _ => {} - } - - match self.receiver.poll() { - Ok(result) => Ok(result), - Err(send_err) => { - // not sure if it can ever happen - warn!("MPSC send error: {:?}", send_err); - Err(std::io::Error::from(std::io::ErrorKind::Other)) + Some(rx) => rx, + }; + + match rx.poll() { + Ok(Async::Ready(Some(item))) => Ok(Async::Ready(Some(item))), + Ok(Async::Ready(None)) | Ok(Async::NotReady) if up_closed => { + self.receiver = None; + Ok(Async::Ready(None)) } + Ok(Async::Ready(None)) | Ok(Async::NotReady) => Ok(Async::NotReady), + Err(_) => Err(std::io::Error::new(std::io::ErrorKind::Other, "MPSC error")), } } } From 893f4e81544a40f68533f74d9cf4c20e3616581f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 6 May 2019 19:09:51 +0200 Subject: [PATCH 047/149] Update parking_lot requirement from 0.7 to 0.8 (#428) Updates the requirements on [parking_lot](https://github.com/Amanieu/parking_lot) to permit the latest version. - [Release notes](https://github.com/Amanieu/parking_lot/releases) - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/compare/0.7.0...0.8.0) Signed-off-by: dependabot[bot] --- ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index e9074f7bf..6c90d2a35 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -15,7 +15,7 @@ tokio-service = "0.1" jsonrpc-core = { version = "11.0", path = "../core" } jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } parity-tokio-ipc = "0.1" -parking_lot = "0.7" +parking_lot = "0.8" [dev-dependencies] env_logger = "0.6" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index d59637483..325d55c8d 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -12,7 +12,7 @@ version = "11.0.0" [dependencies] log = "0.4" -parking_lot = "0.7" +parking_lot = "0.8" jsonrpc-core = { version = "11.0", path = "../core" } serde = "1.0" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index d03a871e0..a9eeb50e1 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -11,7 +11,7 @@ version = "11.0.0" [dependencies] log = "0.4" -parking_lot = "0.7" +parking_lot = "0.8" tokio-service = "0.1" jsonrpc-core = { version = "11.0", path = "../core" } jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index e36d8ad0b..88bdc560d 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -13,7 +13,7 @@ version = "11.0.0" jsonrpc-core = { version = "11.0", path = "../core" } jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } log = "0.4" -parking_lot = "0.7" +parking_lot = "0.8" slab = "0.4" ws = "0.8" From beeaadcd0f4f8d58119c2666e85aa7d22b825f36 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 13 May 2019 10:05:24 +0100 Subject: [PATCH 048/149] Replace derive compiletests with trybuild (#429) * Replace compiletest with trybuild * Fix up existing compilefail tests --- core/examples/params.rs | 2 +- derive/Cargo.toml | 5 ++-- derive/examples/generic-trait-bounds.rs | 2 +- derive/tests/compiletests.rs | 25 ------------------- ...dependency-not-required-for-vanilla-rpc.rs | 1 - ...b-subscription-type-without-deserialize.rs | 4 +-- derive/tests/trybuild.rs | 6 +++++ .../tests/ui/attr-invalid-meta-list-names.rs | 2 -- .../ui/attr-invalid-meta-list-names.stderr | 11 +++----- derive/tests/ui/attr-invalid-meta-words.rs | 2 -- .../tests/ui/attr-invalid-meta-words.stderr | 11 +++----- derive/tests/ui/attr-invalid-name-values.rs | 2 -- .../tests/ui/attr-invalid-name-values.stderr | 11 +++----- derive/tests/ui/attr-missing-rpc-name.rs | 2 -- derive/tests/ui/attr-missing-rpc-name.stderr | 11 +++----- derive/tests/ui/multiple-rpc-attributes.rs | 2 -- .../tests/ui/multiple-rpc-attributes.stderr | 13 ++++------ derive/tests/ui/too-many-params.rs | 4 +-- derive/tests/ui/too-many-params.stderr | 19 ++++++-------- 19 files changed, 41 insertions(+), 94 deletions(-) delete mode 100644 derive/tests/compiletests.rs create mode 100644 derive/tests/trybuild.rs diff --git a/core/examples/params.rs b/core/examples/params.rs index d528b3578..38b183e4d 100644 --- a/core/examples/params.rs +++ b/core/examples/params.rs @@ -1,5 +1,5 @@ use jsonrpc_core::*; -use serde_derive::Deserialize; +use serde::Deserialize; #[derive(Deserialize)] struct HelloParams { diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 9148a018a..afcfbca49 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -24,8 +24,7 @@ jsonrpc-core-client = { version = "11.0", path = "../core-client" } jsonrpc-pubsub = { version = "11.0", path = "../pubsub" } jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } futures = "~0.1.6" -serde = "1.0" -serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = "0.1" -compiletest_rs = { version = "0.3", features = ["stable"] } +trybuild = "1.0" diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs index 09f5d3280..ae4dc3a52 100644 --- a/derive/examples/generic-trait-bounds.rs +++ b/derive/examples/generic-trait-bounds.rs @@ -1,4 +1,4 @@ -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use jsonrpc_core::futures::future::{self, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; diff --git a/derive/tests/compiletests.rs b/derive/tests/compiletests.rs deleted file mode 100644 index ae7f4fbe3..000000000 --- a/derive/tests/compiletests.rs +++ /dev/null @@ -1,25 +0,0 @@ -use compiletest_rs as compiletest; - -use std::path::PathBuf; - -fn run_mode(mode: &'static str) { - let mut config = compiletest::Config::default(); - - config.mode = mode.parse().expect("Invalid mode"); - config.src_base = PathBuf::from(format!("tests/{}", mode)); - config.target_rustcflags = Some(String::from( - "\ - -L ../target/debug/ \ - -L ../target/debug/deps/ \ - ", - )); - config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 - - compiletest::run_tests(&config); -} - -#[test] -fn compile_test() { - run_mode("ui"); - run_mode("run-pass"); -} diff --git a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs index d394905e4..080345ce3 100644 --- a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs +++ b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs @@ -3,7 +3,6 @@ extern crate jsonrpc_core; extern crate jsonrpc_core_client; #[macro_use] extern crate jsonrpc_derive; -extern crate log; use jsonrpc_core::{Result, IoHandler}; diff --git a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs index 0afe9994c..65af085d9 100644 --- a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs @@ -1,12 +1,10 @@ -extern crate serde; #[macro_use] -extern crate serde_derive; +extern crate serde; extern crate jsonrpc_core; extern crate jsonrpc_core_client; extern crate jsonrpc_pubsub; #[macro_use] extern crate jsonrpc_derive; -extern crate log; use std::sync::Arc; use jsonrpc_core::Result; diff --git a/derive/tests/trybuild.rs b/derive/tests/trybuild.rs new file mode 100644 index 000000000..f8c2e2e43 --- /dev/null +++ b/derive/tests/trybuild.rs @@ -0,0 +1,6 @@ +#[test] +fn compile_test() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); + t.pass("tests/run-pass/*.rs"); +} diff --git a/derive/tests/ui/attr-invalid-meta-list-names.rs b/derive/tests/ui/attr-invalid-meta-list-names.rs index 099e7d661..e22716a83 100644 --- a/derive/tests/ui/attr-invalid-meta-list-names.rs +++ b/derive/tests/ui/attr-invalid-meta-list-names.rs @@ -3,8 +3,6 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::Result; - #[rpc] pub trait Rpc { /// Returns a protocol version diff --git a/derive/tests/ui/attr-invalid-meta-list-names.stderr b/derive/tests/ui/attr-invalid-meta-list-names.stderr index ba0ff9b5f..f22ba6e27 100644 --- a/derive/tests/ui/attr-invalid-meta-list-names.stderr +++ b/derive/tests/ui/attr-invalid-meta-list-names.stderr @@ -1,11 +1,8 @@ error: Invalid attribute parameter(s): 'Xalias'. Expected 'alias' - --> $DIR/attr-invalid-meta-list-names.rs:10:2 + --> $DIR/attr-invalid-meta-list-names.rs:8:2 | -10 | /// Returns a protocol version +8 | /// Returns a protocol version | _____^ -11 | | #[rpc(name = "protocolVersion", Xalias("alias"))] -12 | | fn protocol_version(&self) -> Result; +9 | | #[rpc(name = "protocolVersion", Xalias("alias"))] +10 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ - -error: aborting due to previous error - diff --git a/derive/tests/ui/attr-invalid-meta-words.rs b/derive/tests/ui/attr-invalid-meta-words.rs index 74a7c9ffc..6149f7118 100644 --- a/derive/tests/ui/attr-invalid-meta-words.rs +++ b/derive/tests/ui/attr-invalid-meta-words.rs @@ -3,8 +3,6 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::Result; - #[rpc] pub trait Rpc { /// Returns a protocol version diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index 3c2d0e0a7..118a51619 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,11 +1,8 @@ error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta' - --> $DIR/attr-invalid-meta-words.rs:10:2 + --> $DIR/attr-invalid-meta-words.rs:8:2 | -10 | /// Returns a protocol version +8 | /// Returns a protocol version | _____^ -11 | | #[rpc(name = "protocolVersion", Xmeta)] -12 | | fn protocol_version(&self) -> Result; +9 | | #[rpc(name = "protocolVersion", Xmeta)] +10 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ - -error: aborting due to previous error - diff --git a/derive/tests/ui/attr-invalid-name-values.rs b/derive/tests/ui/attr-invalid-name-values.rs index e37c24c24..3c4cf7a12 100644 --- a/derive/tests/ui/attr-invalid-name-values.rs +++ b/derive/tests/ui/attr-invalid-name-values.rs @@ -3,8 +3,6 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::Result; - #[rpc] pub trait Rpc { /// Returns a protocol version diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index f809202a7..aebdba62e 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,11 +1,8 @@ error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns' - --> $DIR/attr-invalid-name-values.rs:10:2 + --> $DIR/attr-invalid-name-values.rs:8:2 | -10 | /// Returns a protocol version +8 | /// Returns a protocol version | _____^ -11 | | #[rpc(Xname = "protocolVersion")] -12 | | fn protocol_version(&self) -> Result; +9 | | #[rpc(Xname = "protocolVersion")] +10 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ - -error: aborting due to previous error - diff --git a/derive/tests/ui/attr-missing-rpc-name.rs b/derive/tests/ui/attr-missing-rpc-name.rs index 16018b302..53b76991a 100644 --- a/derive/tests/ui/attr-missing-rpc-name.rs +++ b/derive/tests/ui/attr-missing-rpc-name.rs @@ -3,8 +3,6 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::Result; - #[rpc] pub trait Rpc { /// Returns a protocol version diff --git a/derive/tests/ui/attr-missing-rpc-name.stderr b/derive/tests/ui/attr-missing-rpc-name.stderr index a96c0aa42..d2287b7d0 100644 --- a/derive/tests/ui/attr-missing-rpc-name.stderr +++ b/derive/tests/ui/attr-missing-rpc-name.stderr @@ -1,11 +1,8 @@ error: rpc attribute should have a name e.g. `name = "method_name"` - --> $DIR/attr-missing-rpc-name.rs:10:2 + --> $DIR/attr-missing-rpc-name.rs:8:2 | -10 | /// Returns a protocol version +8 | /// Returns a protocol version | _____^ -11 | | #[rpc] -12 | | fn protocol_version(&self) -> Result; +9 | | #[rpc] +10 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ - -error: aborting due to previous error - diff --git a/derive/tests/ui/multiple-rpc-attributes.rs b/derive/tests/ui/multiple-rpc-attributes.rs index 10297c01b..01bf8adad 100644 --- a/derive/tests/ui/multiple-rpc-attributes.rs +++ b/derive/tests/ui/multiple-rpc-attributes.rs @@ -3,8 +3,6 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::Result; - #[rpc] pub trait Rpc { /// Returns a protocol version diff --git a/derive/tests/ui/multiple-rpc-attributes.stderr b/derive/tests/ui/multiple-rpc-attributes.stderr index 16af3ee29..6204f119a 100644 --- a/derive/tests/ui/multiple-rpc-attributes.stderr +++ b/derive/tests/ui/multiple-rpc-attributes.stderr @@ -1,12 +1,9 @@ error: Expected only a single rpc attribute per method - --> $DIR/multiple-rpc-attributes.rs:10:2 + --> $DIR/multiple-rpc-attributes.rs:8:2 | -10 | /// Returns a protocol version +8 | /// Returns a protocol version | _____^ -11 | | #[rpc(name = "protocolVersion")] -12 | | #[rpc(name = "protocolVersionAgain")] -13 | | fn protocol_version(&self) -> Result; +9 | | #[rpc(name = "protocolVersion")] +10 | | #[rpc(name = "protocolVersionAgain")] +11 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ - -error: aborting due to previous error - diff --git a/derive/tests/ui/too-many-params.rs b/derive/tests/ui/too-many-params.rs index 40ff9a72b..14c7d07be 100644 --- a/derive/tests/ui/too-many-params.rs +++ b/derive/tests/ui/too-many-params.rs @@ -3,8 +3,6 @@ extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::Result; - #[rpc] pub trait Rpc { /// Has too many params @@ -16,4 +14,4 @@ pub trait Rpc { ) -> Result; } -fn main() {} \ No newline at end of file +fn main() {} diff --git a/derive/tests/ui/too-many-params.stderr b/derive/tests/ui/too-many-params.stderr index d89f4877c..3ae24d7f1 100644 --- a/derive/tests/ui/too-many-params.stderr +++ b/derive/tests/ui/too-many-params.stderr @@ -1,15 +1,12 @@ error: Maximum supported number of params is 16 - --> $DIR/too-many-params.rs:10:2 + --> $DIR/too-many-params.rs:8:2 | -10 | /// Has too many params +8 | /// Has too many params | _____^ -11 | | #[rpc(name = "tooManyParams")] -12 | | fn to_many_params( -13 | | &self, -14 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, -15 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, -16 | | ) -> Result; +9 | | #[rpc(name = "tooManyParams")] +10 | | fn to_many_params( +11 | | &self, +12 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, +13 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, +14 | | ) -> Result; | |________________________^ - -error: aborting due to previous error - From 8c760e31290de0511b3e9f56062510e78e6bbf8f Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 21 May 2019 08:54:30 +0100 Subject: [PATCH 049/149] Client http transport (#420) * Copy @todr's http client prototype * Experimental transport helpers * Extract request/response helper function * Extract local transport * Join transport and client futures * WIP: http test * Add logging to debug http client * Make http test work * Add missing space * Add tests for server errors * Http transport direct request * Fix handling server error * Test http connection refused * Ignored test for call after ConnectionRefused * Extract Duplex transport * Extract RequestBuilder * Remove unused imports * Extract response handling * Fix example * Fix local transport examples * Channel buffer size to max parallel * Make RpcMessage private and move ws/client to core-client (#427) * Run rustfmt. * Make RpcMessage private. * Move ws/client to core-client. * Don't clone outputs * Fix client compiletest * Fix duplex transport * Fix issues after merge * Show actual error in assert * Implement From instead of Into * Remove request unwrap, test for invalid uri * Extract helper to create Duplex with channel --- Cargo.toml | 1 - README.md | 2 +- core-client/Cargo.toml | 11 +- core-client/src/lib.rs | 264 +++----------- core-client/src/logger.rs | 25 ++ core-client/src/transports/duplex.rs | 126 +++++++ core-client/src/transports/http.rs | 331 ++++++++++++++++++ core-client/src/transports/local.rs | 77 ++++ core-client/src/transports/mod.rs | 67 ++++ .../src/transports/ws.rs | 9 +- derive/examples/std.rs | 2 +- derive/src/lib.rs | 2 +- derive/src/to_client.rs | 2 +- derive/tests/client.rs | 2 +- derive/tests/run-pass/client_only.rs | 7 +- ws/client/Cargo.toml | 31 -- 16 files changed, 686 insertions(+), 273 deletions(-) create mode 100644 core-client/src/logger.rs create mode 100644 core-client/src/transports/duplex.rs create mode 100644 core-client/src/transports/http.rs create mode 100644 core-client/src/transports/local.rs create mode 100644 core-client/src/transports/mod.rs rename ws/client/src/lib.rs => core-client/src/transports/ws.rs (91%) delete mode 100644 ws/client/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 6d9af1801..9509ac35d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,4 @@ members = [ "tcp", "test", "ws", - "ws/client", ] diff --git a/README.md b/README.md index 570d8fd61..751d8ad0b 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ fn main() { ### Client support ```rust -use jsonrpc_core_client::local; +use jsonrpc_core_client::transports::local; use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 07b844012..d7928ba05 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -20,15 +20,20 @@ categories = [ [dependencies] failure = "0.1" -futures = "~0.1.6" +futures = "0.1.26" +hyper = "0.12" jsonrpc-core = { version = "11.0", path = "../core" } log = "0.4" serde = "1.0" serde_json = "1.0" +tokio = "0.1" +websocket = "0.22" [dev-dependencies] -serde = "1.0" -tokio = "0.1" +assert_matches = "1.1" +jsonrpc-http-server = { version = "11.0", path = "../http" } +lazy_static = "1.0" +env_logger = "0.6" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index b134e6266..43982e28d 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -4,13 +4,15 @@ use failure::{format_err, Fail}; use futures::sync::{mpsc, oneshot}; use futures::{future, prelude::*}; -use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Params, Request, Response, Version}; -use log::debug; +use jsonrpc_core::{Error, Params}; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::Value; -use std::collections::HashMap; -use std::collections::VecDeque; + +pub mod transports; + +#[cfg(test)] +mod logger; /// The errors returned by the client. #[derive(Debug, Fail)] @@ -38,168 +40,60 @@ impl From for RpcError { } } -/// The future retured by the client. -pub struct RpcFuture { - recv: oneshot::Receiver>, -} - -impl RpcFuture { - /// Creates a new `RpcFuture`. - pub fn new(recv: oneshot::Receiver>) -> Self { - RpcFuture { recv } - } -} - -impl Future for RpcFuture { - type Item = Value; - type Error = RpcError; - - fn poll(&mut self) -> Result, Self::Error> { - // TODO should timeout (#410) - match self.recv.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(error))) => Err(RpcError::JsonRpcError(error)), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(error) => Err(RpcError::Other(error.into())), - } - } -} - /// A message sent to the `RpcClient`. This is public so that /// the derive crate can generate a client. -pub struct RpcMessage { +struct RpcMessage { /// The rpc method name. method: String, /// The rpc method parameters. params: Params, /// The oneshot channel to send the result of the rpc /// call to. - sender: oneshot::Sender>, + sender: oneshot::Sender>, } /// A channel to a `RpcClient`. -pub type RpcChannel = mpsc::Sender; +#[derive(Clone)] +pub struct RpcChannel(mpsc::Sender); -/// The RpcClient handles sending and receiving asynchronous -/// messages through an underlying transport. -pub struct RpcClient { - id: u64, - queue: HashMap>>, - sink: TSink, - stream: TStream, - channel: Option>, - outgoing: VecDeque, +impl RpcChannel { + fn send( + &self, + msg: RpcMessage, + ) -> impl Future, Error = mpsc::SendError> { + self.0.to_owned().send(msg) + } } -impl RpcClient { - /// Creates a new `RpcClient`. - pub fn new(sink: TSink, stream: TStream, channel: mpsc::Receiver) -> Self { - RpcClient { - id: 0, - queue: HashMap::new(), - sink, - stream, - channel: Some(channel), - outgoing: VecDeque::new(), - } +impl From> for RpcChannel { + fn from(sender: mpsc::Sender) -> Self { + RpcChannel(sender) } +} - fn next_id(&mut self) -> Id { - let id = self.id; - self.id = id + 1; - Id::Num(id) +/// The future returned by the rpc call. +pub struct RpcFuture { + recv: oneshot::Receiver>, +} + +impl RpcFuture { + /// Creates a new `RpcFuture`. + pub fn new(recv: oneshot::Receiver>) -> Self { + RpcFuture { recv } } } -impl Future for RpcClient -where - TSink: Sink, - TStream: Stream, -{ - type Item = (); +impl Future for RpcFuture { + type Item = Value; type Error = RpcError; fn poll(&mut self) -> Result, Self::Error> { - // Handle requests from the client. - loop { - if self.channel.is_none() { - break; - } - let msg = match self.channel.as_mut().expect("channel is some; qed").poll() { - Ok(Async::Ready(Some(msg))) => msg, - Ok(Async::Ready(None)) => { - // When the channel is dropped we still need to finish - // outstanding requests. - self.channel.take(); - break; - } - Ok(Async::NotReady) => break, - Err(()) => continue, - }; - let id = self.next_id(); - let request = Request::Single(Call::MethodCall(MethodCall { - jsonrpc: Some(Version::V2), - method: msg.method, - params: msg.params, - id: id.clone(), - })); - self.queue.insert(id, msg.sender); - let request_str = serde_json::to_string(&request).map_err(|error| RpcError::Other(error.into()))?; - self.outgoing.push_back(request_str); - } - // Handle outgoing rpc requests. - loop { - match self.outgoing.pop_front() { - Some(request) => match self.sink.start_send(request)? { - AsyncSink::Ready => {} - AsyncSink::NotReady(request) => { - self.outgoing.push_front(request); - break; - } - }, - None => break, - } - } - let done_sending = match self.sink.poll_complete()? { - Async::Ready(()) => true, - Async::NotReady => false, - }; - // Handle incoming rpc requests. - loop { - let response_str = match self.stream.poll() { - Ok(Async::Ready(Some(response_str))) => response_str, - Ok(Async::Ready(None)) => { - // The websocket connection was closed so the client - // can be shutdown. Reopening closed connections must - // be handled by the transport. - debug!("connection closed"); - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => break, - Err(err) => Err(err)?, - }; - let response = - serde_json::from_str::(&response_str).map_err(|error| RpcError::Other(error.into()))?; - let outputs: Vec = match response { - Response::Single(output) => vec![output], - Response::Batch(outputs) => outputs, - }; - for output in outputs { - let channel = self.queue.remove(output.id()); - let value: Result = output.into(); - match channel { - Some(tx) => tx - .send(value) - .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?, - None => Err(RpcError::UnknownId)?, - }; - } - } - if self.channel.is_none() && self.outgoing.is_empty() && self.queue.is_empty() && done_sending { - debug!("client finished"); - Ok(Async::Ready(())) - } else { - Ok(Async::NotReady) + // TODO should timeout (#410) + match self.recv.poll() { + Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), + Ok(Async::Ready(Err(error))) => Err(error), + Ok(Async::NotReady) => Ok(Async::NotReady), + Err(error) => Err(RpcError::Other(error.into())), } } } @@ -224,7 +118,6 @@ impl RawClient { sender, }; self.0 - .to_owned() .send(msg) .map_err(|error| RpcError::Other(error.into())) .and_then(|_| RpcFuture::new(receiver)) @@ -275,89 +168,10 @@ impl TypedClient { } } -/// Rpc client implementation for `Deref>`. -pub mod local { - use super::*; - use jsonrpc_core::{MetaIoHandler, Metadata}; - use std::ops::Deref; - - /// Implements a rpc client for `MetaIoHandler`. - pub struct LocalRpc { - handler: THandler, - queue: VecDeque, - } - - impl LocalRpc - where - TMetadata: Metadata + Default, - THandler: Deref>, - { - /// Creates a new `LocalRpc`. - pub fn new(handler: THandler) -> Self { - Self { - handler, - queue: VecDeque::new(), - } - } - } - - impl Stream for LocalRpc - where - TMetadata: Metadata + Default, - THandler: Deref>, - { - type Item = String; - type Error = RpcError; - - fn poll(&mut self) -> Result>, Self::Error> { - match self.queue.pop_front() { - Some(response) => Ok(Async::Ready(Some(response))), - None => Ok(Async::NotReady), - } - } - } - - impl Sink for LocalRpc - where - TMetadata: Metadata + Default, - THandler: Deref>, - { - type SinkItem = String; - type SinkError = RpcError; - - fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { - match self.handler.handle_request_sync(&request, TMetadata::default()) { - Some(response) => self.queue.push_back(response), - None => {} - }; - Ok(AsyncSink::Ready) - } - - fn poll_complete(&mut self) -> Result, Self::SinkError> { - Ok(Async::Ready(())) - } - } - - /// Connects to a `IoHandler`. - pub fn connect( - handler: THandler, - ) -> (TClient, impl Future) - where - TClient: From, - TMetadata: Metadata + Default, - THandler: Deref>, - { - let (sink, stream) = local::LocalRpc::new(handler).split(); - let (sender, receiver) = mpsc::channel(0); - let rpc_client = RpcClient::new(sink, stream, receiver); - let client = TClient::from(sender); - (client, rpc_client) - } -} - #[cfg(test)] mod tests { use super::*; + use crate::transports::local; use crate::{RpcChannel, RpcError, TypedClient}; use jsonrpc_core::{self, IoHandler}; diff --git a/core-client/src/logger.rs b/core-client/src/logger.rs new file mode 100644 index 000000000..812a06524 --- /dev/null +++ b/core-client/src/logger.rs @@ -0,0 +1,25 @@ +use env_logger::Builder; +use lazy_static::lazy_static; +use log::LevelFilter; +use std::env; + +lazy_static! { + static ref LOG_DUMMY: bool = { + let mut builder = Builder::new(); + builder.filter(None, LevelFilter::Info); + + if let Ok(log) = env::var("RUST_LOG") { + builder.parse_filters(&log); + } + + if let Ok(_) = builder.try_init() { + println!("logger initialized"); + } + true + }; +} + +/// Intialize log with default settings +pub fn init_log() { + let _ = *LOG_DUMMY; +} diff --git a/core-client/src/transports/duplex.rs b/core-client/src/transports/duplex.rs new file mode 100644 index 000000000..93e1691e6 --- /dev/null +++ b/core-client/src/transports/duplex.rs @@ -0,0 +1,126 @@ +//! Duplex transport + +use failure::format_err; +use futures::prelude::*; +use futures::sync::{mpsc, oneshot}; +use jsonrpc_core::Id; +use log::debug; +use serde_json::Value; +use std::collections::HashMap; +use std::collections::VecDeque; + +use super::RequestBuilder; +use crate::{RpcChannel, RpcError, RpcMessage}; + +/// The Duplex handles sending and receiving asynchronous +/// messages through an underlying transport. +pub struct Duplex { + request_builder: RequestBuilder, + queue: HashMap>>, + sink: TSink, + stream: TStream, + channel: Option>, + outgoing: VecDeque, +} + +impl Duplex { + /// Creates a new `Duplex`. + fn new(sink: TSink, stream: TStream, channel: mpsc::Receiver) -> Self { + Duplex { + request_builder: RequestBuilder::new(), + queue: HashMap::new(), + sink, + stream, + channel: Some(channel), + outgoing: VecDeque::new(), + } + } +} + +/// Creates a new `Duplex`, along with a channel to communicate +pub fn duplex(sink: TSink, stream: TStream) -> (Duplex, RpcChannel) { + let (sender, receiver) = mpsc::channel(0); + let client = Duplex::new(sink, stream, receiver); + (client, sender.into()) +} + +impl Future for Duplex +where + TSink: Sink, + TStream: Stream, +{ + type Item = (); + type Error = RpcError; + + fn poll(&mut self) -> Result, Self::Error> { + // Handle requests from the client. + loop { + if self.channel.is_none() { + break; + } + let msg = match self.channel.as_mut().expect("channel is some; qed").poll() { + Ok(Async::Ready(Some(msg))) => msg, + Ok(Async::Ready(None)) => { + // When the channel is dropped we still need to finish + // outstanding requests. + self.channel.take(); + break; + } + Ok(Async::NotReady) => break, + Err(()) => continue, + }; + let (id, request_str) = self.request_builder.single_request(&msg); + self.queue.insert(id, msg.sender); + self.outgoing.push_back(request_str); + } + // Handle outgoing rpc requests. + loop { + match self.outgoing.pop_front() { + Some(request) => match self.sink.start_send(request)? { + AsyncSink::Ready => {} + AsyncSink::NotReady(request) => { + self.outgoing.push_front(request); + break; + } + }, + None => break, + } + } + let done_sending = match self.sink.poll_complete()? { + Async::Ready(()) => true, + Async::NotReady => false, + }; + // Handle incoming rpc requests. + loop { + let response_str = match self.stream.poll() { + Ok(Async::Ready(Some(response_str))) => response_str, + Ok(Async::Ready(None)) => { + // The websocket connection was closed so the client + // can be shutdown. Reopening closed connections must + // be handled by the transport. + debug!("connection closed"); + return Ok(Async::Ready(())); + } + Ok(Async::NotReady) => break, + Err(err) => Err(err)?, + }; + + let responses = super::parse_response(&response_str)?; + for (id, result) in responses { + let channel = self.queue.remove(&id); + match channel { + Some(tx) => tx + .send(result) + .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?, + None => Err(RpcError::UnknownId)?, + }; + } + } + if self.channel.is_none() && self.outgoing.is_empty() && self.queue.is_empty() && done_sending { + debug!("client finished"); + Ok(Async::Ready(())) + } else { + Ok(Async::NotReady) + } + } +} diff --git a/core-client/src/transports/http.rs b/core-client/src/transports/http.rs new file mode 100644 index 000000000..5d0c991c3 --- /dev/null +++ b/core-client/src/transports/http.rs @@ -0,0 +1,331 @@ +//! HTTP client + +use super::RequestBuilder; +use crate::{RpcChannel, RpcError, RpcMessage}; +use failure::format_err; +use futures::{ + future::{ + self, + Either::{A, B}, + }, + sync::mpsc, + Future, Stream, +}; +use hyper::{http, rt, Client, Request, Uri}; + +/// Create a HTTP Client +pub fn http(url: &str) -> impl Future +where + TClient: From, +{ + let max_parallel = 8; + let url: Uri = match url.parse() { + Ok(url) => url, + Err(e) => return A(future::err(RpcError::Other(e.into()))) + }; + let client = Client::new(); + let mut request_builder = RequestBuilder::new(); + + let (sender, receiver) = mpsc::channel(max_parallel); + + let fut = receiver + .map(move |msg: RpcMessage| { + let (_, request) = request_builder.single_request(&msg); + let request = Request::post(&url) + .header( + http::header::CONTENT_TYPE, + http::header::HeaderValue::from_static("application/json"), + ) + .body(request.into()) + .expect("Uri and request headers are valid; qed"); + client.request(request).then(move |response| Ok((response, msg))) + }) + .buffer_unordered(max_parallel) + .for_each(|(result, msg)| { + let future = match result { + Ok(ref res) if !res.status().is_success() => { + log::trace!("http result status {}", res.status()); + A(future::err(RpcError::Other(format_err!( + "Unexpected response status code: {}", + res.status() + )))) + } + Ok(res) => B(res + .into_body() + .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) + .concat2()), + Err(err) => A(future::err(RpcError::Other(err.into()))), + }; + future.then(|result| { + let response = result + .and_then(|response| { + let response_str = String::from_utf8_lossy(response.as_ref()).into_owned(); + super::parse_response(&response_str) + }) + .and_then(|responses| { + if responses.len() == 1 { + responses.into_iter().nth(0).expect("Exactly one response; qed").1 + } else { + Err(RpcError::Other(format_err!( + "Transport currently only supports Single requests" + ))) + } + }); + + if let Err(err) = msg.sender.send(response) { + log::warn!("Error resuming asynchronous request: {:?}", err); + } + Ok(()) + }) + }); + + B(rt::lazy(move || { + rt::spawn(fut.map_err(|e| log::error!("RPC Client error: {:?}", e))); + Ok(TClient::from(sender.into())) + })) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::*; + use hyper::rt; + use jsonrpc_core::{Error, ErrorCode, IoHandler, Params, Value}; + use jsonrpc_http_server::*; + use std::net::SocketAddr; + use std::time::Duration; + use assert_matches::assert_matches; + + fn id(t: T) -> T { + t + } + + struct TestServer { + uri: String, + socket_addr: SocketAddr, + server: Option, + } + + impl TestServer { + fn serve ServerBuilder>(alter: F) -> Self { + let builder = ServerBuilder::new(io()).rest_api(RestApi::Unsecure); + + let server = alter(builder).start_http(&"127.0.0.1:0".parse().unwrap()).unwrap(); + let socket_addr = server.address().clone(); + let uri = format!("http://{}", socket_addr); + + TestServer { + uri, + socket_addr, + server: Some(server), + } + } + + fn start(&mut self) { + if self.server.is_none() { + let server = ServerBuilder::new(io()) + .rest_api(RestApi::Unsecure) + .start_http(&self.socket_addr) + .unwrap(); + self.server = Some(server); + } else { + panic!("Server already running") + } + } + + fn stop(&mut self) { + let server = self.server.take(); + if let Some(server) = server { + server.close(); + } + } + } + + fn io() -> IoHandler { + let mut io = IoHandler::default(); + io.add_method("hello", |params: Params| match params.parse::<(String,)>() { + Ok((msg,)) => Ok(Value::String(format!("hello {}", msg))), + _ => Ok(Value::String("world".into())), + }); + io.add_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); + + io + } + + #[derive(Clone)] + struct TestClient(TypedClient); + + impl From for TestClient { + fn from(channel: RpcChannel) -> Self { + TestClient(channel.into()) + } + } + + impl TestClient { + fn hello(&self, msg: &'static str) -> impl Future { + self.0.call_method("hello", "String", (msg,)) + } + fn fail(&self) -> impl Future { + self.0.call_method("fail", "()", ()) + } + } + + #[test] + fn should_work() { + crate::logger::init_log(); + + // given + let server = TestServer::serve(id); + let (tx, rx) = std::sync::mpsc::channel(); + + // when + let run = http(&server.uri) + .and_then(|client: TestClient| { + client.hello("http").and_then(move |result| { + drop(client); + let _ = tx.send(result); + Ok(()) + }) + }) + .map_err(|e| log::error!("RPC Client error: {:?}", e)); + + rt::run(run); + + // then + let result = rx.recv_timeout(Duration::from_secs(3)).unwrap(); + assert_eq!("hello http", result); + } + + #[test] + fn handles_invalid_uri() { + crate::logger::init_log(); + + // given + let invalid_uri = "invalid uri"; + + // when + let run = http(invalid_uri); // rx.recv_timeout(Duration::from_secs(3)).unwrap(); + let res: Result = run.wait(); + + // then + assert_matches!( + res.map(|_cli| unreachable!()), Err(RpcError::Other(err)) => { + assert_eq!("InvalidUri(InvalidUriChar)", format!("{:?}", err)); + } + ); + } + + #[test] + fn handles_server_error() { + crate::logger::init_log(); + + // given + let server = TestServer::serve(id); + let (tx, rx) = std::sync::mpsc::channel(); + + // when + let run = http(&server.uri) + .and_then(|client: TestClient| { + client.fail().then(move |res| { + let _ = tx.send(res); + Ok(()) + }) + }) + .map_err(|e| log::error!("RPC Client error: {:?}", e)); + rt::run(run); + + // then + let res = rx.recv_timeout(Duration::from_secs(3)).unwrap(); + + if let Err(RpcError::JsonRpcError(err)) = res { + assert_eq!( + err, + Error { + code: ErrorCode::ServerError(-34), + message: "Server error".into(), + data: None + } + ) + } else { + panic!("Expected JsonRpcError. Received {:?}", res) + } + } + + #[test] + fn handles_connection_refused_error() { + // given + let mut server = TestServer::serve(id); + // stop server so that we get a connection refused + server.stop(); + let (tx, rx) = std::sync::mpsc::channel(); + + let client = http(&server.uri); + + let call = client + .and_then(|client: TestClient| { + client.hello("http").then(move |res| { + let _ = tx.send(res); + Ok(()) + }) + }) + .map_err(|e| log::error!("RPC Client error: {:?}", e)); + + rt::run(call); + + // then + let res = rx.recv_timeout(Duration::from_secs(3)).unwrap(); + + if let Err(RpcError::Other(err)) = res { + if let Some(err) = err.downcast_ref::() { + assert!(err.is_connect(), format!("Expected Connection Error, got {:?}", err)) + } else { + panic!("Expected a hyper::Error") + } + } else { + panic!("Expected JsonRpcError. Received {:?}", res) + } + } + + #[test] + #[ignore] // todo: [AJ] make it pass + fn client_still_works_after_http_connect_error() { + // given + let mut server = TestServer::serve(id); + + // stop server so that we get a connection refused + server.stop(); + + let (tx, rx) = std::sync::mpsc::channel(); + let tx2 = tx.clone(); + + let client = http(&server.uri); + + let call = client + .and_then(move |client: TestClient| { + client + .hello("http") + .then(move |res| { + let _ = tx.send(res); + Ok(()) + }) + .and_then(move |_| { + server.start(); // todo: make the server start on the main thread + client.hello("http2").then(move |res| { + let _ = tx2.send(res); + Ok(()) + }) + }) + }) + .map_err(|e| log::error!("RPC Client error: {:?}", e)); + + // when + rt::run(call); + + let res = rx.recv_timeout(Duration::from_secs(3)).unwrap(); + assert!(res.is_err()); + + // then + let result = rx.recv_timeout(Duration::from_secs(3)).unwrap().unwrap(); + assert_eq!("hello http", result); + } +} diff --git a/core-client/src/transports/local.rs b/core-client/src/transports/local.rs new file mode 100644 index 000000000..58dc15a06 --- /dev/null +++ b/core-client/src/transports/local.rs @@ -0,0 +1,77 @@ +//! Rpc client implementation for `Deref>`. + +use crate::{RpcChannel, RpcError}; +use futures::prelude::*; +use jsonrpc_core::{MetaIoHandler, Metadata}; +use std::collections::VecDeque; +use std::ops::Deref; + +/// Implements a rpc client for `MetaIoHandler`. +pub struct LocalRpc { + handler: THandler, + queue: VecDeque, +} + +impl LocalRpc +where + TMetadata: Metadata + Default, + THandler: Deref>, +{ + /// Creates a new `LocalRpc`. + pub fn new(handler: THandler) -> Self { + Self { + handler, + queue: VecDeque::new(), + } + } +} + +impl Stream for LocalRpc +where + TMetadata: Metadata + Default, + THandler: Deref>, +{ + type Item = String; + type Error = RpcError; + + fn poll(&mut self) -> Result>, Self::Error> { + match self.queue.pop_front() { + Some(response) => Ok(Async::Ready(Some(response))), + None => Ok(Async::NotReady), + } + } +} + +impl Sink for LocalRpc +where + TMetadata: Metadata + Default, + THandler: Deref>, +{ + type SinkItem = String; + type SinkError = RpcError; + + fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { + match self.handler.handle_request_sync(&request, TMetadata::default()) { + Some(response) => self.queue.push_back(response), + None => {} + }; + Ok(AsyncSink::Ready) + } + + fn poll_complete(&mut self) -> Result, Self::SinkError> { + Ok(Async::Ready(())) + } +} + +/// Connects to a `IoHandler`. +pub fn connect(handler: THandler) -> (TClient, impl Future) +where + TClient: From, + TMetadata: Metadata + Default, + THandler: Deref>, +{ + let (sink, stream) = LocalRpc::new(handler).split(); + let (rpc_client, sender) = crate::transports::duplex(sink, stream); + let client = TClient::from(sender); + (client, rpc_client) +} diff --git a/core-client/src/transports/mod.rs b/core-client/src/transports/mod.rs new file mode 100644 index 000000000..09d1c408d --- /dev/null +++ b/core-client/src/transports/mod.rs @@ -0,0 +1,67 @@ +//! Client transport implementations + +use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Response, Version}; +use serde_json::Value; + +use crate::{RpcError, RpcMessage}; + +pub mod duplex; +pub mod http; +pub mod local; +pub mod ws; + +pub use duplex::duplex; + +/// Creates JSON-RPC requests +pub struct RequestBuilder { + id: u64, +} + +impl RequestBuilder { + /// Create a new RequestBuilder + pub fn new() -> Self { + RequestBuilder { id: 0 } + } + + fn next_id(&mut self) -> Id { + let id = self.id; + self.id = id + 1; + Id::Num(id) + } + + /// Build a single request with the next available id + fn single_request(&mut self, msg: &RpcMessage) -> (Id, String) { + let id = self.next_id(); + let request = jsonrpc_core::Request::Single(Call::MethodCall(MethodCall { + jsonrpc: Some(Version::V2), + method: msg.method.clone(), + params: msg.params.clone(), + id: id.clone(), + })); + ( + id, + serde_json::to_string(&request).expect("Request serialization is infallible; qed"), + ) + } +} + +/// Parse raw string into JSON values, together with the request Id +pub fn parse_response(response: &str) -> Result)>, RpcError> { + serde_json::from_str::(&response) + .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) + .map(|response| { + let outputs: Vec = match response { + Response::Single(output) => vec![output], + Response::Batch(outputs) => outputs, + }; + outputs + .into_iter() + .map(|output| { + let id = output.id().clone(); + let value: Result = output.into(); + let result = value.map_err(RpcError::JsonRpcError); + (id, result) + }) + .collect::>() + }) +} diff --git a/ws/client/src/lib.rs b/core-client/src/transports/ws.rs similarity index 91% rename from ws/client/src/lib.rs rename to core-client/src/transports/ws.rs index efc1960b8..f41119459 100644 --- a/ws/client/src/lib.rs +++ b/core-client/src/transports/ws.rs @@ -1,10 +1,7 @@ //! JSON-RPC websocket client implementation. -#![deny(missing_docs)] +use crate::{RpcChannel, RpcError}; use failure::Error; use futures::prelude::*; -use futures::sync::mpsc; -use jsonrpc_core_client::RpcClient; -pub use jsonrpc_core_client::{RpcChannel, RpcError}; use log::info; use std::collections::VecDeque; use websocket::{ClientBuilder, OwnedMessage}; @@ -21,8 +18,8 @@ where .map(|(client, _)| { let (sink, stream) = client.split(); let (sink, stream) = WebsocketClient::new(sink, stream).split(); - let (sender, receiver) = mpsc::channel(0); - let rpc_client = RpcClient::new(sink, stream, receiver).map_err(|error| eprintln!("{:?}", error)); + let (rpc_client, sender) = super::duplex(sink, stream); + let rpc_client = rpc_client.map_err(|error| eprintln!("{:?}", error)); tokio::spawn(rpc_client); sender.into() }) diff --git a/derive/examples/std.rs b/derive/examples/std.rs index 716b5fd7f..12a820e1a 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -2,7 +2,7 @@ #![deny(missing_docs)] use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; -use jsonrpc_core_client::local; +use jsonrpc_core_client::transports::local; use jsonrpc_derive::rpc; /// Rpc trait diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 644f57657..3e562d883 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -127,7 +127,7 @@ //! Client Example //! //! ``` -//! use jsonrpc_core_client::local; +//! use jsonrpc_core_client::transports::local; //! use jsonrpc_core::futures::future::{self, Future, FutureResult}; //! use jsonrpc_core::{Error, IoHandler, Result}; //! use jsonrpc_derive::rpc; diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index 471822cd3..cf999f056 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -49,7 +49,7 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: use _jsonrpc_core::futures::{future, Future, Sink}; use _jsonrpc_core::futures::sync::oneshot; use _jsonrpc_core::serde_json::{self, Value}; - use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, RpcMessage, TypedClient}; + use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, TypedClient}; /// The Client. #[derive(Clone)] diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 3f1d71554..6ac6fb301 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -1,6 +1,6 @@ use futures::prelude::*; use jsonrpc_core::{IoHandler, Result}; -use jsonrpc_core_client::local; +use jsonrpc_core_client::transports::local; use jsonrpc_derive::rpc; #[rpc] diff --git a/derive/tests/run-pass/client_only.rs b/derive/tests/run-pass/client_only.rs index 4ad3c4f2e..bbec014fb 100644 --- a/derive/tests/run-pass/client_only.rs +++ b/derive/tests/run-pass/client_only.rs @@ -4,8 +4,10 @@ extern crate jsonrpc_core_client; #[macro_use] extern crate jsonrpc_derive; +use jsonrpc_core::IoHandler; use jsonrpc_core::futures::future::Future; use jsonrpc_core::futures::sync::mpsc; +use jsonrpc_core_client::transports::local; #[rpc(client)] pub trait Rpc { @@ -20,8 +22,9 @@ pub trait Rpc { fn main() { let fut = { - let (sender, _) = mpsc::channel(0); - gen_client::Client::new(sender) + let mut handler = IoHandler::new(); + let (client, rpc_client) = local::connect::(handler); + client .add(5, 6) .map(|res| println!("5 + 6 = {}", res)) }; diff --git a/ws/client/Cargo.toml b/ws/client/Cargo.toml deleted file mode 100644 index 737f47446..000000000 --- a/ws/client/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -authors = ["Parity Technologies "] -description = "JSON-RPC 2.0 websocket client implementation." -documentation = "https://docs.rs/jsonrpc-ws-client/" -edition = "2018" -homepage = "https://github.com/paritytech/jsonrpc" -keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] -license = "MIT" -name = "jsonrpc-ws-client" -repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" - -categories = [ - "asynchronous", - "network-programming", - "web-programming::http-client", - "web-programming::http-server", - "web-programming::websocket", -] - -[dependencies] -failure = "0.1" -futures = "0.1" -jsonrpc-core = { version = "11.0", path = "../../core" } -jsonrpc-core-client = { version = "11.0", path = "../../core-client" } -log = "0.4" -tokio = "0.1" -websocket = "0.22" - -[badges] -travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} From ff4e20ecce6321835aab7a81cbb6fa2458a6082d Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 23 May 2019 11:54:08 +0200 Subject: [PATCH 050/149] Optional TLS support for HTTP transport (#433) (#435) * Optional TLS support for HTTP transport (#433) * Reorganize CI build matrix with exclusion rules This allows adding other dimensions to the matrix without having to explicitly specify all combinations in the inclusion list. * Add jsonrpc-core-client features to the build matrix * Fix multiple publishing of documentation in CI The after_success step will get executed for each successful row in the build matrix, and the condition in the script would match for several rows after the addition of the tls feature in the previous commit. --- .travis.yml | 27 ++++++++++++++++++--------- core-client/Cargo.toml | 4 ++++ core-client/src/transports/http.rs | 13 +++++++++++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index e7274e7d0..3802e0424 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,17 +7,22 @@ branches: cache: cargo +os: + - linux + - osx + +rust: + - stable + - beta + - nightly + matrix: fast_finish: false - include: - - os: linux - rust: stable - - os: linux + exclude: + - os: osx rust: beta - - os: linux - rust: nightly - os: osx - rust: stable + rust: nightly allow_failures: - rust: nightly @@ -25,8 +30,8 @@ before_script: - rustup component add rustfmt script: - - cargo build --all - - cargo test --all + - cargo build --all --features "$JSONRPC_CLIENT_FEATURES" + - cargo test --all --features "$JSONRPC_CLIENT_FEATURES" - | ([ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true @@ -35,6 +40,7 @@ after_success: | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && [ $TRAVIS_RUST_VERSION = stable ] && + [ $JSONRPC_CLIENT_FEATURES = '' ] && cargo doc --all --no-deps && echo '' > target/doc/index.html && pip install --user ghp-import && @@ -44,3 +50,6 @@ after_success: | env: global: - secure: "QA4Rw78VSsP1vH2Yve1eAVpjG32HH9DZZ79xrhxZXp34wKoemp+aGqaFN/8uXPfsXshlYxtMCTT6M9OiWTTLvku5tI5kBsDneu8mLut7eBZHVniYSp2SbKpTeqfpGMDHoCR0WD9AlWDn9Elm6txbghXjrxhCMg8gkhhsLGnQt/ARFF1wRHnXT0TjJg8fQtd+/OK0TaRfknx1RptruaznxfUi3DBwzDdzaMMZfd3VjWR1hPFRpDSL0mM+l6OjNrLbCeiR//k3lV4rpIhedsz0ODjfW2Hdk63qCaLJsXCkG1Bcuf/FYbYC+osm5SrHhGA1j2EgazWcLA6Wkzt15KPOR/HirNj+PCiS0YbGKM5Ac5LT6m6q0iYSF/pq1+jDurcSwBwYrTOY6X2FZCZQBfTP/4qnSjWgGPOkzBSMS6BNEBDQZgdc3xCASXadj7waF4Y4UGD0bDPuBtXopI4ppKLqSa7CsvKz6TX2yW0UVgUuQ5/jz/S+fkcz74o016d5x027yjaxAu/Z8fQFLSaBtiFU8sBzA+MDU3apFgjsYXiaGYZ8gDrp7WjbfHNYfBAMEHHKY4toywB5Vi8zJxF+Wn1n4hkvb/kDqSV9giFmWEg321U+pAGNAH4yY25tIJqS8gT89cz4oQJp7aWjA3Ke01e104yqqZU+N+CSyZHEeksdPt8=" + matrix: + - JSONRPC_CLIENT_FEATURES='' + - JSONRPC_CLIENT_FEATURES='tls' diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index d7928ba05..764b01153 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -18,10 +18,14 @@ categories = [ "web-programming::websocket", ] +[features] +tls = ["hyper-tls"] + [dependencies] failure = "0.1" futures = "0.1.26" hyper = "0.12" +hyper-tls = { version = "0.3.2", optional = true } jsonrpc-core = { version = "11.0", path = "../core" } log = "0.4" serde = "1.0" diff --git a/core-client/src/transports/http.rs b/core-client/src/transports/http.rs index 5d0c991c3..464a8bf23 100644 --- a/core-client/src/transports/http.rs +++ b/core-client/src/transports/http.rs @@ -1,4 +1,6 @@ //! HTTP client +//! +//! HTTPS support is enabled with the `tls` feature. use super::RequestBuilder; use crate::{RpcChannel, RpcError, RpcMessage}; @@ -23,7 +25,18 @@ where Ok(url) => url, Err(e) => return A(future::err(RpcError::Other(e.into()))) }; + + #[cfg(feature = "tls")] + let connector = match hyper_tls::HttpsConnector::new(4) { + Ok(connector) => connector, + Err(e) => return A(future::err(RpcError::Other(e.into()))) + }; + #[cfg(feature = "tls")] + let client = Client::builder().build::<_, hyper::Body>(connector); + + #[cfg(not(feature = "tls"))] let client = Client::new(); + let mut request_builder = RequestBuilder::new(); let (sender, receiver) = mpsc::channel(max_parallel); From 8c0ada4ff57fb392f2011a1aa5b8f9f2a9eb962b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 30 May 2019 09:33:25 +0100 Subject: [PATCH 051/149] Add client transport features (#439) * Add http feature * Add ws feature * Check core-client features in CI * Remove client features matrix, run tests directly Reduces number of builds --- .travis.yml | 12 ++++++------ core-client/Cargo.toml | 12 +++++++++--- core-client/src/transports/mod.rs | 2 ++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3802e0424..7a8b94ec9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,8 +30,11 @@ before_script: - rustup component add rustfmt script: - - cargo build --all --features "$JSONRPC_CLIENT_FEATURES" - - cargo test --all --features "$JSONRPC_CLIENT_FEATURES" + - cargo build --all + - cargo test --all + - cargo test --manifest-path=core-client/Cargo.toml --features "http" + - cargo test --manifest-path=core-client/Cargo.toml --features "http, tls" + - cargo test --manifest-path=core-client/Cargo.toml --features "ws" - | ([ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true @@ -40,7 +43,6 @@ after_success: | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && [ $TRAVIS_RUST_VERSION = stable ] && - [ $JSONRPC_CLIENT_FEATURES = '' ] && cargo doc --all --no-deps && echo '' > target/doc/index.html && pip install --user ghp-import && @@ -50,6 +52,4 @@ after_success: | env: global: - secure: "QA4Rw78VSsP1vH2Yve1eAVpjG32HH9DZZ79xrhxZXp34wKoemp+aGqaFN/8uXPfsXshlYxtMCTT6M9OiWTTLvku5tI5kBsDneu8mLut7eBZHVniYSp2SbKpTeqfpGMDHoCR0WD9AlWDn9Elm6txbghXjrxhCMg8gkhhsLGnQt/ARFF1wRHnXT0TjJg8fQtd+/OK0TaRfknx1RptruaznxfUi3DBwzDdzaMMZfd3VjWR1hPFRpDSL0mM+l6OjNrLbCeiR//k3lV4rpIhedsz0ODjfW2Hdk63qCaLJsXCkG1Bcuf/FYbYC+osm5SrHhGA1j2EgazWcLA6Wkzt15KPOR/HirNj+PCiS0YbGKM5Ac5LT6m6q0iYSF/pq1+jDurcSwBwYrTOY6X2FZCZQBfTP/4qnSjWgGPOkzBSMS6BNEBDQZgdc3xCASXadj7waF4Y4UGD0bDPuBtXopI4ppKLqSa7CsvKz6TX2yW0UVgUuQ5/jz/S+fkcz74o016d5x027yjaxAu/Z8fQFLSaBtiFU8sBzA+MDU3apFgjsYXiaGYZ8gDrp7WjbfHNYfBAMEHHKY4toywB5Vi8zJxF+Wn1n4hkvb/kDqSV9giFmWEg321U+pAGNAH4yY25tIJqS8gT89cz4oQJp7aWjA3Ke01e104yqqZU+N+CSyZHEeksdPt8=" - matrix: - - JSONRPC_CLIENT_FEATURES='' - - JSONRPC_CLIENT_FEATURES='tls' + diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 764b01153..e4a2e3d5f 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -20,24 +20,30 @@ categories = [ [features] tls = ["hyper-tls"] +http = ["hyper"] +ws = [ + "websocket", + "tokio", +] [dependencies] failure = "0.1" futures = "0.1.26" -hyper = "0.12" +hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } jsonrpc-core = { version = "11.0", path = "../core" } log = "0.4" serde = "1.0" serde_json = "1.0" -tokio = "0.1" -websocket = "0.22" +tokio = { version = "0.1", optional = true } +websocket = { version = "0.22", optional = true } [dev-dependencies] assert_matches = "1.1" jsonrpc-http-server = { version = "11.0", path = "../http" } lazy_static = "1.0" env_logger = "0.6" +tokio = "0.1" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/src/transports/mod.rs b/core-client/src/transports/mod.rs index 09d1c408d..943caf2ee 100644 --- a/core-client/src/transports/mod.rs +++ b/core-client/src/transports/mod.rs @@ -6,8 +6,10 @@ use serde_json::Value; use crate::{RpcError, RpcMessage}; pub mod duplex; +#[cfg(feature = "http")] pub mod http; pub mod local; +#[cfg(feature = "ws")] pub mod ws; pub use duplex::duplex; From 1bbd4036a5a95e6608b1cd5c864bbf98011581a6 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 3 Jun 2019 11:12:57 +0200 Subject: [PATCH 052/149] Add a way to close the http server while waiting (#437) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix a few compiler warnings * WIP * cleanup * Cleanup and docs * Fix whitespace and grammar * Update http/src/lib.rs Co-Authored-By: Tomasz Drwięga * Now that wait() does not use the eventloop/signalling chans, just bundle them up in a tuple --- http/Cargo.toml | 2 +- http/src/lib.rs | 92 +++++++++++++++++++++++++++++++---------------- http/src/tests.rs | 23 ++++++++++-- ipc/src/logger.rs | 2 +- tcp/src/logger.rs | 2 +- ws/src/tests.rs | 1 - 6 files changed, 84 insertions(+), 38 deletions(-) diff --git a/http/Cargo.toml b/http/Cargo.toml index 6b18ddaa2..1cefc7b31 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -17,6 +17,6 @@ jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" - +parking_lot = "0.8.0" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/http/src/lib.rs b/http/src/lib.rs index 0848b6f77..00545d755 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -40,6 +40,8 @@ use std::net::SocketAddr; use std::sync::{mpsc, Arc}; use std::thread; +use parking_lot::Mutex; + use crate::jsonrpc::futures::sync::oneshot; use crate::jsonrpc::futures::{self, future, Future, Stream}; use crate::jsonrpc::MetaIoHandler; @@ -377,10 +379,12 @@ impl> ServerBuilder { let (local_addr_tx, local_addr_rx) = mpsc::channel(); let (close, shutdown_signal) = oneshot::channel(); + let (done_tx, done_rx) = oneshot::channel(); let eloop = self.executor.init_with_name("http.worker0")?; let req_max_size = self.max_request_body_size; + // The first threads `Executor` is initialised differently from the others serve( - (shutdown_signal, local_addr_tx), + (shutdown_signal, local_addr_tx, done_tx), eloop.executor(), addr.to_owned(), cors_domains.clone(), @@ -399,9 +403,10 @@ impl> ServerBuilder { .map(|i| { let (local_addr_tx, local_addr_rx) = mpsc::channel(); let (close, shutdown_signal) = oneshot::channel(); + let (done_tx, done_rx) = oneshot::channel(); let eloop = UninitializedExecutor::Unspawned.init_with_name(format!("http.worker{}", i + 1))?; serve( - (shutdown_signal, local_addr_tx), + (shutdown_signal, local_addr_tx, done_tx), eloop.executor(), addr.to_owned(), cors_domains.clone(), @@ -416,27 +421,34 @@ impl> ServerBuilder { reuse_port, req_max_size, ); - Ok((eloop, close, local_addr_rx)) + Ok((eloop, close, local_addr_rx, done_rx)) }) .collect::>>()?; // Wait for server initialization let local_addr = recv_address(local_addr_rx); // Wait for other threads as well. - let mut handles = handles + let mut handles: Vec<(Executor, oneshot::Sender<()>, oneshot::Receiver<()>)> = handles .into_iter() - .map(|(eloop, close, local_addr_rx)| { + .map(|(eloop, close, local_addr_rx, done_rx)| { let _ = recv_address(local_addr_rx)?; - Ok((eloop, close)) + Ok((eloop, close, done_rx)) }) .collect::)>>()?; - handles.push((eloop, close)); - let (executors, close) = handles.into_iter().unzip(); + handles.push((eloop, close, done_rx)); + + let (executors, done_rxs) = handles + .into_iter() + .fold((vec![], vec![]), |mut acc, (eloop, closer, done_rx)| { + acc.0.push((eloop, closer)); + acc.1.push(done_rx); + acc + }); Ok(Server { address: local_addr?, - executor: Some(executors), - close: Some(close), + executors: Arc::new(Mutex::new(Some(executors))), + done: Some(done_rxs), }) } } @@ -448,7 +460,7 @@ fn recv_address(local_addr_rx: mpsc::Receiver>) -> io::Re } fn serve>( - signals: (oneshot::Receiver<()>, mpsc::Sender>), + signals: (oneshot::Receiver<()>, mpsc::Sender>, oneshot::Sender<()>), executor: tokio::runtime::TaskExecutor, addr: SocketAddr, cors_domains: CorsDomains, @@ -463,7 +475,7 @@ fn serve>( reuse_port: bool, max_request_body_size: usize, ) { - let (shutdown_signal, local_addr_tx) = signals; + let (shutdown_signal, local_addr_tx, done_tx) = signals; executor.spawn(future::lazy(move || { let handle = tokio::reactor::Handle::default(); @@ -537,12 +549,15 @@ fn serve>( .map_err(|e| { warn!("Incoming streams error, closing sever: {:?}", e); }) - .select(shutdown_signal.map_err(|e| { + .select(shutdown_signal + .map_err(|e| { debug!("Shutdown signaller dropped, closing server: {:?}", e); })) .map(|_| ()) .map_err(|_| ()) }) + }).and_then(|_| { + done_tx.send(()) })); } @@ -562,14 +577,31 @@ fn configure_port(_reuse: bool, _tcp: &net2::TcpBuilder) -> io::Result<()> { Ok(()) } +/// Handle used to close the server. Can be cloned and passed around to different threads and be used +/// to close a server that is `wait()`ing. + +#[derive(Clone)] +pub struct CloseHandle(Arc)>>>>); + +impl CloseHandle { + /// Shutdown a running server + pub fn close(self) { + if let Some(executors) = self.0.lock().take() { + for (executor, closer) in executors { + executor.close(); + let _ = closer.send(()); + } + } + } +} + /// jsonrpc http server instance pub struct Server { address: SocketAddr, - executor: Option>, - close: Option>>, + executors: Arc)>>>>, + done: Option>>, } -const PROOF: &str = "Server is always Some until self is consumed."; impl Server { /// Returns address of this server pub fn address(&self) -> &SocketAddr { @@ -577,30 +609,28 @@ impl Server { } /// Closes the server. - pub fn close(mut self) { - for close in self.close.take().expect(PROOF) { - let _ = close.send(()); - } - - for executor in self.executor.take().expect(PROOF) { - executor.close(); - } + pub fn close(self) { + self.close_handle().close() } /// Will block, waiting for the server to finish. pub fn wait(mut self) { - for executor in self.executor.take().expect(PROOF) { - executor.wait(); + if let Some(receivers) = self.done.take() { + for receiver in receivers { + let _ = receiver.wait(); + } } } + + /// Get a handle that allows us to close the server from a different thread and/or while the + /// server is `wait()`ing. + pub fn close_handle(&self) -> CloseHandle { + CloseHandle(self.executors.clone()) + } } impl Drop for Server { fn drop(&mut self) { - if let Some(executors) = self.executor.take() { - for executor in executors { - executor.close(); - } - }; + self.close_handle().close(); } } diff --git a/http/src/tests.rs b/http/src/tests.rs index 28cd772ee..1730be94d 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -4,6 +4,7 @@ use self::jsonrpc_core::{Error, ErrorCode, IoHandler, Params, Value}; use std::io::{Read, Write}; use std::net::TcpStream; use std::str::Lines; +use std::time::Duration; use self::jsonrpc_core::futures::{self, Future}; use super::*; @@ -52,8 +53,6 @@ fn serve_allow_headers(cors_allow_headers: cors::AccessControlAllowHeaders) -> S } fn io() -> IoHandler { - use std::{thread, time}; - let mut io = IoHandler::default(); io.add_method("hello", |params: Params| match params.parse::<(u64,)>() { Ok((num,)) => Ok(Value::String(format!("world: {}", num))), @@ -66,7 +65,7 @@ fn io() -> IoHandler { io.add_method("hello_async2", |_params: Params| { let (c, p) = futures::oneshot(); thread::spawn(move || { - thread::sleep(time::Duration::from_millis(10)); + thread::sleep(Duration::from_millis(10)); c.send(Value::String("world".into())).unwrap(); }); p.map_err(|_| Error::invalid_request()) @@ -1406,6 +1405,24 @@ fn should_return_connection_header() { assert_eq!(response.body, world_batch()); } +#[test] +fn close_handle_makes_wait_return() { + let server = serve(id); + let close_handle = server.close_handle(); + + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + tx.send(server.wait()).unwrap(); + }); + + thread::sleep(Duration::from_secs(3)); + + close_handle.close(); + + rx.recv_timeout(Duration::from_secs(10)).expect("Expected server to close"); +} + #[test] fn should_close_connection_without_keep_alive() { // given diff --git a/ipc/src/logger.rs b/ipc/src/logger.rs index c599dc34b..9b885a72d 100644 --- a/ipc/src/logger.rs +++ b/ipc/src/logger.rs @@ -10,7 +10,7 @@ lazy_static! { builder.filter(None, LevelFilter::Info); if let Ok(log) = env::var("RUST_LOG") { - builder.parse(&log); + builder.parse_filters(&log); } if let Ok(_) = builder.try_init() { diff --git a/tcp/src/logger.rs b/tcp/src/logger.rs index 8502fe870..6edd87759 100644 --- a/tcp/src/logger.rs +++ b/tcp/src/logger.rs @@ -8,7 +8,7 @@ lazy_static! { builder.filter(None, LevelFilter::Info); if let Ok(log) = env::var("RUST_LOG") { - builder.parse(&log); + builder.parse_filters(&log); } if let Ok(_) = builder.try_init() { diff --git a/ws/src/tests.rs b/ws/src/tests.rs index 1f4014637..d46e978f7 100644 --- a/ws/src/tests.rs +++ b/ws/src/tests.rs @@ -62,7 +62,6 @@ fn request(server: Server, request: &str) -> Response { fn serve(port: u16) -> (Server, Arc) { use crate::core::futures::sync::oneshot; - use std::time::Duration; let pending = Arc::new(AtomicUsize::new(0)); From 12b6210a11e449364d05b85c79187cb583acc5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 4 Jun 2019 11:55:39 +0200 Subject: [PATCH 053/149] Bump to 12.0 and change core-client transports a bit. (#440) * Rename http client method, tls enables http. * Bump version. * Add tagging to publish script. * Make sure we generate nice docs for transports as well. * Add transports to publish script. --- .travis.yml | 3 - Cargo.toml | 1 + _automate/publish.sh | 11 +- core-client/Cargo.toml | 29 +-- core-client/src/lib.rs | 225 +----------------- core-client/transports/Cargo.toml | 50 ++++ core-client/transports/src/lib.rs | 218 +++++++++++++++++ core-client/{ => transports}/src/logger.rs | 0 .../{ => transports}/src/transports/duplex.rs | 0 .../{ => transports}/src/transports/http.rs | 12 +- .../{ => transports}/src/transports/local.rs | 0 .../{ => transports}/src/transports/mod.rs | 0 .../{ => transports}/src/transports/ws.rs | 0 core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +- http/Cargo.toml | 6 +- http/README.md | 2 +- ipc/Cargo.toml | 6 +- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +- pubsub/more-examples/Cargo.toml | 10 +- server-utils/Cargo.toml | 4 +- stdio/Cargo.toml | 4 +- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +- tcp/README.md | 2 +- test/Cargo.toml | 10 +- ws/Cargo.toml | 6 +- ws/README.md | 2 +- 29 files changed, 338 insertions(+), 291 deletions(-) create mode 100644 core-client/transports/Cargo.toml create mode 100644 core-client/transports/src/lib.rs rename core-client/{ => transports}/src/logger.rs (100%) rename core-client/{ => transports}/src/transports/duplex.rs (100%) rename core-client/{ => transports}/src/transports/http.rs (96%) rename core-client/{ => transports}/src/transports/local.rs (100%) rename core-client/{ => transports}/src/transports/mod.rs (100%) rename core-client/{ => transports}/src/transports/ws.rs (100%) diff --git a/.travis.yml b/.travis.yml index 7a8b94ec9..d0848d222 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,9 +32,6 @@ before_script: script: - cargo build --all - cargo test --all - - cargo test --manifest-path=core-client/Cargo.toml --features "http" - - cargo test --manifest-path=core-client/Cargo.toml --features "http, tls" - - cargo test --manifest-path=core-client/Cargo.toml --features "ws" - | ([ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true diff --git a/Cargo.toml b/Cargo.toml index 9509ac35d..b509827d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "core", "core-client", + "core-client/transports", "http", "ipc", "derive", diff --git a/_automate/publish.sh b/_automate/publish.sh index ca6b15ae4..9b0dbfe9e 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -2,11 +2,20 @@ set -exu -ORDER=(core core-client server-utils tcp ws ws/client http ipc stdio pubsub derive test) +VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') +ORDER=(core core-client/transports core-client server-utils tcp ws ws/client http ipc stdio pubsub derive test) + +echo "Publishing version $VERSION" +cargo clean for crate in ${ORDER[@]}; do cd $crate + echo "Publishing $crate@$VERSION" + sleep 5 cargo publish $@ cd - done +echo "Tagging version $VERSION" +git tag -a v$VERSION -m "Version $VERSION" +git push --tags diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index e4a2e3d5f..82fb8247a 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" categories = [ "asynchronous", @@ -19,31 +19,12 @@ categories = [ ] [features] -tls = ["hyper-tls"] -http = ["hyper"] -ws = [ - "websocket", - "tokio", -] +tls = ["jsonrpc-client-transports/tls"] +http = ["jsonrpc-client-transports/http"] +ws = ["jsonrpc-client-transports/ws"] [dependencies] -failure = "0.1" -futures = "0.1.26" -hyper = { version = "0.12", optional = true } -hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "11.0", path = "../core" } -log = "0.4" -serde = "1.0" -serde_json = "1.0" -tokio = { version = "0.1", optional = true } -websocket = { version = "0.22", optional = true } - -[dev-dependencies] -assert_matches = "1.1" -jsonrpc-http-server = { version = "11.0", path = "../http" } -lazy_static = "1.0" -env_logger = "0.6" -tokio = "0.1" +jsonrpc-client-transports = { version = "12.0", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index 43982e28d..818716ee8 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -1,217 +1,8 @@ -//! JSON-RPC client implementation. -#![deny(missing_docs)] - -use failure::{format_err, Fail}; -use futures::sync::{mpsc, oneshot}; -use futures::{future, prelude::*}; -use jsonrpc_core::{Error, Params}; -use serde::de::DeserializeOwned; -use serde::Serialize; -use serde_json::Value; - -pub mod transports; - -#[cfg(test)] -mod logger; - -/// The errors returned by the client. -#[derive(Debug, Fail)] -pub enum RpcError { - /// An error returned by the server. - #[fail(display = "Server returned rpc error {}", _0)] - JsonRpcError(Error), - /// Failure to parse server response. - #[fail(display = "Failed to parse server response as {}: {}", _0, _1)] - ParseError(String, failure::Error), - /// Request timed out. - #[fail(display = "Request timed out")] - Timeout, - /// The server returned a response with an unknown id. - #[fail(display = "Server returned a response with an unknown id")] - UnknownId, - /// Not rpc specific errors. - #[fail(display = "{}", _0)] - Other(failure::Error), -} - -impl From for RpcError { - fn from(error: Error) -> Self { - RpcError::JsonRpcError(error) - } -} - -/// A message sent to the `RpcClient`. This is public so that -/// the derive crate can generate a client. -struct RpcMessage { - /// The rpc method name. - method: String, - /// The rpc method parameters. - params: Params, - /// The oneshot channel to send the result of the rpc - /// call to. - sender: oneshot::Sender>, -} - -/// A channel to a `RpcClient`. -#[derive(Clone)] -pub struct RpcChannel(mpsc::Sender); - -impl RpcChannel { - fn send( - &self, - msg: RpcMessage, - ) -> impl Future, Error = mpsc::SendError> { - self.0.to_owned().send(msg) - } -} - -impl From> for RpcChannel { - fn from(sender: mpsc::Sender) -> Self { - RpcChannel(sender) - } -} - -/// The future returned by the rpc call. -pub struct RpcFuture { - recv: oneshot::Receiver>, -} - -impl RpcFuture { - /// Creates a new `RpcFuture`. - pub fn new(recv: oneshot::Receiver>) -> Self { - RpcFuture { recv } - } -} - -impl Future for RpcFuture { - type Item = Value; - type Error = RpcError; - - fn poll(&mut self) -> Result, Self::Error> { - // TODO should timeout (#410) - match self.recv.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(error))) => Err(error), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(error) => Err(RpcError::Other(error.into())), - } - } -} - -/// Client for raw JSON RPC requests -#[derive(Clone)] -pub struct RawClient(RpcChannel); - -impl From for RawClient { - fn from(channel: RpcChannel) -> Self { - RawClient(channel) - } -} - -impl RawClient { - /// Call RPC with raw JSON - pub fn call_method(&self, method: &str, params: Params) -> impl Future { - let (sender, receiver) = oneshot::channel(); - let msg = RpcMessage { - method: method.into(), - params, - sender, - }; - self.0 - .send(msg) - .map_err(|error| RpcError::Other(error.into())) - .and_then(|_| RpcFuture::new(receiver)) - } -} - -/// Client for typed JSON RPC requests -#[derive(Clone)] -pub struct TypedClient(RawClient); - -impl From for TypedClient { - fn from(channel: RpcChannel) -> Self { - TypedClient(channel.into()) - } -} - -impl TypedClient { - /// Create new TypedClient - pub fn new(raw_cli: RawClient) -> Self { - TypedClient(raw_cli) - } - - /// Call RPC with serialization of request and deserialization of response - pub fn call_method( - &self, - method: &str, - returns: &'static str, - args: T, - ) -> impl Future { - let args = - serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC"); - let params = match args { - Value::Array(vec) => Params::Array(vec), - Value::Null => Params::None, - _ => { - return future::Either::A(future::err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, or null" - )))) - } - }; - - future::Either::B(self.0.call_method(method, params).and_then(move |value: Value| { - log::debug!("response: {:?}", value); - let result = - serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns.into(), error.into())); - future::done(result) - })) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::transports::local; - use crate::{RpcChannel, RpcError, TypedClient}; - use jsonrpc_core::{self, IoHandler}; - - #[derive(Clone)] - struct AddClient(TypedClient); - - impl From for AddClient { - fn from(channel: RpcChannel) -> Self { - AddClient(channel.into()) - } - } - - impl AddClient { - fn add(&self, a: u64, b: u64) -> impl Future { - self.0.call_method("add", "u64", (a, b)) - } - } - - #[test] - fn test_client_terminates() { - let mut handler = IoHandler::new(); - handler.add_method("add", |params: Params| { - let (a, b) = params.parse::<(u64, u64)>()?; - let res = a + b; - Ok(jsonrpc_core::to_value(res).unwrap()) - }); - - let (client, rpc_client) = local::connect::(handler); - let fut = client - .clone() - .add(3, 4) - .and_then(move |res| client.add(res, 5)) - .join(rpc_client) - .map(|(res, ())| { - assert_eq!(res, 12); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); - }); - tokio::run(fut); - } -} +//! JSON-RPC client implementation primitives. +//! +//! By default this crate does not implement any transports, +//! use corresponding features (`tls`, `http` or `ws`) to opt-in for them. +//! +//! See documentation of [`jsonrpc-client-transports`](../jsonrpc_client_transports/) for more details. + +pub use jsonrpc_client_transports::*; diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml new file mode 100644 index 000000000..ea60c1e7e --- /dev/null +++ b/core-client/transports/Cargo.toml @@ -0,0 +1,50 @@ +[package] +authors = ["Parity Technologies "] +description = "Transport agnostic JSON-RPC 2.0 client implementation." +documentation = "https://docs.rs/jsonrpc-client-transports/" +edition = "2018" +homepage = "https://github.com/paritytech/jsonrpc" +keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] +license = "MIT" +name = "jsonrpc-client-transports" +repository = "https://github.com/paritytech/jsonrpc" +version = "12.0.0" + +categories = [ + "asynchronous", + "network-programming", + "web-programming::http-client", + "web-programming::http-server", + "web-programming::websocket", +] + +[features] +default = ["http", "tls", "ws"] +tls = ["hyper-tls", "http"] +http = ["hyper"] +ws = [ + "websocket", + "tokio", +] + +[dependencies] +failure = "0.1" +futures = "0.1.26" +hyper = { version = "0.12", optional = true } +hyper-tls = { version = "0.3.2", optional = true } +jsonrpc-core = { version = "12.0", path = "../../core" } +log = "0.4" +serde = "1.0" +serde_json = "1.0" +tokio = { version = "0.1", optional = true } +websocket = { version = "0.22", optional = true } + +[dev-dependencies] +assert_matches = "1.1" +jsonrpc-http-server = { version = "12.0", path = "../../http" } +lazy_static = "1.0" +env_logger = "0.6" +tokio = "0.1" + +[badges] +travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs new file mode 100644 index 000000000..ba7dab823 --- /dev/null +++ b/core-client/transports/src/lib.rs @@ -0,0 +1,218 @@ +//! JSON-RPC client implementation. + +#![deny(missing_docs)] + +use failure::{format_err, Fail}; +use futures::sync::{mpsc, oneshot}; +use futures::{future, prelude::*}; +use jsonrpc_core::{Error, Params}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::Value; + +pub mod transports; + +#[cfg(test)] +mod logger; + +/// The errors returned by the client. +#[derive(Debug, Fail)] +pub enum RpcError { + /// An error returned by the server. + #[fail(display = "Server returned rpc error {}", _0)] + JsonRpcError(Error), + /// Failure to parse server response. + #[fail(display = "Failed to parse server response as {}: {}", _0, _1)] + ParseError(String, failure::Error), + /// Request timed out. + #[fail(display = "Request timed out")] + Timeout, + /// The server returned a response with an unknown id. + #[fail(display = "Server returned a response with an unknown id")] + UnknownId, + /// Not rpc specific errors. + #[fail(display = "{}", _0)] + Other(failure::Error), +} + +impl From for RpcError { + fn from(error: Error) -> Self { + RpcError::JsonRpcError(error) + } +} + +/// A message sent to the `RpcClient`. This is public so that +/// the derive crate can generate a client. +struct RpcMessage { + /// The rpc method name. + method: String, + /// The rpc method parameters. + params: Params, + /// The oneshot channel to send the result of the rpc + /// call to. + sender: oneshot::Sender>, +} + +/// A channel to a `RpcClient`. +#[derive(Clone)] +pub struct RpcChannel(mpsc::Sender); + +impl RpcChannel { + fn send( + &self, + msg: RpcMessage, + ) -> impl Future, Error = mpsc::SendError> { + self.0.to_owned().send(msg) + } +} + +impl From> for RpcChannel { + fn from(sender: mpsc::Sender) -> Self { + RpcChannel(sender) + } +} + +/// The future returned by the rpc call. +pub struct RpcFuture { + recv: oneshot::Receiver>, +} + +impl RpcFuture { + /// Creates a new `RpcFuture`. + pub fn new(recv: oneshot::Receiver>) -> Self { + RpcFuture { recv } + } +} + +impl Future for RpcFuture { + type Item = Value; + type Error = RpcError; + + fn poll(&mut self) -> Result, Self::Error> { + // TODO should timeout (#410) + match self.recv.poll() { + Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), + Ok(Async::Ready(Err(error))) => Err(error), + Ok(Async::NotReady) => Ok(Async::NotReady), + Err(error) => Err(RpcError::Other(error.into())), + } + } +} + +/// Client for raw JSON RPC requests +#[derive(Clone)] +pub struct RawClient(RpcChannel); + +impl From for RawClient { + fn from(channel: RpcChannel) -> Self { + RawClient(channel) + } +} + +impl RawClient { + /// Call RPC with raw JSON + pub fn call_method(&self, method: &str, params: Params) -> impl Future { + let (sender, receiver) = oneshot::channel(); + let msg = RpcMessage { + method: method.into(), + params, + sender, + }; + self.0 + .send(msg) + .map_err(|error| RpcError::Other(error.into())) + .and_then(|_| RpcFuture::new(receiver)) + } +} + +/// Client for typed JSON RPC requests +#[derive(Clone)] +pub struct TypedClient(RawClient); + +impl From for TypedClient { + fn from(channel: RpcChannel) -> Self { + TypedClient(channel.into()) + } +} + +impl TypedClient { + /// Create new TypedClient + pub fn new(raw_cli: RawClient) -> Self { + TypedClient(raw_cli) + } + + /// Call RPC with serialization of request and deserialization of response + pub fn call_method( + &self, + method: &str, + returns: &'static str, + args: T, + ) -> impl Future { + let args = + serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC"); + let params = match args { + Value::Array(vec) => Params::Array(vec), + Value::Null => Params::None, + _ => { + return future::Either::A(future::err(RpcError::Other(format_err!( + "RPC params should serialize to a JSON array, or null" + )))) + } + }; + + future::Either::B(self.0.call_method(method, params).and_then(move |value: Value| { + log::debug!("response: {:?}", value); + let result = + serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns.into(), error.into())); + future::done(result) + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::transports::local; + use crate::{RpcChannel, RpcError, TypedClient}; + use jsonrpc_core::{self, IoHandler}; + + #[derive(Clone)] + struct AddClient(TypedClient); + + impl From for AddClient { + fn from(channel: RpcChannel) -> Self { + AddClient(channel.into()) + } + } + + impl AddClient { + fn add(&self, a: u64, b: u64) -> impl Future { + self.0.call_method("add", "u64", (a, b)) + } + } + + #[test] + fn test_client_terminates() { + let mut handler = IoHandler::new(); + handler.add_method("add", |params: Params| { + let (a, b) = params.parse::<(u64, u64)>()?; + let res = a + b; + Ok(jsonrpc_core::to_value(res).unwrap()) + }); + + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .add(3, 4) + .and_then(move |res| client.add(res, 5)) + .join(rpc_client) + .map(|(res, ())| { + assert_eq!(res, 12); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } +} diff --git a/core-client/src/logger.rs b/core-client/transports/src/logger.rs similarity index 100% rename from core-client/src/logger.rs rename to core-client/transports/src/logger.rs diff --git a/core-client/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs similarity index 100% rename from core-client/src/transports/duplex.rs rename to core-client/transports/src/transports/duplex.rs diff --git a/core-client/src/transports/http.rs b/core-client/transports/src/transports/http.rs similarity index 96% rename from core-client/src/transports/http.rs rename to core-client/transports/src/transports/http.rs index 464a8bf23..180e5a990 100644 --- a/core-client/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -16,7 +16,7 @@ use futures::{ use hyper::{http, rt, Client, Request, Uri}; /// Create a HTTP Client -pub fn http(url: &str) -> impl Future +pub fn connect(url: &str) -> impl Future where TClient: From, { @@ -192,7 +192,7 @@ mod tests { let (tx, rx) = std::sync::mpsc::channel(); // when - let run = http(&server.uri) + let run = connect(&server.uri) .and_then(|client: TestClient| { client.hello("http").and_then(move |result| { drop(client); @@ -217,7 +217,7 @@ mod tests { let invalid_uri = "invalid uri"; // when - let run = http(invalid_uri); // rx.recv_timeout(Duration::from_secs(3)).unwrap(); + let run = connect(invalid_uri); // rx.recv_timeout(Duration::from_secs(3)).unwrap(); let res: Result = run.wait(); // then @@ -237,7 +237,7 @@ mod tests { let (tx, rx) = std::sync::mpsc::channel(); // when - let run = http(&server.uri) + let run = connect(&server.uri) .and_then(|client: TestClient| { client.fail().then(move |res| { let _ = tx.send(res); @@ -272,7 +272,7 @@ mod tests { server.stop(); let (tx, rx) = std::sync::mpsc::channel(); - let client = http(&server.uri); + let client = connect(&server.uri); let call = client .and_then(|client: TestClient| { @@ -311,7 +311,7 @@ mod tests { let (tx, rx) = std::sync::mpsc::channel(); let tx2 = tx.clone(); - let client = http(&server.uri); + let client = connect(&server.uri); let call = client .and_then(move |client: TestClient| { diff --git a/core-client/src/transports/local.rs b/core-client/transports/src/transports/local.rs similarity index 100% rename from core-client/src/transports/local.rs rename to core-client/transports/src/transports/local.rs diff --git a/core-client/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs similarity index 100% rename from core-client/src/transports/mod.rs rename to core-client/transports/src/transports/mod.rs diff --git a/core-client/src/transports/ws.rs b/core-client/transports/src/transports/ws.rs similarity index 100% rename from core-client/src/transports/ws.rs rename to core-client/transports/src/transports/ws.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 73f52ba59..fdc705af9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index afcfbca49..fb5e5db02 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "11.0", path = "../core" } -jsonrpc-core-client = { version = "11.0", path = "../core-client" } -jsonrpc-pubsub = { version = "11.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } +jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-core-client = { version = "12.0", path = "../core-client" } +jsonrpc-pubsub = { version = "12.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "12.0", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 1cefc7b31..4dcd8ff34 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "11.0", path = "../core" } -jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } +jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index 1658fd61a..8a5a5e4c7 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "11.0" +jsonrpc-http-server = "12.0" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 6c90d2a35..c517347f2 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "11.0", path = "../core" } -jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } +jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } parity-tokio-ipc = "0.1" parking_lot = "0.8" diff --git a/ipc/README.md b/ipc/README.md index 73f4c402a..1600826d9 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "11.0" +jsonrpc-ipc-server = "12.0" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 325d55c8d..96c9cf33a 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] log = "0.4" parking_lot = "0.8" -jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-core = { version = "12.0", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "11.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "12.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 58dc5fbd8..11803d693 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "11.0", path = "../../core" } -jsonrpc-pubsub = { version = "11.0", path = "../" } -jsonrpc-ws-server = { version = "11.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "11.0", path = "../../ipc" } +jsonrpc-core = { version = "12.0", path = "../../core" } +jsonrpc-pubsub = { version = "12.0", path = "../" } +jsonrpc-ws-server = { version = "12.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "12.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index e0fc9eb42..d9cc823e6 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-core = { version = "12.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index a8a54da59..5dd60988e 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "11.0", path = "../core" } +jsonrpc-core = { version = "12.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index 1a8be41e0..74c6b4c27 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "11.0" +jsonrpc-stdio-server = "12.0" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index a9eeb50e1..6494020d5 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] log = "0.4" parking_lot = "0.8" tokio-service = "0.1" -jsonrpc-core = { version = "11.0", path = "../core" } -jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } +jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index 7b9aaf364..b1a3dd114 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "11.0" +jsonrpc-tcp-server = "12.0" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index bde095ef1..e20089a2c 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "11.0.0" +version = "12.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { path = "../core", version = "11.0" } -jsonrpc-core-client = { path = "../core-client", version = "11.0" } -jsonrpc-pubsub = { path = "../pubsub", version = "11.0" } +jsonrpc-core = { path = "../core", version = "12.0" } +jsonrpc-core-client = { path = "../core-client", version = "12.0" } +jsonrpc-pubsub = { path = "../pubsub", version = "12.0" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { path = "../derive", version = "11.0" } +jsonrpc-derive = { path = "../derive", version = "12.0" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 88bdc560d..eeaa29d0a 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "11.0.0" +version = "12.0.0" [dependencies] -jsonrpc-core = { version = "11.0", path = "../core" } -jsonrpc-server-utils = { version = "11.0", path = "../server-utils" } +jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } log = "0.4" parking_lot = "0.8" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 177a2ac6a..461c2d8a1 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "11.0" +jsonrpc-ws-server = "12.0" ``` `main.rs` From 3bee17b525923a14c473be561f677d9f048a7dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 5 Jun 2019 15:26:06 +0200 Subject: [PATCH 054/149] Fix ordering, make idempotent. (#441) --- _automate/publish.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index 9b0dbfe9e..0db10a8c1 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -3,7 +3,7 @@ set -exu VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') -ORDER=(core core-client/transports core-client server-utils tcp ws ws/client http ipc stdio pubsub derive test) +ORDER=(core server-utils tcp ws http ipc stdio pubsub core-client/transports core-client derive test) echo "Publishing version $VERSION" cargo clean @@ -12,7 +12,7 @@ for crate in ${ORDER[@]}; do cd $crate echo "Publishing $crate@$VERSION" sleep 5 - cargo publish $@ + cargo publish $@ || read -p "\n\t Publishing $crate failed. Press [enter] to continue." cd - done From 44c501bc4182c7fef7d56d0efc780cd7fbb98419 Mon Sep 17 00:00:00 2001 From: David Craven Date: Thu, 4 Jul 2019 11:40:03 +0200 Subject: [PATCH 055/149] Implement subscription support in the client (#436) * Rustfmt fixes. * Enable logging in tests. * Improve pubsub example documentation. * Allow Response to contain a Notification. * Add jsonrpc-pubsub as a core-client dependency. * Add pubsub support to local transport. * Turn RpcMessage into an enum. * Add SubscriptionStream. * Add subscription support to RawClient. * Add subscription support to RequestBuilder. * Add subscription support to Duplex. * Add subscription test. * Add TypedSubscriptionStream. * Add subscription support to TypedClient. * Test typed client subscription. * Add subscription support to procmacro. * Handle typed::Subscriber. * Address grumbles. * rustfmt fixes. * Fix tests. * Avoid unwrapping. * Fix doc tests. * Impl From instead of Into. * Improve code. * Deny warnings and missing docs. * Fix explicit dyn warning. * Implement Debug for Duplex. * Remove allow(deprecated). * Fix build. * Fix build on windows. * Add constructor to LocalRpc. * Parse output into subscription id. * Should handle multiple subscriptions. * Add Deserialize bound for subscription args in client * Remove deny(warnings) * Rewrite using less maps. * Bounds for wrapped Subscriber generic types (#3) * Handle Subscriber wrapped generic type * Pubsub Subscriber wrapper with multiple generic types --- core-client/src/lib.rs | 2 + core-client/transports/Cargo.toml | 1 + core-client/transports/src/lib.rs | 238 +++++++++++++++- .../transports/src/transports/duplex.rs | 266 +++++++++++++++--- core-client/transports/src/transports/http.rs | 21 +- .../transports/src/transports/local.rs | 77 +++-- core-client/transports/src/transports/mod.rs | 29 +- core/src/calls.rs | 4 +- core/src/io.rs | 6 +- core/src/lib.rs | 4 +- core/src/middleware.rs | 4 +- core/src/types/params.rs | 17 +- core/src/types/response.rs | 51 +++- derive/examples/pubsub-macros.rs | 4 +- derive/src/lib.rs | 2 +- derive/src/to_client.rs | 44 ++- derive/src/to_delegate.rs | 37 +-- derive/tests/run-pass/client_only.rs | 5 +- ...scription-generic-type-with-deserialize.rs | 54 ++++ ...bsub-subscription-type-with-deserialize.rs | 48 ++++ ...b-subscription-type-without-deserialize.rs | 2 +- http/src/handler.rs | 6 +- http/src/lib.rs | 183 ++++++------ http/src/tests.rs | 3 +- ipc/src/lib.rs | 2 +- ipc/src/server.rs | 9 +- pubsub/examples/pubsub_simple.rs | 6 +- pubsub/src/lib.rs | 2 +- pubsub/src/subscription.rs | 4 +- pubsub/src/types.rs | 14 + server-utils/src/lib.rs | 2 +- stdio/src/lib.rs | 2 +- tcp/src/lib.rs | 2 +- tcp/src/server.rs | 2 +- test/src/lib.rs | 6 +- ws/src/error.rs | 2 +- ws/src/lib.rs | 2 +- ws/src/server.rs | 6 +- ws/src/server_builder.rs | 6 +- ws/src/session.rs | 18 +- 40 files changed, 941 insertions(+), 252 deletions(-) create mode 100644 derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs create mode 100644 derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index 818716ee8..b35f33af8 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -5,4 +5,6 @@ //! //! See documentation of [`jsonrpc-client-transports`](../jsonrpc_client_transports/) for more details. +#![deny(missing_docs)] + pub use jsonrpc_client_transports::*; diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index ea60c1e7e..308934e36 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -33,6 +33,7 @@ futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } jsonrpc-core = { version = "12.0", path = "../../core" } +jsonrpc-pubsub = { version = "12.0", path = "../../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index ba7dab823..b0637b9ff 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -9,6 +9,7 @@ use jsonrpc_core::{Error, Params}; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::Value; +use std::marker::PhantomData; pub mod transports; @@ -27,9 +28,6 @@ pub enum RpcError { /// Request timed out. #[fail(display = "Request timed out")] Timeout, - /// The server returned a response with an unknown id. - #[fail(display = "Server returned a response with an unknown id")] - UnknownId, /// Not rpc specific errors. #[fail(display = "{}", _0)] Other(failure::Error), @@ -41,9 +39,8 @@ impl From for RpcError { } } -/// A message sent to the `RpcClient`. This is public so that -/// the derive crate can generate a client. -struct RpcMessage { +/// A rpc call message. +struct CallMessage { /// The rpc method name. method: String, /// The rpc method parameters. @@ -53,6 +50,46 @@ struct RpcMessage { sender: oneshot::Sender>, } +/// A rpc subscription. +struct Subscription { + /// The subscribe method name. + subscribe: String, + /// The subscribe method parameters. + subscribe_params: Params, + /// The name of the notification. + notification: String, + /// The unsubscribe method name. + unsubscribe: String, +} + +/// A rpc subscribe message. +struct SubscribeMessage { + /// The subscription to subscribe to. + subscription: Subscription, + /// The channel to send notifications to. + sender: mpsc::Sender>, +} + +/// A message sent to the `RpcClient`. +enum RpcMessage { + /// Make a rpc call. + Call(CallMessage), + /// Subscribe to a notification. + Subscribe(SubscribeMessage), +} + +impl From for RpcMessage { + fn from(msg: CallMessage) -> Self { + RpcMessage::Call(msg) + } +} + +impl From for RpcMessage { + fn from(msg: SubscribeMessage) -> Self { + RpcMessage::Subscribe(msg) + } +} + /// A channel to a `RpcClient`. #[derive(Clone)] pub struct RpcChannel(mpsc::Sender); @@ -99,6 +136,67 @@ impl Future for RpcFuture { } } +/// The stream returned by a subscribe. +pub struct SubscriptionStream { + recv: mpsc::Receiver>, +} + +impl SubscriptionStream { + /// Crates a new `SubscriptionStream`. + pub fn new(recv: mpsc::Receiver>) -> Self { + SubscriptionStream { recv } + } +} + +impl Stream for SubscriptionStream { + type Item = Value; + type Error = RpcError; + + fn poll(&mut self) -> Result>, Self::Error> { + match self.recv.poll() { + Ok(Async::Ready(Some(Ok(value)))) => Ok(Async::Ready(Some(value))), + Ok(Async::Ready(Some(Err(error)))) => Err(error), + Ok(Async::Ready(None)) => Ok(Async::Ready(None)), + Ok(Async::NotReady) => Ok(Async::NotReady), + Err(()) => Err(RpcError::Other(format_err!("mpsc channel returned an error."))), + } + } +} + +/// A typed subscription stream. +pub struct TypedSubscriptionStream { + _marker: PhantomData, + returns: &'static str, + stream: SubscriptionStream, +} + +impl TypedSubscriptionStream { + /// Creates a new `TypedSubscriptionStream`. + pub fn new(stream: SubscriptionStream, returns: &'static str) -> Self { + TypedSubscriptionStream { + _marker: PhantomData, + returns, + stream, + } + } +} + +impl Stream for TypedSubscriptionStream { + type Item = T; + type Error = RpcError; + + fn poll(&mut self) -> Result>, Self::Error> { + let result = match self.stream.poll()? { + Async::Ready(Some(value)) => serde_json::from_value::(value) + .map(|result| Async::Ready(Some(result))) + .map_err(|error| RpcError::ParseError(self.returns.into(), error.into()))?, + Async::Ready(None) => Async::Ready(None), + Async::NotReady => Async::NotReady, + }; + Ok(result) + } +} + /// Client for raw JSON RPC requests #[derive(Clone)] pub struct RawClient(RpcChannel); @@ -113,16 +211,40 @@ impl RawClient { /// Call RPC with raw JSON pub fn call_method(&self, method: &str, params: Params) -> impl Future { let (sender, receiver) = oneshot::channel(); - let msg = RpcMessage { + let msg = CallMessage { method: method.into(), params, sender, }; self.0 - .send(msg) + .send(msg.into()) .map_err(|error| RpcError::Other(error.into())) .and_then(|_| RpcFuture::new(receiver)) } + + /// Subscribe to topic with raw JSON + pub fn subscribe( + &self, + subscribe: &str, + subscribe_params: Params, + notification: &str, + unsubscribe: &str, + ) -> impl Future { + let (sender, receiver) = mpsc::channel(0); + let msg = SubscribeMessage { + subscription: Subscription { + subscribe: subscribe.into(), + subscribe_params, + notification: notification.into(), + unsubscribe: unsubscribe.into(), + }, + sender, + }; + self.0 + .send(msg.into()) + .map_err(|error| RpcError::Other(error.into())) + .map(|_| SubscriptionStream::new(receiver)) + } } /// Client for typed JSON RPC requests @@ -167,6 +289,35 @@ impl TypedClient { future::done(result) })) } + + /// Subscribe with serialization of request and deserialization of response + pub fn subscribe( + &self, + subscribe: &str, + subscribe_params: T, + topic: &str, + unsubscribe: &str, + returns: &'static str, + ) -> impl Future, Error = RpcError> { + let args = serde_json::to_value(subscribe_params) + .expect("Only types with infallible serialisation can be used for JSON-RPC"); + + let params = match args { + Value::Array(vec) => Params::Array(vec), + Value::Null => Params::None, + _ => { + return future::Either::A(future::err(RpcError::Other(format_err!( + "RPC params should serialize to a JSON array, or null" + )))) + } + }; + + let typed_stream = self + .0 + .subscribe(subscribe, params, topic, unsubscribe) + .map(move |stream| TypedSubscriptionStream::new(stream, returns)); + future::Either::B(typed_stream) + } } #[cfg(test)] @@ -174,7 +325,10 @@ mod tests { use super::*; use crate::transports::local; use crate::{RpcChannel, RpcError, TypedClient}; - use jsonrpc_core::{self, IoHandler}; + use jsonrpc_core::{self as core, IoHandler}; + use jsonrpc_pubsub::{PubSubHandler, Subscriber, SubscriptionId}; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; #[derive(Clone)] struct AddClient(TypedClient); @@ -193,6 +347,7 @@ mod tests { #[test] fn test_client_terminates() { + crate::logger::init_log(); let mut handler = IoHandler::new(); handler.add_method("add", |params: Params| { let (a, b) = params.parse::<(u64, u64)>()?; @@ -215,4 +370,69 @@ mod tests { }); tokio::run(fut); } + + #[test] + fn should_handle_subscription() { + crate::logger::init_log(); + // given + let mut handler = PubSubHandler::::default(); + let called = Arc::new(AtomicBool::new(false)); + let called2 = called.clone(); + handler.add_subscription( + "hello", + ("subscribe_hello", |params, _meta, subscriber: Subscriber| { + assert_eq!(params, core::Params::None); + let sink = subscriber + .assign_id(SubscriptionId::Number(5)) + .expect("assigned subscription id"); + std::thread::spawn(move || { + for i in 0..3 { + std::thread::sleep(std::time::Duration::from_millis(100)); + let value = serde_json::json!({ + "subscription": 5, + "result": vec![i], + }); + sink.notify(serde_json::from_value(value).unwrap()) + .wait() + .expect("sent notification"); + } + }); + }), + ("unsubscribe_hello", move |id, _meta| { + // Should be called because session is dropped. + called2.store(true, Ordering::SeqCst); + assert_eq!(id, SubscriptionId::Number(5)); + future::ok(core::Value::Bool(true)) + }), + ); + + // when + let (client, rpc_client) = local::connect_with_pubsub::(handler); + let received = Arc::new(std::sync::Mutex::new(vec![])); + let r2 = received.clone(); + let fut = client + .subscribe::<_, (u32,)>("subscribe_hello", (), "hello", "unsubscribe_hello", "u32") + .and_then(|stream| { + stream + .into_future() + .map(move |(result, _)| { + drop(client); + r2.lock().unwrap().push(result.unwrap()); + }) + .map_err(|_| { + panic!("Expected message not received."); + }) + }) + .join(rpc_client) + .map(|(res, _)| { + log::info!("ok {:?}", res); + }) + .map_err(|err| { + log::error!("err {:?}", err); + }); + tokio::run(fut); + assert_eq!(called.load(Ordering::SeqCst), true); + assert!(!received.lock().unwrap().is_empty(), "Expected at least one received item."); + } + } diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index 93e1691e6..a8bc70349 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -4,6 +4,7 @@ use failure::format_err; use futures::prelude::*; use futures::sync::{mpsc, oneshot}; use jsonrpc_core::Id; +use jsonrpc_pubsub::SubscriptionId; use log::debug; use serde_json::Value; use std::collections::HashMap; @@ -12,27 +13,70 @@ use std::collections::VecDeque; use super::RequestBuilder; use crate::{RpcChannel, RpcError, RpcMessage}; +struct Subscription { + /// Subscription id received when subscribing. + id: Option, + /// A method name used for notification. + notification: String, + /// Rpc method to unsubscribe. + unsubscribe: String, + /// Where to send messages to. + channel: mpsc::Sender>, +} + +impl Subscription { + fn new( + channel: mpsc::Sender>, + notification: String, + unsubscribe: String, + ) -> Self { + Subscription { + id: None, + notification, + unsubscribe, + channel, + } + } +} + +enum PendingRequest { + Call(oneshot::Sender>), + Subscription(Subscription), +} + /// The Duplex handles sending and receiving asynchronous /// messages through an underlying transport. pub struct Duplex { request_builder: RequestBuilder, - queue: HashMap>>, - sink: TSink, - stream: TStream, + /// Channel from the client. channel: Option>, + /// Requests that haven't received a response yet. + pending_requests: HashMap, + /// A map from the subscription name to the subscription. + subscriptions: HashMap<(SubscriptionId, String), Subscription>, + /// Incoming messages from the underlying transport. + stream: TStream, + /// Unprocessed incoming messages. + incoming: VecDeque<(Id, Result, Option, Option)>, + /// Unprocessed outgoing messages. outgoing: VecDeque, + /// Outgoing messages from the underlying transport. + sink: TSink, } impl Duplex { /// Creates a new `Duplex`. fn new(sink: TSink, stream: TStream, channel: mpsc::Receiver) -> Self { + log::debug!("open"); Duplex { request_builder: RequestBuilder::new(), - queue: HashMap::new(), - sink, - stream, channel: Some(channel), - outgoing: VecDeque::new(), + pending_requests: Default::default(), + subscriptions: Default::default(), + stream, + incoming: Default::default(), + outgoing: Default::default(), + sink, } } } @@ -54,11 +98,14 @@ where fn poll(&mut self) -> Result, Self::Error> { // Handle requests from the client. + log::debug!("handle requests from client"); loop { - if self.channel.is_none() { - break; - } - let msg = match self.channel.as_mut().expect("channel is some; qed").poll() { + // Check that the client channel is open + let channel = match self.channel.as_mut() { + Some(channel) => channel, + None => break, + }; + let msg = match channel.poll() { Ok(Async::Ready(Some(msg))) => msg, Ok(Async::Ready(None)) => { // When the channel is dropped we still need to finish @@ -69,28 +116,41 @@ where Ok(Async::NotReady) => break, Err(()) => continue, }; - let (id, request_str) = self.request_builder.single_request(&msg); - self.queue.insert(id, msg.sender); - self.outgoing.push_back(request_str); - } - // Handle outgoing rpc requests. - loop { - match self.outgoing.pop_front() { - Some(request) => match self.sink.start_send(request)? { - AsyncSink::Ready => {} - AsyncSink::NotReady(request) => { - self.outgoing.push_front(request); - break; + let request_str = match msg { + RpcMessage::Call(msg) => { + let (id, request_str) = self.request_builder.call_request(&msg); + + if self.pending_requests.insert(id.clone(), PendingRequest::Call(msg.sender)).is_some() { + log::error!("reuse of request id {:?}", id); } - }, - None => break, - } + request_str + } + RpcMessage::Subscribe(msg) => { + let crate::Subscription { + subscribe, + subscribe_params, + notification, + unsubscribe, + } = msg.subscription; + let (id, request_str) = self.request_builder.subscribe_request( + subscribe, + subscribe_params, + ); + log::debug!("subscribing to {}", notification); + let subscription = Subscription::new(msg.sender, notification, unsubscribe); + if self.pending_requests.insert(id.clone(), PendingRequest::Subscription(subscription)).is_some() { + log::error!("reuse of request id {:?}", id); + } + request_str + } + }; + log::debug!("outgoing: {}", request_str); + self.outgoing.push_back(request_str); } - let done_sending = match self.sink.poll_complete()? { - Async::Ready(()) => true, - Async::NotReady => false, - }; - // Handle incoming rpc requests. + + // Handle stream. + // Reads from stream and queues to incoming queue. + log::debug!("handle stream"); loop { let response_str = match self.stream.poll() { Ok(Async::Ready(Some(response_str))) => response_str, @@ -104,23 +164,145 @@ where Ok(Async::NotReady) => break, Err(err) => Err(err)?, }; + log::debug!("incoming: {}", response_str); + for (id, result, method, sid) in super::parse_response(&response_str)? { + log::debug!("id: {:?} (sid: {:?}) result: {:?} method: {:?}", id, sid, result, method); + self.incoming.push_back((id, result, method, sid)); + } + } + + // Handle incoming queue. + log::debug!("handle incoming"); + loop { + match self.incoming.pop_front() { + Some((id, result, method, sid)) => { + let sid_and_method = sid.and_then(|sid| method.map(|method| (sid, method))); + // Handle the response to a pending request. + match self.pending_requests.remove(&id) { + // It's a regular Req-Res call, so just answer. + Some(PendingRequest::Call(tx)) => { + tx.send(result) + .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?; + continue; + }, + // It was a subscription request, + // turn it into a proper subscription. + Some(PendingRequest::Subscription(mut subscription)) => { + let sid = result.as_ref().ok().and_then(|res| SubscriptionId::parse_value(res)); + let method = subscription.notification.clone(); + + if let Some(sid) = sid { + subscription.id = Some(sid.clone()); + if self.subscriptions.insert((sid.clone(), method.clone()), subscription).is_some() { + log::warn!( + "Overwriting existing subscription under {:?} ({:?}). \ + Seems that server returned the same subscription id.", + sid, + method, + ); + } + } else { + log::warn!( + "The response does not have expected subscription id in response: {:?} ({:?}): {:?}", + id, + method, + result, + ); + } + continue; + }, + // It's not a pending request nor a notification + None if sid_and_method.is_none() => { + log::warn!("Got unexpected response with id {:?} ({:?})", id, sid_and_method); + continue; + }, + // just fall-through in case it's a notification + None => {} + }; + + let sid_and_method = if let Some(x) = sid_and_method { + x + } else { + continue; + }; - let responses = super::parse_response(&response_str)?; - for (id, result) in responses { - let channel = self.queue.remove(&id); - match channel { - Some(tx) => tx - .send(result) - .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?, - None => Err(RpcError::UnknownId)?, - }; + if let Some(subscription) = self.subscriptions.get_mut(&sid_and_method) { + match subscription.channel.poll_ready() { + Ok(Async::Ready(())) => { + subscription.channel.try_send(result).expect("The channel is ready; qed"); + } + Ok(Async::NotReady) => { + let (sid, method) = sid_and_method; + self.incoming.push_front((id, result, Some(method), Some(sid))); + break; + } + Err(_) => { + let subscription = self.subscriptions + .remove(&sid_and_method) + .expect("Subscription was just polled; qed"); + let sid = subscription.id + .expect("Every subscription that ends up in `self.subscriptions` has id already \ + assigned; assignment happens during response to subscribe request."); + let (_id, request_str) = + self.request_builder.unsubscribe_request(subscription.unsubscribe, sid); + log::debug!("outgoing: {}", request_str); + self.outgoing.push_back(request_str); + log::debug!("unsubscribed from {:?}", sid_and_method); + } + } + } else { + log::warn!("Received unexpected subscription notification: {:?}", sid_and_method); + } + }, + None => break, } } - if self.channel.is_none() && self.outgoing.is_empty() && self.queue.is_empty() && done_sending { - debug!("client finished"); + + // Handle outgoing queue. + // Writes queued messages to sink. + log::debug!("handle outgoing"); + loop { + match self.outgoing.pop_front() { + Some(request) => match self.sink.start_send(request)? { + AsyncSink::Ready => {} + AsyncSink::NotReady(request) => { + self.outgoing.push_front(request); + break; + } + }, + None => break, + } + } + log::debug!("handle sink"); + let sink_empty = match self.sink.poll_complete()? { + Async::Ready(()) => true, + Async::NotReady => false, + }; + + log::debug!("{:?}", self); + // Return ready when the future is complete + if self.channel.is_none() + && self.outgoing.is_empty() + && self.incoming.is_empty() + && self.pending_requests.is_empty() + && self.subscriptions.is_empty() + && sink_empty + { + log::debug!("close"); Ok(Async::Ready(())) } else { Ok(Async::NotReady) } } } + +impl std::fmt::Debug for Duplex { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + writeln!(f, "channel is none: {}", self.channel.is_none())?; + writeln!(f, "outgoing: {}", self.outgoing.len())?; + writeln!(f, "incoming: {}", self.incoming.len())?; + writeln!(f, "pending_requests: {}", self.pending_requests.len())?; + writeln!(f, "subscriptions: {}", self.subscriptions.len())?; + Ok(()) + } +} diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index 180e5a990..9ea9361a4 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -23,13 +23,13 @@ where let max_parallel = 8; let url: Uri = match url.parse() { Ok(url) => url, - Err(e) => return A(future::err(RpcError::Other(e.into()))) + Err(e) => return A(future::err(RpcError::Other(e.into()))), }; #[cfg(feature = "tls")] let connector = match hyper_tls::HttpsConnector::new(4) { Ok(connector) => connector, - Err(e) => return A(future::err(RpcError::Other(e.into()))) + Err(e) => return A(future::err(RpcError::Other(e.into()))), }; #[cfg(feature = "tls")] let client = Client::builder().build::<_, hyper::Body>(connector); @@ -42,8 +42,15 @@ where let (sender, receiver) = mpsc::channel(max_parallel); let fut = receiver - .map(move |msg: RpcMessage| { - let (_, request) = request_builder.single_request(&msg); + .filter_map(move |msg: RpcMessage| { + let msg = match msg { + RpcMessage::Call(call) => call, + RpcMessage::Subscribe(_) => { + log::warn!("Unsupported `RpcMessage` type `Subscribe`."); + return None; + } + }; + let (_, request) = request_builder.call_request(&msg); let request = Request::post(&url) .header( http::header::CONTENT_TYPE, @@ -51,7 +58,7 @@ where ) .body(request.into()) .expect("Uri and request headers are valid; qed"); - client.request(request).then(move |response| Ok((response, msg))) + Some(client.request(request).then(move |response| Ok((response, msg)))) }) .buffer_unordered(max_parallel) .for_each(|(result, msg)| { @@ -102,12 +109,12 @@ where mod tests { use super::*; use crate::*; + use assert_matches::assert_matches; use hyper::rt; use jsonrpc_core::{Error, ErrorCode, IoHandler, Params, Value}; use jsonrpc_http_server::*; use std::net::SocketAddr; use std::time::Duration; - use assert_matches::assert_matches; fn id(t: T) -> T { t @@ -223,7 +230,7 @@ mod tests { // then assert_matches!( res.map(|_cli| unreachable!()), Err(RpcError::Other(err)) => { - assert_eq!("InvalidUri(InvalidUriChar)", format!("{:?}", err)); + assert_eq!(format!("{}", err), "invalid uri character"); } ); } diff --git a/core-client/transports/src/transports/local.rs b/core-client/transports/src/transports/local.rs index 58dc15a06..a69e47680 100644 --- a/core-client/transports/src/transports/local.rs +++ b/core-client/transports/src/transports/local.rs @@ -1,34 +1,48 @@ -//! Rpc client implementation for `Deref>`. +//! Rpc client implementation for `Deref>`. use crate::{RpcChannel, RpcError}; +use failure::format_err; use futures::prelude::*; +use futures::sync::mpsc; use jsonrpc_core::{MetaIoHandler, Metadata}; +use jsonrpc_pubsub::Session; use std::collections::VecDeque; use std::ops::Deref; +use std::sync::Arc; /// Implements a rpc client for `MetaIoHandler`. -pub struct LocalRpc { +pub struct LocalRpc { handler: THandler, + meta: TMetadata, queue: VecDeque, } -impl LocalRpc +impl LocalRpc where - TMetadata: Metadata + Default, + TMetadata: Metadata, THandler: Deref>, { - /// Creates a new `LocalRpc`. - pub fn new(handler: THandler) -> Self { + /// Creates a new `LocalRpc` with default metadata. + pub fn new(handler: THandler) -> Self + where + TMetadata: Default + { + Self::with_metadata(handler, Default::default()) + } + + /// Creates a new `LocalRpc` with given handler and metadata. + pub fn with_metadata(handler: THandler, meta: TMetadata) -> Self { Self { handler, - queue: VecDeque::new(), + meta, + queue: Default::default(), } } } -impl Stream for LocalRpc +impl Stream for LocalRpc where - TMetadata: Metadata + Default, + TMetadata: Metadata, THandler: Deref>, { type Item = String; @@ -42,16 +56,16 @@ where } } -impl Sink for LocalRpc +impl Sink for LocalRpc where - TMetadata: Metadata + Default, + TMetadata: Metadata, THandler: Deref>, { type SinkItem = String; type SinkError = RpcError; fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { - match self.handler.handle_request_sync(&request, TMetadata::default()) { + match self.handler.handle_request_sync(&request, self.meta.clone()) { Some(response) => self.queue.push_back(response), None => {} }; @@ -63,14 +77,45 @@ where } } -/// Connects to a `IoHandler`. -pub fn connect(handler: THandler) -> (TClient, impl Future) +/// Connects to a `Deref`. +pub fn connect_with_metadata( + handler: THandler, + meta: TMetadata, +) -> (TClient, impl Future) where TClient: From, - TMetadata: Metadata + Default, THandler: Deref>, + TMetadata: Metadata, +{ + let (sink, stream) = LocalRpc::with_metadata(handler, meta).split(); + let (rpc_client, sender) = crate::transports::duplex(sink, stream); + let client = TClient::from(sender); + (client, rpc_client) +} + +/// Connects to a `Deref`. +pub fn connect(handler: THandler) -> (TClient, impl Future) +where + TClient: From, + THandler: Deref>, + TMetadata: Metadata + Default, +{ + connect_with_metadata(handler, Default::default()) +} + +/// Metadata for LocalRpc. +pub type LocalMeta = Arc; + +/// Connects with pubsub. +pub fn connect_with_pubsub(handler: THandler) -> (TClient, impl Future) +where + TClient: From, + THandler: Deref>, { - let (sink, stream) = LocalRpc::new(handler).split(); + let (tx, rx) = mpsc::channel(0); + let meta = Arc::new(Session::new(tx)); + let (sink, stream) = LocalRpc::with_metadata(handler, meta).split(); + let stream = stream.select(rx.map_err(|_| RpcError::Other(format_err!("Pubsub channel returned an error")))); let (rpc_client, sender) = crate::transports::duplex(sink, stream); let client = TClient::from(sender); (client, rpc_client) diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index 943caf2ee..94462918e 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -1,9 +1,10 @@ //! Client transport implementations -use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Response, Version}; +use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Params, Response, Version}; +use jsonrpc_pubsub::SubscriptionId; use serde_json::Value; -use crate::{RpcError, RpcMessage}; +use crate::{CallMessage, RpcError}; pub mod duplex; #[cfg(feature = "http")] @@ -32,12 +33,12 @@ impl RequestBuilder { } /// Build a single request with the next available id - fn single_request(&mut self, msg: &RpcMessage) -> (Id, String) { + fn single_request(&mut self, method: String, params: Params) -> (Id, String) { let id = self.next_id(); let request = jsonrpc_core::Request::Single(Call::MethodCall(MethodCall { jsonrpc: Some(Version::V2), - method: msg.method.clone(), - params: msg.params.clone(), + method, + params, id: id.clone(), })); ( @@ -45,10 +46,22 @@ impl RequestBuilder { serde_json::to_string(&request).expect("Request serialization is infallible; qed"), ) } + + fn call_request(&mut self, msg: &CallMessage) -> (Id, String) { + self.single_request(msg.method.clone(), msg.params.clone()) + } + + fn subscribe_request(&mut self, subscribe: String, subscribe_params: Params) -> (Id, String) { + self.single_request(subscribe, subscribe_params) + } + + fn unsubscribe_request(&mut self, unsubscribe: String, sid: SubscriptionId) -> (Id, String) { + self.single_request(unsubscribe, Params::Array(vec![Value::from(sid)])) + } } /// Parse raw string into JSON values, together with the request Id -pub fn parse_response(response: &str) -> Result)>, RpcError> { +pub fn parse_response(response: &str) -> Result, Option, Option)>, RpcError> { serde_json::from_str::(&response) .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) .map(|response| { @@ -60,9 +73,11 @@ pub fn parse_response(response: &str) -> Result .into_iter() .map(|output| { let id = output.id().clone(); + let sid = SubscriptionId::parse_output(&output); + let method = output.method(); let value: Result = output.into(); let result = value.map_err(RpcError::JsonRpcError); - (id, result) + (id, result, method, sid) }) .collect::>() }) diff --git a/core/src/calls.rs b/core/src/calls.rs index 251e669fc..335d75fb7 100644 --- a/core/src/calls.rs +++ b/core/src/calls.rs @@ -41,9 +41,9 @@ pub trait RpcNotification: Send + Sync + 'static { #[derive(Clone)] pub enum RemoteProcedure { /// A method call - Method(Arc>), + Method(Arc>), /// A notification - Notification(Arc>), + Notification(Arc>), /// An alias to other method, Alias(String), } diff --git a/core/src/io.rs b/core/src/io.rs index c5537e6bd..adf28c685 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -11,10 +11,10 @@ use crate::types::{Call, Output, Request, Response}; use crate::types::{Error, ErrorCode, Version}; /// A type representing middleware or RPC response before serialization. -pub type FutureResponse = Box, Error = ()> + Send>; +pub type FutureResponse = Box, Error = ()> + Send>; /// A type representing middleware or RPC call output. -pub type FutureOutput = Box, Error = ()> + Send>; +pub type FutureOutput = Box, Error = ()> + Send>; /// A type representing future string response. pub type FutureResult = future::Map< @@ -238,7 +238,7 @@ impl> MetaIoHandler { let jsonrpc = method.jsonrpc; let valid_version = self.compatibility.is_version_valid(jsonrpc); - let call_method = |method: &Arc>| { + let call_method = |method: &Arc>| { let method = method.clone(); futures::lazy(move || method.call(params, meta)) }; diff --git a/core/src/lib.rs b/core/src/lib.rs index 772be921a..bb6862035 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -19,7 +19,7 @@ //! } //! ``` -#![warn(missing_docs)] +#![deny(missing_docs)] #[macro_use] extern crate log; @@ -39,7 +39,7 @@ pub mod middleware; pub mod types; /// A `Future` trait object. -pub type BoxFuture = Box + Send>; +pub type BoxFuture = Box + Send>; /// A Result type. pub type Result = ::std::result::Result; diff --git a/core/src/middleware.rs b/core/src/middleware.rs index 36a916014..b9b87ee94 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -36,9 +36,9 @@ pub trait Middleware: Send + Sync + 'static { } /// Dummy future used as a noop result of middleware. -pub type NoopFuture = Box, Error = ()> + Send>; +pub type NoopFuture = Box, Error = ()> + Send>; /// Dummy future used as a noop call result of middleware. -pub type NoopCallFuture = Box, Error = ()> + Send>; +pub type NoopCallFuture = Box, Error = ()> + Send>; /// No-op middleware implementation #[derive(Debug, Default)] diff --git a/core/src/types/params.rs b/core/src/types/params.rs index 0e40eb6a9..7f9830f25 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -24,12 +24,7 @@ impl Params { where D: DeserializeOwned, { - let value = match self { - Params::Array(vec) => Value::Array(vec), - Params::Map(map) => Value::Object(map), - Params::None => Value::Null, - }; - + let value: Value = self.into(); from_value(value).map_err(|e| Error::invalid_params(format!("Invalid params: {}.", e))) } @@ -43,6 +38,16 @@ impl Params { } } +impl From for Value { + fn from(params: Params) -> Value { + match params { + Params::Array(vec) => Value::Array(vec), + Params::Map(map) => Value::Object(map), + Params::None => Value::Null, + } + } +} + #[cfg(test)] mod tests { use super::Params; diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 484e2d226..f84c638d3 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -1,5 +1,5 @@ //! jsonrpc response -use super::{Error, ErrorCode, Id, Value, Version}; +use super::{Error, ErrorCode, Id, Notification, Params, Value, Version}; use crate::Result as CoreResult; /// Successful response @@ -30,6 +30,8 @@ pub struct Failure { #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] #[serde(untagged)] pub enum Output { + /// Notification + Notification(Notification), /// Success Success(Success), /// Failure @@ -59,6 +61,7 @@ impl Output { match *self { Output::Success(ref s) => s.jsonrpc, Output::Failure(ref f) => f.jsonrpc, + Output::Notification(ref n) => n.jsonrpc, } } @@ -67,6 +70,15 @@ impl Output { match *self { Output::Success(ref s) => &s.id, Output::Failure(ref f) => &f.id, + Output::Notification(_) => &Id::Null, + } + } + + /// Get the method name if the output is a notification. + pub fn method(&self) -> Option { + match *self { + Output::Notification(ref n) => Some(n.method.to_owned()), + _ => None, } } } @@ -77,6 +89,25 @@ impl From for CoreResult { match output { Output::Success(s) => Ok(s.result), Output::Failure(f) => Err(f.error), + Output::Notification(n) => match &n.params { + Params::Map(map) => { + let subscription = map.get("subscription"); + let result = map.get("result"); + let error = map.get("error"); + + match (subscription, result, error) { + (Some(_), Some(result), _) => Ok(result.to_owned()), + (Some(_), _, Some(error)) => { + let error = serde_json::from_value::(error.to_owned()) + .ok() + .unwrap_or_else(|| Error::parse_error()); + Err(error) + } + _ => Ok(n.params.into()), + } + } + _ => Ok(n.params.into()), + }, } } } @@ -241,3 +272,21 @@ fn batch_response_deserialize() { ]) ); } + +#[test] +fn notification_deserialize() { + use super::Params; + use serde_json; + use serde_json::Value; + + let dsr = r#"{"jsonrpc":"2.0","method":"hello","params":[10]}"#; + let deserialized: Response = serde_json::from_str(dsr).unwrap(); + assert_eq!( + deserialized, + Response::Single(Output::Notification(Notification { + jsonrpc: Some(Version::V2), + method: "hello".into(), + params: Params::Array(vec![Value::from(10)]), + })) + ); +} diff --git a/derive/examples/pubsub-macros.rs b/derive/examples/pubsub-macros.rs index 65b650516..5346e1068 100644 --- a/derive/examples/pubsub-macros.rs +++ b/derive/examples/pubsub-macros.rs @@ -14,11 +14,11 @@ pub trait Rpc { /// Hello subscription #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] - fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + fn subscribe(&self, meta: Self::Metadata, subscriber: typed::Subscriber, param: u64); /// Unsubscribe from hello subscription. #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] - fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; + fn unsubscribe(&self, meta: Option, subscription: SubscriptionId) -> Result; } #[derive(Default)] diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 3e562d883..48c71e94b 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -71,7 +71,7 @@ //! name = "hello_subscribe", //! alias("hello_sub") //! )] -//! fn subscribe(&self, _: Self::Metadata, _: Subscriber, _: u64); +//! fn subscribe(&self, _: Self::Metadata, _: Subscriber, param: u64); //! //! /// Unsubscribe from hello subscription. //! #[pubsub( diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index cf999f056..890a12bd8 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -46,10 +46,10 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: Call, Error, ErrorCode, Id, MethodCall, Params, Request, Response, Version, }; - use _jsonrpc_core::futures::{future, Future, Sink}; - use _jsonrpc_core::futures::sync::oneshot; + use _jsonrpc_core::futures::prelude::*; + use _jsonrpc_core::futures::sync::{mpsc, oneshot}; use _jsonrpc_core::serde_json::{self, Value}; - use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, TypedClient}; + use _jsonrpc_core_client::{RpcChannel, RpcError, RpcFuture, TypedClient, TypedSubscriptionStream}; /// The Client. #[derive(Clone)] @@ -111,8 +111,28 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result { - println!("warning: pubsub methods are currently not supported in the generated client.") + MethodRegistration::PubSub { + name: subscription, + subscribe, + unsubscribe, + } => { + let attrs = get_doc_comments(&subscribe.trait_item.attrs); + let name = &subscribe.trait_item.sig.ident; + let mut args = compute_args(&subscribe.trait_item).into_iter(); + let returns = compute_subscription_type(&args.next().unwrap()); + let returns_str = quote!(#returns).to_string(); + let args = args.collect(); + let arg_names = compute_arg_identifiers(&args)?; + let subscribe = subscribe.name(); + let unsubscribe = unsubscribe.name(); + let client_method = syn::parse_quote!( + #(#attrs)* + pub fn #name(&self, #args) -> impl Future, Error=RpcError> { + let args_tuple = (#(#arg_names,)*); + self.inner.subscribe(#subscribe, args_tuple, #subscription, #unsubscribe, #returns_str) + } + ); + client_methods.push(client_method); } } } @@ -240,3 +260,17 @@ fn get_first_type_argument(args: &syn::PathArguments) -> Option { _ => None, } } + +fn compute_subscription_type(arg: &syn::FnArg) -> syn::Type { + let ty = match arg { + syn::FnArg::Captured(cap) => match &cap.ty { + syn::Type::Path(path) => { + let last = &path.path.segments[&path.path.segments.len() - 1]; + get_first_type_argument(&last.arguments) + } + _ => None, + }, + _ => None, + }; + ty.expect("a subscription needs a return type") +} diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index e08e49393..23fcaf335 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -75,7 +75,7 @@ impl MethodRegistration { } } -const SUBCRIBER_TYPE_IDENT: &str = "Subscriber"; +const SUBSCRIBER_TYPE_IDENT: &str = "Subscriber"; const METADATA_CLOSURE_ARG: &str = "meta"; const SUBSCRIBER_CLOSURE_ARG: &str = "subscriber"; @@ -258,7 +258,7 @@ impl RpcMethod { }); let subscriber_arg = param_types.get(1).and_then(|ty| { if let syn::Type::Path(path) = ty { - if path.path.segments.iter().any(|s| s.ident == SUBCRIBER_TYPE_IDENT) { + if path.path.segments.iter().any(|s| s.ident == SUBSCRIBER_TYPE_IDENT) { Some(ty.clone()) } else { None @@ -369,8 +369,8 @@ pub fn generate_where_clause_serialization_predicates( #[derive(Default)] struct FindTyParams { trait_generics: HashSet, - serialize_type_params: HashSet, - deserialize_type_params: HashSet, + server_to_client_type_params: HashSet, + client_to_server_type_params: HashSet, visiting_return_type: bool, visiting_fn_arg: bool, visiting_subscriber_arg: bool, @@ -385,15 +385,20 @@ pub fn generate_where_clause_serialization_predicates( self.visiting_return_type = false } fn visit_path_segment(&mut self, segment: &'ast syn::PathSegment) { - if self.visiting_return_type && self.trait_generics.contains(&segment.ident) { - self.serialize_type_params.insert(segment.ident.clone()); - } - if self.visiting_fn_arg && self.trait_generics.contains(&segment.ident) && !self.visiting_subscriber_arg { - self.deserialize_type_params.insert(segment.ident.clone()); - } - self.visiting_subscriber_arg = self.visiting_fn_arg && segment.ident == SUBCRIBER_TYPE_IDENT; + self.visiting_subscriber_arg = + self.visiting_subscriber_arg || (self.visiting_fn_arg && segment.ident == SUBSCRIBER_TYPE_IDENT); visit::visit_path_segment(self, segment); - self.visiting_subscriber_arg = false; + self.visiting_subscriber_arg = self.visiting_subscriber_arg && segment.ident != SUBSCRIBER_TYPE_IDENT; + } + fn visit_ident(&mut self, ident: &'ast syn::Ident) { + if self.trait_generics.contains(&ident) { + if self.visiting_return_type || self.visiting_subscriber_arg { + self.server_to_client_type_params.insert(ident.clone()); + } + if self.visiting_fn_arg && !self.visiting_subscriber_arg { + self.client_to_server_type_params.insert(ident.clone()); + } + } } fn visit_fn_arg(&mut self, arg: &'ast syn::FnArg) { self.visiting_fn_arg = true; @@ -415,17 +420,17 @@ pub fn generate_where_clause_serialization_predicates( let mut bounds: Punctuated = parse_quote!(Send + Sync + 'static); // add json serialization trait bounds if client { - if visitor.serialize_type_params.contains(&ty.ident) { + if visitor.server_to_client_type_params.contains(&ty.ident) { bounds.push(parse_quote!(_serde::de::DeserializeOwned)) } - if visitor.deserialize_type_params.contains(&ty.ident) { + if visitor.client_to_server_type_params.contains(&ty.ident) { bounds.push(parse_quote!(_serde::Serialize)) } } else { - if visitor.serialize_type_params.contains(&ty.ident) { + if visitor.server_to_client_type_params.contains(&ty.ident) { bounds.push(parse_quote!(_serde::Serialize)) } - if visitor.deserialize_type_params.contains(&ty.ident) { + if visitor.client_to_server_type_params.contains(&ty.ident) { bounds.push(parse_quote!(_serde::de::DeserializeOwned)) } } diff --git a/derive/tests/run-pass/client_only.rs b/derive/tests/run-pass/client_only.rs index bbec014fb..9fed6c578 100644 --- a/derive/tests/run-pass/client_only.rs +++ b/derive/tests/run-pass/client_only.rs @@ -6,7 +6,6 @@ extern crate jsonrpc_derive; use jsonrpc_core::IoHandler; use jsonrpc_core::futures::future::Future; -use jsonrpc_core::futures::sync::mpsc; use jsonrpc_core_client::transports::local; #[rpc(client)] @@ -22,8 +21,8 @@ pub trait Rpc { fn main() { let fut = { - let mut handler = IoHandler::new(); - let (client, rpc_client) = local::connect::(handler); + let handler = IoHandler::new(); + let (client, _rpc_client) = local::connect::(handler); client .add(5, 6) .map(|res| println!("5 + 6 = {}", res)) diff --git a/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs new file mode 100644 index 000000000..28204d3df --- /dev/null +++ b/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs @@ -0,0 +1,54 @@ +#[macro_use] +extern crate serde; +extern crate jsonrpc_core; +extern crate jsonrpc_core_client; +extern crate jsonrpc_pubsub; +#[macro_use] +extern crate jsonrpc_derive; + +use std::sync::Arc; +use jsonrpc_core::Result; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, Session, PubSubHandler}; + +#[derive(Serialize, Deserialize)] +pub struct Wrapper { + inner: T, + inner2: U, +} + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: Subscriber>); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, a: Option, b: SubscriptionId) -> Result; +} + +#[derive(Serialize, Deserialize)] +struct SerializeAndDeserialize { + foo: String, +} + +struct RpcImpl; +impl Rpc for RpcImpl { + type Metadata = Arc; + + fn subscribe(&self, _: Self::Metadata, _: Subscriber>) { + unimplemented!(); + } + + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result { + unimplemented!(); + } +} + +fn main() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl; + io.extend_with(rpc.to_delegate()); +} diff --git a/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs new file mode 100644 index 000000000..49e928108 --- /dev/null +++ b/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs @@ -0,0 +1,48 @@ +#[macro_use] +extern crate serde; +extern crate jsonrpc_core; +extern crate jsonrpc_core_client; +extern crate jsonrpc_pubsub; +#[macro_use] +extern crate jsonrpc_derive; + +use std::sync::Arc; +use jsonrpc_core::Result; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, Session, PubSubHandler}; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: Subscriber); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, a: Option, b: SubscriptionId) -> Result; +} + +#[derive(Serialize, Deserialize)] +struct SerializeAndDeserialize { + foo: String, +} + +struct RpcImpl; +impl Rpc for RpcImpl { + type Metadata = Arc; + + fn subscribe(&self, _: Self::Metadata, _: Subscriber) { + unimplemented!(); + } + + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result { + unimplemented!(); + } +} + +fn main() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl; + io.extend_with(rpc.to_delegate()); +} diff --git a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs index 65af085d9..e6400e498 100644 --- a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use jsonrpc_core::Result; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, Session, PubSubHandler}; -#[rpc] +#[rpc(server)] pub trait Rpc { type Metadata; diff --git a/http/src/handler.rs b/http/src/handler.rs index aebd2547e..9fb6d4019 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -21,7 +21,7 @@ pub struct ServerHandler = middleware::Noop> cors_domains: CorsDomains, cors_max_age: Option, cors_allowed_headers: cors::AccessControlAllowHeaders, - middleware: Arc, + middleware: Arc, rest_api: RestApi, health_api: Option<(String, String)>, max_request_body_size: usize, @@ -36,7 +36,7 @@ impl> ServerHandler { cors_max_age: Option, cors_allowed_headers: cors::AccessControlAllowHeaders, allowed_hosts: AllowedHosts, - middleware: Arc, + middleware: Arc, rest_api: RestApi, health_api: Option<(String, String)>, max_request_body_size: usize, @@ -114,7 +114,7 @@ impl> Service for ServerHandler { pub enum Handler> { Rpc(RpcHandler), Err(Option), - Middleware(Box, Error = hyper::Error> + Send>), + Middleware(Box, Error = hyper::Error> + Send>), } impl> Future for Handler { diff --git a/http/src/lib.rs b/http/src/lib.rs index 00545d755..a25eeebbd 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -18,7 +18,7 @@ //! } //! ``` -#![warn(missing_docs)] +#![deny(missing_docs)] use jsonrpc_server_utils as server_utils; use net2; @@ -71,7 +71,7 @@ pub enum RequestMiddlewareAction { /// Should standard hosts validation be performed? should_validate_hosts: bool, /// a future for server response - response: Box, Error = hyper::Error> + Send>, + response: Box, Error = hyper::Error> + Send>, }, } @@ -157,7 +157,7 @@ pub struct Rpc = jsonrpc::m /// RPC Handler pub handler: Arc>, /// Metadata extractor - pub extractor: Arc>, + pub extractor: Arc>, } impl> Clone for Rpc { @@ -194,8 +194,8 @@ pub enum RestApi { pub struct ServerBuilder = jsonrpc::middleware::Noop> { handler: Arc>, executor: UninitializedExecutor, - meta_extractor: Arc>, - request_middleware: Arc, + meta_extractor: Arc>, + request_middleware: Arc, cors_domains: CorsDomains, cors_max_age: Option, allowed_headers: cors::AccessControlAllowHeaders, @@ -295,6 +295,7 @@ impl> ServerBuilder { /// /// Panics when set to `0`. #[cfg(not(unix))] + #[allow(unused_mut)] pub fn threads(mut self, _threads: usize) -> Self { warn!("Multi-threaded server is not available on Windows. Falling back to single thread."); self @@ -460,13 +461,17 @@ fn recv_address(local_addr_rx: mpsc::Receiver>) -> io::Re } fn serve>( - signals: (oneshot::Receiver<()>, mpsc::Sender>, oneshot::Sender<()>), + signals: ( + oneshot::Receiver<()>, + mpsc::Sender>, + oneshot::Sender<()>, + ), executor: tokio::runtime::TaskExecutor, addr: SocketAddr, cors_domains: CorsDomains, cors_max_age: Option, allowed_headers: cors::AccessControlAllowHeaders, - request_middleware: Arc, + request_middleware: Arc, allowed_hosts: AllowedHosts, jsonrpc_handler: Rpc, rest_api: RestApi, @@ -476,89 +481,89 @@ fn serve>( max_request_body_size: usize, ) { let (shutdown_signal, local_addr_tx, done_tx) = signals; - executor.spawn(future::lazy(move || { - let handle = tokio::reactor::Handle::default(); - - let bind = move || { - let listener = match addr { - SocketAddr::V4(_) => net2::TcpBuilder::new_v4()?, - SocketAddr::V6(_) => net2::TcpBuilder::new_v6()?, + executor.spawn( + future::lazy(move || { + let handle = tokio::reactor::Handle::default(); + + let bind = move || { + let listener = match addr { + SocketAddr::V4(_) => net2::TcpBuilder::new_v4()?, + SocketAddr::V6(_) => net2::TcpBuilder::new_v6()?, + }; + configure_port(reuse_port, &listener)?; + listener.reuse_address(true)?; + listener.bind(&addr)?; + let listener = listener.listen(1024)?; + let listener = tokio::net::TcpListener::from_std(listener, &handle)?; + // Add current host to allowed headers. + // NOTE: we need to use `l.local_addr()` instead of `addr` + // it might be different! + let local_addr = listener.local_addr()?; + + Ok((listener, local_addr)) }; - configure_port(reuse_port, &listener)?; - listener.reuse_address(true)?; - listener.bind(&addr)?; - let listener = listener.listen(1024)?; - let listener = tokio::net::TcpListener::from_std(listener, &handle)?; - // Add current host to allowed headers. - // NOTE: we need to use `l.local_addr()` instead of `addr` - // it might be different! - let local_addr = listener.local_addr()?; - - Ok((listener, local_addr)) - }; - let bind_result = match bind() { - Ok((listener, local_addr)) => { - // Send local address - match local_addr_tx.send(Ok(local_addr)) { - Ok(_) => futures::future::ok((listener, local_addr)), - Err(_) => { - warn!( - "Thread {:?} unable to reach receiver, closing server", - thread::current().name() - ); - futures::future::err(()) + let bind_result = match bind() { + Ok((listener, local_addr)) => { + // Send local address + match local_addr_tx.send(Ok(local_addr)) { + Ok(_) => futures::future::ok((listener, local_addr)), + Err(_) => { + warn!( + "Thread {:?} unable to reach receiver, closing server", + thread::current().name() + ); + futures::future::err(()) + } } } - } - Err(err) => { - // Send error - let _send_result = local_addr_tx.send(Err(err)); + Err(err) => { + // Send error + let _send_result = local_addr_tx.send(Err(err)); - futures::future::err(()) - } - }; + futures::future::err(()) + } + }; - bind_result.and_then(move |(listener, local_addr)| { - let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); - - let mut http = server::conn::Http::new(); - http.keep_alive(keep_alive); - let tcp_stream = SuspendableStream::new(listener.incoming()); - - tcp_stream - .for_each(move |socket| { - let service = ServerHandler::new( - jsonrpc_handler.clone(), - cors_domains.clone(), - cors_max_age, - allowed_headers.clone(), - allowed_hosts.clone(), - request_middleware.clone(), - rest_api, - health_api.clone(), - max_request_body_size, - keep_alive, - ); - tokio::spawn( - http.serve_connection(socket, service) - .map_err(|e| error!("Error serving connection: {:?}", e)), - ); - Ok(()) - }) - .map_err(|e| { - warn!("Incoming streams error, closing sever: {:?}", e); - }) - .select(shutdown_signal - .map_err(|e| { - debug!("Shutdown signaller dropped, closing server: {:?}", e); - })) - .map(|_| ()) - .map_err(|_| ()) + bind_result.and_then(move |(listener, local_addr)| { + let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); + + let mut http = server::conn::Http::new(); + http.keep_alive(keep_alive); + let tcp_stream = SuspendableStream::new(listener.incoming()); + + tcp_stream + .for_each(move |socket| { + let service = ServerHandler::new( + jsonrpc_handler.clone(), + cors_domains.clone(), + cors_max_age, + allowed_headers.clone(), + allowed_hosts.clone(), + request_middleware.clone(), + rest_api, + health_api.clone(), + max_request_body_size, + keep_alive, + ); + tokio::spawn( + http.serve_connection(socket, service) + .map_err(|e| error!("Error serving connection: {:?}", e)), + ); + Ok(()) + }) + .map_err(|e| { + warn!("Incoming streams error, closing sever: {:?}", e); + }) + .select(shutdown_signal.map_err(|e| { + debug!("Shutdown signaller dropped, closing server: {:?}", e); + })) + .map(|_| ()) + .map_err(|_| ()) + }) }) - }).and_then(|_| { - done_tx.send(()) - })); + .and_then(|_| done_tx.send(())), + ); } #[cfg(unix)] @@ -586,12 +591,12 @@ pub struct CloseHandle(Arc)>>>>) impl CloseHandle { /// Shutdown a running server pub fn close(self) { - if let Some(executors) = self.0.lock().take() { - for (executor, closer) in executors { - executor.close(); - let _ = closer.send(()); - } - } + if let Some(executors) = self.0.lock().take() { + for (executor, closer) in executors { + executor.close(); + let _ = closer.send(()); + } + } } } diff --git a/http/src/tests.rs b/http/src/tests.rs index 1730be94d..29ce30b58 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -1420,7 +1420,8 @@ fn close_handle_makes_wait_return() { close_handle.close(); - rx.recv_timeout(Duration::from_secs(10)).expect("Expected server to close"); + rx.recv_timeout(Duration::from_secs(10)) + .expect("Expected server to close"); } #[test] diff --git a/ipc/src/lib.rs b/ipc/src/lib.rs index 89a8ed54a..ab9e73130 100644 --- a/ipc/src/lib.rs +++ b/ipc/src/lib.rs @@ -1,6 +1,6 @@ //! Cross-platform JSON-RPC IPC transport. -#![warn(missing_docs)] +#![deny(missing_docs)] use jsonrpc_server_utils as server_utils; diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 3ed1b88be..506007ee1 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -1,6 +1,3 @@ -#![allow(deprecated)] - -use std; use std::sync::Arc; use crate::jsonrpc::futures::sync::{mpsc, oneshot}; @@ -50,8 +47,8 @@ impl> tokio_service::Service for Service { /// IPC server builder pub struct ServerBuilder = middleware::Noop> { handler: Arc>, - meta_extractor: Arc>, - session_stats: Option>, + meta_extractor: Arc>, + session_stats: Option>, executor: reactor::UninitializedExecutor, incoming_separator: codecs::Separator, outgoing_separator: codecs::Separator, @@ -154,7 +151,7 @@ impl> ServerBuilder { } } - let endpoint_handle = Handle::current(); + let endpoint_handle = Handle::default(); let connections = match endpoint.incoming(&endpoint_handle) { Ok(connections) => connections, Err(e) => { diff --git a/pubsub/examples/pubsub_simple.rs b/pubsub/examples/pubsub_simple.rs index cc9b30b54..bfd8fadac 100644 --- a/pubsub/examples/pubsub_simple.rs +++ b/pubsub/examples/pubsub_simple.rs @@ -10,8 +10,10 @@ use jsonrpc_core::futures::Future; /// To test the server: /// /// ```bash -/// $ netcat localhost 3030 - -/// {"id":1,"jsonrpc":"2.0","method":"hello_subscribe","params":[10]} +/// $ netcat localhost 3030 +/// > {"id":1,"jsonrpc":"2.0","method":"subscribe_hello","params":null} +/// < {"id":1,"jsonrpc":"2.0","result":5,"id":1} +/// < {"jsonrpc":"2.0","method":"hello","params":[10]} /// /// ``` fn main() { diff --git a/pubsub/src/lib.rs b/pubsub/src/lib.rs index 18f18071b..a4f74eca5 100644 --- a/pubsub/src/lib.rs +++ b/pubsub/src/lib.rs @@ -1,6 +1,6 @@ //! Publish-Subscribe extension for JSON-RPC -#![warn(missing_docs)] +#![deny(missing_docs)] use jsonrpc_core as core; diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index fa004cf6a..473f0c47d 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -15,9 +15,9 @@ use crate::types::{PubSubMetadata, SinkResult, SubscriptionId, TransportError, T /// RPC client session /// Keeps track of active subscriptions and unsubscribes from them upon dropping. pub struct Session { - active_subscriptions: Mutex>>, + active_subscriptions: Mutex>>, transport: TransportSender, - on_drop: Mutex>>, + on_drop: Mutex>>, } impl fmt::Debug for Session { diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index be5fefc49..52c2f40bd 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -53,6 +53,20 @@ impl SubscriptionId { _ => None, } } + + /// Parses an output into a subscription id. + pub fn parse_output(output: &core::Output) -> Option { + match output { + core::Output::Notification(n) => match &n.params { + core::Params::Map(map) => match map.get("subscription") { + Some(value) => Self::parse_value(value), + None => None, + } + _ => None + } + _ => None + } + } } impl From for SubscriptionId { diff --git a/server-utils/src/lib.rs b/server-utils/src/lib.rs index ec51184ff..4a1e2a0e1 100644 --- a/server-utils/src/lib.rs +++ b/server-utils/src/lib.rs @@ -1,6 +1,6 @@ //! JSON-RPC servers utilities. -#![warn(missing_docs)] +#![deny(missing_docs)] #[macro_use] extern crate log; diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index 09753528f..3982abc0a 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -15,7 +15,7 @@ //! } //! ``` -#![warn(missing_docs)] +#![deny(missing_docs)] use tokio; use tokio_stdin_stdout; diff --git a/tcp/src/lib.rs b/tcp/src/lib.rs index bf0af845c..3c0c1c847 100644 --- a/tcp/src/lib.rs +++ b/tcp/src/lib.rs @@ -17,7 +17,7 @@ //! } //! ``` -#![warn(missing_docs)] +#![deny(missing_docs)] use jsonrpc_server_utils as server_utils; diff --git a/tcp/src/server.rs b/tcp/src/server.rs index fff4f8327..347abd341 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -17,7 +17,7 @@ use crate::service::Service; pub struct ServerBuilder = middleware::Noop> { executor: reactor::UninitializedExecutor, handler: Arc>, - meta_extractor: Arc>, + meta_extractor: Arc>, channels: Arc, incoming_separator: codecs::Separator, outgoing_separator: codecs::Separator, diff --git a/test/src/lib.rs b/test/src/lib.rs index 8d07f4fa9..699d76d2a 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -43,7 +43,7 @@ //! } //! ``` -#![warn(missing_docs)] +#![deny(missing_docs)] extern crate jsonrpc_core as rpc; use serde; @@ -131,6 +131,10 @@ impl Rpc { Encoding::Compact => serde_json::to_string(&error), Encoding::Pretty => serde_json::to_string_pretty(&error), }, + response::Output::Notification(notification) => match encoding { + Encoding::Compact => serde_json::to_string(¬ification), + Encoding::Pretty => serde_json::to_string_pretty(¬ification), + }, } .expect("Serialization is infallible; qed"); diff --git a/ws/src/error.rs b/ws/src/error.rs index 1cc589248..553c27c50 100644 --- a/ws/src/error.rs +++ b/ws/src/error.rs @@ -27,7 +27,7 @@ impl fmt::Display for Error { } impl error::Error for Error { - fn source(&self) -> Option<&(error::Error + 'static)> { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::Io(io) => Some(io), Error::WsError(ws) => Some(ws), diff --git a/ws/src/lib.rs b/ws/src/lib.rs index a5d58fc55..aeae08a5d 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -1,6 +1,6 @@ //! `WebSockets` server. -#![warn(missing_docs)] +#![deny(missing_docs)] use jsonrpc_server_utils as server_utils; diff --git a/ws/src/server.rs b/ws/src/server.rs index 11a4b68a7..4fb83d67a 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -50,11 +50,11 @@ impl Server { pub fn start>( addr: &SocketAddr, handler: Arc>, - meta_extractor: Arc>, + meta_extractor: Arc>, allowed_origins: Option>, allowed_hosts: Option>, - request_middleware: Option>, - stats: Option>, + request_middleware: Option>, + stats: Option>, executor: UninitializedExecutor, max_connections: usize, max_payload_bytes: usize, diff --git a/ws/src/server_builder.rs b/ws/src/server_builder.rs index bae91a1d7..1af8e6763 100644 --- a/ws/src/server_builder.rs +++ b/ws/src/server_builder.rs @@ -16,11 +16,11 @@ use crate::session; /// Builder for `WebSockets` server pub struct ServerBuilder> { handler: Arc>, - meta_extractor: Arc>, + meta_extractor: Arc>, allowed_origins: Option>, allowed_hosts: Option>, - request_middleware: Option>, - session_stats: Option>, + request_middleware: Option>, + session_stats: Option>, executor: UninitializedExecutor, max_connections: usize, max_payload_bytes: usize, diff --git a/ws/src/session.rs b/ws/src/session.rs index ee2ed9082..63c40acb8 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -148,11 +148,11 @@ pub struct Session> { active: Arc, context: metadata::RequestContext, handler: Arc>, - meta_extractor: Arc>, + meta_extractor: Arc>, allowed_origins: Option>, allowed_hosts: Option>, - request_middleware: Option>, - stats: Option>, + request_middleware: Option>, + stats: Option>, metadata: Option, executor: TaskExecutor, task_slab: Arc, @@ -297,22 +297,22 @@ impl> ws::Handler for Session { pub struct Factory> { session_id: SessionId, handler: Arc>, - meta_extractor: Arc>, + meta_extractor: Arc>, allowed_origins: Option>, allowed_hosts: Option>, - request_middleware: Option>, - stats: Option>, + request_middleware: Option>, + stats: Option>, executor: TaskExecutor, } impl> Factory { pub fn new( handler: Arc>, - meta_extractor: Arc>, + meta_extractor: Arc>, allowed_origins: Option>, allowed_hosts: Option>, - request_middleware: Option>, - stats: Option>, + request_middleware: Option>, + stats: Option>, executor: TaskExecutor, ) -> Self { Factory { From 584327bd561c79ae8a70c3bf3fb261e5912313a5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 8 Jul 2019 15:04:12 +0100 Subject: [PATCH 056/149] Bump version (#446) * Replace depedency version when field not in first position * Bump version -> lucky 13.0 * Revert "Replace depedency version when field not in first position" This reverts commit 7c3cb40c * Put version field first to make bump_version script work * Revert "Bump version -> lucky 13.0" This reverts commit fde40536 * Bump version -> 12.1 --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 8 ++++---- core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 18 files changed, 46 insertions(+), 46 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 82fb8247a..e3a4510dc 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" categories = [ "asynchronous", @@ -24,7 +24,7 @@ http = ["jsonrpc-client-transports/http"] ws = ["jsonrpc-client-transports/ws"] [dependencies] -jsonrpc-client-transports = { version = "12.0", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "12.1", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 308934e36..06d3a4212 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" categories = [ "asynchronous", @@ -32,8 +32,8 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "12.0", path = "../../core" } -jsonrpc-pubsub = { version = "12.0", path = "../../pubsub" } +jsonrpc-core = { version = "12.1", path = "../../core" } +jsonrpc-pubsub = { version = "12.1", path = "../../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -42,7 +42,7 @@ websocket = { version = "0.22", optional = true } [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "12.0", path = "../../http" } +jsonrpc-http-server = { version = "12.1", path = "../../http" } lazy_static = "1.0" env_logger = "0.6" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index fdc705af9..d690b6ddf 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index fb5e5db02..94c85be96 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "12.0", path = "../core" } -jsonrpc-core-client = { version = "12.0", path = "../core-client" } -jsonrpc-pubsub = { version = "12.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "12.0", path = "../tcp" } +jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-core-client = { version = "12.1", path = "../core-client" } +jsonrpc-pubsub = { version = "12.1", path = "../pubsub" } +jsonrpc-tcp-server = { version = "12.1", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 4dcd8ff34..d46c1734a 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "12.0", path = "../core" } -jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } +jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index 8a5a5e4c7..51a012bb4 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "12.0" +jsonrpc-http-server = "12.1" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index c517347f2..b50621628 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "12.0", path = "../core" } -jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } +jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } parity-tokio-ipc = "0.1" parking_lot = "0.8" diff --git a/ipc/README.md b/ipc/README.md index 1600826d9..d55326a43 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "12.0" +jsonrpc-ipc-server = "12.1" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 96c9cf33a..f4f8dff74 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] log = "0.4" parking_lot = "0.8" -jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-core = { version = "12.1", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "12.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "12.1", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 11803d693..d0783da05 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "12.0", path = "../../core" } -jsonrpc-pubsub = { version = "12.0", path = "../" } -jsonrpc-ws-server = { version = "12.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "12.0", path = "../../ipc" } +jsonrpc-core = { version = "12.1", path = "../../core" } +jsonrpc-pubsub = { version = "12.1", path = "../" } +jsonrpc-ws-server = { version = "12.1", path = "../../ws" } +jsonrpc-ipc-server = { version = "12.1", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index d9cc823e6..7fff81bb9 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-core = { version = "12.1", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 5dd60988e..fc2735cd2 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "12.0", path = "../core" } +jsonrpc-core = { version = "12.1", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index 74c6b4c27..6039873ed 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "12.0" +jsonrpc-stdio-server = "12.1" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 6494020d5..ae3f2936c 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] log = "0.4" parking_lot = "0.8" tokio-service = "0.1" -jsonrpc-core = { version = "12.0", path = "../core" } -jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } +jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index b1a3dd114..5746d76a5 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "12.0" +jsonrpc-tcp-server = "12.1" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index e20089a2c..376408174 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "12.0.0" +version = "12.1.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { path = "../core", version = "12.0" } -jsonrpc-core-client = { path = "../core-client", version = "12.0" } -jsonrpc-pubsub = { path = "../pubsub", version = "12.0" } +jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-core-client = { version = "12.1", path = "../core-client" } +jsonrpc-pubsub = { version = "12.1", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { path = "../derive", version = "12.0" } +jsonrpc-derive = { version = "12.1", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index eeaa29d0a..202602d33 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.0.0" +version = "12.1.0" [dependencies] -jsonrpc-core = { version = "12.0", path = "../core" } -jsonrpc-server-utils = { version = "12.0", path = "../server-utils" } +jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } log = "0.4" parking_lot = "0.8" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 461c2d8a1..54aab1b4e 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "12.0" +jsonrpc-ws-server = "12.1" ``` `main.rs` From 091577fece722a3471da7eeb3c892ba707063f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 11 Jul 2019 11:55:17 +0200 Subject: [PATCH 057/149] Strict and non-strict parsing for `core` (#448) * Strict and non-strict mode. * Remove the non-strict mode. * Reformat. --- core-client/transports/src/lib.rs | 5 +- .../transports/src/transports/duplex.rs | 62 ++++++++++++------- .../transports/src/transports/local.rs | 2 +- core-client/transports/src/transports/mod.rs | 4 +- core/examples/params.rs | 2 +- core/src/types/error.rs | 1 + core/src/types/id.rs | 1 + core/src/types/params.rs | 1 + core/src/types/request.rs | 4 +- core/src/types/response.rs | 27 ++++++++ pubsub/src/types.rs | 8 +-- 11 files changed, 85 insertions(+), 32 deletions(-) diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index b0637b9ff..8ff7336c6 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -432,7 +432,10 @@ mod tests { }); tokio::run(fut); assert_eq!(called.load(Ordering::SeqCst), true); - assert!(!received.lock().unwrap().is_empty(), "Expected at least one received item."); + assert!( + !received.lock().unwrap().is_empty(), + "Expected at least one received item." + ); } } diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index a8bc70349..5c604147d 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -25,11 +25,7 @@ struct Subscription { } impl Subscription { - fn new( - channel: mpsc::Sender>, - notification: String, - unsubscribe: String, - ) -> Self { + fn new(channel: mpsc::Sender>, notification: String, unsubscribe: String) -> Self { Subscription { id: None, notification, @@ -120,7 +116,11 @@ where RpcMessage::Call(msg) => { let (id, request_str) = self.request_builder.call_request(&msg); - if self.pending_requests.insert(id.clone(), PendingRequest::Call(msg.sender)).is_some() { + if self + .pending_requests + .insert(id.clone(), PendingRequest::Call(msg.sender)) + .is_some() + { log::error!("reuse of request id {:?}", id); } request_str @@ -132,13 +132,14 @@ where notification, unsubscribe, } = msg.subscription; - let (id, request_str) = self.request_builder.subscribe_request( - subscribe, - subscribe_params, - ); + let (id, request_str) = self.request_builder.subscribe_request(subscribe, subscribe_params); log::debug!("subscribing to {}", notification); let subscription = Subscription::new(msg.sender, notification, unsubscribe); - if self.pending_requests.insert(id.clone(), PendingRequest::Subscription(subscription)).is_some() { + if self + .pending_requests + .insert(id.clone(), PendingRequest::Subscription(subscription)) + .is_some() + { log::error!("reuse of request id {:?}", id); } request_str @@ -166,7 +167,13 @@ where }; log::debug!("incoming: {}", response_str); for (id, result, method, sid) in super::parse_response(&response_str)? { - log::debug!("id: {:?} (sid: {:?}) result: {:?} method: {:?}", id, sid, result, method); + log::debug!( + "id: {:?} (sid: {:?}) result: {:?} method: {:?}", + id, + sid, + result, + method + ); self.incoming.push_back((id, result, method, sid)); } } @@ -184,7 +191,7 @@ where tx.send(result) .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?; continue; - }, + } // It was a subscription request, // turn it into a proper subscription. Some(PendingRequest::Subscription(mut subscription)) => { @@ -193,10 +200,14 @@ where if let Some(sid) = sid { subscription.id = Some(sid.clone()); - if self.subscriptions.insert((sid.clone(), method.clone()), subscription).is_some() { + if self + .subscriptions + .insert((sid.clone(), method.clone()), subscription) + .is_some() + { log::warn!( "Overwriting existing subscription under {:?} ({:?}). \ - Seems that server returned the same subscription id.", + Seems that server returned the same subscription id.", sid, method, ); @@ -210,12 +221,12 @@ where ); } continue; - }, + } // It's not a pending request nor a notification None if sid_and_method.is_none() => { log::warn!("Got unexpected response with id {:?} ({:?})", id, sid_and_method); continue; - }, + } // just fall-through in case it's a notification None => {} }; @@ -229,7 +240,10 @@ where if let Some(subscription) = self.subscriptions.get_mut(&sid_and_method) { match subscription.channel.poll_ready() { Ok(Async::Ready(())) => { - subscription.channel.try_send(result).expect("The channel is ready; qed"); + subscription + .channel + .try_send(result) + .expect("The channel is ready; qed"); } Ok(Async::NotReady) => { let (sid, method) = sid_and_method; @@ -237,12 +251,14 @@ where break; } Err(_) => { - let subscription = self.subscriptions + let subscription = self + .subscriptions .remove(&sid_and_method) .expect("Subscription was just polled; qed"); - let sid = subscription.id - .expect("Every subscription that ends up in `self.subscriptions` has id already \ - assigned; assignment happens during response to subscribe request."); + let sid = subscription.id.expect( + "Every subscription that ends up in `self.subscriptions` has id already \ + assigned; assignment happens during response to subscribe request.", + ); let (_id, request_str) = self.request_builder.unsubscribe_request(subscription.unsubscribe, sid); log::debug!("outgoing: {}", request_str); @@ -253,7 +269,7 @@ where } else { log::warn!("Received unexpected subscription notification: {:?}", sid_and_method); } - }, + } None => break, } } diff --git a/core-client/transports/src/transports/local.rs b/core-client/transports/src/transports/local.rs index a69e47680..5dd1dc73c 100644 --- a/core-client/transports/src/transports/local.rs +++ b/core-client/transports/src/transports/local.rs @@ -25,7 +25,7 @@ where /// Creates a new `LocalRpc` with default metadata. pub fn new(handler: THandler) -> Self where - TMetadata: Default + TMetadata: Default, { Self::with_metadata(handler, Default::default()) } diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index 94462918e..fda2240d9 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -61,7 +61,9 @@ impl RequestBuilder { } /// Parse raw string into JSON values, together with the request Id -pub fn parse_response(response: &str) -> Result, Option, Option)>, RpcError> { +pub fn parse_response( + response: &str, +) -> Result, Option, Option)>, RpcError> { serde_json::from_str::(&response) .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) .map(|response| { diff --git a/core/examples/params.rs b/core/examples/params.rs index 38b183e4d..d528b3578 100644 --- a/core/examples/params.rs +++ b/core/examples/params.rs @@ -1,5 +1,5 @@ use jsonrpc_core::*; -use serde::Deserialize; +use serde_derive::Deserialize; #[derive(Deserialize)] struct HelloParams { diff --git a/core/src/types/error.rs b/core/src/types/error.rs index 12408d86c..9da0f916b 100644 --- a/core/src/types/error.rs +++ b/core/src/types/error.rs @@ -83,6 +83,7 @@ impl Serialize for ErrorCode { /// Error object as defined in Spec #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Error { /// Code pub code: ErrorCode, diff --git a/core/src/types/id.rs b/core/src/types/id.rs index 603c93be9..79a3e7897 100644 --- a/core/src/types/id.rs +++ b/core/src/types/id.rs @@ -2,6 +2,7 @@ /// Request Id #[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum Id { /// No id (notification) diff --git a/core/src/types/params.rs b/core/src/types/params.rs index 7f9830f25..de32fd04e 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -8,6 +8,7 @@ use super::{Error, Value}; /// Request parameters #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum Params { /// No parameters diff --git a/core/src/types/request.rs b/core/src/types/request.rs index 5514f8b7a..61905cffb 100644 --- a/core/src/types/request.rs +++ b/core/src/types/request.rs @@ -72,6 +72,7 @@ impl From for Call { /// Represents jsonrpc request. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum Request { /// Single request (call) @@ -187,7 +188,7 @@ mod tests { let s = r#"{"jsonrpc": "2.0", "method": "update", "params": [1,2], "id": 1}"#; let deserialized: Result = serde_json::from_str(s); - assert!(deserialized.is_err()) + assert!(deserialized.is_err()); } #[test] @@ -285,6 +286,7 @@ mod tests { let s = r#"{"id":120,"method":"my_method","params":["foo", "bar"],"extra_field":[]}"#; let deserialized: Request = serde_json::from_str(s).unwrap(); + match deserialized { Request::Single(Call::Invalid { id: Id::Num(120) }) => {} _ => panic!("Request wrongly deserialized: {:?}", deserialized), diff --git a/core/src/types/response.rs b/core/src/types/response.rs index f84c638d3..4ec5b3c34 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -4,6 +4,7 @@ use crate::Result as CoreResult; /// Successful response #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Success { /// Protocol version #[serde(skip_serializing_if = "Option::is_none")] @@ -16,6 +17,7 @@ pub struct Success { /// Unsuccessful response #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Failure { /// Protocol Version #[serde(skip_serializing_if = "Option::is_none")] @@ -28,6 +30,7 @@ pub struct Failure { /// Represents output - failure or success #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum Output { /// Notification @@ -114,6 +117,7 @@ impl From for CoreResult { /// Synchronous response #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] #[serde(untagged)] pub enum Response { /// Single response @@ -290,3 +294,26 @@ fn notification_deserialize() { })) ); } + +#[test] +fn handle_incorrect_responses() { + use serde_json; + + let dsr = r#" +{ + "id": 2, + "jsonrpc": "2.0", + "result": "0x62d3776be72cc7fa62cad6fe8ed873d9bc7ca2ee576e400d987419a3f21079d5", + "error": { + "message": "VM Exception while processing transaction: revert", + "code": -32000, + "data": {} + } +}"#; + + let deserialized: Result = serde_json::from_str(dsr); + assert!( + deserialized.is_err(), + "Expected error when deserializing invalid payload." + ); +} diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index 52c2f40bd..54e33263f 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -61,10 +61,10 @@ impl SubscriptionId { core::Params::Map(map) => match map.get("subscription") { Some(value) => Self::parse_value(value), None => None, - } - _ => None - } - _ => None + }, + _ => None, + }, + _ => None, } } } From 38f8e911ef3a380fb01f79de4dab92cadd7454aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 12 Jul 2019 17:42:40 +0200 Subject: [PATCH 058/149] Introduce IoDelegateExtension. (#447) * Introduce IoDelegateExtension. * Implement for empty tuple as well. * Allow one delegate to extend another. * Remove stray new line. --- core/src/delegates.rs | 19 ++++- core/src/io.rs | 102 +++++++++++++++++++++++- core/src/lib.rs | 5 +- derive/examples/generic-trait-bounds.rs | 4 +- pubsub/src/delegates.rs | 20 ++++- test/src/lib.rs | 4 +- 6 files changed, 139 insertions(+), 15 deletions(-) diff --git a/core/src/delegates.rs b/core/src/delegates.rs index f487aa202..20b2ff836 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -145,12 +145,25 @@ where } } -impl Into>> for IoDelegate +impl crate::io::IoHandlerExtension for IoDelegate where T: Send + Sync + 'static, M: Metadata, { - fn into(self) -> HashMap> { - self.methods + fn augment>(self, handler: &mut crate::MetaIoHandler) { + handler.extend_with(self.methods) + } +} + +impl IntoIterator for IoDelegate +where + T: Send + Sync + 'static, + M: Metadata, +{ + type Item = (String, RemoteProcedure); + type IntoIter = std::collections::hash_map::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.methods.into_iter() } } diff --git a/core/src/io.rs b/core/src/io.rs index adf28c685..ac4cc8675 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -155,9 +155,9 @@ impl> MetaIoHandler { /// Extend this `MetaIoHandler` with methods defined elsewhere. pub fn extend_with(&mut self, methods: F) where - F: Into>>, + F: IntoIterator)>, { - self.methods.extend(methods.into()) + self.methods.extend(methods) } /// Handle given request synchronously - will block until response is available. @@ -289,6 +289,69 @@ impl> MetaIoHandler { } } +/// A type that can augment `MetaIoHandler`. +/// +/// This allows your code to accept generic extensions for `IoHandler` +/// and compose them to create the RPC server. +pub trait IoHandlerExtension { + /// Extend given `handler` with additional methods. + fn augment>(self, handler: &mut MetaIoHandler); +} + +macro_rules! impl_io_handler_extension { + ($( $x:ident, )*) => { + impl IoHandlerExtension for ($( $x, )*) where + M: Metadata, + $( + $x: IoHandlerExtension, + )* + { + #[allow(unused)] + fn augment>(self, handler: &mut MetaIoHandler) { + #[allow(non_snake_case)] + let ( + $( $x, )* + ) = self; + $( + $x.augment(handler); + )* + } + } + } +} + +impl_io_handler_extension!(); +impl_io_handler_extension!(A,); +impl_io_handler_extension!(A,B,); +impl_io_handler_extension!(A,B,C,); +impl_io_handler_extension!(A,B,C,D,); +impl_io_handler_extension!(A,B,C,D,E,); +impl_io_handler_extension!(A,B,C,D,E,F,); +impl_io_handler_extension!(A,B,C,D,E,F,G,); +impl_io_handler_extension!(A,B,C,D,E,F,G,H,); +impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,); +impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,J,); +impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,J,K,); +impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,J,K,L,); + +impl IoHandlerExtension for Vec<(String, RemoteProcedure)> { + fn augment>(self, handler: &mut MetaIoHandler) { + handler.methods.extend(self) + } +} + +impl IoHandlerExtension for HashMap> { + fn augment>(self, handler: &mut MetaIoHandler) { + handler.methods.extend(self) + } +} + +impl> IoHandlerExtension for MetaIoHandler { + fn augment>(self, handler: &mut MetaIoHandler) { + handler.methods.extend(self.methods) + } +} + /// Simplified `IoHandler` with no `Metadata` associated with each request. #[derive(Debug, Default)] pub struct IoHandler(MetaIoHandler); @@ -350,6 +413,12 @@ impl From for MetaIoHandler<()> { } } +impl IoHandlerExtension for IoHandler { + fn augment>(self, handler: &mut MetaIoHandler) { + handler.methods.extend(self.0.methods) + } +} + fn read_request(request_str: &str) -> Result { serde_json::from_str(request_str).map_err(|_| Error::new(ErrorCode::ParseError)) } @@ -473,4 +542,33 @@ mod tests { assert!(is_send_sync(io)) } + + #[test] + fn test_extending_by_multiple_delegates() { + use std::sync::Arc; + use crate::delegates::IoDelegate; + use super::IoHandlerExtension; + + struct Test; + impl Test { + fn abc(&self, _p: crate::Params) -> Result { + Ok(5.into()) + } + } + + let mut io = IoHandler::new(); + let mut del1 = IoDelegate::new(Arc::new(Test)); + del1.add_method("rpc_test", Test::abc); + let mut del2 = IoDelegate::new(Arc::new(Test)); + del2.add_method("rpc_test", Test::abc); + + fn augment(x: X, io: &mut IoHandler) { + x.augment(io); + } + + augment( + (del1, del2), + &mut io + ); + } } diff --git a/core/src/lib.rs b/core/src/lib.rs index bb6862035..5b4913acf 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -44,10 +44,13 @@ pub type BoxFuture = Box + Send> /// A Result type. pub type Result = ::std::result::Result; -pub use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple}; +pub use crate::calls::{ + Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple +}; pub use crate::delegates::IoDelegate; pub use crate::io::{ Compatibility, FutureOutput, FutureResponse, FutureResult, FutureRpcResult, IoHandler, MetaIoHandler, + IoHandlerExtension, }; pub use crate::middleware::{Middleware, Noop as NoopMiddleware}; pub use crate::types::*; diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs index ae4dc3a52..39527475d 100644 --- a/derive/examples/generic-trait-bounds.rs +++ b/derive/examples/generic-trait-bounds.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use jsonrpc_core::futures::future::{self, FutureResult}; -use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_core::{Error, IoHandler, Result, IoHandlerExtension}; use jsonrpc_derive::rpc; // One is both parameter and a result so requires both Serialize and DeserializeOwned @@ -57,5 +57,5 @@ impl Rpc for RpcImpl { fn main() { let mut io = IoHandler::new(); - io.extend_with(Rpc::to_delegate(RpcImpl)); + RpcImpl.to_delegate().augment(&mut io); } diff --git a/pubsub/src/delegates.rs b/pubsub/src/delegates.rs index 008a314c9..d2da169fe 100644 --- a/pubsub/src/delegates.rs +++ b/pubsub/src/delegates.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; @@ -131,12 +130,25 @@ where } } -impl Into>> for IoDelegate +impl core::IoHandlerExtension for IoDelegate where T: Send + Sync + 'static, M: Metadata, { - fn into(self) -> HashMap> { - self.inner.into() + fn augment>(self, handler: &mut core::MetaIoHandler) { + handler.extend_with(self.inner) + } +} + +impl IntoIterator for IoDelegate +where + T: Send + Sync + 'static, + M: Metadata, +{ + type Item = (String, RemoteProcedure); + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() } } diff --git a/test/src/lib.rs b/test/src/lib.rs index 699d76d2a..b664e2842 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -49,8 +49,6 @@ extern crate jsonrpc_core as rpc; use serde; use serde_json; -use std::collections::HashMap; - /// Test RPC options. #[derive(Default, Debug)] pub struct Options { @@ -88,7 +86,7 @@ impl Rpc { /// Create a new RPC instance from a single delegate. pub fn new(delegate: D) -> Self where - D: Into>>, + D: IntoIterator)>, { let mut io = rpc::IoHandler::new(); io.extend_with(delegate); From dcaf7e385fdb44986dfa500f156881e49fd6881a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2019 10:29:37 +0200 Subject: [PATCH 059/149] Update websocket requirement from 0.22 to 0.23 (#451) Updates the requirements on [websocket](https://github.com/websockets-rs/rust-websocket) to permit the latest version. - [Release notes](https://github.com/websockets-rs/rust-websocket/releases) - [Commits](https://github.com/websockets-rs/rust-websocket/compare/v0.22.0...v0.23.0) Signed-off-by: dependabot-preview[bot] --- core-client/transports/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 06d3a4212..319826210 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -38,7 +38,7 @@ log = "0.4" serde = "1.0" serde_json = "1.0" tokio = { version = "0.1", optional = true } -websocket = { version = "0.22", optional = true } +websocket = { version = "0.23", optional = true } [dev-dependencies] assert_matches = "1.1" From e567d6de5db59e456f8a0d412e1dc204a848f6f2 Mon Sep 17 00:00:00 2001 From: Eyal Kalderon Date: Tue, 16 Jul 2019 16:47:40 +0800 Subject: [PATCH 060/149] Allow access to raw Params object in jsonrpc-derive (#452) * Add raw_params meta word to jsonrpc-derive crate * Skip deserialization of Params if raw_params is set * Update meta-macros example to include raw_params usage * Add macro test case for raw_params * Update attr-invalid-meta-words UI test --- derive/examples/meta-macros.rs | 15 +++++-- derive/src/rpc_attr.rs | 11 +++-- derive/src/to_delegate.rs | 2 + derive/tests/macros.rs | 45 +++++++++++++++++++ .../tests/ui/attr-invalid-meta-words.stderr | 2 +- 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index b21fda90e..8ba7777ea 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use jsonrpc_core::futures::future::FutureResult; +use jsonrpc_core::types::params::Params; use jsonrpc_core::{futures, Error, MetaIoHandler, Metadata, Result, Value}; use jsonrpc_derive::rpc; @@ -16,7 +17,7 @@ pub trait Rpc { #[rpc(name = "getOne")] fn one(&self) -> Result; - /// Adds two numbers and returns a result + /// Adds two numbers and returns a result. #[rpc(name = "add")] fn add(&self, a: u64, b: u64) -> Result; @@ -24,11 +25,15 @@ pub trait Rpc { #[rpc(name = "mul")] fn mul(&self, a: u64, b: Option) -> Result; - /// Performs asynchronous operation + /// Retrieves and debug prints the underlying `Params` object. + #[rpc(name = "raw", raw_params)] + fn raw(&self, params: Params) -> Result; + + /// Performs an asynchronous operation. #[rpc(name = "callAsync")] fn call(&self, a: u64) -> FutureResult; - /// Performs asynchronous operation with meta + /// Performs an asynchronous operation with meta. #[rpc(meta, name = "callAsyncMeta", alias("callAsyncMetaAlias"))] fn call_meta(&self, a: Self::Metadata, b: BTreeMap) -> FutureResult; } @@ -49,6 +54,10 @@ impl Rpc for RpcImpl { Ok(a * b.unwrap_or(1)) } + fn raw(&self, params: Params) -> Result { + Ok(format!("Got: {:?}", params)) + } + fn call(&self, x: u64) -> FutureResult { futures::finished(format!("OK: {}", x)) } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 9988c6373..ce912a1d3 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -9,6 +9,7 @@ pub struct RpcMethodAttribute { pub name: String, pub aliases: Vec, pub kind: AttributeKind, + pub raw_params: bool, } #[derive(Clone, Debug)] @@ -35,6 +36,7 @@ const SUBSCRIPTION_NAME_KEY: &str = "subscription"; const ALIASES_KEY: &str = "alias"; const PUB_SUB_ATTR_NAME: &str = "pubsub"; const METADATA_META_WORD: &str = "meta"; +const RAW_PARAMS_META_WORD: &str = "raw_params"; const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; const RETURNS_META_WORD: &str = "returns"; @@ -79,12 +81,15 @@ impl RpcMethodAttribute { get_meta_list(meta) .and_then(|ml| get_name_value(RPC_NAME_KEY, ml)) .map_or(Err(Error::new_spanned(attr, MISSING_NAME_ERR)), |name| { - let aliases = get_meta_list(meta).map_or(Vec::new(), |ml| get_aliases(ml)); + let aliases = get_meta_list(&meta).map_or(Vec::new(), |ml| get_aliases(ml)); + let raw_params = + get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); Ok(RpcMethodAttribute { attr: attr.clone(), name, aliases, kind, + raw_params, }) }) }) @@ -144,7 +149,7 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { match meta.name().to_string().as_ref() { RPC_ATTR_NAME => { - validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD])?; + validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD])?; validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } @@ -152,7 +157,7 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { validate_idents( &meta, &visitor.meta_words, - &[SUBSCRIBE_META_WORD, UNSUBSCRIBE_META_WORD], + &[SUBSCRIBE_META_WORD, UNSUBSCRIBE_META_WORD, RAW_PARAMS_META_WORD], )?; validate_idents(&meta, &visitor.name_value_names, &[SUBSCRIPTION_NAME_KEY, RPC_NAME_KEY])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 23fcaf335..a392c95da 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -198,6 +198,8 @@ impl RpcMethod { self.params_with_trailing(trailing_args_num, param_types, tuple_fields) } else if param_types.is_empty() { quote! { let params = params.expect_no_params(); } + } else if self.attr.raw_params { + quote! { let params = Ok((params,)); } } else { quote! { let params = params.parse::<(#(#param_types, )*)>(); } } diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index 5b3310a94..afe501e1e 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -1,3 +1,4 @@ +use jsonrpc_core::types::params::Params; use jsonrpc_core::{IoHandler, Response}; use jsonrpc_derive::rpc; use serde_json; @@ -24,6 +25,9 @@ pub trait Rpc { /// Adds two numbers and returns a result #[rpc(name = "add", alias("add_alias1", "add_alias2"))] fn add(&self, a: u64, b: u64) -> Result; + + #[rpc(name = "raw", raw_params)] + fn raw(&self, params: Params) -> Result; } #[derive(Default)] @@ -41,6 +45,10 @@ impl Rpc for RpcImpl { fn add(&self, a: u64, b: u64) -> Result { Ok(a + b) } + + fn raw(&self, _params: Params) -> Result { + Ok("OK".into()) + } } #[test] @@ -167,3 +175,40 @@ fn should_use_method_name_aliases() { .unwrap() ); } + +#[test] +fn should_accept_any_raw_params() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"raw","params":[1, 2]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"raw","params":{"foo":"bar"}}"#; + let req3 = r#"{"jsonrpc":"2.0","id":1,"method":"raw","params":null}"#; + let req4 = r#"{"jsonrpc":"2.0","id":1,"method":"raw"}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + let res3 = io.handle_request_sync(req3); + let res4 = io.handle_request_sync(req4); + let expected = r#"{ + "jsonrpc": "2.0", + "result": "OK", + "id": 1 + }"#; + let expected: Response = serde_json::from_str(expected).unwrap(); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(expected, result1); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!(expected, result2); + + let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); + assert_eq!(expected, result3); + + let result4: Response = serde_json::from_str(&res4.unwrap()).unwrap(); + assert_eq!(expected, result4); +} diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index 118a51619..c4a310ae7 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,4 +1,4 @@ -error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta' +error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params' --> $DIR/attr-invalid-meta-words.rs:8:2 | 8 | /// Returns a protocol version From c76288c6885bc5b606ef8c2d51b6a46e8ca7b233 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2019 12:20:10 +0200 Subject: [PATCH 061/149] Update parking_lot requirement from 0.8.0 to 0.9.0 (#450) * Update parking_lot requirement from 0.8.0 to 0.9.0 Updates the requirements on [parking_lot](https://github.com/Amanieu/parking_lot) to permit the latest version. - [Release notes](https://github.com/Amanieu/parking_lot/releases) - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/compare/0.8.0...0.9.0) Signed-off-by: dependabot-preview[bot] * Fix formatting. --- core/src/io.rs | 31 +++++++++++-------------- core/src/lib.rs | 8 +++---- derive/examples/generic-trait-bounds.rs | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 8 files changed, 23 insertions(+), 28 deletions(-) diff --git a/core/src/io.rs b/core/src/io.rs index ac4cc8675..ac568602a 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -322,17 +322,17 @@ macro_rules! impl_io_handler_extension { impl_io_handler_extension!(); impl_io_handler_extension!(A,); -impl_io_handler_extension!(A,B,); -impl_io_handler_extension!(A,B,C,); -impl_io_handler_extension!(A,B,C,D,); -impl_io_handler_extension!(A,B,C,D,E,); -impl_io_handler_extension!(A,B,C,D,E,F,); -impl_io_handler_extension!(A,B,C,D,E,F,G,); -impl_io_handler_extension!(A,B,C,D,E,F,G,H,); -impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,); -impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,J,); -impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,J,K,); -impl_io_handler_extension!(A,B,C,D,E,F,G,H,I,J,K,L,); +impl_io_handler_extension!(A, B,); +impl_io_handler_extension!(A, B, C,); +impl_io_handler_extension!(A, B, C, D,); +impl_io_handler_extension!(A, B, C, D, E,); +impl_io_handler_extension!(A, B, C, D, E, F,); +impl_io_handler_extension!(A, B, C, D, E, F, G,); +impl_io_handler_extension!(A, B, C, D, E, F, G, H,); +impl_io_handler_extension!(A, B, C, D, E, F, G, H, I,); +impl_io_handler_extension!(A, B, C, D, E, F, G, H, I, J,); +impl_io_handler_extension!(A, B, C, D, E, F, G, H, I, J, K,); +impl_io_handler_extension!(A, B, C, D, E, F, G, H, I, J, K, L,); impl IoHandlerExtension for Vec<(String, RemoteProcedure)> { fn augment>(self, handler: &mut MetaIoHandler) { @@ -545,9 +545,9 @@ mod tests { #[test] fn test_extending_by_multiple_delegates() { - use std::sync::Arc; - use crate::delegates::IoDelegate; use super::IoHandlerExtension; + use crate::delegates::IoDelegate; + use std::sync::Arc; struct Test; impl Test { @@ -566,9 +566,6 @@ mod tests { x.augment(io); } - augment( - (del1, del2), - &mut io - ); + augment((del1, del2), &mut io); } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 5b4913acf..2bcdfadca 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -44,13 +44,11 @@ pub type BoxFuture = Box + Send> /// A Result type. pub type Result = ::std::result::Result; -pub use crate::calls::{ - Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple -}; +pub use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple}; pub use crate::delegates::IoDelegate; pub use crate::io::{ - Compatibility, FutureOutput, FutureResponse, FutureResult, FutureRpcResult, IoHandler, MetaIoHandler, - IoHandlerExtension, + Compatibility, FutureOutput, FutureResponse, FutureResult, FutureRpcResult, IoHandler, IoHandlerExtension, + MetaIoHandler, }; pub use crate::middleware::{Middleware, Noop as NoopMiddleware}; pub use crate::types::*; diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs index 39527475d..552a86a30 100644 --- a/derive/examples/generic-trait-bounds.rs +++ b/derive/examples/generic-trait-bounds.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use jsonrpc_core::futures::future::{self, FutureResult}; -use jsonrpc_core::{Error, IoHandler, Result, IoHandlerExtension}; +use jsonrpc_core::{Error, IoHandler, IoHandlerExtension, Result}; use jsonrpc_derive::rpc; // One is both parameter and a result so requires both Serialize and DeserializeOwned diff --git a/http/Cargo.toml b/http/Cargo.toml index d46c1734a..0f664e4cb 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -17,6 +17,6 @@ jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" -parking_lot = "0.8.0" +parking_lot = "0.9.0" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index b50621628..bdc2c446c 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -15,7 +15,7 @@ tokio-service = "0.1" jsonrpc-core = { version = "12.1", path = "../core" } jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } parity-tokio-ipc = "0.1" -parking_lot = "0.8" +parking_lot = "0.9" [dev-dependencies] env_logger = "0.6" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index f4f8dff74..95118a44b 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -12,7 +12,7 @@ version = "12.1.0" [dependencies] log = "0.4" -parking_lot = "0.8" +parking_lot = "0.9" jsonrpc-core = { version = "12.1", path = "../core" } serde = "1.0" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index ae3f2936c..156361764 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -11,7 +11,7 @@ version = "12.1.0" [dependencies] log = "0.4" -parking_lot = "0.8" +parking_lot = "0.9" tokio-service = "0.1" jsonrpc-core = { version = "12.1", path = "../core" } jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 202602d33..871a95dbb 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -13,7 +13,7 @@ version = "12.1.0" jsonrpc-core = { version = "12.1", path = "../core" } jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } log = "0.4" -parking_lot = "0.8" +parking_lot = "0.9" slab = "0.4" ws = "0.8" From 0011d4f7b26482ba64b4b45dbb64d042e378516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 18 Jul 2019 17:26:36 +0200 Subject: [PATCH 062/149] Bump to 13.0 (#456) --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 8 ++++---- core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 18 files changed, 46 insertions(+), 46 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index e3a4510dc..80c89c725 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" categories = [ "asynchronous", @@ -24,7 +24,7 @@ http = ["jsonrpc-client-transports/http"] ws = ["jsonrpc-client-transports/ws"] [dependencies] -jsonrpc-client-transports = { version = "12.1", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "13.0", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 319826210..35d706ad5 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" categories = [ "asynchronous", @@ -32,8 +32,8 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "12.1", path = "../../core" } -jsonrpc-pubsub = { version = "12.1", path = "../../pubsub" } +jsonrpc-core = { version = "13.0", path = "../../core" } +jsonrpc-pubsub = { version = "13.0", path = "../../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -42,7 +42,7 @@ websocket = { version = "0.23", optional = true } [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "12.1", path = "../../http" } +jsonrpc-http-server = { version = "13.0", path = "../../http" } lazy_static = "1.0" env_logger = "0.6" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index d690b6ddf..115286bd6 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 94c85be96..45d34a99b 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "12.1", path = "../core" } -jsonrpc-core-client = { version = "12.1", path = "../core-client" } -jsonrpc-pubsub = { version = "12.1", path = "../pubsub" } -jsonrpc-tcp-server = { version = "12.1", path = "../tcp" } +jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-core-client = { version = "13.0", path = "../core-client" } +jsonrpc-pubsub = { version = "13.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "13.0", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 0f664e4cb..c8b8541b6 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "12.1", path = "../core" } -jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } +jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index 51a012bb4..15bb983b2 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "12.1" +jsonrpc-http-server = "13.0" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index bdc2c446c..ce34c7ba6 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "12.1", path = "../core" } -jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } +jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } parity-tokio-ipc = "0.1" parking_lot = "0.9" diff --git a/ipc/README.md b/ipc/README.md index d55326a43..f351fbb0e 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "12.1" +jsonrpc-ipc-server = "13.0" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 95118a44b..0c6cd3728 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] log = "0.4" parking_lot = "0.9" -jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-core = { version = "13.0", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "12.1", path = "../tcp" } +jsonrpc-tcp-server = { version = "13.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index d0783da05..f4a98eca1 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "12.1", path = "../../core" } -jsonrpc-pubsub = { version = "12.1", path = "../" } -jsonrpc-ws-server = { version = "12.1", path = "../../ws" } -jsonrpc-ipc-server = { version = "12.1", path = "../../ipc" } +jsonrpc-core = { version = "13.0", path = "../../core" } +jsonrpc-pubsub = { version = "13.0", path = "../" } +jsonrpc-ws-server = { version = "13.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "13.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 7fff81bb9..dd36671f8 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-core = { version = "13.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index fc2735cd2..9c667eb26 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "12.1", path = "../core" } +jsonrpc-core = { version = "13.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index 6039873ed..1ae41f2fd 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "12.1" +jsonrpc-stdio-server = "13.0" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 156361764..e8061fe81 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] log = "0.4" parking_lot = "0.9" tokio-service = "0.1" -jsonrpc-core = { version = "12.1", path = "../core" } -jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } +jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index 5746d76a5..a0f0c79aa 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "12.1" +jsonrpc-tcp-server = "13.0" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index 376408174..6f022f094 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "12.1.0" +version = "13.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "12.1", path = "../core" } -jsonrpc-core-client = { version = "12.1", path = "../core-client" } -jsonrpc-pubsub = { version = "12.1", path = "../pubsub" } +jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-core-client = { version = "13.0", path = "../core-client" } +jsonrpc-pubsub = { version = "13.0", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { version = "12.1", path = "../derive" } +jsonrpc-derive = { version = "13.0", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 871a95dbb..6000a18a1 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "12.1.0" +version = "13.0.0" [dependencies] -jsonrpc-core = { version = "12.1", path = "../core" } -jsonrpc-server-utils = { version = "12.1", path = "../server-utils" } +jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } log = "0.4" parking_lot = "0.9" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 54aab1b4e..ae047b454 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "12.1" +jsonrpc-ws-server = "13.0" ``` `main.rs` From 2f69657c1ef59f3b4fd425f657399af40478f19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 22 Jul 2019 21:32:55 +0200 Subject: [PATCH 063/149] Remove Notification from Output type. (#462) --- core-client/transports/Cargo.toml | 2 +- .../transports/src/transports/duplex.rs | 20 +-- core-client/transports/src/transports/http.rs | 11 +- core-client/transports/src/transports/mod.rs | 155 +++++++++++++++--- core/src/types/response.rs | 51 +----- pubsub/src/types.rs | 14 -- test/src/lib.rs | 4 - 7 files changed, 149 insertions(+), 108 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 35d706ad5..756f4ed0e 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -35,7 +35,7 @@ hyper-tls = { version = "0.3.2", optional = true } jsonrpc-core = { version = "13.0", path = "../../core" } jsonrpc-pubsub = { version = "13.0", path = "../../pubsub" } log = "0.4" -serde = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "0.1", optional = true } websocket = { version = "0.23", optional = true } diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index 5c604147d..baf4cbe49 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -166,16 +166,16 @@ where Err(err) => Err(err)?, }; log::debug!("incoming: {}", response_str); - for (id, result, method, sid) in super::parse_response(&response_str)? { - log::debug!( - "id: {:?} (sid: {:?}) result: {:?} method: {:?}", - id, - sid, - result, - method - ); - self.incoming.push_back((id, result, method, sid)); - } + // we only send one request at the time, so there can only be one response. + let (id, result, method, sid) = super::parse_response(&response_str)?; + log::debug!( + "id: {:?} (sid: {:?}) result: {:?} method: {:?}", + id, + sid, + result, + method + ); + self.incoming.push_back((id, result, method, sid)); } // Handle incoming queue. diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index 9ea9361a4..2cabd9b1e 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -82,16 +82,7 @@ where let response_str = String::from_utf8_lossy(response.as_ref()).into_owned(); super::parse_response(&response_str) }) - .and_then(|responses| { - if responses.len() == 1 { - responses.into_iter().nth(0).expect("Exactly one response; qed").1 - } else { - Err(RpcError::Other(format_err!( - "Transport currently only supports Single requests" - ))) - } - }); - + .and_then(|r| r.1); if let Err(err) = msg.sender.send(response) { log::warn!("Error resuming asynchronous request: {:?}", err); } diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index fda2240d9..12ba57ba1 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -1,7 +1,8 @@ //! Client transport implementations -use jsonrpc_core::{Call, Error, Id, MethodCall, Output, Params, Response, Version}; +use jsonrpc_core::{Call, Error, Id, MethodCall, Params, Version}; use jsonrpc_pubsub::SubscriptionId; +use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::{CallMessage, RpcError}; @@ -60,27 +61,143 @@ impl RequestBuilder { } } -/// Parse raw string into JSON values, together with the request Id +/// Parse raw string into a single JSON value, together with the request Id. +/// +/// This method will attempt to parse a JSON-RPC response object (either `Failure` or `Success`) +/// and a `Notification` (for Subscriptions). +/// Note that if you have more specific expectations about the returned type and don't want +/// to handle all of them it might be best to deserialize on your own. pub fn parse_response( response: &str, -) -> Result, Option, Option)>, RpcError> { - serde_json::from_str::(&response) +) -> Result<(Id, Result, Option, Option), RpcError> { + serde_json::from_str::(&response) .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) .map(|response| { - let outputs: Vec = match response { - Response::Single(output) => vec![output], - Response::Batch(outputs) => outputs, - }; - outputs - .into_iter() - .map(|output| { - let id = output.id().clone(); - let sid = SubscriptionId::parse_output(&output); - let method = output.method(); - let value: Result = output.into(); - let result = value.map_err(RpcError::JsonRpcError); - (id, result, method, sid) - }) - .collect::>() + let id = response.id().unwrap_or(Id::Null); + let sid = response.subscription_id(); + let method = response.method(); + let value: Result = response.into(); + let result = value.map_err(RpcError::JsonRpcError); + (id, result, method, sid) }) } + +/// A type representing all possible values sent from the server to the client. +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum ClientResponse { + /// A regular JSON-RPC request output (single response). + Output(jsonrpc_core::Output), + /// A notification. + Notification(jsonrpc_core::Notification), +} + +impl ClientResponse { + /// Get the id of the response (if any). + pub fn id(&self) -> Option { + match *self { + ClientResponse::Output(ref output) => Some(output.id().clone()), + ClientResponse::Notification(_) => None, + } + } + + /// Get the method name if the output is a notification. + pub fn method(&self) -> Option { + match *self { + ClientResponse::Notification(ref n) => Some(n.method.to_owned()), + ClientResponse::Output(_) => None, + } + } + + /// Parses the response into a subscription id. + pub fn subscription_id(&self) -> Option { + match *self { + ClientResponse::Notification(ref n) => match &n.params { + jsonrpc_core::Params::Map(map) => match map.get("subscription") { + Some(value) => SubscriptionId::parse_value(value), + None => None, + }, + _ => None, + }, + _ => None, + } + } +} + +impl From for Result { + fn from(res: ClientResponse) -> Self { + match res { + ClientResponse::Output(output) => output.into(), + ClientResponse::Notification(n) => match &n.params { + Params::Map(map) => { + let subscription = map.get("subscription"); + let result = map.get("result"); + let error = map.get("error"); + + match (subscription, result, error) { + (Some(_), Some(result), _) => Ok(result.to_owned()), + (Some(_), _, Some(error)) => { + let error = serde_json::from_value::(error.to_owned()) + .ok() + .unwrap_or_else(|| Error::parse_error()); + Err(error) + } + _ => Ok(n.params.into()), + } + } + _ => Ok(n.params.into()), + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use jsonrpc_core::{Value, Version, Params, Failure, Success, Output, Notification}; + use serde_json; + + #[test] + fn notification_deserialize() { + let dsr = r#"{"jsonrpc":"2.0","method":"hello","params":[10]}"#; + let deserialized: ClientResponse = serde_json::from_str(dsr).unwrap(); + assert_eq!( + deserialized, + ClientResponse::Notification(Notification { + jsonrpc: Some(Version::V2), + method: "hello".into(), + params: Params::Array(vec![Value::from(10)]), + }) + ); + } + + #[test] + fn success_deserialize() { + let dsr = r#"{"jsonrpc":"2.0","result":1,"id":1}"#; + let deserialized: ClientResponse = serde_json::from_str(dsr).unwrap(); + assert_eq!( + deserialized, + ClientResponse::Output(Output::Success(Success { + jsonrpc: Some(Version::V2), + id: Id::Num(1), + result: 1.into(), + })) + ); + } + + #[test] + fn failure_output_deserialize() { + let dfo = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}"#; + + let deserialized: ClientResponse = serde_json::from_str(dfo).unwrap(); + assert_eq!( + deserialized, + ClientResponse::Output(Output::Failure(Failure { + jsonrpc: Some(Version::V2), + error: Error::parse_error(), + id: Id::Num(1) + })) + ); + } +} diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 4ec5b3c34..8010f00b3 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -1,5 +1,5 @@ //! jsonrpc response -use super::{Error, ErrorCode, Id, Notification, Params, Value, Version}; +use super::{Error, ErrorCode, Id, Value, Version}; use crate::Result as CoreResult; /// Successful response @@ -33,8 +33,6 @@ pub struct Failure { #[serde(deny_unknown_fields)] #[serde(untagged)] pub enum Output { - /// Notification - Notification(Notification), /// Success Success(Success), /// Failure @@ -64,7 +62,6 @@ impl Output { match *self { Output::Success(ref s) => s.jsonrpc, Output::Failure(ref f) => f.jsonrpc, - Output::Notification(ref n) => n.jsonrpc, } } @@ -73,15 +70,6 @@ impl Output { match *self { Output::Success(ref s) => &s.id, Output::Failure(ref f) => &f.id, - Output::Notification(_) => &Id::Null, - } - } - - /// Get the method name if the output is a notification. - pub fn method(&self) -> Option { - match *self { - Output::Notification(ref n) => Some(n.method.to_owned()), - _ => None, } } } @@ -92,25 +80,6 @@ impl From for CoreResult { match output { Output::Success(s) => Ok(s.result), Output::Failure(f) => Err(f.error), - Output::Notification(n) => match &n.params { - Params::Map(map) => { - let subscription = map.get("subscription"); - let result = map.get("result"); - let error = map.get("error"); - - match (subscription, result, error) { - (Some(_), Some(result), _) => Ok(result.to_owned()), - (Some(_), _, Some(error)) => { - let error = serde_json::from_value::(error.to_owned()) - .ok() - .unwrap_or_else(|| Error::parse_error()); - Err(error) - } - _ => Ok(n.params.into()), - } - } - _ => Ok(n.params.into()), - }, } } } @@ -277,24 +246,6 @@ fn batch_response_deserialize() { ); } -#[test] -fn notification_deserialize() { - use super::Params; - use serde_json; - use serde_json::Value; - - let dsr = r#"{"jsonrpc":"2.0","method":"hello","params":[10]}"#; - let deserialized: Response = serde_json::from_str(dsr).unwrap(); - assert_eq!( - deserialized, - Response::Single(Output::Notification(Notification { - jsonrpc: Some(Version::V2), - method: "hello".into(), - params: Params::Array(vec![Value::from(10)]), - })) - ); -} - #[test] fn handle_incorrect_responses() { use serde_json; diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index 54e33263f..be5fefc49 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -53,20 +53,6 @@ impl SubscriptionId { _ => None, } } - - /// Parses an output into a subscription id. - pub fn parse_output(output: &core::Output) -> Option { - match output { - core::Output::Notification(n) => match &n.params { - core::Params::Map(map) => match map.get("subscription") { - Some(value) => Self::parse_value(value), - None => None, - }, - _ => None, - }, - _ => None, - } - } } impl From for SubscriptionId { diff --git a/test/src/lib.rs b/test/src/lib.rs index b664e2842..4b5fe5889 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -129,10 +129,6 @@ impl Rpc { Encoding::Compact => serde_json::to_string(&error), Encoding::Pretty => serde_json::to_string_pretty(&error), }, - response::Output::Notification(notification) => match encoding { - Encoding::Compact => serde_json::to_string(¬ification), - Encoding::Pretty => serde_json::to_string_pretty(¬ification), - }, } .expect("Serialization is infallible; qed"); From a7b09c080b54528b137e1bb72e9e10bd7e742327 Mon Sep 17 00:00:00 2001 From: Eyal Kalderon Date: Tue, 23 Jul 2019 14:26:01 +0800 Subject: [PATCH 064/149] Add support for notifications in jsonrpc-derive (#454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add notification attribute to rpc macro in jsonrpc-derive * Add missing IoDelegate::add_notification_with_meta() method * Update meta-macros example to include notification usage * Fix type inference bug of Result alias in to_delegate.rs * Update macros and pubsub-macros integration tests * Replace "responds" with "handles" in doc comment Co-Authored-By: Tomasz Drwięga * Replace "responds" with "handles" in another doc comment Co-Authored-By: Tomasz Drwięga * Replace `notification` attr with return type detection of `rpc` attr * Remove unused NOTIFICATION_ATTR_NAME constant * Add should_accept_only_notifications() test case for jsonrpc-derive * Generate unimplemented!() client method for notifications --- core/src/delegates.rs | 35 ++++++++++++++++++++++++- derive/examples/meta-macros.rs | 8 ++++++ derive/src/rpc_attr.rs | 42 +++++++++++++++++++++++------ derive/src/rpc_trait.rs | 21 ++++++++++++--- derive/src/to_client.rs | 18 ++++++++++--- derive/src/to_delegate.rs | 28 +++++++++++++++++++- derive/tests/macros.rs | 48 +++++++++++++++++++++++++++++++--- derive/tests/pubsub-macros.rs | 12 +++++++-- 8 files changed, 190 insertions(+), 22 deletions(-) diff --git a/core/src/delegates.rs b/core/src/delegates.rs index 20b2ff836..577d9d8d1 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -55,10 +55,10 @@ struct DelegateNotification { impl RpcNotification for DelegateNotification where + M: Metadata, F: Fn(&T, Params) + 'static, F: Send + Sync + 'static, T: Send + Sync + 'static, - M: Metadata, { fn execute(&self, params: Params, _meta: M) { let closure = &self.closure; @@ -66,6 +66,24 @@ where } } +struct DelegateNotificationWithMeta { + delegate: Arc, + closure: F, +} + +impl RpcNotification for DelegateNotificationWithMeta +where + M: Metadata, + F: Fn(&T, Params, M) + 'static, + F: Send + Sync + 'static, + T: Send + Sync + 'static, +{ + fn execute(&self, params: Params, meta: M) { + let closure = &self.closure; + closure(&self.delegate, params, meta) + } +} + /// A set of RPC methods and notifications tied to single `delegate` struct. pub struct IoDelegate where @@ -143,6 +161,21 @@ where })), ); } + + /// Adds notification with metadata to the delegate. + pub fn add_notification_with_meta(&mut self, name: &str, notification: F) + where + F: Fn(&T, Params, M), + F: Send + Sync + 'static, + { + self.methods.insert( + name.into(), + RemoteProcedure::Notification(Arc::new(DelegateNotificationWithMeta { + delegate: self.delegate.clone(), + closure: notification, + })), + ); + } } impl crate::io::IoHandlerExtension for IoDelegate diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 8ba7777ea..01603de4e 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -36,6 +36,10 @@ pub trait Rpc { /// Performs an asynchronous operation with meta. #[rpc(meta, name = "callAsyncMeta", alias("callAsyncMetaAlias"))] fn call_meta(&self, a: Self::Metadata, b: BTreeMap) -> FutureResult; + + /// Handles notification. + #[rpc(name = "notify")] + fn notify(&self, a: u64); } struct RpcImpl; @@ -65,6 +69,10 @@ impl Rpc for RpcImpl { fn call_meta(&self, meta: Self::Metadata, map: BTreeMap) -> FutureResult { futures::finished(format!("From: {}, got: {:?}", meta.0, map)) } + + fn notify(&self, a: u64) { + println!("Received `notify` with value: {}", a); + } } fn main() { diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index ce912a1d3..7633c669c 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -17,6 +17,7 @@ pub enum AttributeKind { Rpc { has_metadata: bool, returns: Option, + is_notification: bool, }, PubSub { subscription_name: String, @@ -50,10 +51,11 @@ const NEITHER_SUB_OR_UNSUB_ERR: &str = "pubsub attribute not annotated with eith impl RpcMethodAttribute { pub fn parse_attr(method: &syn::TraitItemMethod) -> Result> { + let output = &method.sig.decl.output; let attrs = method .attrs .iter() - .filter_map(Self::parse_meta) + .filter_map(|attr| Self::parse_meta(attr, &output)) .collect::>>()?; if attrs.len() <= 1 { @@ -63,16 +65,11 @@ impl RpcMethodAttribute { } } - fn parse_meta(attr: &syn::Attribute) -> Option> { + fn parse_meta(attr: &syn::Attribute, output: &syn::ReturnType) -> Option> { match attr.parse_meta().and_then(validate_attribute_meta) { Ok(ref meta) => { let attr_kind = match meta.name().to_string().as_ref() { - RPC_ATTR_NAME => { - let has_metadata = - get_meta_list(meta).map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); - let returns = get_meta_list(meta).map_or(None, |ml| get_name_value(RETURNS_META_WORD, ml)); - Some(Ok(AttributeKind::Rpc { has_metadata, returns })) - } + RPC_ATTR_NAME => Some(Self::parse_rpc(meta, output)), PUB_SUB_ATTR_NAME => Some(Self::parse_pubsub(meta)), _ => None, }; @@ -99,6 +96,28 @@ impl RpcMethodAttribute { } } + fn parse_rpc(meta: &syn::Meta, output: &syn::ReturnType) -> Result { + let has_metadata = get_meta_list(meta).map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); + let returns = get_meta_list(meta).map_or(None, |ml| get_name_value(RETURNS_META_WORD, ml)); + let is_notification = match output { + syn::ReturnType::Default => true, + syn::ReturnType::Type(_, ret) => match **ret { + syn::Type::Tuple(ref tup) if tup.elems.empty_or_trailing() => true, + _ => false, + }, + }; + + if is_notification && returns.is_some() { + return Err(syn::Error::new_spanned(output, &"Notifications must return ()")); + } + + Ok(AttributeKind::Rpc { + has_metadata, + returns, + is_notification, + }) + } + fn parse_pubsub(meta: &syn::Meta) -> Result { let name_and_list = get_meta_list(meta).and_then(|ml| get_name_value(SUBSCRIPTION_NAME_KEY, ml).map(|name| (name, ml))); @@ -125,6 +144,13 @@ impl RpcMethodAttribute { AttributeKind::Rpc { .. } => false, } } + + pub fn is_notification(&self) -> bool { + match self.kind { + AttributeKind::Rpc { is_notification, .. } => is_notification, + AttributeKind::PubSub { .. } => false, + } + } } fn validate_attribute_meta(meta: syn::Meta) -> Result { diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 8a409bf6c..cf0076252 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -79,10 +79,23 @@ fn compute_method_registrations(item_trait: &syn::ItemTrait) -> Result<(Vec method_registrations.push(MethodRegistration::Standard { - method: method.clone(), - has_metadata: *has_metadata, - }), + AttributeKind::Rpc { + has_metadata, + is_notification, + .. + } => { + if *is_notification { + method_registrations.push(MethodRegistration::Notification { + method: method.clone(), + has_metadata: *has_metadata, + }) + } else { + method_registrations.push(MethodRegistration::Standard { + method: method.clone(), + has_metadata: *has_metadata, + }) + } + } AttributeKind::PubSub { subscription_name, kind, diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index 890a12bd8..e39e8d886 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -97,9 +97,7 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result compute_returns(&method.trait_item, returns)?, - AttributeKind::PubSub { .. } => { - continue; - } + AttributeKind::PubSub { .. } => continue, }; let returns_str = quote!(#returns).to_string(); let client_method = syn::parse_quote! { @@ -134,6 +132,20 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result { + let attrs = get_doc_comments(&method.trait_item.attrs); + let name = &method.trait_item.sig.ident; + let args = compute_args(&method.trait_item); + let arg_names = compute_arg_identifiers(&args)?; + let client_method = syn::parse_quote! { + #(#attrs)* + pub fn #name(&self, #args) { + let _args_tuple = (#(#arg_names,)*); + unimplemented!() + } + }; + client_methods.push(client_method); + } } } Ok(client_methods) diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index a392c95da..1bcf638b4 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -19,6 +19,10 @@ pub enum MethodRegistration { subscribe: RpcMethod, unsubscribe: RpcMethod, }, + Notification { + method: RpcMethod, + has_metadata: bool, + }, } impl MethodRegistration { @@ -71,6 +75,21 @@ impl MethodRegistration { #unsub_aliases }) } + MethodRegistration::Notification { method, has_metadata } => { + let name = &method.name(); + let add_notification = if *has_metadata { + quote!(add_notification_with_meta) + } else { + quote!(add_notification) + }; + let closure = method.generate_delegate_closure(false)?; + let add_aliases = method.generate_add_aliases(); + + Ok(quote! { + del.#add_notification(#name, #closure); + #add_aliases + }) + } } } } @@ -199,7 +218,7 @@ impl RpcMethod { } else if param_types.is_empty() { quote! { let params = params.expect_no_params(); } } else if self.attr.raw_params { - quote! { let params = Ok((params,)); } + quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } } else { quote! { let params = params.parse::<(#(#param_types, )*)>(); } } @@ -224,6 +243,13 @@ impl RpcMethod { return } } + } else if self.attr.is_notification() { + quote! { + Ok((#(#tuple_fields, )*)) => { + (method)#method_call + }, + Err(_) => return, + } } else { quote! { Ok((#(#tuple_fields, )*)) => { diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index afe501e1e..632c27a8c 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -14,20 +14,25 @@ type Result = ::std::result::Result; #[rpc] pub trait Rpc { - /// Returns a protocol version + /// Returns a protocol version. #[rpc(name = "protocolVersion")] fn protocol_version(&self) -> Result; - /// Negates number and returns a result + /// Negates number and returns a result. #[rpc(name = "neg")] fn neg(&self, a: i64) -> Result; - /// Adds two numbers and returns a result + /// Adds two numbers and returns a result. #[rpc(name = "add", alias("add_alias1", "add_alias2"))] fn add(&self, a: u64, b: u64) -> Result; + /// Retrieves and debug prints the underlying `Params` object. #[rpc(name = "raw", raw_params)] fn raw(&self, params: Params) -> Result; + + /// Handles a notification. + #[rpc(name = "notify")] + fn notify(&self, a: u64); } #[derive(Default)] @@ -49,6 +54,10 @@ impl Rpc for RpcImpl { fn raw(&self, _params: Params) -> Result { Ok("OK".into()) } + + fn notify(&self, a: u64) { + println!("Received `notify` with value: {}", a); + } } #[test] @@ -212,3 +221,36 @@ fn should_accept_any_raw_params() { let result4: Response = serde_json::from_str(&res4.unwrap()).unwrap(); assert_eq!(expected, result4); } + +#[test] +fn should_accept_only_notifications() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","method":"notify","params":[1]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"notify","params":[1]}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + + // then + assert!(res1.is_none()); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!( + result2, + serde_json::from_str( + r#"{ + "jsonrpc": "2.0", + "error": { + "code": -32601, + "message": "Method not found" + }, + "id":1 + }"# + ) + .unwrap() + ); +} diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index e6f1b3ab5..4f88ce643 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -22,7 +22,7 @@ type Result = ::std::result::Result; pub trait Rpc { type Metadata; - /// Hello subscription + /// Hello subscription. #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_alias"))] fn subscribe(&self, a: Self::Metadata, b: Subscriber, c: u32, d: Option); @@ -30,9 +30,13 @@ pub trait Rpc { #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] fn unsubscribe(&self, a: Option, b: SubscriptionId) -> Result; - /// A regular rpc method alongside pubsub + /// A regular rpc method alongside pubsub. #[rpc(name = "add")] fn add(&self, a: u64, b: u64) -> Result; + + /// A notification alongside pubsub. + #[rpc(name = "notify")] + fn notify(&self, a: u64); } #[derive(Default)] @@ -52,6 +56,10 @@ impl Rpc for RpcImpl { fn add(&self, a: u64, b: u64) -> Result { Ok(a + b) } + + fn notify(&self, a: u64) { + println!("Received `notify` with value: {}", a); + } } #[derive(Clone, Default)] From 60ac6a1a62627b116b01f3b06d9198dada7b0c72 Mon Sep 17 00:00:00 2001 From: Eyal Kalderon Date: Tue, 23 Jul 2019 16:46:13 +0800 Subject: [PATCH 065/149] Add notification to std example in jsonrpc-derive (#464) * Add missing 'a' in doc comment * Add notification method to std jsonrpc-derive example --- derive/examples/meta-macros.rs | 2 +- derive/examples/std.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 01603de4e..58164e31a 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -37,7 +37,7 @@ pub trait Rpc { #[rpc(meta, name = "callAsyncMeta", alias("callAsyncMetaAlias"))] fn call_meta(&self, a: Self::Metadata, b: BTreeMap) -> FutureResult; - /// Handles notification. + /// Handles a notification. #[rpc(name = "notify")] fn notify(&self, a: u64); } diff --git a/derive/examples/std.rs b/derive/examples/std.rs index 12a820e1a..401bb05c5 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -8,17 +8,21 @@ use jsonrpc_derive::rpc; /// Rpc trait #[rpc] pub trait Rpc { - /// Returns a protocol version + /// Returns a protocol version. #[rpc(name = "protocolVersion")] fn protocol_version(&self) -> Result; - /// Adds two numbers and returns a result + /// Adds two numbers and returns a result. #[rpc(name = "add", alias("callAsyncMetaAlias"))] fn add(&self, a: u64, b: u64) -> Result; - /// Performs asynchronous operation + /// Performs asynchronous operation. #[rpc(name = "callAsync")] fn call(&self, a: u64) -> FutureResult; + + /// Handles a notification. + #[rpc(name = "notify")] + fn notify(&self, a: u64); } struct RpcImpl; @@ -35,6 +39,10 @@ impl Rpc for RpcImpl { fn call(&self, _: u64) -> FutureResult { future::ok("OK".to_owned()) } + + fn notify(&self, a: u64) { + println!("Received `notify` with value: {}", a); + } } fn main() { From 9ec3761a56fa4d4af3af7d6fe37b53d6994876c3 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 23 Jul 2019 14:12:55 +0200 Subject: [PATCH 066/149] Add an IPC client transport using parity-tokio-ipc (#460) * core-client: Add IPC transport * Link to IpcConnection::connect panic fix PR --- core-client/Cargo.toml | 1 + core-client/transports/Cargo.toml | 10 +- core-client/transports/src/transports/ipc.rs | 126 +++++++++++++++++++ core-client/transports/src/transports/mod.rs | 2 + 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 core-client/transports/src/transports/ipc.rs diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 80c89c725..ced65c5a7 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -22,6 +22,7 @@ categories = [ tls = ["jsonrpc-client-transports/tls"] http = ["jsonrpc-client-transports/http"] ws = ["jsonrpc-client-transports/ws"] +ipc = ["jsonrpc-client-transports/ipc"] [dependencies] jsonrpc-client-transports = { version = "13.0", path = "./transports", default-features = false } diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 756f4ed0e..4ca61dd99 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -26,6 +26,11 @@ ws = [ "websocket", "tokio", ] +ipc = [ + "parity-tokio-ipc", + "jsonrpc-server-utils", + "tokio", +] [dependencies] failure = "0.1" @@ -34,7 +39,9 @@ hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } jsonrpc-core = { version = "13.0", path = "../../core" } jsonrpc-pubsub = { version = "13.0", path = "../../pubsub" } +jsonrpc-server-utils = { version = "13.0", path = "../../server-utils", optional = true } log = "0.4" +parity-tokio-ipc = { version = "0.1", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "0.1", optional = true } @@ -43,9 +50,10 @@ websocket = { version = "0.23", optional = true } [dev-dependencies] assert_matches = "1.1" jsonrpc-http-server = { version = "13.0", path = "../../http" } +jsonrpc-ipc-server = { version = "13.0", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.6" tokio = "0.1" [badges] -travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} +travis-ci = { repository = "paritytech/jsonrpc", branch = "master" } diff --git a/core-client/transports/src/transports/ipc.rs b/core-client/transports/src/transports/ipc.rs new file mode 100644 index 000000000..225b10bab --- /dev/null +++ b/core-client/transports/src/transports/ipc.rs @@ -0,0 +1,126 @@ +//! JSON-RPC IPC client implementation using Unix Domain Sockets on UNIX-likes +//! and Named Pipes on Windows. + +use crate::transports::duplex::duplex; +use crate::{RpcChannel, RpcError}; +use futures::prelude::*; +use jsonrpc_server_utils::codecs::StreamCodec; +use parity_tokio_ipc::IpcConnection; +use std::io; +use std::path::Path; +use tokio::codec::Decoder; + +/// Connect to a JSON-RPC IPC server. +pub fn connect, Client: From>( + path: P, + reactor: &tokio::reactor::Handle, +) -> Result, io::Error> { + let connection = IpcConnection::connect(path, reactor)?; + + Ok(futures::lazy(move || { + let (sink, stream) = StreamCodec::stream_incoming().framed(connection).split(); + let sink = sink.sink_map_err(|e| RpcError::Other(e.into())); + let stream = stream.map_err(|e| RpcError::Other(e.into())); + + let (client, sender) = duplex(sink, stream); + + tokio::spawn(client.map_err(|e| log::warn!("IPC client error: {:?}", e))); + Ok(sender.into()) + })) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::*; + use jsonrpc_core::{Error, ErrorCode, IoHandler, Params, Value}; + use jsonrpc_ipc_server::ServerBuilder; + use parity_tokio_ipc::dummy_endpoint; + use serde_json::map::Map; + use tokio::runtime::Runtime; + + #[test] + fn should_call_one() { + let mut rt = Runtime::new().unwrap(); + #[allow(deprecated)] + let reactor = rt.reactor().clone(); + let sock_path = dummy_endpoint(); + + let mut io = IoHandler::new(); + io.add_method("greeting", |params| { + let map_obj = match params { + Params::Map(obj) => obj, + _ => return Err(Error::invalid_params("missing object")), + }; + let name = match map_obj.get("name") { + Some(val) => val.as_str().unwrap(), + None => return Err(Error::invalid_params("no name")), + }; + Ok(Value::String(format!("Hello {}!", name))) + }); + let builder = ServerBuilder::new(io).event_loop_executor(rt.executor()); + let server = builder.start(&sock_path).expect("Couldn't open socket"); + + let client: RawClient = rt.block_on(connect(sock_path, &reactor).unwrap()).unwrap(); + let mut map = Map::new(); + map.insert("name".to_string(), "Jeffry".into()); + let fut = client.call_method("greeting", Params::Map(map)); + + // FIXME: it seems that IPC server on Windows won't be polled with + // default I/O reactor, work around with sending stop signal which polls + // the server (https://github.com/paritytech/jsonrpc/pull/459) + server.close(); + + match rt.block_on(fut) { + Ok(val) => assert_eq!(&val, "Hello Jeffry!"), + Err(err) => panic!("IPC RPC call failed: {}", err), + } + rt.shutdown_now().wait().unwrap(); + } + + // Windows currently panics in `IpcConnection::connect` if a given path doesn't exist. + // Should be fixed by https://github.com/NikVolf/parity-tokio-ipc/pull/12 + #[cfg(not(windows))] + #[test] + fn should_fail_without_server() { + let rt = Runtime::new().unwrap(); + #[allow(deprecated)] + let reactor = rt.reactor(); + + match connect::<_, RawClient>(dummy_endpoint(), reactor) { + Err(..) => {} + Ok(..) => panic!("Should not be able to connect to an IPC socket that's not open"), + } + rt.shutdown_now().wait().unwrap(); + } + + #[test] + fn should_handle_server_error() { + let mut rt = Runtime::new().unwrap(); + #[allow(deprecated)] + let reactor = rt.reactor().clone(); + let sock_path = dummy_endpoint(); + + let mut io = IoHandler::new(); + io.add_method("greeting", |_params| Err(Error::invalid_params("test error"))); + let builder = ServerBuilder::new(io).event_loop_executor(rt.executor()); + let server = builder.start(&sock_path).expect("Couldn't open socket"); + + let client: RawClient = rt.block_on(connect(sock_path, &reactor).unwrap()).unwrap(); + let mut map = Map::new(); + map.insert("name".to_string(), "Jeffry".into()); + let fut = client.call_method("greeting", Params::Map(map)); + + // FIXME: it seems that IPC server on Windows won't be polled with + // default I/O reactor, work around with sending stop signal which polls + // the server (https://github.com/paritytech/jsonrpc/pull/459) + server.close(); + + match rt.block_on(fut) { + Err(RpcError::JsonRpcError(err)) => assert_eq!(err.code, ErrorCode::InvalidParams), + Ok(_) => panic!("Expected the call to fail"), + _ => panic!("Unexpected error type"), + } + rt.shutdown_now().wait().unwrap(); + } +} diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index 12ba57ba1..84b397b9a 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -10,6 +10,8 @@ use crate::{CallMessage, RpcError}; pub mod duplex; #[cfg(feature = "http")] pub mod http; +#[cfg(feature = "ipc")] +pub mod ipc; pub mod local; #[cfg(feature = "ws")] pub mod ws; From 9cb109cd13553553dfeecd923c88e4ed790051ef Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 24 Jul 2019 14:47:57 +0200 Subject: [PATCH 067/149] ipc: Allow specifying different event loop reactor in server builder (#459) * ipc: Allow specifying different event loop reactor in server builder * Use Option for user-specified reactor --- ipc/src/server.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 506007ee1..0bddbb5bd 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -50,6 +50,7 @@ pub struct ServerBuilder = middleware::Noop> meta_extractor: Arc>, session_stats: Option>, executor: reactor::UninitializedExecutor, + reactor: Option, incoming_separator: codecs::Separator, outgoing_separator: codecs::Separator, security_attributes: SecurityAttributes, @@ -78,6 +79,7 @@ impl> ServerBuilder { meta_extractor: Arc::new(extractor), session_stats: None, executor: reactor::UninitializedExecutor::Unspawned, + reactor: None, incoming_separator: codecs::Separator::Empty, outgoing_separator: codecs::Separator::default(), security_attributes: SecurityAttributes::empty(), @@ -91,6 +93,12 @@ impl> ServerBuilder { self } + /// Sets different event loop I/O reactor. + pub fn event_loop_reactor(mut self, reactor: Handle) -> Self { + self.reactor = Some(reactor); + self + } + /// Sets session metadata extractor. pub fn session_meta_extractor(mut self, meta_extractor: X) -> Self where @@ -128,6 +136,7 @@ impl> ServerBuilder { /// Creates a new server from the given endpoint. pub fn start(self, path: &str) -> std::io::Result { let executor = self.executor.initialize()?; + let reactor = self.reactor; let rpc_handler = self.handler; let endpoint_addr = path.to_owned(); let meta_extractor = self.meta_extractor; @@ -151,8 +160,9 @@ impl> ServerBuilder { } } - let endpoint_handle = Handle::default(); - let connections = match endpoint.incoming(&endpoint_handle) { + // Make sure to construct Handle::default() inside Tokio runtime + let reactor = reactor.unwrap_or_else(Handle::default); + let connections = match endpoint.incoming(&reactor) { Ok(connections) => connections, Err(e) => { start_signal From c08393d4cb3966daafd4431dc7e5d9b5ef531a09 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 24 Jul 2019 15:05:10 +0200 Subject: [PATCH 068/149] Bump parity-tokio-ipc to 0.2 (#465) * Bump parity-tokio-ipc to 0.2 * core-client: Fix broken Windows IPC test without server --- core-client/transports/Cargo.toml | 2 +- core-client/transports/src/transports/ipc.rs | 3 --- ipc/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 4ca61dd99..30ef7e992 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -41,7 +41,7 @@ jsonrpc-core = { version = "13.0", path = "../../core" } jsonrpc-pubsub = { version = "13.0", path = "../../pubsub" } jsonrpc-server-utils = { version = "13.0", path = "../../server-utils", optional = true } log = "0.4" -parity-tokio-ipc = { version = "0.1", optional = true } +parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "0.1", optional = true } diff --git a/core-client/transports/src/transports/ipc.rs b/core-client/transports/src/transports/ipc.rs index 225b10bab..8d3407489 100644 --- a/core-client/transports/src/transports/ipc.rs +++ b/core-client/transports/src/transports/ipc.rs @@ -78,9 +78,6 @@ mod tests { rt.shutdown_now().wait().unwrap(); } - // Windows currently panics in `IpcConnection::connect` if a given path doesn't exist. - // Should be fixed by https://github.com/NikVolf/parity-tokio-ipc/pull/12 - #[cfg(not(windows))] #[test] fn should_fail_without_server() { let rt = Runtime::new().unwrap(); diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index ce34c7ba6..d6c15b9df 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4" tokio-service = "0.1" jsonrpc-core = { version = "13.0", path = "../core" } jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } -parity-tokio-ipc = "0.1" +parity-tokio-ipc = "0.2" parking_lot = "0.9" [dev-dependencies] From 97178ceed4de1761e490527d0245da9845fe77bd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 5 Aug 2019 09:09:32 +0100 Subject: [PATCH 069/149] [core-client] add websockets connect method accepting valid Url (#468) --- core-client/transports/Cargo.toml | 1 + core-client/transports/src/transports/ws.rs | 28 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 30ef7e992..8dbb3faf6 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -46,6 +46,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "0.1", optional = true } websocket = { version = "0.23", optional = true } +url = "1.7" [dev-dependencies] assert_matches = "1.1" diff --git a/core-client/transports/src/transports/ws.rs b/core-client/transports/src/transports/ws.rs index f41119459..e9f89531d 100644 --- a/core-client/transports/src/transports/ws.rs +++ b/core-client/transports/src/transports/ws.rs @@ -9,11 +9,32 @@ use websocket::{ClientBuilder, OwnedMessage}; /// Connect to a JSON-RPC websocket server. /// /// Uses an unbuffered channel to queue outgoing rpc messages. -pub fn connect(url: &str) -> Result, Error> +/// +/// Returns `Err` if the `url` is invalid. +pub fn try_connect(url: &str) -> Result, Error> +where + T: From, +{ + let client_builder = ClientBuilder::new(url)?; + Ok(do_connect(client_builder)) +} + +/// Connect to a JSON-RPC websocket server. +/// +/// Uses an unbuffered channel to queue outgoing rpc messages. +pub fn connect(url: &url::Url) -> impl Future +where + T: From, +{ + let client_builder = ClientBuilder::from_url(url); + do_connect(client_builder) +} + +fn do_connect(client_builder: ClientBuilder) -> impl Future where T: From, { - let client = ClientBuilder::new(url)? + client_builder .async_connect(None) .map(|(client, _)| { let (sink, stream) = client.split(); @@ -23,8 +44,7 @@ where tokio::spawn(rpc_client); sender.into() }) - .map_err(|error| RpcError::Other(error.into())); - Ok(client) + .map_err(|error| RpcError::Other(error.into())) } struct WebsocketClient { From 3e7ae2fa0b33c9a46448a8acb0dadbe6fb089908 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2019 10:10:31 +0200 Subject: [PATCH 070/149] Update ws requirement from 0.8 to 0.9 (#472) Updates the requirements on [ws](https://github.com/housleyjk/ws-rs) to permit the latest version. - [Release notes](https://github.com/housleyjk/ws-rs/releases) - [Changelog](https://github.com/housleyjk/ws-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/housleyjk/ws-rs/compare/v0.8.0...v0.9.0) Signed-off-by: dependabot-preview[bot] --- ws/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 6000a18a1..4860333b7 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -15,7 +15,7 @@ jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } log = "0.4" parking_lot = "0.9" slab = "0.4" -ws = "0.8" +ws = "0.9" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} From 5b07faa3b896b3eff9dc36d688bce203d4a6cb46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 6 Aug 2019 14:17:23 +0200 Subject: [PATCH 071/149] Add a method to handle empty response. (#473) --- core/src/io.rs | 18 ++++++++++++++++++ core/src/types/response.rs | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/core/src/io.rs b/core/src/io.rs index ac568602a..c9d6e8180 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -529,6 +529,24 @@ mod tests { assert_eq!(called.load(atomic::Ordering::SeqCst), true); } + #[test] + fn test_batch_notification() { + use std::sync::atomic; + use std::sync::Arc; + + let mut io = IoHandler::new(); + + let called = Arc::new(atomic::AtomicBool::new(false)); + let c = called.clone(); + io.add_notification("say_hello", move |_| { + c.store(true, atomic::Ordering::SeqCst); + }); + + let request = r#"[{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23]}]"#; + assert_eq!(io.handle_request_sync(request), None); + assert_eq!(called.load(atomic::Ordering::SeqCst), true); + } + #[test] fn test_send_sync() { fn is_send_sync(_obj: T) -> bool diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 8010f00b3..7b027bb93 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -105,6 +105,17 @@ impl Response { } .into() } + + /// Deserialize `Response` from given JSON string. + /// + /// This method will handle an empty string as empty batch response. + pub fn from_json(s: &str) -> Result { + if s.is_empty() { + Ok(Response::Batch(vec![])) + } else { + serde_json::from_str(s) + } + } } impl From for Response { @@ -268,3 +279,15 @@ fn handle_incorrect_responses() { "Expected error when deserializing invalid payload." ); } + +#[test] +fn should_parse_empty_response_as_batch() { + use serde_json; + + let dsr = r#""#; + + let deserialized1: Result= serde_json::from_str(dsr); + let deserialized2: Result = Response::from_json(dsr); + assert!(deserialized1.is_err(), "Empty string is not valid JSON, so we should get an error."); + assert_eq!(deserialized2.unwrap(), Response::Batch(vec![])); +} From 54c4de527f536f8b6c5c6f4616446f82d00ed936 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 19 Aug 2019 18:40:37 +0100 Subject: [PATCH 072/149] [client] don't block other subscriptions if subscriber not ready (#479) --- core-client/transports/src/transports/duplex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index baf4cbe49..620db2912 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -247,7 +247,7 @@ where } Ok(Async::NotReady) => { let (sid, method) = sid_and_method; - self.incoming.push_front((id, result, Some(method), Some(sid))); + self.incoming.push_back((id, result, Some(method), Some(sid))); break; } Err(_) => { From f5f91bd10e847d8a5e8bfca74754693742299495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 26 Aug 2019 14:26:26 +0200 Subject: [PATCH 073/149] Make IoHandlers cloneable. (#483) * Make IoHandlers cloneable. * Bump version. --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- core/src/io.rs | 4 ++-- core/src/middleware.rs | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 20 files changed, 51 insertions(+), 51 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index ced65c5a7..5f602fbcf 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" categories = [ "asynchronous", @@ -25,7 +25,7 @@ ws = ["jsonrpc-client-transports/ws"] ipc = ["jsonrpc-client-transports/ipc"] [dependencies] -jsonrpc-client-transports = { version = "13.0", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "13.1", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 8dbb3faf6..ea9ae4074 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" categories = [ "asynchronous", @@ -37,9 +37,9 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "13.0", path = "../../core" } -jsonrpc-pubsub = { version = "13.0", path = "../../pubsub" } -jsonrpc-server-utils = { version = "13.0", path = "../../server-utils", optional = true } +jsonrpc-core = { version = "13.1", path = "../../core" } +jsonrpc-pubsub = { version = "13.1", path = "../../pubsub" } +jsonrpc-server-utils = { version = "13.1", path = "../../server-utils", optional = true } log = "0.4" parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } @@ -50,8 +50,8 @@ url = "1.7" [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "13.0", path = "../../http" } -jsonrpc-ipc-server = { version = "13.0", path = "../../ipc" } +jsonrpc-http-server = { version = "13.1", path = "../../http" } +jsonrpc-ipc-server = { version = "13.1", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.6" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index 115286bd6..39511a207 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" categories = [ "asynchronous", diff --git a/core/src/io.rs b/core/src/io.rs index c9d6e8180..42dd81686 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -70,7 +70,7 @@ impl Compatibility { /// Request handler /// /// By default compatible only with jsonrpc v2 -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct MetaIoHandler = middleware::Noop> { middleware: S, compatibility: Compatibility, @@ -353,7 +353,7 @@ impl> IoHandlerExtension for MetaIoHandler(MetaIoHandler); // Type inference helper diff --git a/core/src/middleware.rs b/core/src/middleware.rs index b9b87ee94..a33ae4add 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -41,7 +41,7 @@ pub type NoopFuture = Box, Error = ()> + Send pub type NoopCallFuture = Box, Error = ()> + Send>; /// No-op middleware implementation -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct Noop; impl Middleware for Noop { type Future = NoopFuture; diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 45d34a99b..3ce7cad62 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "13.0", path = "../core" } -jsonrpc-core-client = { version = "13.0", path = "../core-client" } -jsonrpc-pubsub = { version = "13.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "13.0", path = "../tcp" } +jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-core-client = { version = "13.1", path = "../core-client" } +jsonrpc-pubsub = { version = "13.1", path = "../pubsub" } +jsonrpc-tcp-server = { version = "13.1", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index c8b8541b6..50bac3b67 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "13.0", path = "../core" } -jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } +jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index 15bb983b2..f2991a33c 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "13.0" +jsonrpc-http-server = "13.1" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index d6c15b9df..263d09b64 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "13.0", path = "../core" } -jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } +jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } parity-tokio-ipc = "0.2" parking_lot = "0.9" diff --git a/ipc/README.md b/ipc/README.md index f351fbb0e..bddc83fbd 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "13.0" +jsonrpc-ipc-server = "13.1" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 0c6cd3728..e06287262 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] log = "0.4" parking_lot = "0.9" -jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-core = { version = "13.1", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "13.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "13.1", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index f4a98eca1..d969ef579 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "13.0", path = "../../core" } -jsonrpc-pubsub = { version = "13.0", path = "../" } -jsonrpc-ws-server = { version = "13.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "13.0", path = "../../ipc" } +jsonrpc-core = { version = "13.1", path = "../../core" } +jsonrpc-pubsub = { version = "13.1", path = "../" } +jsonrpc-ws-server = { version = "13.1", path = "../../ws" } +jsonrpc-ipc-server = { version = "13.1", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index dd36671f8..b7353f9e1 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-core = { version = "13.1", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 9c667eb26..f40e33fe2 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "13.0", path = "../core" } +jsonrpc-core = { version = "13.1", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index 1ae41f2fd..ce23d8414 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "13.0" +jsonrpc-stdio-server = "13.1" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index e8061fe81..24e72b344 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] log = "0.4" parking_lot = "0.9" tokio-service = "0.1" -jsonrpc-core = { version = "13.0", path = "../core" } -jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } +jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index a0f0c79aa..b8c0a6e1d 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "13.0" +jsonrpc-tcp-server = "13.1" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index 6f022f094..76ac6cdcd 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "13.0.0" +version = "13.1.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "13.0", path = "../core" } -jsonrpc-core-client = { version = "13.0", path = "../core-client" } -jsonrpc-pubsub = { version = "13.0", path = "../pubsub" } +jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-core-client = { version = "13.1", path = "../core-client" } +jsonrpc-pubsub = { version = "13.1", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { version = "13.0", path = "../derive" } +jsonrpc-derive = { version = "13.1", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 4860333b7..58bcb936a 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.0.0" +version = "13.1.0" [dependencies] -jsonrpc-core = { version = "13.0", path = "../core" } -jsonrpc-server-utils = { version = "13.0", path = "../server-utils" } +jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } log = "0.4" parking_lot = "0.9" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index ae047b454..2e415b41d 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "13.0" +jsonrpc-ws-server = "13.1" ``` `main.rs` From 38043dd3a1b07330a378946b81a6fb7321d3868a Mon Sep 17 00:00:00 2001 From: Eyal Kalderon Date: Wed, 4 Sep 2019 23:04:34 +0800 Subject: [PATCH 074/149] Implement client notification support (#486) * Add RawClient::notify() and TypedClient::notify() methods * Add RequestBuilder::notification() method * Add notification support to duplex client transport * Mark notifications as unsupported for http client transport * Add client notification unit test * Fix notification params in unit test * Add client notification support in jsonrpc-derive * Drop client after sending notification * Update client_server_roundtrip test in jsonrpc-derive * Add notification support to http client transport --- core-client/transports/src/lib.rs | 94 ++++++++++++++++--- .../transports/src/transports/duplex.rs | 1 + core-client/transports/src/transports/http.rs | 65 ++++++++++--- core-client/transports/src/transports/mod.rs | 15 ++- derive/src/to_client.rs | 7 +- derive/tests/client.rs | 11 ++- 6 files changed, 161 insertions(+), 32 deletions(-) diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index 8ff7336c6..d8f384552 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -39,18 +39,26 @@ impl From for RpcError { } } -/// A rpc call message. +/// An RPC call message. struct CallMessage { - /// The rpc method name. + /// The RPC method name. method: String, - /// The rpc method parameters. + /// The RPC method parameters. params: Params, /// The oneshot channel to send the result of the rpc /// call to. sender: oneshot::Sender>, } -/// A rpc subscription. +/// An RPC notification. +struct NotifyMessage { + /// The RPC method name. + method: String, + /// The RPC method paramters. + params: Params, +} + +/// An RPC subscription. struct Subscription { /// The subscribe method name. subscribe: String, @@ -62,7 +70,7 @@ struct Subscription { unsubscribe: String, } -/// A rpc subscribe message. +/// An RPC subscribe message. struct SubscribeMessage { /// The subscription to subscribe to. subscription: Subscription, @@ -72,8 +80,10 @@ struct SubscribeMessage { /// A message sent to the `RpcClient`. enum RpcMessage { - /// Make a rpc call. + /// Make an RPC call. Call(CallMessage), + /// Send a notification. + Notify(NotifyMessage), /// Subscribe to a notification. Subscribe(SubscribeMessage), } @@ -84,6 +94,12 @@ impl From for RpcMessage { } } +impl From for RpcMessage { + fn from(msg: NotifyMessage) -> Self { + RpcMessage::Notify(msg) + } +} + impl From for RpcMessage { fn from(msg: SubscribeMessage) -> Self { RpcMessage::Subscribe(msg) @@ -208,7 +224,7 @@ impl From for RawClient { } impl RawClient { - /// Call RPC with raw JSON + /// Call RPC method with raw JSON. pub fn call_method(&self, method: &str, params: Params) -> impl Future { let (sender, receiver) = oneshot::channel(); let msg = CallMessage { @@ -222,7 +238,19 @@ impl RawClient { .and_then(|_| RpcFuture::new(receiver)) } - /// Subscribe to topic with raw JSON + /// Send RPC notification with raw JSON. + pub fn notify(&self, method: &str, params: Params) -> impl Future { + let msg = NotifyMessage { + method: method.into(), + params, + }; + self.0 + .send(msg.into()) + .map(|_| ()) + .map_err(|error| RpcError::Other(error.into())) + } + + /// Subscribe to topic with raw JSON. pub fn subscribe( &self, subscribe: &str, @@ -258,12 +286,12 @@ impl From for TypedClient { } impl TypedClient { - /// Create new TypedClient + /// Create a new `TypedClient`. pub fn new(raw_cli: RawClient) -> Self { TypedClient(raw_cli) } - /// Call RPC with serialization of request and deserialization of response + /// Call RPC with serialization of request and deserialization of response. pub fn call_method( &self, method: &str, @@ -290,7 +318,24 @@ impl TypedClient { })) } - /// Subscribe with serialization of request and deserialization of response + /// Call RPC with serialization of request only. + pub fn notify(&self, method: &str, args: T) -> impl Future { + let args = + serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC"); + let params = match args { + Value::Array(vec) => Params::Array(vec), + Value::Null => Params::None, + _ => { + return future::Either::A(future::err(RpcError::Other(format_err!( + "RPC params should serialize to a JSON array, or null" + )))) + } + }; + + future::Either::B(self.0.notify(method, params)) + } + + /// Subscribe with serialization of request and deserialization of response. pub fn subscribe( &self, subscribe: &str, @@ -343,6 +388,10 @@ mod tests { fn add(&self, a: u64, b: u64) -> impl Future { self.0.call_method("add", "u64", (a, b)) } + + fn completed(&self, success: bool) -> impl Future { + self.0.notify("completed", (success,)) + } } #[test] @@ -371,6 +420,29 @@ mod tests { tokio::run(fut); } + #[test] + fn should_send_notification() { + crate::logger::init_log(); + let mut handler = IoHandler::new(); + handler.add_notification("completed", |params: Params| { + let (success,) = params.parse::<(bool,)>().expect("expected to receive one boolean"); + assert_eq!(success, true); + }); + + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .completed(true) + .map(move |()| drop(client)) + .join(rpc_client) + .map(|_| ()) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } + #[test] fn should_handle_subscription() { crate::logger::init_log(); diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index 620db2912..a0ff43d21 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -144,6 +144,7 @@ where } request_str } + RpcMessage::Notify(msg) => self.request_builder.notification(&msg), }; log::debug!("outgoing: {}", request_str); self.outgoing.push_back(request_str); diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index 2cabd9b1e..0ad0cb5f5 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -43,14 +43,18 @@ where let fut = receiver .filter_map(move |msg: RpcMessage| { - let msg = match msg { - RpcMessage::Call(call) => call, + let (request, sender) = match msg { + RpcMessage::Call(call) => { + let (_, request) = request_builder.call_request(&call); + (request, Some(call.sender)) + } + RpcMessage::Notify(notify) => (request_builder.notification(¬ify), None), RpcMessage::Subscribe(_) => { log::warn!("Unsupported `RpcMessage` type `Subscribe`."); return None; } }; - let (_, request) = request_builder.call_request(&msg); + let request = Request::post(&url) .header( http::header::CONTENT_TYPE, @@ -58,10 +62,11 @@ where ) .body(request.into()) .expect("Uri and request headers are valid; qed"); - Some(client.request(request).then(move |response| Ok((response, msg)))) + + Some(client.request(request).then(move |response| Ok((response, sender)))) }) .buffer_unordered(max_parallel) - .for_each(|(result, msg)| { + .for_each(|(result, sender)| { let future = match result { Ok(ref res) if !res.status().is_success() => { log::trace!("http result status {}", res.status()); @@ -77,14 +82,16 @@ where Err(err) => A(future::err(RpcError::Other(err.into()))), }; future.then(|result| { - let response = result - .and_then(|response| { - let response_str = String::from_utf8_lossy(response.as_ref()).into_owned(); - super::parse_response(&response_str) - }) - .and_then(|r| r.1); - if let Err(err) = msg.sender.send(response) { - log::warn!("Error resuming asynchronous request: {:?}", err); + if let Some(sender) = sender { + let response = result + .and_then(|response| { + let response_str = String::from_utf8_lossy(response.as_ref()).into_owned(); + super::parse_response(&response_str) + }) + .and_then(|r| r.1); + if let Err(err) = sender.send(response) { + log::warn!("Error resuming asynchronous request: {:?}", err); + } } Ok(()) }) @@ -159,6 +166,10 @@ mod tests { _ => Ok(Value::String("world".into())), }); io.add_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); + io.add_notification("notify", |params: Params| { + let (value,) = params.parse::<(u64,)>().expect("expected one u64 as param"); + assert_eq!(value, 12); + }); io } @@ -179,6 +190,9 @@ mod tests { fn fail(&self) -> impl Future { self.0.call_method("fail", "()", ()) } + fn notify(&self, value: u64) -> impl Future { + self.0.notify("notify", (value,)) + } } #[test] @@ -207,6 +221,31 @@ mod tests { assert_eq!("hello http", result); } + #[test] + fn should_send_notification() { + crate::logger::init_log(); + + // given + let server = TestServer::serve(id); + let (tx, rx) = std::sync::mpsc::channel(); + + // when + let run = connect(&server.uri) + .and_then(|client: TestClient| { + client.notify(12).and_then(move |result| { + drop(client); + let _ = tx.send(result); + Ok(()) + }) + }) + .map_err(|e| log::error!("RPC Client error: {:?}", e)); + + rt::run(run); + + // then + rx.recv_timeout(Duration::from_secs(3)).unwrap(); + } + #[test] fn handles_invalid_uri() { crate::logger::init_log(); diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index 84b397b9a..3d5991c48 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -1,11 +1,11 @@ //! Client transport implementations -use jsonrpc_core::{Call, Error, Id, MethodCall, Params, Version}; +use jsonrpc_core::{Call, Error, Id, MethodCall, Notification, Params, Version}; use jsonrpc_pubsub::SubscriptionId; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::{CallMessage, RpcError}; +use crate::{CallMessage, NotifyMessage, RpcError}; pub mod duplex; #[cfg(feature = "http")] @@ -61,6 +61,15 @@ impl RequestBuilder { fn unsubscribe_request(&mut self, unsubscribe: String, sid: SubscriptionId) -> (Id, String) { self.single_request(unsubscribe, Params::Array(vec![Value::from(sid)])) } + + fn notification(&mut self, msg: &NotifyMessage) -> String { + let request = jsonrpc_core::Request::Single(Call::Notification(Notification { + jsonrpc: Some(Version::V2), + method: msg.method.clone(), + params: msg.params.clone(), + })); + serde_json::to_string(&request).expect("Request serialization is infallible; qed") + } } /// Parse raw string into a single JSON value, together with the request Id. @@ -157,7 +166,7 @@ impl From for Result { #[cfg(test)] mod tests { use super::*; - use jsonrpc_core::{Value, Version, Params, Failure, Success, Output, Notification}; + use jsonrpc_core::{Failure, Notification, Output, Params, Success, Value, Version}; use serde_json; #[test] diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index e39e8d886..af920414c 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -134,14 +134,15 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result { let attrs = get_doc_comments(&method.trait_item.attrs); + let rpc_name = method.name(); let name = &method.trait_item.sig.ident; let args = compute_args(&method.trait_item); let arg_names = compute_arg_identifiers(&args)?; let client_method = syn::parse_quote! { #(#attrs)* - pub fn #name(&self, #args) { - let _args_tuple = (#(#arg_names,)*); - unimplemented!() + pub fn #name(&self, #args) -> impl Future { + let args_tuple = (#(#arg_names,)*); + self.inner.notify(#rpc_name, args_tuple) } }; client_methods.push(client_method); diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 6ac6fb301..469175cdf 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -7,6 +7,9 @@ use jsonrpc_derive::rpc; pub trait Rpc { #[rpc(name = "add")] fn add(&self, a: u64, b: u64) -> Result; + + #[rpc(name = "notify")] + fn notify(&self, foo: u64); } struct RpcServer; @@ -15,6 +18,10 @@ impl Rpc for RpcServer { fn add(&self, a: u64, b: u64) -> Result { Ok(a + b) } + + fn notify(&self, foo: u64) { + println!("received {}", foo); + } } #[test] @@ -25,10 +32,10 @@ fn client_server_roundtrip() { let fut = client .clone() .add(3, 4) - .and_then(move |res| client.add(res, 5)) + .and_then(move |res| client.notify(res).map(move |_| res)) .join(rpc_client) .map(|(res, ())| { - assert_eq!(res, 12); + assert_eq!(res, 7); }) .map_err(|err| { eprintln!("{:?}", err); From 97b08dae468142352aad31b8f6df237c7c192e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 13 Sep 2019 10:45:56 +0200 Subject: [PATCH 075/149] Add `iter` and `IntoIterator` to `IoHandler` (#488) * Add iterators over methods. * Bump version. * Formatting. * Import the types. --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 +++++------ core/Cargo.toml | 2 +- core/src/io.rs | 34 ++++++++++++++++++++++++++++++- derive/Cargo.toml | 10 ++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 ++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 ++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 19 files changed, 81 insertions(+), 49 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 5f602fbcf..04dc38e2c 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" categories = [ "asynchronous", @@ -25,7 +25,7 @@ ws = ["jsonrpc-client-transports/ws"] ipc = ["jsonrpc-client-transports/ipc"] [dependencies] -jsonrpc-client-transports = { version = "13.1", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "13.2", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index ea9ae4074..2f4c3b4cb 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" categories = [ "asynchronous", @@ -37,9 +37,9 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "13.1", path = "../../core" } -jsonrpc-pubsub = { version = "13.1", path = "../../pubsub" } -jsonrpc-server-utils = { version = "13.1", path = "../../server-utils", optional = true } +jsonrpc-core = { version = "13.2", path = "../../core" } +jsonrpc-pubsub = { version = "13.2", path = "../../pubsub" } +jsonrpc-server-utils = { version = "13.2", path = "../../server-utils", optional = true } log = "0.4" parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } @@ -50,8 +50,8 @@ url = "1.7" [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "13.1", path = "../../http" } -jsonrpc-ipc-server = { version = "13.1", path = "../../ipc" } +jsonrpc-http-server = { version = "13.2", path = "../../http" } +jsonrpc-ipc-server = { version = "13.2", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.6" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index 39511a207..30efa8d77 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" categories = [ "asynchronous", diff --git a/core/src/io.rs b/core/src/io.rs index 42dd81686..b53de5722 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{hash_map::{Iter, IntoIter}, HashMap}; use std::ops::{Deref, DerefMut}; use std::sync::Arc; @@ -83,6 +83,24 @@ impl Default for MetaIoHandler { } } +impl> IntoIterator for MetaIoHandler { + type Item = (String, RemoteProcedure); + type IntoIter = IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.methods.into_iter() + } +} + +impl<'a, T: Metadata, S: Middleware> IntoIterator for &'a MetaIoHandler { + type Item = (&'a String, &'a RemoteProcedure); + type IntoIter = Iter<'a, String, RemoteProcedure>; + + fn into_iter(self) -> Self::IntoIter { + self.methods.iter() + } +} + impl MetaIoHandler { /// Creates new `MetaIoHandler` compatible with specified protocol version. pub fn with_compatibility(compatibility: Compatibility) -> Self { @@ -287,6 +305,11 @@ impl> MetaIoHandler { )))), }) } + + /// Returns an iterator visiting all methods in arbitrary order. + pub fn iter(&self) -> impl Iterator)> { + self.methods.iter() + } } /// A type that can augment `MetaIoHandler`. @@ -356,6 +379,15 @@ impl> IoHandlerExtension for MetaIoHandler(MetaIoHandler); +impl IntoIterator for IoHandler { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + // Type inference helper impl IoHandler { /// Creates new `IoHandler` without any metadata. diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 3ce7cad62..70c32345f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "13.1", path = "../core" } -jsonrpc-core-client = { version = "13.1", path = "../core-client" } -jsonrpc-pubsub = { version = "13.1", path = "../pubsub" } -jsonrpc-tcp-server = { version = "13.1", path = "../tcp" } +jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-core-client = { version = "13.2", path = "../core-client" } +jsonrpc-pubsub = { version = "13.2", path = "../pubsub" } +jsonrpc-tcp-server = { version = "13.2", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 50bac3b67..26681f282 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "13.1", path = "../core" } -jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } +jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index f2991a33c..fc25ce224 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "13.1" +jsonrpc-http-server = "13.2" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 263d09b64..a0ee0b038 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "13.1", path = "../core" } -jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } +jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } parity-tokio-ipc = "0.2" parking_lot = "0.9" diff --git a/ipc/README.md b/ipc/README.md index bddc83fbd..cc572e00f 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "13.1" +jsonrpc-ipc-server = "13.2" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index e06287262..a6fab04e0 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] log = "0.4" parking_lot = "0.9" -jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-core = { version = "13.2", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "13.1", path = "../tcp" } +jsonrpc-tcp-server = { version = "13.2", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index d969ef579..01e77cd7a 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "13.1", path = "../../core" } -jsonrpc-pubsub = { version = "13.1", path = "../" } -jsonrpc-ws-server = { version = "13.1", path = "../../ws" } -jsonrpc-ipc-server = { version = "13.1", path = "../../ipc" } +jsonrpc-core = { version = "13.2", path = "../../core" } +jsonrpc-pubsub = { version = "13.2", path = "../" } +jsonrpc-ws-server = { version = "13.2", path = "../../ws" } +jsonrpc-ipc-server = { version = "13.2", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index b7353f9e1..0d4a75581 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-core = { version = "13.2", path = "../core" } lazy_static = "1.1.0" log = "0.4" num_cpus = "1.8" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index f40e33fe2..52ff518f5 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "13.1", path = "../core" } +jsonrpc-core = { version = "13.2", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index ce23d8414..a030fd64c 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "13.1" +jsonrpc-stdio-server = "13.2" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 24e72b344..c2f558ebe 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] log = "0.4" parking_lot = "0.9" tokio-service = "0.1" -jsonrpc-core = { version = "13.1", path = "../core" } -jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } +jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index b8c0a6e1d..9ccd73c51 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "13.1" +jsonrpc-tcp-server = "13.2" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index 76ac6cdcd..b8756ddfd 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "13.1.0" +version = "13.2.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "13.1", path = "../core" } -jsonrpc-core-client = { version = "13.1", path = "../core-client" } -jsonrpc-pubsub = { version = "13.1", path = "../pubsub" } +jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-core-client = { version = "13.2", path = "../core-client" } +jsonrpc-pubsub = { version = "13.2", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { version = "13.1", path = "../derive" } +jsonrpc-derive = { version = "13.2", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 58bcb936a..8c868bba4 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.1.0" +version = "13.2.0" [dependencies] -jsonrpc-core = { version = "13.1", path = "../core" } -jsonrpc-server-utils = { version = "13.1", path = "../server-utils" } +jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } log = "0.4" parking_lot = "0.9" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 2e415b41d..5da6fc47f 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "13.1" +jsonrpc-ws-server = "13.2" ``` `main.rs` From 77d7fb6980f29b371f6561130800722c2a93fbd1 Mon Sep 17 00:00:00 2001 From: Andrey Kuznetsov Date: Mon, 30 Sep 2019 11:02:46 +0300 Subject: [PATCH 076/149] fix for #490, related to NikVolf/parity-tokio-ipc#15 (#491) --- ipc/src/server.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 0bddbb5bd..037c0b88b 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -161,7 +161,13 @@ impl> ServerBuilder { } // Make sure to construct Handle::default() inside Tokio runtime - let reactor = reactor.unwrap_or_else(Handle::default); + let reactor = if cfg!(windows) { + #[allow(deprecated)] + reactor.unwrap_or_else(Handle::current) + } else { + reactor.unwrap_or_else(Handle::default) + }; + let connections = match endpoint.incoming(&reactor) { Ok(connections) => connections, Err(e) => { From d8cfec5fc9d9dc1642de5df83b5c314ffb4d317b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 30 Sep 2019 16:16:28 +0200 Subject: [PATCH 077/149] Run format only on stable @ linux. (#494) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d0848d222..3d6ce4c7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ script: - cargo build --all - cargo test --all - | - ([ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true + ([ $TRAVIS_OS_NAME == 'linux' ] && [ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true after_success: | [ $TRAVIS_OS_NAME == 'linux' ] && From 3b790c646117bd0d8273491e321dfd6638f0c1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 7 Oct 2019 14:48:40 +0200 Subject: [PATCH 078/149] Fix threading and closing of servers (#495) * Remove unnecessary spawns. * Fix how we manage threads. * Remove unused import. * Parallelize incoming connections. * Add close server test. * Add some docs. * Tone down warnings. * Fix test assert. --- http/src/lib.rs | 157 +++++++++++++------------ http/src/tests.rs | 34 ++++++ ipc/src/server.rs | 10 +- server-utils/Cargo.toml | 1 - server-utils/src/reactor.rs | 61 ++++------ server-utils/src/suspendable_stream.rs | 4 +- tcp/src/server.rs | 19 +-- 7 files changed, 154 insertions(+), 132 deletions(-) diff --git a/http/src/lib.rs b/http/src/lib.rs index a25eeebbd..1ea19530e 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -43,7 +43,7 @@ use std::thread; use parking_lot::Mutex; use crate::jsonrpc::futures::sync::oneshot; -use crate::jsonrpc::futures::{self, future, Future, Stream}; +use crate::jsonrpc::futures::{self, Future, Stream}; use crate::jsonrpc::MetaIoHandler; use crate::server_utils::reactor::{Executor, UninitializedExecutor}; use hyper::{server, Body}; @@ -304,6 +304,12 @@ impl> ServerBuilder { /// Sets number of threads of the server to run. /// /// Panics when set to `0`. + /// The first thread will use provided `Executor` instance + /// and all other threads will use `UninitializedExecutor` to spawn + /// a new runtime for futures. + /// So it's also possible to run a multi-threaded server by + /// passing the default `tokio::runtime` executor to this builder + /// and setting `threads` to 1. #[cfg(unix)] pub fn threads(mut self, threads: usize) -> Self { self.threads = threads; @@ -481,89 +487,86 @@ fn serve>( max_request_body_size: usize, ) { let (shutdown_signal, local_addr_tx, done_tx) = signals; - executor.spawn( - future::lazy(move || { - let handle = tokio::reactor::Handle::default(); - - let bind = move || { - let listener = match addr { - SocketAddr::V4(_) => net2::TcpBuilder::new_v4()?, - SocketAddr::V6(_) => net2::TcpBuilder::new_v6()?, - }; - configure_port(reuse_port, &listener)?; - listener.reuse_address(true)?; - listener.bind(&addr)?; - let listener = listener.listen(1024)?; - let listener = tokio::net::TcpListener::from_std(listener, &handle)?; - // Add current host to allowed headers. - // NOTE: we need to use `l.local_addr()` instead of `addr` - // it might be different! - let local_addr = listener.local_addr()?; - - Ok((listener, local_addr)) + executor.spawn({ + let handle = tokio::reactor::Handle::default(); + + let bind = move || { + let listener = match addr { + SocketAddr::V4(_) => net2::TcpBuilder::new_v4()?, + SocketAddr::V6(_) => net2::TcpBuilder::new_v6()?, }; + configure_port(reuse_port, &listener)?; + listener.reuse_address(true)?; + listener.bind(&addr)?; + let listener = listener.listen(1024)?; + let listener = tokio::net::TcpListener::from_std(listener, &handle)?; + // Add current host to allowed headers. + // NOTE: we need to use `l.local_addr()` instead of `addr` + // it might be different! + let local_addr = listener.local_addr()?; + + Ok((listener, local_addr)) + }; - let bind_result = match bind() { - Ok((listener, local_addr)) => { - // Send local address - match local_addr_tx.send(Ok(local_addr)) { - Ok(_) => futures::future::ok((listener, local_addr)), - Err(_) => { - warn!( - "Thread {:?} unable to reach receiver, closing server", - thread::current().name() - ); - futures::future::err(()) - } + let bind_result = match bind() { + Ok((listener, local_addr)) => { + // Send local address + match local_addr_tx.send(Ok(local_addr)) { + Ok(_) => futures::future::ok((listener, local_addr)), + Err(_) => { + warn!( + "Thread {:?} unable to reach receiver, closing server", + thread::current().name() + ); + futures::future::err(()) } } - Err(err) => { - // Send error - let _send_result = local_addr_tx.send(Err(err)); + } + Err(err) => { + // Send error + let _send_result = local_addr_tx.send(Err(err)); - futures::future::err(()) - } - }; + futures::future::err(()) + } + }; - bind_result.and_then(move |(listener, local_addr)| { - let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); - - let mut http = server::conn::Http::new(); - http.keep_alive(keep_alive); - let tcp_stream = SuspendableStream::new(listener.incoming()); - - tcp_stream - .for_each(move |socket| { - let service = ServerHandler::new( - jsonrpc_handler.clone(), - cors_domains.clone(), - cors_max_age, - allowed_headers.clone(), - allowed_hosts.clone(), - request_middleware.clone(), - rest_api, - health_api.clone(), - max_request_body_size, - keep_alive, - ); - tokio::spawn( - http.serve_connection(socket, service) - .map_err(|e| error!("Error serving connection: {:?}", e)), - ); - Ok(()) - }) - .map_err(|e| { - warn!("Incoming streams error, closing sever: {:?}", e); - }) - .select(shutdown_signal.map_err(|e| { - debug!("Shutdown signaller dropped, closing server: {:?}", e); - })) - .map(|_| ()) - .map_err(|_| ()) - }) + bind_result.and_then(move |(listener, local_addr)| { + let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); + + let mut http = server::conn::Http::new(); + http.keep_alive(keep_alive); + let tcp_stream = SuspendableStream::new(listener.incoming()); + + tcp_stream + .map(move |socket| { + let service = ServerHandler::new( + jsonrpc_handler.clone(), + cors_domains.clone(), + cors_max_age, + allowed_headers.clone(), + allowed_hosts.clone(), + request_middleware.clone(), + rest_api, + health_api.clone(), + max_request_body_size, + keep_alive, + ); + + http.serve_connection(socket, service) + .map_err(|e| error!("Error serving connection: {:?}", e)) + }) + .buffer_unordered(1024) + .for_each(|_| Ok(())) + .map_err(|e| { + warn!("Incoming streams error, closing sever: {:?}", e); + }) + .select(shutdown_signal.map_err(|e| { + debug!("Shutdown signaller dropped, closing server: {:?}", e); + })) + .map_err(|_| ()) }) - .and_then(|_| done_tx.send(())), - ); + .and_then(|_| done_tx.send(())) + }); } #[cfg(unix)] diff --git a/http/src/tests.rs b/http/src/tests.rs index 29ce30b58..eeecf5570 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -1495,6 +1495,40 @@ fn should_respond_with_close_even_if_client_wants_to_keep_alive() { assert_eq!(response.body, world_batch()); } +#[test] +fn should_drop_io_handler_when_server_is_closed() { + use std::sync::{Arc, Mutex}; + // given + let (weak, _req) = { + let my_ref = Arc::new(Mutex::new(5)); + let weak = Arc::downgrade(&my_ref); + let mut io = IoHandler::default(); + io.add_method("hello", move |_| { + Ok(Value::String(format!("{}", my_ref.lock().unwrap()))) + }); + let server = ServerBuilder::new(io) + .start_http(&"127.0.0.1:0".parse().unwrap()) + .unwrap(); + + let addr = server.address().clone(); + + // when + let req = TcpStream::connect(addr).unwrap(); + server.close(); + (weak, req) + }; + + // then + for _ in 1..1000 { + if weak.upgrade().is_none() { + return; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + } + + panic!("expected server to be closed and io handler to be dropped") +} + fn invalid_host() -> String { "Provided Host header is not whitelisted.\n".into() } diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 037c0b88b..8406afb86 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -7,7 +7,7 @@ use tokio_service::{self, Service as TokioService}; use crate::server_utils::{ codecs, reactor, session, - tokio::{self, reactor::Handle, runtime::TaskExecutor}, + tokio::{reactor::Handle, runtime::TaskExecutor}, tokio_codec::Framed, }; use parking_lot::Mutex; @@ -180,7 +180,7 @@ impl> ServerBuilder { let mut id = 0u64; - let server = connections.for_each(move |(io_stream, remote_id)| { + let server = connections.map(move |(io_stream, remote_id)| { id = id.wrapping_add(1); let session_id = id; let session_stats = session_stats.clone(); @@ -228,9 +228,7 @@ impl> ServerBuilder { Ok(()) }); - tokio::spawn(writer); - - Ok(()) + writer }); start_signal .send(Ok(())) @@ -239,6 +237,8 @@ impl> ServerBuilder { let stop = stop_receiver.map_err(|_| std::io::ErrorKind::Interrupted.into()); future::Either::B( server + .buffer_unordered(1024) + .for_each(|_| Ok(())) .select(stop) .map(|_| { let _ = wait_signal.send(()); diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 0d4a75581..cdfb2533a 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -16,7 +16,6 @@ globset = "0.4" jsonrpc-core = { version = "13.2", path = "../core" } lazy_static = "1.1.0" log = "0.4" -num_cpus = "1.8" tokio = { version = "0.1" } tokio-codec = { version = "0.1" } unicase = "2.0" diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index 85834e30a..bd9d6b90c 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -1,9 +1,11 @@ //! Event Loop Executor +//! //! Either spawns a new event loop, or re-uses provided one. +//! Spawned event loop is always single threaded (mostly for +//! historical/backward compatibility reasons) despite the fact +//! that `tokio::runtime` can be multi-threaded. -use num_cpus; -use std::sync::mpsc; -use std::{io, thread}; +use std::io; use tokio; use crate::core::futures::{self, Future}; @@ -82,7 +84,7 @@ impl Executor { pub struct RpcEventLoop { executor: tokio::runtime::TaskExecutor, close: Option>, - handle: Option>, + handle: Option, } impl Drop for RpcEventLoop { @@ -100,42 +102,21 @@ impl RpcEventLoop { /// Spawns a new named thread with the `EventLoop`. pub fn with_name(name: Option) -> io::Result { let (stop, stopped) = futures::oneshot(); - let (tx, rx) = mpsc::channel(); - let mut tb = thread::Builder::new(); + + let mut tb = tokio::runtime::Builder::new(); + tb.core_threads(1); + if let Some(name) = name { - tb = tb.name(name); + tb.name_prefix(name); } - let handle = tb - .spawn(move || { - let core_threads = match num_cpus::get_physical() { - 1 => 1, - 2..=4 => 2, - _ => 3, - }; - - let runtime = tokio::runtime::Builder::new() - .core_threads(core_threads) - .name_prefix("jsonrpc-eventloop-") - .build(); - - match runtime { - Ok(mut runtime) => { - tx.send(Ok(runtime.executor())).expect("Rx is blocking upper thread."); - let terminate = futures::empty().select(stopped).map(|_| ()).map_err(|_| ()); - runtime.spawn(terminate); - runtime.shutdown_on_idle().wait().unwrap(); - } - Err(err) => { - tx.send(Err(err)).expect("Rx is blocking upper thread."); - } - } - }) - .expect("Couldn't spawn a thread."); - - let exec = rx.recv().expect("tx is transfered to a newly spawned thread."); - - exec.map(|executor| RpcEventLoop { + let mut runtime = tb.build()?; + let executor = runtime.executor(); + let terminate = futures::empty().select(stopped).map(|_| ()).map_err(|_| ()); + runtime.spawn(terminate); + let handle = runtime.shutdown_on_idle(); + + Ok(RpcEventLoop { executor, close: Some(stop), handle: Some(handle), @@ -148,11 +129,11 @@ impl RpcEventLoop { } /// Blocks current thread and waits until the event loop is finished. - pub fn wait(mut self) -> thread::Result<()> { + pub fn wait(mut self) -> Result<(), ()> { self.handle .take() - .expect("Handle is always set before self is consumed.") - .join() + .ok_or(())? + .wait() } /// Finishes this event loop. diff --git a/server-utils/src/suspendable_stream.rs b/server-utils/src/suspendable_stream.rs index 303a18b04..f563cdebe 100644 --- a/server-utils/src/suspendable_stream.rs +++ b/server-utils/src/suspendable_stream.rs @@ -74,8 +74,8 @@ where } else { self.next_delay }; - warn!("Error accepting connection: {}", err); - warn!("The server will stop accepting connections for {:?}", self.next_delay); + debug!("Error accepting connection: {}", err); + debug!("The server will stop accepting connections for {:?}", self.next_delay); self.timeout = Some(Delay::new(Instant::now() + self.next_delay)); } } diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 347abd341..8b42ed322 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -87,7 +87,7 @@ impl + 'static> ServerBuilder { let listener = tokio::net::TcpListener::bind(&address)?; let connections = SuspendableStream::new(listener.incoming()); - let server = connections.for_each(move |socket| { + let server = connections.map(move |socket| { let peer_addr = socket.peer_addr().expect("Unable to determine socket peer address"); trace!(target: "tcp", "Accepted incoming connection from {}", &peer_addr); let (sender, receiver) = mpsc::channel(65536); @@ -137,9 +137,7 @@ impl + 'static> ServerBuilder { Ok(()) }); - tokio::spawn(writer); - - Ok(()) + writer }); Ok(server) @@ -149,9 +147,16 @@ impl + 'static> ServerBuilder { match start() { Ok(server) => { tx.send(Ok(())).expect("Rx is blocking parent thread."); - future::Either::A(server.select(stop).map(|_| ()).map_err(|(e, _)| { - error!("Error while executing the server: {:?}", e); - })) + future::Either::A( + server + .buffer_unordered(1024) + .for_each(|_| Ok(())) + .select(stop) + .map(|_| ()) + .map_err(|(e, _)| { + error!("Error while executing the server: {:?}", e); + }) + ) } Err(e) => { tx.send(Err(e)).expect("Rx is blocking parent thread."); From 7b94363c79c95f343b29fda362ed17b6d28319f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 9 Oct 2019 13:06:49 +0200 Subject: [PATCH 079/149] Bump to 14.0 (#496) * Disable cargo cache. * Bump to 14.0 * Update cache settings. --- .travis.yml | 16 +++++++++++++++- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 19 files changed, 63 insertions(+), 49 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3d6ce4c7b..b7b78c7d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,21 @@ branches: - master - /^parity-.*$/ -cache: cargo +cache: + # Don't use `cache: cargo` since it adds the `target` directory and that can be huge. + # Saving and loading this directory dwarfes actual compilation and test times. But what is more + # important, is that travis timeouts the build since the job doesn't produce any output for more + # than 10 minutes. + # + # So we just cache ~/.cargo directory + directories: + - /home/travis/.cargo + +before_cache: + # Travis can't cache files that are not readable by "others" + - chmod -R a+r $HOME/.cargo + # According to the Travis CI docs for building Rust project this is done by, + - rm -rf /home/travis/.cargo/registry os: - linux diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 04dc38e2c..bcffa4387 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" categories = [ "asynchronous", @@ -25,7 +25,7 @@ ws = ["jsonrpc-client-transports/ws"] ipc = ["jsonrpc-client-transports/ipc"] [dependencies] -jsonrpc-client-transports = { version = "13.2", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "14.0", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 2f4c3b4cb..ef1a0813f 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" categories = [ "asynchronous", @@ -37,9 +37,9 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "13.2", path = "../../core" } -jsonrpc-pubsub = { version = "13.2", path = "../../pubsub" } -jsonrpc-server-utils = { version = "13.2", path = "../../server-utils", optional = true } +jsonrpc-core = { version = "14.0", path = "../../core" } +jsonrpc-pubsub = { version = "14.0", path = "../../pubsub" } +jsonrpc-server-utils = { version = "14.0", path = "../../server-utils", optional = true } log = "0.4" parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } @@ -50,8 +50,8 @@ url = "1.7" [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "13.2", path = "../../http" } -jsonrpc-ipc-server = { version = "13.2", path = "../../ipc" } +jsonrpc-http-server = { version = "14.0", path = "../../http" } +jsonrpc-ipc-server = { version = "14.0", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.6" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index 30efa8d77..af3152c89 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 70c32345f..a1635413b 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "0.6" proc-macro-crate = "0.1.3" [dev-dependencies] -jsonrpc-core = { version = "13.2", path = "../core" } -jsonrpc-core-client = { version = "13.2", path = "../core-client" } -jsonrpc-pubsub = { version = "13.2", path = "../pubsub" } -jsonrpc-tcp-server = { version = "13.2", path = "../tcp" } +jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-core-client = { version = "14.0", path = "../core-client" } +jsonrpc-pubsub = { version = "14.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "14.0", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 26681f282..721e5ea2b 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "13.2", path = "../core" } -jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } +jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index fc25ce224..57d6a1ca5 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "13.2" +jsonrpc-http-server = "14.0" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index a0ee0b038..ccf22b72d 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "13.2", path = "../core" } -jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } +jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } parity-tokio-ipc = "0.2" parking_lot = "0.9" diff --git a/ipc/README.md b/ipc/README.md index cc572e00f..e9e5bf6a2 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "13.2" +jsonrpc-ipc-server = "14.0" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index a6fab04e0..4f64dd770 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] log = "0.4" parking_lot = "0.9" -jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-core = { version = "14.0", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "13.2", path = "../tcp" } +jsonrpc-tcp-server = { version = "14.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 01e77cd7a..17ae6e0f4 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "13.2", path = "../../core" } -jsonrpc-pubsub = { version = "13.2", path = "../" } -jsonrpc-ws-server = { version = "13.2", path = "../../ws" } -jsonrpc-ipc-server = { version = "13.2", path = "../../ipc" } +jsonrpc-core = { version = "14.0", path = "../../core" } +jsonrpc-pubsub = { version = "14.0", path = "../" } +jsonrpc-ws-server = { version = "14.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "14.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index cdfb2533a..7a6fb7b32 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-core = { version = "14.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.1" } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 52ff518f5..e006cc7f0 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "13.2", path = "../core" } +jsonrpc-core = { version = "14.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index a030fd64c..a7997cdf1 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "13.2" +jsonrpc-stdio-server = "14.0" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index c2f558ebe..77f4315fe 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] log = "0.4" parking_lot = "0.9" tokio-service = "0.1" -jsonrpc-core = { version = "13.2", path = "../core" } -jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } +jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index 9ccd73c51..12caa7284 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "13.2" +jsonrpc-tcp-server = "14.0" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index b8756ddfd..657bf06eb 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "13.2.0" +version = "14.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,13 +10,13 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "13.2", path = "../core" } -jsonrpc-core-client = { version = "13.2", path = "../core-client" } -jsonrpc-pubsub = { version = "13.2", path = "../pubsub" } +jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-core-client = { version = "14.0", path = "../core-client" } +jsonrpc-pubsub = { version = "14.0", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" [dev-dependencies] -jsonrpc-derive = { version = "13.2", path = "../derive" } +jsonrpc-derive = { version = "14.0", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 8c868bba4..85ee325b9 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "13.2.0" +version = "14.0.0" [dependencies] -jsonrpc-core = { version = "13.2", path = "../core" } -jsonrpc-server-utils = { version = "13.2", path = "../server-utils" } +jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } log = "0.4" parking_lot = "0.9" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 5da6fc47f..b8211c66d 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "13.2" +jsonrpc-ws-server = "14.0" ``` `main.rs` From cc8f26d3d79cd42a6e8e7fc85874dbaaddb371f3 Mon Sep 17 00:00:00 2001 From: simeir Date: Thu, 10 Oct 2019 00:14:57 -0700 Subject: [PATCH 080/149] Solve compiler error when serde is not a dependency of user project (#481) (#498) Use `serde` directly instead of ask for user of the library to add it as a dependency. Tests are modified since this is a ui change. The doc comment gives an example using `#[rpc(server)]` instead of original `#[rpc]`, telling users that this attribute has an option to be used to configure it. --- core/src/lib.rs | 2 ++ derive/src/lib.rs | 2 +- derive/src/rpc_trait.rs | 2 -- derive/src/to_delegate.rs | 8 ++++---- derive/tests/run-pass/client_only.rs | 1 - ...ub-dependency-not-required-for-vanilla-rpc.rs | 1 - derive/tests/run-pass/server_only.rs | 1 - derive/tests/ui/attr-invalid-meta-list-names.rs | 1 - .../tests/ui/attr-invalid-meta-list-names.stderr | 14 +++++++------- derive/tests/ui/attr-invalid-meta-words.rs | 1 - derive/tests/ui/attr-invalid-meta-words.stderr | 14 +++++++------- derive/tests/ui/attr-invalid-name-values.rs | 1 - derive/tests/ui/attr-invalid-name-values.stderr | 14 +++++++------- derive/tests/ui/attr-missing-rpc-name.rs | 1 - derive/tests/ui/attr-missing-rpc-name.stderr | 14 +++++++------- derive/tests/ui/multiple-rpc-attributes.rs | 1 - derive/tests/ui/multiple-rpc-attributes.stderr | 10 +++++----- derive/tests/ui/too-many-params.rs | 1 - derive/tests/ui/too-many-params.stderr | 16 ++++++++-------- 19 files changed, 48 insertions(+), 57 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 2bcdfadca..5f293ce92 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,6 +30,8 @@ pub use futures; #[doc(hidden)] pub extern crate serde_json; +#[doc(hidden)] +pub extern crate serde; mod calls; mod io; diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 48c71e94b..8fe096eb8 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -9,7 +9,7 @@ //! use jsonrpc_core::{IoHandler, Error, Result}; //! use jsonrpc_core::futures::future::{self, FutureResult}; //! -//! #[rpc] +//! #[rpc(server)] //! pub trait Rpc { //! #[rpc(name = "protocolVersion")] //! fn protocol_version(&self) -> Result; diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index cf0076252..30516d68a 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -236,7 +236,6 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result Result $DIR/attr-invalid-meta-list-names.rs:8:2 - | -8 | /// Returns a protocol version - | _____^ -9 | | #[rpc(name = "protocolVersion", Xalias("alias"))] -10 | | fn protocol_version(&self) -> Result; - | |_________________________________________________^ + --> $DIR/attr-invalid-meta-list-names.rs:7:2 + | +7 | /// Returns a protocol version + | _____^ +8 | | #[rpc(name = "protocolVersion", Xalias("alias"))] +9 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ diff --git a/derive/tests/ui/attr-invalid-meta-words.rs b/derive/tests/ui/attr-invalid-meta-words.rs index 6149f7118..106df1baa 100644 --- a/derive/tests/ui/attr-invalid-meta-words.rs +++ b/derive/tests/ui/attr-invalid-meta-words.rs @@ -1,4 +1,3 @@ -extern crate serde; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index c4a310ae7..8b1c99051 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,8 +1,8 @@ error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params' - --> $DIR/attr-invalid-meta-words.rs:8:2 - | -8 | /// Returns a protocol version - | _____^ -9 | | #[rpc(name = "protocolVersion", Xmeta)] -10 | | fn protocol_version(&self) -> Result; - | |_________________________________________________^ + --> $DIR/attr-invalid-meta-words.rs:7:2 + | +7 | /// Returns a protocol version + | _____^ +8 | | #[rpc(name = "protocolVersion", Xmeta)] +9 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ diff --git a/derive/tests/ui/attr-invalid-name-values.rs b/derive/tests/ui/attr-invalid-name-values.rs index 3c4cf7a12..4a3fa4acf 100644 --- a/derive/tests/ui/attr-invalid-name-values.rs +++ b/derive/tests/ui/attr-invalid-name-values.rs @@ -1,4 +1,3 @@ -extern crate serde; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index aebdba62e..69a56cc44 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,8 +1,8 @@ error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns' - --> $DIR/attr-invalid-name-values.rs:8:2 - | -8 | /// Returns a protocol version - | _____^ -9 | | #[rpc(Xname = "protocolVersion")] -10 | | fn protocol_version(&self) -> Result; - | |_________________________________________________^ + --> $DIR/attr-invalid-name-values.rs:7:2 + | +7 | /// Returns a protocol version + | _____^ +8 | | #[rpc(Xname = "protocolVersion")] +9 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ diff --git a/derive/tests/ui/attr-missing-rpc-name.rs b/derive/tests/ui/attr-missing-rpc-name.rs index 53b76991a..401359af4 100644 --- a/derive/tests/ui/attr-missing-rpc-name.rs +++ b/derive/tests/ui/attr-missing-rpc-name.rs @@ -1,4 +1,3 @@ -extern crate serde; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; diff --git a/derive/tests/ui/attr-missing-rpc-name.stderr b/derive/tests/ui/attr-missing-rpc-name.stderr index d2287b7d0..788008029 100644 --- a/derive/tests/ui/attr-missing-rpc-name.stderr +++ b/derive/tests/ui/attr-missing-rpc-name.stderr @@ -1,8 +1,8 @@ error: rpc attribute should have a name e.g. `name = "method_name"` - --> $DIR/attr-missing-rpc-name.rs:8:2 - | -8 | /// Returns a protocol version - | _____^ -9 | | #[rpc] -10 | | fn protocol_version(&self) -> Result; - | |_________________________________________________^ + --> $DIR/attr-missing-rpc-name.rs:7:2 + | +7 | /// Returns a protocol version + | _____^ +8 | | #[rpc] +9 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ diff --git a/derive/tests/ui/multiple-rpc-attributes.rs b/derive/tests/ui/multiple-rpc-attributes.rs index 01bf8adad..7a48b3c0c 100644 --- a/derive/tests/ui/multiple-rpc-attributes.rs +++ b/derive/tests/ui/multiple-rpc-attributes.rs @@ -1,4 +1,3 @@ -extern crate serde; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; diff --git a/derive/tests/ui/multiple-rpc-attributes.stderr b/derive/tests/ui/multiple-rpc-attributes.stderr index 6204f119a..28ce7cbef 100644 --- a/derive/tests/ui/multiple-rpc-attributes.stderr +++ b/derive/tests/ui/multiple-rpc-attributes.stderr @@ -1,9 +1,9 @@ error: Expected only a single rpc attribute per method - --> $DIR/multiple-rpc-attributes.rs:8:2 + --> $DIR/multiple-rpc-attributes.rs:7:2 | -8 | /// Returns a protocol version +7 | /// Returns a protocol version | _____^ -9 | | #[rpc(name = "protocolVersion")] -10 | | #[rpc(name = "protocolVersionAgain")] -11 | | fn protocol_version(&self) -> Result; +8 | | #[rpc(name = "protocolVersion")] +9 | | #[rpc(name = "protocolVersionAgain")] +10 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/too-many-params.rs b/derive/tests/ui/too-many-params.rs index 14c7d07be..7e217c22a 100644 --- a/derive/tests/ui/too-many-params.rs +++ b/derive/tests/ui/too-many-params.rs @@ -1,4 +1,3 @@ -extern crate serde; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_derive; diff --git a/derive/tests/ui/too-many-params.stderr b/derive/tests/ui/too-many-params.stderr index 3ae24d7f1..25e04b856 100644 --- a/derive/tests/ui/too-many-params.stderr +++ b/derive/tests/ui/too-many-params.stderr @@ -1,12 +1,12 @@ error: Maximum supported number of params is 16 - --> $DIR/too-many-params.rs:8:2 + --> $DIR/too-many-params.rs:7:2 | -8 | /// Has too many params +7 | /// Has too many params | _____^ -9 | | #[rpc(name = "tooManyParams")] -10 | | fn to_many_params( -11 | | &self, -12 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, -13 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, -14 | | ) -> Result; +8 | | #[rpc(name = "tooManyParams")] +9 | | fn to_many_params( +10 | | &self, +11 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, +12 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, +13 | | ) -> Result; | |________________________^ From 44184766aeb59a95b4d6f4c7eaff0c9e1ef278eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 10 Oct 2019 13:33:07 +0200 Subject: [PATCH 081/149] Make sure RpcEventLoop is Send+Sync (#499) * Add Send + Sync test. * Add test to make sure the tpc server is Send+Sync too. * Review suggestions. * Make sure to use latest tokio version. --- server-utils/Cargo.toml | 2 +- server-utils/src/reactor.rs | 12 ++++++++++++ tcp/src/server.rs | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 7a6fb7b32..e07156ece 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -16,7 +16,7 @@ globset = "0.4" jsonrpc-core = { version = "14.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" -tokio = { version = "0.1" } +tokio = { version = "0.1.15" } tokio-codec = { version = "0.1" } unicase = "2.0" diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index bd9d6b90c..d93708fbf 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -148,3 +148,15 @@ impl RpcEventLoop { }); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn make_sure_rpc_event_loop_is_send_and_sync() { + fn is_send_and_sync() {} + + is_send_and_sync::(); + } +} diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 8b42ed322..a6ac59c03 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -208,3 +208,15 @@ impl Drop for Server { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn server_is_send_and_sync() { + fn is_send_and_sync() {} + + is_send_and_sync::(); + } +} From d3d8c6b0c72f09b179283c9f2cd408da0442cd00 Mon Sep 17 00:00:00 2001 From: Seun LanLege Date: Wed, 16 Oct 2019 09:00:27 +0100 Subject: [PATCH 082/149] ignore dropped connections (#502) * ignore dropped connections * bump version, warn instead of trace --- tcp/Cargo.toml | 2 +- tcp/src/server.rs | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 77f4315fe..d0f4b1c50 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] log = "0.4" diff --git a/tcp/src/server.rs b/tcp/src/server.rs index a6ac59c03..620e40f97 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -88,7 +88,13 @@ impl + 'static> ServerBuilder { let connections = SuspendableStream::new(listener.incoming()); let server = connections.map(move |socket| { - let peer_addr = socket.peer_addr().expect("Unable to determine socket peer address"); + let peer_addr = match socket.peer_addr() { + Ok(addr) => addr, + Err(e) => { + warn!(target: "tcp", "Unable to determine socket peer address, ignoring connection {}", e); + return future::Either::A(future::ok(())) + } + }; trace!(target: "tcp", "Accepted incoming connection from {}", &peer_addr); let (sender, receiver) = mpsc::channel(65536); @@ -101,7 +107,10 @@ impl + 'static> ServerBuilder { let service = Service::new(peer_addr, rpc_handler.clone(), meta); let (writer, reader) = Framed::new( socket, - codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()), + codecs::StreamCodec::new( + incoming_separator.clone(), + outgoing_separator.clone() + ), ) .split(); @@ -137,7 +146,7 @@ impl + 'static> ServerBuilder { Ok(()) }); - writer + future::Either::B(writer) }); Ok(server) From 1f1107521436d42e3b6cc9bb41d0374979c628a3 Mon Sep 17 00:00:00 2001 From: felix <32549900+fevo1971@users.noreply.github.com> Date: Wed, 16 Oct 2019 11:52:43 +0200 Subject: [PATCH 083/149] Integrate gitlab ci (#503) * added giltab-ci * removed '--lock' from test * no time command in onw windows * added stage: checkstyle * shortend the config a bit * bit more compact ci-file * gitlab-ci intergation * changed order of ci jobs * Re-format. --- .gitlab-ci.yml | 52 ++++++++++++++++++++++ core-client/transports/src/lib.rs | 1 - core/src/io.rs | 5 ++- core/src/lib.rs | 4 +- core/src/types/response.rs | 7 ++- http/src/lib.rs | 73 ++++++++++++++++--------------- pubsub/src/handler.rs | 1 - server-utils/src/cors.rs | 1 - server-utils/src/reactor.rs | 5 +-- tcp/src/server.rs | 2 +- 10 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..f615f46d0 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,52 @@ +stages: + - checkstyle + - test + +.test_and_build: &test_and_build + script: + - cargo build --all + - cargo test --all + +.only: &only + only: + - triggers + - tags + - master + - schedules + - web + - /^[0-9]+$/ + +# test rust stable +test-linux-stable: + image: parity/rust-builder:latest + stage: test + <<: *test_and_build + <<: *only + tags: + - linux-docker + +test-windows-stable: + stage: test + <<: *test_and_build + <<: *only + tags: + - rust-windows + +test-mac-stable: + stage: test + <<: *test_and_build + <<: *only + tags: + - rust-mac + +# check style +checkstyle-linux-stable: + image: parity/rust-builder:latest + stage: checkstyle + script: + - rustup component add rustfmt + - cargo fmt --all -- --check + allow_failure: true + <<: *only + tags: + - linux-docker diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index d8f384552..ab5a4bc6f 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -509,5 +509,4 @@ mod tests { "Expected at least one received item." ); } - } diff --git a/core/src/io.rs b/core/src/io.rs index b53de5722..320bd0385 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -1,4 +1,7 @@ -use std::collections::{hash_map::{Iter, IntoIter}, HashMap}; +use std::collections::{ + hash_map::{IntoIter, Iter}, + HashMap, +}; use std::ops::{Deref, DerefMut}; use std::sync::Arc; diff --git a/core/src/lib.rs b/core/src/lib.rs index 5f293ce92..87d05f7a0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -28,10 +28,10 @@ extern crate serde_derive; pub use futures; -#[doc(hidden)] -pub extern crate serde_json; #[doc(hidden)] pub extern crate serde; +#[doc(hidden)] +pub extern crate serde_json; mod calls; mod io; diff --git a/core/src/types/response.rs b/core/src/types/response.rs index 7b027bb93..ba380a27f 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -286,8 +286,11 @@ fn should_parse_empty_response_as_batch() { let dsr = r#""#; - let deserialized1: Result= serde_json::from_str(dsr); + let deserialized1: Result = serde_json::from_str(dsr); let deserialized2: Result = Response::from_json(dsr); - assert!(deserialized1.is_err(), "Empty string is not valid JSON, so we should get an error."); + assert!( + deserialized1.is_err(), + "Empty string is not valid JSON, so we should get an error." + ); assert_eq!(deserialized2.unwrap(), Response::Batch(vec![])); } diff --git a/http/src/lib.rs b/http/src/lib.rs index 1ea19530e..caa599aa8 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -530,42 +530,43 @@ fn serve>( } }; - bind_result.and_then(move |(listener, local_addr)| { - let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); - - let mut http = server::conn::Http::new(); - http.keep_alive(keep_alive); - let tcp_stream = SuspendableStream::new(listener.incoming()); - - tcp_stream - .map(move |socket| { - let service = ServerHandler::new( - jsonrpc_handler.clone(), - cors_domains.clone(), - cors_max_age, - allowed_headers.clone(), - allowed_hosts.clone(), - request_middleware.clone(), - rest_api, - health_api.clone(), - max_request_body_size, - keep_alive, - ); - - http.serve_connection(socket, service) - .map_err(|e| error!("Error serving connection: {:?}", e)) - }) - .buffer_unordered(1024) - .for_each(|_| Ok(())) - .map_err(|e| { - warn!("Incoming streams error, closing sever: {:?}", e); - }) - .select(shutdown_signal.map_err(|e| { - debug!("Shutdown signaller dropped, closing server: {:?}", e); - })) - .map_err(|_| ()) - }) - .and_then(|_| done_tx.send(())) + bind_result + .and_then(move |(listener, local_addr)| { + let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); + + let mut http = server::conn::Http::new(); + http.keep_alive(keep_alive); + let tcp_stream = SuspendableStream::new(listener.incoming()); + + tcp_stream + .map(move |socket| { + let service = ServerHandler::new( + jsonrpc_handler.clone(), + cors_domains.clone(), + cors_max_age, + allowed_headers.clone(), + allowed_hosts.clone(), + request_middleware.clone(), + rest_api, + health_api.clone(), + max_request_body_size, + keep_alive, + ); + + http.serve_connection(socket, service) + .map_err(|e| error!("Error serving connection: {:?}", e)) + }) + .buffer_unordered(1024) + .for_each(|_| Ok(())) + .map_err(|e| { + warn!("Incoming streams error, closing sever: {:?}", e); + }) + .select(shutdown_signal.map_err(|e| { + debug!("Shutdown signaller dropped, closing server: {:?}", e); + })) + .map_err(|_| ()) + }) + .and_then(|_| done_tx.send(())) }); } diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index d23834727..1b3567579 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -146,5 +146,4 @@ mod tests { assert_eq!(res, Some(response.into())); assert_eq!(called.load(Ordering::SeqCst), true); } - } diff --git a/server-utils/src/cors.rs b/server-utils/src/cors.rs index 172400c67..3f1a5ef82 100644 --- a/server-utils/src/cors.rs +++ b/server-utils/src/cors.rs @@ -590,5 +590,4 @@ mod tests { // then assert_eq!(res, AllowCors::NotRequired); } - } diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index d93708fbf..f66c73c12 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -130,10 +130,7 @@ impl RpcEventLoop { /// Blocks current thread and waits until the event loop is finished. pub fn wait(mut self) -> Result<(), ()> { - self.handle - .take() - .ok_or(())? - .wait() + self.handle.take().ok_or(())?.wait() } /// Finishes this event loop. diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 620e40f97..51fbfa8a7 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -164,7 +164,7 @@ impl + 'static> ServerBuilder { .map(|_| ()) .map_err(|(e, _)| { error!("Error while executing the server: {:?}", e); - }) + }), ) } Err(e) => { From 8ebb62d8390879bb7ebd38d439360a713c7f933b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 16 Oct 2019 12:15:45 +0200 Subject: [PATCH 084/149] Disable travis & appveyor. (#506) * Disable travis & appveyor. * Fix fmt. --- .travis.yml | 66 ----------------------------------------------- appveyor.yml | 24 ----------------- tcp/src/server.rs | 7 ++--- 3 files changed, 2 insertions(+), 95 deletions(-) delete mode 100644 .travis.yml delete mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b7b78c7d0..000000000 --- a/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -sudo: false -language: rust -branches: - only: - - master - - /^parity-.*$/ - -cache: - # Don't use `cache: cargo` since it adds the `target` directory and that can be huge. - # Saving and loading this directory dwarfes actual compilation and test times. But what is more - # important, is that travis timeouts the build since the job doesn't produce any output for more - # than 10 minutes. - # - # So we just cache ~/.cargo directory - directories: - - /home/travis/.cargo - -before_cache: - # Travis can't cache files that are not readable by "others" - - chmod -R a+r $HOME/.cargo - # According to the Travis CI docs for building Rust project this is done by, - - rm -rf /home/travis/.cargo/registry - -os: - - linux - - osx - -rust: - - stable - - beta - - nightly - -matrix: - fast_finish: false - exclude: - - os: osx - rust: beta - - os: osx - rust: nightly - allow_failures: - - rust: nightly - -before_script: - - rustup component add rustfmt - -script: - - cargo build --all - - cargo test --all - - | - ([ $TRAVIS_OS_NAME == 'linux' ] && [ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true - -after_success: | - [ $TRAVIS_OS_NAME == 'linux' ] && - [ $TRAVIS_BRANCH = master ] && - [ $TRAVIS_PULL_REQUEST = false ] && - [ $TRAVIS_RUST_VERSION = stable ] && - cargo doc --all --no-deps && - echo '' > target/doc/index.html && - pip install --user ghp-import && - /home/travis/.local/bin/ghp-import -n target/doc && - git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages - -env: - global: - - secure: "QA4Rw78VSsP1vH2Yve1eAVpjG32HH9DZZ79xrhxZXp34wKoemp+aGqaFN/8uXPfsXshlYxtMCTT6M9OiWTTLvku5tI5kBsDneu8mLut7eBZHVniYSp2SbKpTeqfpGMDHoCR0WD9AlWDn9Elm6txbghXjrxhCMg8gkhhsLGnQt/ARFF1wRHnXT0TjJg8fQtd+/OK0TaRfknx1RptruaznxfUi3DBwzDdzaMMZfd3VjWR1hPFRpDSL0mM+l6OjNrLbCeiR//k3lV4rpIhedsz0ODjfW2Hdk63qCaLJsXCkG1Bcuf/FYbYC+osm5SrHhGA1j2EgazWcLA6Wkzt15KPOR/HirNj+PCiS0YbGKM5Ac5LT6m6q0iYSF/pq1+jDurcSwBwYrTOY6X2FZCZQBfTP/4qnSjWgGPOkzBSMS6BNEBDQZgdc3xCASXadj7waF4Y4UGD0bDPuBtXopI4ppKLqSa7CsvKz6TX2yW0UVgUuQ5/jz/S+fkcz74o016d5x027yjaxAu/Z8fQFLSaBtiFU8sBzA+MDU3apFgjsYXiaGYZ8gDrp7WjbfHNYfBAMEHHKY4toywB5Vi8zJxF+Wn1n4hkvb/kDqSV9giFmWEg321U+pAGNAH4yY25tIJqS8gT89cz4oQJp7aWjA3Ke01e104yqqZU+N+CSyZHEeksdPt8=" - diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 33956025d..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,24 +0,0 @@ -environment: - matrix: - - TARGET: x86_64-pc-windows-msvc - -install: - - curl -sSf -o rustup-init.exe https://win.rustup.rs/ - - rustup-init.exe -y --default-host %TARGET% - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin;C:\MinGW\bin - - rustc -vV - - cargo -vV - -build: false - -test_script: - - cargo test --all --verbose - -# avoid running tests twice -branches: - only: - - master - - /^parity-.*$/ - -cache: - - C:\Users\appveyor\.cargo diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 51fbfa8a7..9af74d038 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -92,7 +92,7 @@ impl + 'static> ServerBuilder { Ok(addr) => addr, Err(e) => { warn!(target: "tcp", "Unable to determine socket peer address, ignoring connection {}", e); - return future::Either::A(future::ok(())) + return future::Either::A(future::ok(())); } }; trace!(target: "tcp", "Accepted incoming connection from {}", &peer_addr); @@ -107,10 +107,7 @@ impl + 'static> ServerBuilder { let service = Service::new(peer_addr, rpc_handler.clone(), meta); let (writer, reader) = Framed::new( socket, - codecs::StreamCodec::new( - incoming_separator.clone(), - outgoing_separator.clone() - ), + codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()), ) .split(); From d1993a80176afdcbae6949bbf73ebbaf5625e644 Mon Sep 17 00:00:00 2001 From: foriequal0 Date: Wed, 16 Oct 2019 19:36:47 +0900 Subject: [PATCH 085/149] Fix race condition on `wait()` (#504) Manually dropping the future passed from `Future::select` before sending `done_tx` prevents the race condition. `Future::select` pass the unresolved future, which is a `Server` holding `rpc_handler`, to the following callback. Therefore, it is dropped after the `done_tx.send(())` after the callback exits. It is possible that the thread that has executed `wait()`, which is usually the main thread, terminates before the thread sending `done_tx` drops `rpc_handler`. Static variables are destructed at the end of termination of the main thread, and then a segfault occurs when the thread dropping the rpc_handler accesses the variables. --- http/src/lib.rs | 7 ++++++- ipc/src/server.rs | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/http/src/lib.rs b/http/src/lib.rs index caa599aa8..fc692f352 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -566,7 +566,12 @@ fn serve>( })) .map_err(|_| ()) }) - .and_then(|_| done_tx.send(())) + .and_then(|(_, server)| { + // We drop the server first to prevent a situation where main thread terminates + // before the server is properly dropped (see #504 for more details) + drop(server); + done_tx.send(()) + }) }); } diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 8406afb86..933ad8648 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -240,7 +240,10 @@ impl> ServerBuilder { .buffer_unordered(1024) .for_each(|_| Ok(())) .select(stop) - .map(|_| { + .map(|(_, server)| { + // We drop the server first to prevent a situation where main thread terminates + // before the server is properly dropped (see #504 for more details) + drop(server); let _ = wait_signal.send(()); }) .map_err(|_| ()), From 3fc22d1744487763279a57cc78c45878e99e7649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 18 Oct 2019 18:03:14 +0200 Subject: [PATCH 086/149] Update syn & co (#507) * Update syn, quote & proc-macro2 * Apply review suggestions. * Re-format. --- derive/Cargo.toml | 8 ++++---- derive/src/rpc_attr.rs | 43 ++++++++++++++++++++++++--------------- derive/src/to_client.rs | 22 ++++++++++---------- derive/src/to_delegate.rs | 20 ++++-------------- 4 files changed, 46 insertions(+), 47 deletions(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index a1635413b..579772b6f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -13,10 +13,10 @@ version = "14.0.0" proc-macro = true [dependencies] -syn = { version = "^0.15.22", features = ["full", "extra-traits", "visit", "fold"] } -proc-macro2 = "0.4" -quote = "0.6" -proc-macro-crate = "0.1.3" +syn = { version = "1.0", features = ["full", "extra-traits", "visit", "fold"] } +proc-macro2 = "1.0" +quote = "1.0" +proc-macro-crate = "0.1.4" [dev-dependencies] jsonrpc-core = { version = "14.0", path = "../core" } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 7633c669c..95a7c1286 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -51,7 +51,7 @@ const NEITHER_SUB_OR_UNSUB_ERR: &str = "pubsub attribute not annotated with eith impl RpcMethodAttribute { pub fn parse_attr(method: &syn::TraitItemMethod) -> Result> { - let output = &method.sig.decl.output; + let output = &method.sig.output; let attrs = method .attrs .iter() @@ -68,9 +68,9 @@ impl RpcMethodAttribute { fn parse_meta(attr: &syn::Attribute, output: &syn::ReturnType) -> Option> { match attr.parse_meta().and_then(validate_attribute_meta) { Ok(ref meta) => { - let attr_kind = match meta.name().to_string().as_ref() { - RPC_ATTR_NAME => Some(Self::parse_rpc(meta, output)), - PUB_SUB_ATTR_NAME => Some(Self::parse_pubsub(meta)), + let attr_kind = match path_to_str(meta.path()).as_ref().map(String::as_str) { + Some(RPC_ATTR_NAME) => Some(Self::parse_rpc(meta, output)), + Some(PUB_SUB_ATTR_NAME) => Some(Self::parse_pubsub(meta)), _ => None, }; attr_kind.map(|kind| { @@ -162,10 +162,12 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { } impl<'a> Visit<'a> for Visitor { fn visit_meta(&mut self, meta: &syn::Meta) { - match meta { - syn::Meta::List(list) => self.meta_list_names.push(list.ident.to_string()), - syn::Meta::Word(ident) => self.meta_words.push(ident.to_string()), - syn::Meta::NameValue(nv) => self.name_value_names.push(nv.ident.to_string()), + if let Some(ident) = path_to_str(meta.path()) { + match meta { + syn::Meta::Path(_) => self.meta_words.push(ident), + syn::Meta::List(_) => self.meta_list_names.push(ident), + syn::Meta::NameValue(_) => self.name_value_names.push(ident), + } } } } @@ -173,13 +175,14 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { let mut visitor = Visitor::default(); visit::visit_meta(&mut visitor, &meta); - match meta.name().to_string().as_ref() { - RPC_ATTR_NAME => { + let ident = path_to_str(meta.path()); + match ident.as_ref().map(String::as_str) { + Some(RPC_ATTR_NAME) => { validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD])?; validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } - PUB_SUB_ATTR_NAME => { + Some(PUB_SUB_ATTR_NAME) => { validate_idents( &meta, &visitor.meta_words, @@ -223,7 +226,7 @@ fn get_meta_list(meta: &syn::Meta) -> Option<&syn::MetaList> { fn get_name_value(key: &str, ml: &syn::MetaList) -> Option { ml.nested.iter().find_map(|nested| { if let syn::NestedMeta::Meta(syn::Meta::NameValue(mnv)) = nested { - if mnv.ident == key { + if path_eq_str(&mnv.path, key) { if let syn::Lit::Str(ref lit) = mnv.lit { Some(lit.value()) } else { @@ -240,8 +243,8 @@ fn get_name_value(key: &str, ml: &syn::MetaList) -> Option { fn has_meta_word(word: &str, ml: &syn::MetaList) -> bool { ml.nested.iter().any(|nested| { - if let syn::NestedMeta::Meta(syn::Meta::Word(w)) = nested { - w == word + if let syn::NestedMeta::Meta(syn::Meta::Path(p)) = nested { + path_eq_str(&p, word) } else { false } @@ -253,7 +256,7 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { .iter() .find_map(|nested| { if let syn::NestedMeta::Meta(syn::Meta::List(list)) = nested { - if list.ident == ALIASES_KEY { + if path_eq_str(&list.path, ALIASES_KEY) { Some(list) } else { None @@ -266,7 +269,7 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { list.nested .iter() .filter_map(|nm| { - if let syn::NestedMeta::Literal(syn::Lit::Str(alias)) = nm { + if let syn::NestedMeta::Lit(syn::Lit::Str(alias)) = nm { Some(alias.value()) } else { None @@ -275,3 +278,11 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { .collect() }) } + +fn path_eq_str(path: &syn::Path, s: &str) -> bool { + path.get_ident().map_or(false, |i| i == s) +} + +fn path_to_str(path: &syn::Path) -> Option { + Some(path.get_ident()?.to_string()) +} diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index af920414c..4de04ac2b 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -173,14 +173,14 @@ fn get_doc_comments(attrs: &[syn::Attribute]) -> Vec { fn compute_args(method: &syn::TraitItemMethod) -> Punctuated { let mut args = Punctuated::new(); - for arg in &method.sig.decl.inputs { + for arg in &method.sig.inputs { let ty = match arg { - syn::FnArg::Captured(syn::ArgCaptured { ty, .. }) => ty, + syn::FnArg::Typed(syn::PatType { ty, .. }) => ty, _ => continue, }; - let segments = match ty { + let segments = match &**ty { syn::Type::Path(syn::TypePath { - path: syn::Path { segments, .. }, + path: syn::Path { ref segments, .. }, .. }) => segments, _ => continue, @@ -200,12 +200,12 @@ fn compute_arg_identifiers(args: &Punctuated) -> let mut arg_names = vec![]; for arg in args { let pat = match arg { - syn::FnArg::Captured(syn::ArgCaptured { pat, .. }) => pat, + syn::FnArg::Typed(syn::PatType { pat, .. }) => pat, _ => continue, }; - let ident = match pat { - syn::Pat::Ident(syn::PatIdent { ident, .. }) => ident, - syn::Pat::Wild(wild) => { + let ident = match **pat { + syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => ident, + syn::Pat::Wild(ref wild) => { let span = wild.underscore_token.spans[0]; let msg = "No wildcard patterns allowed in rpc trait."; return Err(syn::Error::new(span, msg)); @@ -223,7 +223,7 @@ fn compute_returns(method: &syn::TraitItemMethod, returns: &Option) -> R None => None, }; let returns = match returns { - None => try_infer_returns(&method.sig.decl.output), + None => try_infer_returns(&method.sig.output), _ => returns, }; let returns = match returns { @@ -276,8 +276,8 @@ fn get_first_type_argument(args: &syn::PathArguments) -> Option { fn compute_subscription_type(arg: &syn::FnArg) -> syn::Type { let ty = match arg { - syn::FnArg::Captured(cap) => match &cap.ty { - syn::Type::Path(path) => { + syn::FnArg::Typed(cap) => match *cap.ty { + syn::Type::Path(ref path) => { let last = &path.path.segments[&path.path.segments.len() - 1]; get_first_type_argument(&last.arguments) } diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 55967dc81..9ee396cc7 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -142,13 +142,7 @@ pub fn generate_trait_item_method( let predicates = generate_where_clause_serialization_predicates(&trait_item, false); let mut method = method.clone(); - method - .sig - .decl - .generics - .make_where_clause() - .predicates - .extend(predicates); + method.sig.generics.make_where_clause().predicates.extend(predicates); Ok(method) } @@ -183,13 +177,11 @@ impl RpcMethod { let mut param_types: Vec<_> = self .trait_item .sig - .decl .inputs .iter() .cloned() .filter_map(|arg| match arg { - syn::FnArg::Captured(arg_captured) => Some(arg_captured.ty), - syn::FnArg::Ignored(ty) => Some(ty), + syn::FnArg::Typed(ty) => Some(*ty.ty), _ => None, }) .collect(); @@ -225,7 +217,7 @@ impl RpcMethod { }; let method_ident = self.ident(); - let result = &self.trait_item.sig.decl.output; + let result = &self.trait_item.sig.output; let extra_closure_args: &Vec<_> = &special_args.iter().cloned().map(|arg| arg.0).collect(); let extra_method_types: &Vec<_> = &special_args.iter().cloned().map(|arg| arg.1).collect(); @@ -380,11 +372,7 @@ fn ident(s: &str) -> syn::Ident { fn is_option_type(ty: &syn::Type) -> bool { if let syn::Type::Path(path) = ty { - path.path - .segments - .first() - .map(|t| t.value().ident == "Option") - .unwrap_or(false) + path.path.segments.first().map_or(false, |t| t.ident == "Option") } else { false } From b4635c533a6bef6634b1ee06897d78d31dce9c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 18 Oct 2019 19:17:06 +0200 Subject: [PATCH 087/149] Bump version. (#509) --- core-client/Cargo.toml | 2 +- core-client/transports/Cargo.toml | 2 +- core/Cargo.toml | 2 +- derive/Cargo.toml | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- test/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index bcffa4387..217556abf 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" categories = [ "asynchronous", diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index ef1a0813f..931f687da 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" categories = [ "asynchronous", diff --git a/core/Cargo.toml b/core/Cargo.toml index af3152c89..8986f738c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 579772b6f..a442d4440 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index 721e5ea2b..2d8c033fd 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] hyper = "0.12" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index ccf22b72d..30e43fe76 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] log = "0.4" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 4f64dd770..670af634f 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 17ae6e0f4..3a839c011 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index e07156ece..83ad8c6c9 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index e006cc7f0..89b0a0038 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] futures = "0.1.23" diff --git a/test/Cargo.toml b/test/Cargo.toml index 657bf06eb..305e4f5c0 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.0.0" +version = "14.0.1" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 85ee325b9..e7a2bd058 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.0" +version = "14.0.1" [dependencies] jsonrpc-core = { version = "14.0", path = "../core" } From 163af8c9ce895cf28dbb1fa070c255632837638c Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 24 Oct 2019 17:40:27 +0300 Subject: [PATCH 088/149] Multiple methods to initiate same subscriptions (#512) * Basic version working. * Test for derive. * Apply formatting. * Use different signature for second subscription * Check Subscriber type match. * Add ui failing test --- derive/src/rpc_trait.rs | 66 ++++++++++--------- derive/src/to_client.rs | 38 ++++++----- derive/src/to_delegate.rs | 57 ++++++++++++---- derive/tests/pubsub-macros.rs | 29 ++++++++ .../ui/pubsub/mixed-subscriber-signatures.rs | 24 +++++++ .../pubsub/mixed-subscriber-signatures.stderr | 5 ++ 6 files changed, 157 insertions(+), 62 deletions(-) create mode 100644 derive/tests/ui/pubsub/mixed-subscriber-signatures.rs create mode 100644 derive/tests/ui/pubsub/mixed-subscriber-signatures.stderr diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 30516d68a..3caf22c74 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -3,7 +3,7 @@ use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute}; use crate::to_client::generate_client_module; use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod}; use proc_macro2::{Span, TokenStream}; -use quote::quote; +use quote::{quote, ToTokens}; use std::collections::HashMap; use syn::{ fold::{self, Fold}, @@ -74,7 +74,7 @@ fn compute_method_registrations(item_trait: &syn::ItemTrait) -> Result<(Vec, Option)> = HashMap::new(); + let mut pubsub_method_pairs: HashMap, Option)> = HashMap::new(); let mut method_registrations: Vec = Vec::new(); for method in methods.iter() { @@ -102,21 +102,9 @@ fn compute_method_registrations(item_trait: &syn::ItemTrait) -> Result<(Vec { let (ref mut sub, ref mut unsub) = pubsub_method_pairs .entry(subscription_name.clone()) - .or_insert((None, None)); + .or_insert((vec![], None)); match kind { - PubSubMethodKind::Subscribe => { - if sub.is_none() { - *sub = Some(method.clone()) - } else { - return Err(syn::Error::new_spanned( - &method.trait_item, - format!( - "Subscription '{}' subscribe method is already defined", - subscription_name - ), - )); - } - } + PubSubMethodKind::Subscribe => sub.push(method.clone()), PubSubMethodKind::Unsubscribe => { if unsub.is_none() { *unsub = Some(method.clone()) @@ -137,24 +125,42 @@ fn compute_method_registrations(item_trait: &syn::ItemTrait) -> Result<(Vec method_registrations.push(MethodRegistration::PubSub { - name: name.clone(), - subscribe: subscribe.clone(), - unsubscribe: unsubscribe.clone(), - }), - (Some(method), None) => { - return Err(syn::Error::new_spanned( - &method.trait_item, - format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR), - )); + (subscribers, Some(unsubscribe)) => { + if subscribers.is_empty() { + return Err(syn::Error::new_spanned( + &unsubscribe.trait_item, + format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR), + )); + } + + let mut subscriber_args = subscribers.iter().filter_map(|s| s.subscriber_arg()); + if let Some(subscriber_arg) = subscriber_args.next() { + for next_method_arg in subscriber_args { + if next_method_arg != subscriber_arg { + return Err(syn::Error::new_spanned( + &next_method_arg, + format!( + "Inconsistent signature for 'Subscriber' argument: {}, previously defined: {}", + next_method_arg.clone().into_token_stream(), + subscriber_arg.clone().into_token_stream() + ), + )); + } + } + } + + method_registrations.push(MethodRegistration::PubSub { + name: name.clone(), + subscribes: subscribers.clone(), + unsubscribe: unsubscribe.clone(), + }); } - (None, Some(method)) => { + (_, None) => { return Err(syn::Error::new_spanned( - &method.trait_item, - format!("subscription '{}'. {}", name, MISSING_SUBSCRIBE_METHOD_ERR), + &item_trait, + format!("subscription '{}'. {}", name, MISSING_UNSUBSCRIBE_METHOD_ERR), )); } - (None, None) => unreachable!(), } } diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index 4de04ac2b..f622a3b96 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -111,26 +111,28 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result { - let attrs = get_doc_comments(&subscribe.trait_item.attrs); - let name = &subscribe.trait_item.sig.ident; - let mut args = compute_args(&subscribe.trait_item).into_iter(); - let returns = compute_subscription_type(&args.next().unwrap()); - let returns_str = quote!(#returns).to_string(); - let args = args.collect(); - let arg_names = compute_arg_identifiers(&args)?; - let subscribe = subscribe.name(); - let unsubscribe = unsubscribe.name(); - let client_method = syn::parse_quote!( - #(#attrs)* - pub fn #name(&self, #args) -> impl Future, Error=RpcError> { - let args_tuple = (#(#arg_names,)*); - self.inner.subscribe(#subscribe, args_tuple, #subscription, #unsubscribe, #returns_str) - } - ); - client_methods.push(client_method); + for subscribe in subscribes { + let attrs = get_doc_comments(&subscribe.trait_item.attrs); + let name = &subscribe.trait_item.sig.ident; + let mut args = compute_args(&subscribe.trait_item).into_iter(); + let returns = compute_subscription_type(&args.next().unwrap()); + let returns_str = quote!(#returns).to_string(); + let args = args.collect(); + let arg_names = compute_arg_identifiers(&args)?; + let subscribe = subscribe.name(); + let unsubscribe = unsubscribe.name(); + let client_method = syn::parse_quote!( + #(#attrs)* + pub fn #name(&self, #args) -> impl Future, Error=RpcError> { + let args_tuple = (#(#arg_names,)*); + self.inner.subscribe(#subscribe, args_tuple, #subscription, #unsubscribe, #returns_str) + } + ); + client_methods.push(client_method); + } } MethodRegistration::Notification { method, .. } => { let attrs = get_doc_comments(&method.trait_item.attrs); diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 9ee396cc7..81e801628 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -16,7 +16,7 @@ pub enum MethodRegistration { }, PubSub { name: String, - subscribe: RpcMethod, + subscribes: Vec, unsubscribe: RpcMethod, }, Notification { @@ -45,13 +45,9 @@ impl MethodRegistration { } MethodRegistration::PubSub { name, - subscribe, + subscribes, unsubscribe, } => { - let sub_name = subscribe.name(); - let sub_closure = subscribe.generate_delegate_closure(true)?; - let sub_aliases = subscribe.generate_add_aliases(); - let unsub_name = unsubscribe.name(); let unsub_method_ident = unsubscribe.ident(); let unsub_closure = quote! { @@ -63,15 +59,29 @@ impl MethodRegistration { .map_err(Into::into) } }; + + let mut add_subscriptions = proc_macro2::TokenStream::new(); + + for subscribe in subscribes.iter() { + let sub_name = subscribe.name(); + let sub_closure = subscribe.generate_delegate_closure(true)?; + let sub_aliases = subscribe.generate_add_aliases(); + + add_subscriptions = quote! { + #add_subscriptions + del.add_subscription( + #name, + (#sub_name, #sub_closure), + (#unsub_name, #unsub_closure), + ); + #sub_aliases + }; + } + let unsub_aliases = unsubscribe.generate_add_aliases(); Ok(quote! { - del.add_subscription( - #name, - (#sub_name, #sub_closure), - (#unsub_name, #unsub_closure), - ); - #sub_aliases + #add_subscriptions #unsub_aliases }) } @@ -173,6 +183,25 @@ impl RpcMethod { self.attr.is_pubsub() } + pub fn subscriber_arg(&self) -> Option { + self.trait_item + .sig + .inputs + .iter() + .filter_map(|arg| match arg { + syn::FnArg::Typed(ty) => Some(*ty.ty.clone()), + _ => None, + }) + .find(|ty| { + if let syn::Type::Path(path) = ty { + if path.path.segments.iter().any(|s| s.ident == SUBSCRIBER_TYPE_IDENT) { + return true; + } + } + false + }) + } + fn generate_delegate_closure(&self, is_subscribe: bool) -> Result { let mut param_types: Vec<_> = self .trait_item @@ -289,10 +318,10 @@ impl RpcMethod { }); let mut special_args = Vec::new(); - if let Some(ref meta) = meta_arg { + if let Some(meta) = meta_arg { special_args.push((ident(METADATA_CLOSURE_ARG), meta.clone())); } - if let Some(ref subscriber) = subscriber_arg { + if let Some(subscriber) = subscriber_arg { special_args.push((ident(SUBSCRIBER_CLOSURE_ARG), subscriber.clone())); } special_args diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index 4f88ce643..0b5c88912 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -26,6 +26,10 @@ pub trait Rpc { #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_alias"))] fn subscribe(&self, a: Self::Metadata, b: Subscriber, c: u32, d: Option); + /// Hello subscription through different method. + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe_second")] + fn subscribe_second(&self, a: Self::Metadata, b: Subscriber, e: String); + /// Unsubscribe from hello subscription. #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] fn unsubscribe(&self, a: Option, b: SubscriptionId) -> Result; @@ -49,6 +53,10 @@ impl Rpc for RpcImpl { let _sink = subscriber.assign_id(SubscriptionId::Number(5)); } + fn subscribe_second(&self, _meta: Self::Metadata, subscriber: Subscriber, _e: String) { + let _sink = subscriber.assign_id(SubscriptionId::Number(6)); + } + fn unsubscribe(&self, _meta: Option, _id: SubscriptionId) -> Result { Ok(true) } @@ -116,3 +124,24 @@ fn test_subscribe_with_alias() { let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); assert_eq!(expected, result); } + +#[test] +fn test_subscribe_alternate_method() { + let mut io = PubSubHandler::default(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let meta = Metadata; + let req = r#"{"jsonrpc":"2.0","id":1,"method":"hello_subscribe_second","params":["Data"]}"#; + let res = io.handle_request_sync(req, meta); + let expected = r#"{ + "jsonrpc": "2.0", + "result": 6, + "id": 1 + }"#; + + let expected: jsonrpc_core::Response = serde_json::from_str(expected).unwrap(); + let result: jsonrpc_core::Response = serde_json::from_str(&res.unwrap()).unwrap(); + assert_eq!(expected, result); +} diff --git a/derive/tests/ui/pubsub/mixed-subscriber-signatures.rs b/derive/tests/ui/pubsub/mixed-subscriber-signatures.rs new file mode 100644 index 000000000..25948adbf --- /dev/null +++ b/derive/tests/ui/pubsub/mixed-subscriber-signatures.rs @@ -0,0 +1,24 @@ +#[macro_use] +extern crate jsonrpc_derive; +extern crate jsonrpc_core; +extern crate jsonrpc_pubsub; + +#[rpc] +pub trait Rpc { + type Metadata; + + /// Hello subscription + #[pubsub(subscription = "hello", subscribe, name = "hello_subscribe", alias("hello_sub"))] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Hello subscription with probably different impl and mismatched Subscriber type + #[pubsub(subscription = "hello", subscribe, name = "hello_anotherSubscribe")] + fn another_subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + + /// Unsubscribe from hello subscription. + #[pubsub(subscription = "hello", unsubscribe, name = "hello_unsubscribe")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; + // note that the unsubscribe method is missing +} + +fn main() {} diff --git a/derive/tests/ui/pubsub/mixed-subscriber-signatures.stderr b/derive/tests/ui/pubsub/mixed-subscriber-signatures.stderr new file mode 100644 index 000000000..3a0e93791 --- /dev/null +++ b/derive/tests/ui/pubsub/mixed-subscriber-signatures.stderr @@ -0,0 +1,5 @@ +error: Inconsistent signature for 'Subscriber' argument: typed :: Subscriber < usize >, previously defined: typed :: Subscriber < String > + --> $DIR/mixed-subscriber-signatures.rs:16:52 + | +16 | fn another_subscribe(&self, _: Self::Metadata, _: typed::Subscriber, _: u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^ From a22e0db5dd4c9eb7034827beb6941760bcb004f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 24 Oct 2019 17:06:32 +0200 Subject: [PATCH 089/149] Version bump. (#513) --- core-client/Cargo.toml | 2 +- core-client/transports/Cargo.toml | 2 +- core/Cargo.toml | 2 +- derive/Cargo.toml | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- test/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 217556abf..1795b02cf 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" categories = [ "asynchronous", diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 931f687da..370765580 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" categories = [ "asynchronous", diff --git a/core/Cargo.toml b/core/Cargo.toml index 8986f738c..ebdfe31af 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index a442d4440..ad49291b4 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index 2d8c033fd..0393f6639 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] hyper = "0.12" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 30e43fe76..bab7c6031 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] log = "0.4" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 670af634f..88f11c5ef 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 3a839c011..5457ba562 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 83ad8c6c9..74bc434ca 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 89b0a0038..54e09b680 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] futures = "0.1.23" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index d0f4b1c50..7932c0b2b 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] log = "0.4" diff --git a/test/Cargo.toml b/test/Cargo.toml index 305e4f5c0..4e1a31d07 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.0.1" +version = "14.0.2" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index e7a2bd058..d6b780fb0 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.1" +version = "14.0.2" [dependencies] jsonrpc-core = { version = "14.0", path = "../core" } From 2135c25df57715238f1709365e3ea3bedc88e030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 25 Oct 2019 14:47:30 +0200 Subject: [PATCH 090/149] Avoid closing HTTP server on connection error (#515) * Avoid killing the server if failing connection fails. * Add a tests. * Bump version. --- core-client/Cargo.toml | 2 +- core-client/transports/Cargo.toml | 2 +- core/Cargo.toml | 2 +- derive/Cargo.toml | 2 +- http/Cargo.toml | 6 ++++- http/examples/server.rs | 2 ++ http/src/lib.rs | 1 + http/src/tests.rs | 37 +++++++++++++++++++++++++++++++ ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- test/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 16 files changed, 57 insertions(+), 13 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 1795b02cf..7e5c942a2 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" categories = [ "asynchronous", diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 370765580..9cdc3401c 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" categories = [ "asynchronous", diff --git a/core/Cargo.toml b/core/Cargo.toml index ebdfe31af..5eb848e1e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index ad49291b4..4e8b83911 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index 0393f6639..d040248a2 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] hyper = "0.12" @@ -18,5 +18,9 @@ log = "0.4" net2 = "0.2" unicase = "2.0" parking_lot = "0.9.0" + +[dev-dependencies] +env_logger = "0.6" + [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/http/examples/server.rs b/http/examples/server.rs index 2b25ee0bc..2e9ebc5e0 100644 --- a/http/examples/server.rs +++ b/http/examples/server.rs @@ -2,6 +2,8 @@ use jsonrpc_http_server::jsonrpc_core::*; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, RestApi, ServerBuilder}; fn main() { + env_logger::init(); + let mut io = IoHandler::default(); io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); diff --git a/http/src/lib.rs b/http/src/lib.rs index fc692f352..1568c4a9d 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -555,6 +555,7 @@ fn serve>( http.serve_connection(socket, service) .map_err(|e| error!("Error serving connection: {:?}", e)) + .then(|_| Ok(())) }) .buffer_unordered(1024) .for_each(|_| Ok(())) diff --git a/http/src/tests.rs b/http/src/tests.rs index eeecf5570..31664a6bc 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -1529,6 +1529,43 @@ fn should_drop_io_handler_when_server_is_closed() { panic!("expected server to be closed and io handler to be dropped") } +#[test] +fn should_not_close_server_when_serving_errors() { + // given + let server = serve(|builder| builder.keep_alive(false)); + let addr = server.address().clone(); + + // when + let req = "{}"; + let request = format!( + "\ + POST / HTTP/1.1\r\n\ + Host: localhost:{}\r\n\ + Content-Type: application/json\r\n\ + Content-Length: {}\r\n\ + 😈: 😈\r\n\ + \r\n\ + {}\r\n\ + ", + addr.port(), + req.as_bytes().len(), + req + ); + + let mut req = TcpStream::connect(addr).unwrap(); + req.write_all(request.as_bytes()).unwrap(); + let mut response = String::new(); + req.read_to_string(&mut response).unwrap(); + assert!(!response.is_empty(), "Response should not be empty: {}", response); + + // then make a second request and it must not fail. + let mut req = TcpStream::connect(addr).unwrap(); + req.write_all(request.as_bytes()).unwrap(); + let mut response = String::new(); + req.read_to_string(&mut response).unwrap(); + assert!(!response.is_empty(), "Response should not be empty: {}", response); +} + fn invalid_host() -> String { "Provided Host header is not whitelisted.\n".into() } diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index bab7c6031..fdf7365ab 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] log = "0.4" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 88f11c5ef..f50edb00f 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 5457ba562..09fb04f01 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 74bc434ca..6658ff784 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 54e09b680..4b12a3267 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] futures = "0.1.23" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 7932c0b2b..93d3db289 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] log = "0.4" diff --git a/test/Cargo.toml b/test/Cargo.toml index 4e1a31d07..634562716 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.0.2" +version = "14.0.3" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index d6b780fb0..6a329e142 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.2" +version = "14.0.3" [dependencies] jsonrpc-core = { version = "14.0", path = "../core" } From 2d38e6424d8461cdf72e78425ce67d51af9c6586 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2019 09:00:23 +0100 Subject: [PATCH 091/149] Update env_logger requirement from 0.6 to 0.7 (#493) Updates the requirements on [env_logger](https://github.com/sebasmagri/env_logger) to permit the latest version. - [Release notes](https://github.com/sebasmagri/env_logger/releases) - [Changelog](https://github.com/sebasmagri/env_logger/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebasmagri/env_logger/compare/v0.6.0...v0.7.0) Signed-off-by: dependabot-preview[bot] --- core-client/transports/Cargo.toml | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 9cdc3401c..1433580aa 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -53,7 +53,7 @@ assert_matches = "1.1" jsonrpc-http-server = { version = "14.0", path = "../../http" } jsonrpc-ipc-server = { version = "14.0", path = "../../ipc" } lazy_static = "1.0" -env_logger = "0.6" +env_logger = "0.7" tokio = "0.1" [badges] diff --git a/http/Cargo.toml b/http/Cargo.toml index d040248a2..8b4bc5748 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -20,7 +20,7 @@ unicase = "2.0" parking_lot = "0.9.0" [dev-dependencies] -env_logger = "0.6" +env_logger = "0.7" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index fdf7365ab..a3616a54d 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -18,7 +18,7 @@ parity-tokio-ipc = "0.2" parking_lot = "0.9" [dev-dependencies] -env_logger = "0.6" +env_logger = "0.7" lazy_static = "1.0" [target.'cfg(not(windows))'.dev-dependencies] diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 4b12a3267..f550f2eb5 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -20,7 +20,7 @@ tokio-stdin-stdout = "0.1.4" [dev-dependencies] lazy_static = "1.0" -env_logger = "0.6" +env_logger = "0.7" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 93d3db289..f911a6151 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -18,7 +18,7 @@ jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" -env_logger = "0.6" +env_logger = "0.7" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} From 8912104da5882050e188accca137ea6639b11424 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2019 11:08:13 +0100 Subject: [PATCH 092/149] Update websocket requirement from 0.23 to 0.24 (#516) * Update websocket requirement from 0.23 to 0.24 Updates the requirements on [websocket](https://github.com/websockets-rs/rust-websocket) to permit the latest version. - [Release notes](https://github.com/websockets-rs/rust-websocket/releases) - [Commits](https://github.com/websockets-rs/rust-websocket/compare/v0.23.0...v0.24.0) Signed-off-by: dependabot-preview[bot] * Update fmt. --- core-client/transports/Cargo.toml | 2 +- tcp/src/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 1433580aa..50adbb2ac 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -45,7 +45,7 @@ parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "0.1", optional = true } -websocket = { version = "0.23", optional = true } +websocket = { version = "0.24", optional = true } url = "1.7" [dev-dependencies] diff --git a/tcp/src/tests.rs b/tcp/src/tests.rs index 786633f37..71b819646 100644 --- a/tcp/src/tests.rs +++ b/tcp/src/tests.rs @@ -80,7 +80,7 @@ fn dummy_request(addr: &SocketAddr, data: Vec) -> Vec { io::read_to_end(stream, vec![]) }) .and_then(move |(_stream, read_buf)| ret_tx.send(read_buf).map_err(|err| panic!("Unable to send {:?}", err))) - .map_err(|err| panic!("Error connecting or closing connection: {:?}", err));; + .map_err(|err| panic!("Error connecting or closing connection: {:?}", err)); tokio::run(stream); ret_rx.wait().expect("Unable to receive result") From 1d351d3669a8d24bffd1cf63e0442421fae3221d Mon Sep 17 00:00:00 2001 From: Seun LanLege Date: Tue, 19 Nov 2019 08:44:52 +0100 Subject: [PATCH 093/149] upgrade parity-tokio-ipc (#519) --- ipc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index a3616a54d..4383926a5 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4" tokio-service = "0.1" jsonrpc-core = { version = "14.0", path = "../core" } jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } -parity-tokio-ipc = "0.2" +parity-tokio-ipc = "0.3" parking_lot = "0.9" [dev-dependencies] From 591e916c9acb713d8c9fba36d3c7ee59ec8034e8 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 22 Nov 2019 13:00:13 +0100 Subject: [PATCH 094/149] workaround serde deserializing incorrectly when arbitrary_precision enabled (#521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * workaround serde deserializing incorrectly serde deserializes incorrectly when arbitrary precision is enabled. Fix by first deserializing into `Value` before `ClientResponse` * rustfmt format for stylecheck * fix tests when using feature `arbitrary_precision` add cfg! to other places JSON is being deserialized * add generic function for arbitrary_precision replaces cfg! in the wild with one function * rustfmt Co-Authored-By: Tomasz Drwięga --- core-client/Cargo.toml | 1 + core-client/transports/Cargo.toml | 1 + core-client/transports/src/transports/mod.rs | 11 ++++++----- core/Cargo.toml | 3 +++ core/src/io.rs | 2 +- core/src/lib.rs | 17 +++++++++++++++++ core/src/types/response.rs | 2 +- test/Cargo.toml | 3 +++ test/src/lib.rs | 2 +- 9 files changed, 34 insertions(+), 8 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 7e5c942a2..c74a8c14b 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -23,6 +23,7 @@ tls = ["jsonrpc-client-transports/tls"] http = ["jsonrpc-client-transports/http"] ws = ["jsonrpc-client-transports/ws"] ipc = ["jsonrpc-client-transports/ipc"] +arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] jsonrpc-client-transports = { version = "14.0", path = "./transports", default-features = false } diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 50adbb2ac..3ee0072e5 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -31,6 +31,7 @@ ipc = [ "jsonrpc-server-utils", "tokio", ] +arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dependencies] failure = "0.1" diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index 3d5991c48..fdf54d74a 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -81,7 +81,7 @@ impl RequestBuilder { pub fn parse_response( response: &str, ) -> Result<(Id, Result, Option, Option), RpcError> { - serde_json::from_str::(&response) + jsonrpc_core::serde_from_str::(response) .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) .map(|response| { let id = response.id().unwrap_or(Id::Null); @@ -167,12 +167,12 @@ impl From for Result { mod tests { use super::*; use jsonrpc_core::{Failure, Notification, Output, Params, Success, Value, Version}; - use serde_json; #[test] fn notification_deserialize() { let dsr = r#"{"jsonrpc":"2.0","method":"hello","params":[10]}"#; - let deserialized: ClientResponse = serde_json::from_str(dsr).unwrap(); + + let deserialized: ClientResponse = jsonrpc_core::serde_from_str(dsr).unwrap(); assert_eq!( deserialized, ClientResponse::Notification(Notification { @@ -186,7 +186,8 @@ mod tests { #[test] fn success_deserialize() { let dsr = r#"{"jsonrpc":"2.0","result":1,"id":1}"#; - let deserialized: ClientResponse = serde_json::from_str(dsr).unwrap(); + + let deserialized: ClientResponse = jsonrpc_core::serde_from_str(dsr).unwrap(); assert_eq!( deserialized, ClientResponse::Output(Output::Success(Success { @@ -201,7 +202,7 @@ mod tests { fn failure_output_deserialize() { let dfo = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}"#; - let deserialized: ClientResponse = serde_json::from_str(dfo).unwrap(); + let deserialized: ClientResponse = jsonrpc_core::serde_from_str(dfo).unwrap(); assert_eq!( deserialized, ClientResponse::Output(Output::Failure(Failure { diff --git a/core/Cargo.toml b/core/Cargo.toml index 5eb848e1e..57c1f4183 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -25,5 +25,8 @@ serde = "1.0" serde_json = "1.0" serde_derive = "1.0" +[features] +arbitrary_precision = ["serde_json/arbitrary_precision"] + [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core/src/io.rs b/core/src/io.rs index 320bd0385..3ca0ed1eb 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -455,7 +455,7 @@ impl IoHandlerExtension for IoHandler { } fn read_request(request_str: &str) -> Result { - serde_json::from_str(request_str).map_err(|_| Error::new(ErrorCode::ParseError)) + crate::serde_from_str(request_str).map_err(|_| Error::new(ErrorCode::ParseError)) } fn write_response(response: Response) -> String { diff --git a/core/src/lib.rs b/core/src/lib.rs index 87d05f7a0..1e0f27fd1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -54,3 +54,20 @@ pub use crate::io::{ }; pub use crate::middleware::{Middleware, Noop as NoopMiddleware}; pub use crate::types::*; + +use serde_json::Error as SerdeError; + +/// workaround for https://github.com/serde-rs/json/issues/505 +/// Arbitrary precision confuses serde when deserializing into untagged enums, +/// this is a workaround +pub fn serde_from_str<'a, T>(input: &'a str) -> std::result::Result +where + T: serde::de::Deserialize<'a>, +{ + if cfg!(feature = "arbitrary_precision") { + let val = serde_json::from_str::(input)?; + T::deserialize(val) + } else { + serde_json::from_str::(input) + } +} diff --git a/core/src/types/response.rs b/core/src/types/response.rs index ba380a27f..f7d54b07b 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -113,7 +113,7 @@ impl Response { if s.is_empty() { Ok(Response::Batch(vec![])) } else { - serde_json::from_str(s) + crate::serde_from_str(s) } } } diff --git a/test/Cargo.toml b/test/Cargo.toml index 634562716..062e11ae2 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -17,6 +17,9 @@ log = "0.4" serde = "1.0" serde_json = "1.0" +[features] +arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] + [dev-dependencies] jsonrpc-derive = { version = "14.0", path = "../derive" } diff --git a/test/src/lib.rs b/test/src/lib.rs index 4b5fe5889..985709fdb 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -120,7 +120,7 @@ impl Rpc { .expect("We are sending a method call not notification."); // extract interesting part from the response - let extracted = match serde_json::from_str(&response).expect("We will always get a single output.") { + let extracted = match rpc::serde_from_str(&response).expect("We will always get a single output.") { response::Output::Success(response::Success { result, .. }) => match encoding { Encoding::Compact => serde_json::to_string(&result), Encoding::Pretty => serde_json::to_string_pretty(&result), From d661b0c7d6a3c969b332588c5ea3526184254c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 26 Nov 2019 13:24:12 +0100 Subject: [PATCH 095/149] Avoid using buffer_unordered (#518) * Avoid using buffer_unordered and rather spawn tasks directly on the runtime. * Review fixes. * Fix compilation issues. --- http/src/handler.rs | 37 ++++++++++++++++++++++--------- http/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++----- http/src/response.rs | 9 ++++++++ 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/http/src/handler.rs b/http/src/handler.rs index 9fb6d4019..6f72a345b 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -1,4 +1,4 @@ -use crate::Rpc; +use crate::WeakRpc; use std::sync::Arc; use std::{fmt, mem, str}; @@ -16,7 +16,7 @@ use crate::{utils, AllowedHosts, CorsDomains, RequestMiddleware, RequestMiddlewa /// jsonrpc http request handler. pub struct ServerHandler = middleware::Noop> { - jsonrpc_handler: Rpc, + jsonrpc_handler: WeakRpc, allowed_hosts: AllowedHosts, cors_domains: CorsDomains, cors_max_age: Option, @@ -31,7 +31,7 @@ pub struct ServerHandler = middleware::Noop> impl> ServerHandler { /// Create new request handler. pub fn new( - jsonrpc_handler: Rpc, + jsonrpc_handler: WeakRpc, cors_domains: CorsDomains, cors_max_age: Option, cors_allowed_headers: cors::AccessControlAllowHeaders, @@ -217,7 +217,7 @@ where } pub struct RpcHandler> { - jsonrpc_handler: Rpc, + jsonrpc_handler: WeakRpc, state: RpcHandlerState, is_options: bool, cors_allow_origin: cors::AllowCors, @@ -352,7 +352,11 @@ impl> RpcHandler { } // Read metadata - let metadata = self.jsonrpc_handler.extractor.read_metadata(&request); + let handler = match self.jsonrpc_handler.upgrade() { + Some(handler) => handler, + None => return RpcHandlerState::Writing(Response::closing()), + }; + let metadata = handler.extractor.read_metadata(&request); // Proceed match *request.method() { @@ -412,8 +416,13 @@ impl> RpcHandler { id: Id::Num(1), })); + let response = match self.jsonrpc_handler.upgrade() { + Some(h) => h.handler.handle_rpc_request(call, metadata), + None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), + }; + Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse( - future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)).map(|res| match res { + future::Either::B(response).map(|res| match res { Some(core::Response::Single(Output::Success(Success { result, .. }))) => { let result = serde_json::to_string(&result).expect("Serialization of result is infallible;qed"); @@ -457,8 +466,13 @@ impl> RpcHandler { id: Id::Num(1), })); + let response = match self.jsonrpc_handler.upgrade() { + Some(h) => h.handler.handle_rpc_request(call, metadata), + None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), + }; + Ok(RpcPollState::Ready(RpcHandlerState::Waiting( - future::Either::B(self.jsonrpc_handler.handler.handle_rpc_request(call, metadata)).map(|res| { + future::Either::B(response).map(|res| { res.map(|x| serde_json::to_string(&x).expect("Serialization of response is infallible;qed")) }), ))) @@ -497,10 +511,13 @@ impl> RpcHandler { } }; + let response = match self.jsonrpc_handler.upgrade() { + Some(h) => h.handler.handle_request(content, metadata), + None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), + }; + // Content is ready - return Ok(RpcPollState::Ready(RpcHandlerState::Waiting( - self.jsonrpc_handler.handler.handle_request(content, metadata), - ))); + return Ok(RpcPollState::Ready(RpcHandlerState::Waiting(response))); } Async::NotReady => { return Ok(RpcPollState::NotReady(RpcHandlerState::ReadingBody { diff --git a/http/src/lib.rs b/http/src/lib.rs index 1568c4a9d..63bb8f61b 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -37,7 +37,7 @@ mod utils; use std::io; use std::net::SocketAddr; -use std::sync::{mpsc, Arc}; +use std::sync::{mpsc, Arc, Weak}; use std::thread; use parking_lot::Mutex; @@ -169,6 +169,46 @@ impl> Clone for Rpc { } } +impl> Rpc { + /// Downgrade the `Rpc` to `WeakRpc`. + /// + /// Downgrades internal `Arc`s to `Weak` references. + pub fn downgrade(&self) -> WeakRpc { + WeakRpc { + handler: Arc::downgrade(&self.handler), + extractor: Arc::downgrade(&self.extractor), + } + } +} +/// A weak handle to the RPC server. +/// +/// Since request handling futures are spawned directly on the executor, +/// whenever the server is closed we want to make sure that existing +/// tasks are not blocking the server and are dropped as soon as the server stops. +pub struct WeakRpc = jsonrpc::middleware::Noop> { + handler: Weak>, + extractor: Weak>, +} + +impl> Clone for WeakRpc { + fn clone(&self) -> Self { + WeakRpc { + handler: self.handler.clone(), + extractor: self.extractor.clone(), + } + } +} + +impl> WeakRpc { + /// Upgrade the handle to a strong one (`Rpc`) if possible. + pub fn upgrade(&self) -> Option> { + let handler = self.handler.upgrade()?; + let extractor = self.extractor.upgrade()?; + + Some(Rpc { handler, extractor }) + } +} + type AllowedHosts = Option>; type CorsDomains = Option>; @@ -541,7 +581,7 @@ fn serve>( tcp_stream .map(move |socket| { let service = ServerHandler::new( - jsonrpc_handler.clone(), + jsonrpc_handler.downgrade(), cors_domains.clone(), cors_max_age, allowed_headers.clone(), @@ -553,11 +593,12 @@ fn serve>( keep_alive, ); - http.serve_connection(socket, service) - .map_err(|e| error!("Error serving connection: {:?}", e)) - .then(|_| Ok(())) + tokio::spawn( + http.serve_connection(socket, service) + .map_err(|e| error!("Error serving connection: {:?}", e)) + .then(|_| Ok(())), + ) }) - .buffer_unordered(1024) .for_each(|_| Ok(())) .map_err(|e| { warn!("Incoming streams error, closing sever: {:?}", e); diff --git a/http/src/response.rs b/http/src/response.rs index 34b8f76b2..a66fec9ca 100644 --- a/http/src/response.rs +++ b/http/src/response.rs @@ -108,6 +108,15 @@ impl Response { content: msg.into(), } } + + /// Create a 500 response when server is closing. + pub(crate) fn closing() -> Self { + Response { + code: StatusCode::SERVICE_UNAVAILABLE, + content_type: plain_text(), + content: "Server is closing.".into(), + } + } } fn plain_text() -> HeaderValue { From b6e75215fffd7a059d7697832efbdb726e8934ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 26 Nov 2019 16:21:04 +0100 Subject: [PATCH 096/149] Bump version. (#522) * Bump http version. * Bump all the crates. --- core-client/Cargo.toml | 2 +- core-client/transports/Cargo.toml | 2 +- core/Cargo.toml | 2 +- derive/Cargo.toml | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- test/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index c74a8c14b..bf1188796 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" categories = [ "asynchronous", diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 3ee0072e5..96ac533b6 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" categories = [ "asynchronous", diff --git a/core/Cargo.toml b/core/Cargo.toml index 57c1f4183..155bb1057 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 4e8b83911..9e072bda9 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index 8b4bc5748..0cc4da314 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] hyper = "0.12" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 4383926a5..daa4037dc 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] log = "0.4" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index f50edb00f..fc9c46372 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 09fb04f01..b57f8610d 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 6658ff784..213e828d1 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index f550f2eb5..dc9b8b5f3 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] futures = "0.1.23" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index f911a6151..3ffe6c869 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] log = "0.4" diff --git a/test/Cargo.toml b/test/Cargo.toml index 062e11ae2..d4b3d12ad 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.0.3" +version = "14.0.4" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 6a329e142..4f359790d 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.3" +version = "14.0.4" [dependencies] jsonrpc-core = { version = "14.0", path = "../core" } From d2e0bb709a318e9643b0930a63b88222fd96137a Mon Sep 17 00:00:00 2001 From: Rutaihwa Date: Wed, 27 Nov 2019 21:28:54 +0100 Subject: [PATCH 097/149] Fix basic example from on the readme file (#523) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 751d8ad0b..5496985d1 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` ### Basic Usage (with HTTP transport) ```rust -use jsonrpc_core::{IoHandler, Value, Params}; +use jsonrpc_http_server::jsonrpc_core::{IoHandler, Value, Params}; use jsonrpc_http_server::{ServerBuilder}; fn main() { @@ -71,7 +71,7 @@ fn main() { .start_http(&"127.0.0.1:3030".parse().unwrap()) .unwrap(); - server.wait().unwrap(); + server.wait(); } ``` From 23e6097224326be8e46bda42e8a3335b4ba30a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 28 Nov 2019 14:27:15 +0100 Subject: [PATCH 098/149] 14.0.5 bump (#524) * 14.0.5 bump * Update the script. --- _automate/publish.sh | 21 ++++++++------------- core-client/Cargo.toml | 2 +- core-client/transports/Cargo.toml | 2 +- core/Cargo.toml | 2 +- derive/Cargo.toml | 2 +- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- pubsub/Cargo.toml | 2 +- pubsub/more-examples/Cargo.toml | 2 +- server-utils/Cargo.toml | 2 +- stdio/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- test/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 14 files changed, 21 insertions(+), 26 deletions(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index 0db10a8c1..8a7f51e7d 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -2,20 +2,15 @@ set -exu -VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') -ORDER=(core server-utils tcp ws http ipc stdio pubsub core-client/transports core-client derive test) +# NOTE https://github.com/sunng87/cargo-release is required. +VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') echo "Publishing version $VERSION" cargo clean - -for crate in ${ORDER[@]}; do - cd $crate - echo "Publishing $crate@$VERSION" - sleep 5 - cargo publish $@ || read -p "\n\t Publishing $crate failed. Press [enter] to continue." - cd - -done - -echo "Tagging version $VERSION" -git tag -a v$VERSION -m "Version $VERSION" +read -p "\n\t Press [enter] to continue." +cargo release --dry-run --sign --skip-push --no-dev-version +read -p "\n\t Press [enter] to confirm release the plan." +cargo release --sign --skip-push --no-dev-version +echo "Release finished." +git push git push --tags diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index bf1188796..8b6ccad6c 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" categories = [ "asynchronous", diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 96ac533b6..d660c7ce0 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" categories = [ "asynchronous", diff --git a/core/Cargo.toml b/core/Cargo.toml index 155bb1057..d5d9dd6a4 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 9e072bda9..71e6bb35e 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [lib] proc-macro = true diff --git a/http/Cargo.toml b/http/Cargo.toml index 0cc4da314..4b7f0f6b9 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] hyper = "0.12" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index daa4037dc..77472f6a5 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] log = "0.4" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index fc9c46372..fe0363942 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] log = "0.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index b57f8610d..0010f3593 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,7 +3,7 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" authors = ["tomusdrw "] license = "MIT" diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 213e828d1..9286c273e 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] bytes = "0.4" diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index dc9b8b5f3..e9d747da3 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] futures = "0.1.23" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 3ffe6c869..696f01449 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] log = "0.4" diff --git a/test/Cargo.toml b/test/Cargo.toml index d4b3d12ad..241142c32 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.0.4" +version = "14.0.5" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 4f359790d..408c5d89b 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.4" +version = "14.0.5" [dependencies] jsonrpc-core = { version = "14.0", path = "../core" } From a427b62922e81a576272f65b5d6b9dd0ec87c54c Mon Sep 17 00:00:00 2001 From: Seun LanLege Date: Wed, 4 Dec 2019 14:29:56 +0100 Subject: [PATCH 099/149] Bump parity-tokio-ipc (#525) * bump parity-tokio-ipc * Bump IPC crate. --- ipc/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 77472f6a5..915a8f2fe 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.0.6" [dependencies] log = "0.4" tokio-service = "0.1" jsonrpc-core = { version = "14.0", path = "../core" } jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } -parity-tokio-ipc = "0.3" +parity-tokio-ipc = "0.4" parking_lot = "0.9" [dev-dependencies] From a2a692997be998fd87455855cdaa4d2b63572c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 4 Dec 2019 14:30:13 +0100 Subject: [PATCH 100/149] Fix automate script. (#526) --- _automate/publish.sh | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index 8a7f51e7d..14f13aa1a 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -1,16 +1,37 @@ #!/bin/bash -set -exu +set -eu -# NOTE https://github.com/sunng87/cargo-release is required. +ORDER=(core server-utils tcp ws http ipc stdio pubsub core-client/transports core-client derive test) + +# First display the plan +for crate in ${ORDER[@]}; do + cd $crate > /dev/null + VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') + echo "$crate@$VERSION" + cd - > /dev/null +done + +read -p ">>>> Really publish?. Press [enter] to continue. " + +set -x -VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') -echo "Publishing version $VERSION" cargo clean -read -p "\n\t Press [enter] to continue." -cargo release --dry-run --sign --skip-push --no-dev-version -read -p "\n\t Press [enter] to confirm release the plan." -cargo release --sign --skip-push --no-dev-version -echo "Release finished." -git push + +# Then actually perform publishing. +for crate in ${ORDER[@]}; do + cd $crate + VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') + echo "Publishing $crate@$VERSION" + sleep 5 + cargo publish $@ || read -p ">>>>> Publishing $crate failed. Press [enter] to continue. " + git tag -a "$crate-$VERSION" -m "$crate $VERSION" + cd - +done + +git push --tags + +VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') +echo "Tagging main $VERSION" +git tag -a v$VERSION -m "Version $VERSION" git push --tags From 6bddbc7da174202e06cb679d234388c770967b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 5 Dec 2019 11:11:24 +0100 Subject: [PATCH 101/149] New automate script. (#527) --- _automate/publish.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index 14f13aa1a..e8d9829d8 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -25,7 +25,15 @@ for crate in ${ORDER[@]}; do echo "Publishing $crate@$VERSION" sleep 5 cargo publish $@ || read -p ">>>>> Publishing $crate failed. Press [enter] to continue. " - git tag -a "$crate-$VERSION" -m "$crate $VERSION" + cd - +done + +# Make tags in one go +for crate in ${ORDER[@]}; do + cd $crate + VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') + echo "Tagging $crate@$VERSION" + git tag -a "$crate-$VERSION" -m "$crate $VERSION" || true cd - done From 3bfb894200a2d6955c6d408340a8be307876d51d Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Sat, 21 Dec 2019 17:33:58 +0300 Subject: [PATCH 102/149] remove no longer needed externs (#529) --- ...ub-dependency-not-required-for-vanilla-rpc.rs | 6 +----- ...subscription-generic-type-with-deserialize.rs | 9 ++------- .../pubsub-subscription-type-with-deserialize.rs | 9 ++------- ...bsub-subscription-type-without-deserialize.rs | 9 ++------- derive/tests/ui/attr-invalid-meta-list-names.rs | 4 +--- .../tests/ui/attr-invalid-meta-list-names.stderr | 8 ++++---- derive/tests/ui/attr-invalid-meta-words.rs | 4 +--- derive/tests/ui/attr-invalid-meta-words.stderr | 8 ++++---- derive/tests/ui/attr-invalid-name-values.rs | 4 +--- derive/tests/ui/attr-invalid-name-values.stderr | 8 ++++---- derive/tests/ui/attr-missing-rpc-name.rs | 4 +--- derive/tests/ui/attr-missing-rpc-name.stderr | 8 ++++---- derive/tests/ui/multiple-rpc-attributes.rs | 4 +--- derive/tests/ui/multiple-rpc-attributes.stderr | 16 ++++++++-------- derive/tests/ui/too-many-params.rs | 4 +--- derive/tests/ui/too-many-params.stderr | 16 ++++++++-------- 16 files changed, 45 insertions(+), 76 deletions(-) diff --git a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs index c42d30065..cae0bac2d 100644 --- a/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs +++ b/derive/tests/run-pass/pubsub-dependency-not-required-for-vanilla-rpc.rs @@ -1,8 +1,4 @@ -extern crate jsonrpc_core; -extern crate jsonrpc_core_client; -#[macro_use] -extern crate jsonrpc_derive; - +use jsonrpc_derive::rpc; use jsonrpc_core::{Result, IoHandler}; #[rpc] diff --git a/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs index 28204d3df..45b8bedc2 100644 --- a/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-generic-type-with-deserialize.rs @@ -1,10 +1,5 @@ -#[macro_use] -extern crate serde; -extern crate jsonrpc_core; -extern crate jsonrpc_core_client; -extern crate jsonrpc_pubsub; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; +use serde::{Serialize, Deserialize}; use std::sync::Arc; use jsonrpc_core::Result; diff --git a/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs index 49e928108..b3e4f7bde 100644 --- a/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-type-with-deserialize.rs @@ -1,10 +1,5 @@ -#[macro_use] -extern crate serde; -extern crate jsonrpc_core; -extern crate jsonrpc_core_client; -extern crate jsonrpc_pubsub; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; +use serde::{Serialize, Deserialize}; use std::sync::Arc; use jsonrpc_core::Result; diff --git a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs index e6400e498..e0024595e 100644 --- a/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs +++ b/derive/tests/run-pass/pubsub-subscription-type-without-deserialize.rs @@ -1,10 +1,5 @@ -#[macro_use] -extern crate serde; -extern crate jsonrpc_core; -extern crate jsonrpc_core_client; -extern crate jsonrpc_pubsub; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; +use serde::Serialize; use std::sync::Arc; use jsonrpc_core::Result; diff --git a/derive/tests/ui/attr-invalid-meta-list-names.rs b/derive/tests/ui/attr-invalid-meta-list-names.rs index 938d168d1..7d8860065 100644 --- a/derive/tests/ui/attr-invalid-meta-list-names.rs +++ b/derive/tests/ui/attr-invalid-meta-list-names.rs @@ -1,6 +1,4 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { diff --git a/derive/tests/ui/attr-invalid-meta-list-names.stderr b/derive/tests/ui/attr-invalid-meta-list-names.stderr index 1e93d28c0..443966e27 100644 --- a/derive/tests/ui/attr-invalid-meta-list-names.stderr +++ b/derive/tests/ui/attr-invalid-meta-list-names.stderr @@ -1,8 +1,8 @@ error: Invalid attribute parameter(s): 'Xalias'. Expected 'alias' - --> $DIR/attr-invalid-meta-list-names.rs:7:2 + --> $DIR/attr-invalid-meta-list-names.rs:5:2 | -7 | /// Returns a protocol version +5 | /// Returns a protocol version | _____^ -8 | | #[rpc(name = "protocolVersion", Xalias("alias"))] -9 | | fn protocol_version(&self) -> Result; +6 | | #[rpc(name = "protocolVersion", Xalias("alias"))] +7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/attr-invalid-meta-words.rs b/derive/tests/ui/attr-invalid-meta-words.rs index 106df1baa..e4eb6876e 100644 --- a/derive/tests/ui/attr-invalid-meta-words.rs +++ b/derive/tests/ui/attr-invalid-meta-words.rs @@ -1,6 +1,4 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index 8b1c99051..7b5d63d17 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,8 +1,8 @@ error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params' - --> $DIR/attr-invalid-meta-words.rs:7:2 + --> $DIR/attr-invalid-meta-words.rs:5:2 | -7 | /// Returns a protocol version +5 | /// Returns a protocol version | _____^ -8 | | #[rpc(name = "protocolVersion", Xmeta)] -9 | | fn protocol_version(&self) -> Result; +6 | | #[rpc(name = "protocolVersion", Xmeta)] +7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/attr-invalid-name-values.rs b/derive/tests/ui/attr-invalid-name-values.rs index 4a3fa4acf..16cdb4b6c 100644 --- a/derive/tests/ui/attr-invalid-name-values.rs +++ b/derive/tests/ui/attr-invalid-name-values.rs @@ -1,6 +1,4 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index 69a56cc44..9bb48a1c7 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,8 +1,8 @@ error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns' - --> $DIR/attr-invalid-name-values.rs:7:2 + --> $DIR/attr-invalid-name-values.rs:5:2 | -7 | /// Returns a protocol version +5 | /// Returns a protocol version | _____^ -8 | | #[rpc(Xname = "protocolVersion")] -9 | | fn protocol_version(&self) -> Result; +6 | | #[rpc(Xname = "protocolVersion")] +7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/attr-missing-rpc-name.rs b/derive/tests/ui/attr-missing-rpc-name.rs index 401359af4..044d9ae7a 100644 --- a/derive/tests/ui/attr-missing-rpc-name.rs +++ b/derive/tests/ui/attr-missing-rpc-name.rs @@ -1,6 +1,4 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { diff --git a/derive/tests/ui/attr-missing-rpc-name.stderr b/derive/tests/ui/attr-missing-rpc-name.stderr index 788008029..fca07fc75 100644 --- a/derive/tests/ui/attr-missing-rpc-name.stderr +++ b/derive/tests/ui/attr-missing-rpc-name.stderr @@ -1,8 +1,8 @@ error: rpc attribute should have a name e.g. `name = "method_name"` - --> $DIR/attr-missing-rpc-name.rs:7:2 + --> $DIR/attr-missing-rpc-name.rs:5:2 | -7 | /// Returns a protocol version +5 | /// Returns a protocol version | _____^ -8 | | #[rpc] -9 | | fn protocol_version(&self) -> Result; +6 | | #[rpc] +7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/multiple-rpc-attributes.rs b/derive/tests/ui/multiple-rpc-attributes.rs index 7a48b3c0c..a6fde45aa 100644 --- a/derive/tests/ui/multiple-rpc-attributes.rs +++ b/derive/tests/ui/multiple-rpc-attributes.rs @@ -1,6 +1,4 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { diff --git a/derive/tests/ui/multiple-rpc-attributes.stderr b/derive/tests/ui/multiple-rpc-attributes.stderr index 28ce7cbef..05099f7e3 100644 --- a/derive/tests/ui/multiple-rpc-attributes.stderr +++ b/derive/tests/ui/multiple-rpc-attributes.stderr @@ -1,9 +1,9 @@ error: Expected only a single rpc attribute per method - --> $DIR/multiple-rpc-attributes.rs:7:2 - | -7 | /// Returns a protocol version - | _____^ -8 | | #[rpc(name = "protocolVersion")] -9 | | #[rpc(name = "protocolVersionAgain")] -10 | | fn protocol_version(&self) -> Result; - | |_________________________________________________^ + --> $DIR/multiple-rpc-attributes.rs:5:2 + | +5 | /// Returns a protocol version + | _____^ +6 | | #[rpc(name = "protocolVersion")] +7 | | #[rpc(name = "protocolVersionAgain")] +8 | | fn protocol_version(&self) -> Result; + | |_________________________________________________^ diff --git a/derive/tests/ui/too-many-params.rs b/derive/tests/ui/too-many-params.rs index 7e217c22a..326c77dab 100644 --- a/derive/tests/ui/too-many-params.rs +++ b/derive/tests/ui/too-many-params.rs @@ -1,6 +1,4 @@ -extern crate jsonrpc_core; -#[macro_use] -extern crate jsonrpc_derive; +use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { diff --git a/derive/tests/ui/too-many-params.stderr b/derive/tests/ui/too-many-params.stderr index 25e04b856..b83347661 100644 --- a/derive/tests/ui/too-many-params.stderr +++ b/derive/tests/ui/too-many-params.stderr @@ -1,12 +1,12 @@ error: Maximum supported number of params is 16 - --> $DIR/too-many-params.rs:7:2 + --> $DIR/too-many-params.rs:5:2 | -7 | /// Has too many params +5 | /// Has too many params | _____^ -8 | | #[rpc(name = "tooManyParams")] -9 | | fn to_many_params( -10 | | &self, -11 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, -12 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, -13 | | ) -> Result; +6 | | #[rpc(name = "tooManyParams")] +7 | | fn to_many_params( +8 | | &self, +9 | | a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64, i: u64, j: u64, +10 | | k: u64, l: u64, m: u64, n: u64, o: u64, p: u64, q: u64, +11 | | ) -> Result; | |________________________^ From 2c26aadaf381951731187a9b7f5e1c8082375452 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 7 Jan 2020 17:00:14 +0000 Subject: [PATCH 103/149] [client] notify subscriber if subscription was rejected (#530) * [client] notify subscriber if subscription was rejected * Format code * Remove errant branch name in log message --- .../transports/src/transports/duplex.rs | 21 ++++++++++++++++--- core/src/types/request.rs | 5 ++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index a0ff43d21..5465c37e4 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -214,12 +214,27 @@ where ); } } else { - log::warn!( - "The response does not have expected subscription id in response: {:?} ({:?}): {:?}", + let err = RpcError::Other(format_err!( + "Subscription {:?} ({:?}) rejected: {:?}", id, method, result, - ); + )); + match subscription.channel.poll_ready() { + Ok(Async::Ready(())) => { + subscription + .channel + .try_send(result) + .expect("The channel is ready; qed"); + } + Ok(Async::NotReady) => { + self.incoming.push_back((id, result, Some(method), sid)); + break; + } + Err(_) => { + log::warn!("{}, but the reply channel has closed.", err); + } + }; } continue; } diff --git a/core/src/types/request.rs b/core/src/types/request.rs index 61905cffb..adbf0eccd 100644 --- a/core/src/types/request.rs +++ b/core/src/types/request.rs @@ -154,7 +154,10 @@ mod tests { ]); let serialized = serde_json::to_string(&batch).unwrap(); - assert_eq!(serialized, r#"[{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1},{"jsonrpc":"2.0","method":"update","params":[1]}]"#); + assert_eq!( + serialized, + r#"[{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1},{"jsonrpc":"2.0","method":"update","params":[1]}]"# + ); } #[test] From 596ed3a10c1f6cda16096ade62dad943c5a1abc2 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 16 Jan 2020 20:14:31 +0300 Subject: [PATCH 104/149] fix warning (#533) --- http/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/src/lib.rs b/http/src/lib.rs index 63bb8f61b..0b5a6016d 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -481,7 +481,7 @@ impl> ServerBuilder { let _ = recv_address(local_addr_rx)?; Ok((eloop, close, done_rx)) }) - .collect::)>>()?; + .collect::>>()?; handles.push((eloop, close, done_rx)); let (executors, done_rxs) = handles From 20485387ed06a48f1a70bf4d609a7cde6cf0accf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Jan 2020 17:17:16 +0100 Subject: [PATCH 105/149] IoHandlerExtension for Option (#535) --- core/src/io.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/io.rs b/core/src/io.rs index 3ca0ed1eb..65f67162b 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -378,6 +378,14 @@ impl> IoHandlerExtension for MetaIoHandler> IoHandlerExtension for Option { + fn augment>(self, handler: &mut MetaIoHandler) { + if let Some(x) = self { + x.augment(handler) + } + } +} + /// Simplified `IoHandler` with no `Metadata` associated with each request. #[derive(Clone, Debug, Default)] pub struct IoHandler(MetaIoHandler); From c20705c27dae02a0a9b0a9ba2697f5819c309490 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 1 Feb 2020 06:17:50 +0800 Subject: [PATCH 106/149] Fix checkstyle for rustc 1.41.0 (#537) --- derive/src/rpc_trait.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 3caf22c74..737c18665 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -14,8 +14,7 @@ use syn::{ const METADATA_TYPE: &str = "Metadata"; -const MISSING_SUBSCRIBE_METHOD_ERR: &str = - "Can't find subscribe method, expected a method annotated with `subscribe` \ +const MISSING_SUBSCRIBE_METHOD_ERR: &str = "Can't find subscribe method, expected a method annotated with `subscribe` \ e.g. `#[pubsub(subscription = \"hello\", subscribe, name = \"hello_subscribe\")]`"; const MISSING_UNSUBSCRIBE_METHOD_ERR: &str = From 93e477394d25b758aa48b8f97d8d357b43b133cd Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 1 Feb 2020 17:45:28 +0800 Subject: [PATCH 107/149] Fix ServerBuilder::cors_max_age doc comment time unit (#536) --- http/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/http/src/lib.rs b/http/src/lib.rs index 0b5a6016d..0a58276f9 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -364,8 +364,7 @@ impl> ServerBuilder { /// Configure CORS `AccessControlMaxAge` header returned. /// - /// Passing `Some(millis)` informs the client that the CORS preflight request is not necessary - /// for at least `millis` ms. + /// Informs the client that the CORS preflight request is not necessary for `cors_max_age` seconds. /// Disabled by default. pub fn cors_max_age>>(mut self, cors_max_age: T) -> Self { self.cors_max_age = cors_max_age.into(); From a1f57ae941ff45ecb7e41b7473e8d7e34649cf55 Mon Sep 17 00:00:00 2001 From: Sean Bailey Date: Wed, 5 Feb 2020 10:33:57 +1100 Subject: [PATCH 108/149] Fix a broken import in stdio example (#538) --- stdio/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdio/README.md b/stdio/README.md index a7997cdf1..7a737570f 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -16,7 +16,7 @@ jsonrpc-stdio-server = "14.0" `main.rs` ```rust -use jsonrpc_stdio_server::server; +use jsonrpc_stdio_server::ServerBuilder; use jsonrpc_stdio_server::jsonrpc_core::*; fn main() { From ef44a62c543fd969dd5bedf5a4ece3f3814bb111 Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Fri, 7 Feb 2020 16:26:10 +0100 Subject: [PATCH 109/149] bump parking_lot and patch versions (#539) --- http/Cargo.toml | 4 ++-- ipc/Cargo.toml | 4 ++-- pubsub/Cargo.toml | 4 ++-- tcp/Cargo.toml | 4 ++-- ws/Cargo.toml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/http/Cargo.toml b/http/Cargo.toml index 4b7f0f6b9..43f53c8f7 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.0.6" [dependencies] hyper = "0.12" @@ -17,7 +17,7 @@ jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" -parking_lot = "0.9.0" +parking_lot = "0.10.0" [dev-dependencies] env_logger = "0.7" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 915a8f2fe..fe5c86179 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.6" +version = "14.0.7" [dependencies] log = "0.4" @@ -15,7 +15,7 @@ tokio-service = "0.1" jsonrpc-core = { version = "14.0", path = "../core" } jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } parity-tokio-ipc = "0.4" -parking_lot = "0.9" +parking_lot = "0.10.0" [dev-dependencies] env_logger = "0.7" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index fe0363942..edced41e0 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,11 +8,11 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.0.6" [dependencies] log = "0.4" -parking_lot = "0.9" +parking_lot = "0.10.0" jsonrpc-core = { version = "14.0", path = "../core" } serde = "1.0" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 696f01449..151769f4f 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.0.6" [dependencies] log = "0.4" -parking_lot = "0.9" +parking_lot = "0.10.0" tokio-service = "0.1" jsonrpc-core = { version = "14.0", path = "../core" } jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 408c5d89b..40999299d 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.0.6" [dependencies] jsonrpc-core = { version = "14.0", path = "../core" } jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } log = "0.4" -parking_lot = "0.9" +parking_lot = "0.10.0" slab = "0.4" ws = "0.9" From 4a6c49971a059e8c489bc51c738cb4ee08f80536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 20 Feb 2020 14:52:41 +0100 Subject: [PATCH 110/149] Wait for http server to close on Drop. (#543) --- http/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/http/src/lib.rs b/http/src/lib.rs index 0a58276f9..7cc40bedf 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -670,11 +670,7 @@ impl Server { /// Will block, waiting for the server to finish. pub fn wait(mut self) { - if let Some(receivers) = self.done.take() { - for receiver in receivers { - let _ = receiver.wait(); - } - } + self.wait_internal(); } /// Get a handle that allows us to close the server from a different thread and/or while the @@ -682,10 +678,19 @@ impl Server { pub fn close_handle(&self) -> CloseHandle { CloseHandle(self.executors.clone()) } + + fn wait_internal(&mut self) { + if let Some(receivers) = self.done.take() { + for receiver in receivers { + let _ = receiver.wait(); + } + } + } } impl Drop for Server { fn drop(&mut self) { self.close_handle().close(); + self.wait_internal(); } } From 84f2c420b538ac0f74e889f1e8eb032d20751fe0 Mon Sep 17 00:00:00 2001 From: yinhu-prismalab <46338399+yinhu-prismalab@users.noreply.github.com> Date: Mon, 24 Feb 2020 06:08:02 +0800 Subject: [PATCH 111/149] Add "Accept:application/json" header for all requests. (#544) --- core-client/transports/src/transports/http.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index 0ad0cb5f5..ba7d3f12a 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -60,6 +60,10 @@ where http::header::CONTENT_TYPE, http::header::HeaderValue::from_static("application/json"), ) + .header( + http::header::ACCEPT, + http::header::HeaderValue::from_static("application/json"), + ) .body(request.into()) .expect("Uri and request headers are valid; qed"); From c64a48a2b1e38f5fae66b61a58b8f73a3d240463 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 24 Mar 2020 02:58:11 +1000 Subject: [PATCH 112/149] Client call with named params (#541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add support for json objects in TypedClient.call_method * adds named params switch to #[rpc] method annotation * add generating of map style params in macro * prevent creating a server when using the named_params switch * add test for attempt to derive with named_params on server. refactors error string * adds test that correct params object is generated * apply cargo fmt * adds ParamStyle enum and conversions. Parses this from meta * updates existing tests * adds client side support for raw params * adds compiler error on invalid params value * add client tests * adds global switch to trait attribute * add logic to override code generation with default from top level * adds error on changing trait default when generating server with incompatible params type * adds tests for preventing server generation with named params * Update derive/src/rpc_attr.rs * Update derive/src/rpc_trait.rs Co-Authored-By: Tomasz Drwięga * remove unwrap and throw useful compile time error * fixes error, notifies that server also supports raw params * add placeholder for future support for named params server side * remove raw_params from examples and replace with params = raw * run cargo fmt * add deprecation message placeholder * bump and lock quote version * rollback quote to 1.0.1 Co-authored-by: Tomasz Drwięga --- core-client/transports/src/lib.rs | 3 +- derive/Cargo.toml | 2 +- derive/examples/meta-macros.rs | 2 +- derive/src/lib.rs | 4 +- derive/src/options.rs | 72 ++++++--- derive/src/params_style.rs | 34 ++++ derive/src/rpc_attr.rs | 32 +++- derive/src/rpc_trait.rs | 18 ++- derive/src/to_client.rs | 34 +++- derive/src/to_delegate.rs | 10 +- derive/tests/client.rs | 148 ++++++++++++++---- derive/tests/macros.rs | 2 +- .../tests/ui/attr-invalid-name-values.stderr | 2 +- .../tests/ui/attr-named-params-on-server.rs | 10 ++ .../ui/attr-named-params-on-server.stderr | 9 ++ .../ui/trait-attr-named-params-on-server.rs | 7 + .../trait-attr-named-params-on-server.stderr | 7 + 17 files changed, 327 insertions(+), 69 deletions(-) create mode 100644 derive/src/params_style.rs create mode 100644 derive/tests/ui/attr-named-params-on-server.rs create mode 100644 derive/tests/ui/attr-named-params-on-server.stderr create mode 100644 derive/tests/ui/trait-attr-named-params-on-server.rs create mode 100644 derive/tests/ui/trait-attr-named-params-on-server.stderr diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index ab5a4bc6f..b6da7c7e2 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -303,9 +303,10 @@ impl TypedClient { let params = match args { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, + Value::Object(map) => Params::Map(map), _ => { return future::Either::A(future::err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, or null" + "RPC params should serialize to a JSON array, JSON object or null" )))) } }; diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 71e6bb35e..8199f796a 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -15,7 +15,7 @@ proc-macro = true [dependencies] syn = { version = "1.0", features = ["full", "extra-traits", "visit", "fold"] } proc-macro2 = "1.0" -quote = "1.0" +quote = "=1.0.1" proc-macro-crate = "0.1.4" [dev-dependencies] diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 58164e31a..8d7a64d7f 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -26,7 +26,7 @@ pub trait Rpc { fn mul(&self, a: u64, b: Option) -> Result; /// Retrieves and debug prints the underlying `Params` object. - #[rpc(name = "raw", raw_params)] + #[rpc(name = "raw", params = "raw")] fn raw(&self, params: Params) -> Result; /// Performs an asynchronous operation. diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 8fe096eb8..fa8d9fdfe 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -186,6 +186,7 @@ use proc_macro::TokenStream; use syn::parse_macro_input; mod options; +mod params_style; mod rpc_attr; mod rpc_trait; mod to_client; @@ -198,13 +199,14 @@ mod to_delegate; #[proc_macro_attribute] pub fn rpc(args: TokenStream, input: TokenStream) -> TokenStream { let input_toks = parse_macro_input!(input as syn::Item); + let args = syn::parse_macro_input!(args as syn::AttributeArgs); let options = match options::DeriveOptions::try_from(args) { Ok(options) => options, Err(error) => return error.to_compile_error().into(), }; - match rpc_trait::rpc_impl(input_toks, options) { + match rpc_trait::rpc_impl(input_toks, &options) { Ok(output) => output.into(), Err(err) => err.to_compile_error().into(), } diff --git a/derive/src/options.rs b/derive/src/options.rs index a6ba98ec0..fe52c07dc 100644 --- a/derive/src/options.rs +++ b/derive/src/options.rs @@ -1,37 +1,71 @@ -use proc_macro::TokenStream; +use std::str::FromStr; + +use crate::params_style::ParamStyle; +use crate::rpc_attr::path_eq_str; + +const CLIENT_META_WORD: &str = "client"; +const SERVER_META_WORD: &str = "server"; +const PARAMS_META_KEY: &str = "params"; #[derive(Debug)] pub struct DeriveOptions { pub enable_client: bool, pub enable_server: bool, + pub params_style: ParamStyle, } impl DeriveOptions { - pub fn new(enable_client: bool, enable_server: bool) -> Self { + pub fn new(enable_client: bool, enable_server: bool, params_style: ParamStyle) -> Self { DeriveOptions { enable_client, enable_server, + params_style, } } - pub fn try_from(tokens: TokenStream) -> Result { - if tokens.is_empty() { - return Ok(Self::new(true, true)); - } - let ident: syn::Ident = syn::parse::(tokens)?; - let options = { - let ident = ident.to_string(); - if ident == "client" { - Some(Self::new(true, false)) - } else if ident == "server" { - Some(Self::new(false, true)) - } else { - None + pub fn try_from(args: syn::AttributeArgs) -> Result { + let mut options = DeriveOptions::new(false, false, ParamStyle::default()); + for arg in args { + if let syn::NestedMeta::Meta(meta) = arg { + match meta { + syn::Meta::Path(ref p) => { + match p + .get_ident() + .ok_or(syn::Error::new_spanned( + p, + format!("Expecting identifier `{}` or `{}`", CLIENT_META_WORD, SERVER_META_WORD), + ))? + .to_string() + .as_ref() + { + CLIENT_META_WORD => options.enable_client = true, + SERVER_META_WORD => options.enable_server = true, + _ => {} + }; + } + syn::Meta::NameValue(nv) => { + if path_eq_str(&nv.path, PARAMS_META_KEY) { + if let syn::Lit::Str(ref lit) = nv.lit { + options.params_style = ParamStyle::from_str(&lit.value()) + .map_err(|e| syn::Error::new_spanned(nv.clone(), e))?; + } + } else { + return Err(syn::Error::new_spanned(nv, "Unexpected RPC attribute key")); + } + } + _ => return Err(syn::Error::new_spanned(meta, "Unexpected use of RPC attribute macro")), + } } - }; - match options { - Some(options) => Ok(options), - None => Err(syn::Error::new(ident.span(), "Unknown attribute.")), } + if !options.enable_client && !options.enable_server { + // if nothing provided default to both + options.enable_client = true; + options.enable_server = true; + } + if options.enable_server && options.params_style == ParamStyle::Named { + // This is not allowed at this time + panic!("Server code generation only supports `params = \"positional\"` (default) or `params = \"raw\" at this time.") + } + Ok(options) } } diff --git a/derive/src/params_style.rs b/derive/src/params_style.rs new file mode 100644 index 000000000..fe8b9dfe7 --- /dev/null +++ b/derive/src/params_style.rs @@ -0,0 +1,34 @@ +use std::str::FromStr; + +const POSITIONAL: &str = "positional"; +const NAMED: &str = "named"; +const RAW: &str = "raw"; + +#[derive(Clone, Debug, PartialEq)] +pub enum ParamStyle { + Positional, + Named, + Raw, +} + +impl Default for ParamStyle { + fn default() -> Self { + Self::Positional + } +} + +impl FromStr for ParamStyle { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + POSITIONAL => Ok(Self::Positional), + NAMED => Ok(Self::Named), + RAW => Ok(Self::Raw), + _ => Err(format!( + "Invalid value for params key. Must be one of [{}, {}, {}]", + POSITIONAL, NAMED, RAW + )), + } + } +} diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 95a7c1286..215979d19 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -1,3 +1,5 @@ +use crate::params_style::ParamStyle; +use std::str::FromStr; use syn::{ visit::{self, Visit}, Error, Result, @@ -9,7 +11,7 @@ pub struct RpcMethodAttribute { pub name: String, pub aliases: Vec, pub kind: AttributeKind, - pub raw_params: bool, + pub params_style: Option, // None means do not override the top level default } #[derive(Clone, Debug)] @@ -37,10 +39,11 @@ const SUBSCRIPTION_NAME_KEY: &str = "subscription"; const ALIASES_KEY: &str = "alias"; const PUB_SUB_ATTR_NAME: &str = "pubsub"; const METADATA_META_WORD: &str = "meta"; -const RAW_PARAMS_META_WORD: &str = "raw_params"; +const RAW_PARAMS_META_WORD: &str = "raw_params"; // to be deprecated and replaced with `params = "raw"` const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; const RETURNS_META_WORD: &str = "returns"; +const PARAMS_STYLE_KEY: &str = "params"; const MULTIPLE_RPC_ATTRIBUTES_ERR: &str = "Expected only a single rpc attribute per method"; const INVALID_ATTR_PARAM_NAMES_ERR: &str = "Invalid attribute parameter(s):"; @@ -81,12 +84,21 @@ impl RpcMethodAttribute { let aliases = get_meta_list(&meta).map_or(Vec::new(), |ml| get_aliases(ml)); let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); + let params_style = match raw_params { + true => { + // "`raw_params` will be deprecated in a future release. Use `params = \"raw\" instead`" + Ok(Some(ParamStyle::Raw)) + } + false => { + get_meta_list(meta).map_or(Ok(None), |ml| get_params_style(ml).map(|s| Some(s))) + } + }?; Ok(RpcMethodAttribute { attr: attr.clone(), name, aliases, kind, - raw_params, + params_style, }) }) }) @@ -179,7 +191,11 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { match ident.as_ref().map(String::as_str) { Some(RPC_ATTR_NAME) => { validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD])?; - validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; + validate_idents( + &meta, + &visitor.name_value_names, + &[RPC_NAME_KEY, RETURNS_META_WORD, PARAMS_STYLE_KEY], + )?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } Some(PUB_SUB_ATTR_NAME) => { @@ -279,7 +295,13 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { }) } -fn path_eq_str(path: &syn::Path, s: &str) -> bool { +fn get_params_style(ml: &syn::MetaList) -> Result { + get_name_value(PARAMS_STYLE_KEY, ml).map_or(Ok(ParamStyle::default()), |s| { + ParamStyle::from_str(&s).map_err(|e| Error::new_spanned(ml, e)) + }) +} + +pub fn path_eq_str(path: &syn::Path, s: &str) -> bool { path.get_ident().map_or(false, |i| i == s) } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 737c18665..7550ade4f 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -1,4 +1,5 @@ use crate::options::DeriveOptions; +use crate::params_style::ParamStyle; use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute}; use crate::to_client::generate_client_module; use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod}; @@ -21,6 +22,10 @@ const MISSING_UNSUBSCRIBE_METHOD_ERR: &str = "Can't find unsubscribe method, expected a method annotated with `unsubscribe` \ e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`"; +pub const USING_NAMED_PARAMS_WITH_SERVER_ERR: &str = + "`params = \"named\"` can only be used to generate a client (on a trait annotated with #[rpc(client)]). \ + At this time the server does not support named parameters."; + const RPC_MOD_NAME_PREFIX: &str = "rpc_impl_"; struct RpcTrait { @@ -218,13 +223,19 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { syn::Ident::new(&mod_name, proc_macro2::Span::call_site()) } +fn has_named_params(methods: &[RpcMethod]) -> bool { + methods + .iter() + .any(|method| method.attr.params_style == Some(ParamStyle::Named)) +} + pub fn crate_name(name: &str) -> Result { proc_macro_crate::crate_name(name) .map(|name| Ident::new(&name, Span::call_site())) .map_err(|e| Error::new(Span::call_site(), &e)) } -pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result { +pub fn rpc_impl(input: syn::Item, options: &DeriveOptions) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, item => { @@ -245,13 +256,16 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result Result { - let client_methods = generate_client_methods(methods)?; +pub fn generate_client_module( + methods: &[MethodRegistration], + item_trait: &syn::ItemTrait, + options: &DeriveOptions, +) -> Result { + let client_methods = generate_client_methods(methods, &options)?; let generics = &item_trait.generics; let where_clause = generate_where_clause_serialization_predicates(&item_trait, true); let where_clause2 = where_clause.clone(); @@ -85,7 +91,7 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: }) } -fn generate_client_methods(methods: &[MethodRegistration]) -> Result> { +fn generate_client_methods(methods: &[MethodRegistration], options: &DeriveOptions) -> Result> { let mut client_methods = vec![]; for method in methods { match method { @@ -100,11 +106,29 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result continue, }; let returns_str = quote!(#returns).to_string(); + + let args_serialized = match method.attr.params_style.clone().unwrap_or(options.params_style.clone()) { + ParamStyle::Named => { + quote! { // use object style serialization with field names taken from the function param names + serde_json::json!({ + #(stringify!(#arg_names): #arg_names,)* + }) + } + } + ParamStyle::Positional => quote! { // use tuple style serialization + (#(#arg_names,)*) + }, + ParamStyle::Raw => match arg_names.first() { + Some(arg_name) => quote! {#arg_name}, + None => quote! {serde_json::Value::Null}, + }, + }; + let client_method = syn::parse_quote! { #(#attrs)* pub fn #name(&self, #args) -> impl Future { - let args_tuple = (#(#arg_names,)*); - self.inner.call_method(#rpc_name, #returns_str, args_tuple) + let args = #args_serialized; + self.inner.call_method(#rpc_name, #returns_str, args) } }; client_methods.push(client_method); diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 81e801628..80c83ab31 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use crate::params_style::ParamStyle; use crate::rpc_attr::RpcMethodAttribute; use quote::quote; use syn::{ @@ -218,7 +219,6 @@ impl RpcMethod { // special args are those which are not passed directly via rpc params: metadata, subscriber let special_args = Self::special_args(¶m_types); param_types.retain(|ty| special_args.iter().find(|(_, sty)| sty == ty).is_none()); - if param_types.len() > TUPLE_FIELD_NAMES.len() { return Err(syn::Error::new_spanned( &self.trait_item, @@ -238,10 +238,14 @@ impl RpcMethod { self.params_with_trailing(trailing_args_num, param_types, tuple_fields) } else if param_types.is_empty() { quote! { let params = params.expect_no_params(); } - } else if self.attr.raw_params { + } else if self.attr.params_style == Some(ParamStyle::Raw) { quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } - } else { + } else if self.attr.params_style == Some(ParamStyle::Positional) { quote! { let params = params.parse::<(#(#param_types, )*)>(); } + } else + /* if self.attr.params_style == Some(ParamStyle::Named) */ + { + unimplemented!("Server side named parameters are not implemented"); } }; diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 469175cdf..f5eff5ee8 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -3,43 +3,133 @@ use jsonrpc_core::{IoHandler, Result}; use jsonrpc_core_client::transports::local; use jsonrpc_derive::rpc; -#[rpc] -pub trait Rpc { - #[rpc(name = "add")] - fn add(&self, a: u64, b: u64) -> Result; +mod client_server { + use super::*; - #[rpc(name = "notify")] - fn notify(&self, foo: u64); + #[rpc(params = "positional")] + pub trait Rpc { + #[rpc(name = "add")] + fn add(&self, a: u64, b: u64) -> Result; + + #[rpc(name = "notify")] + fn notify(&self, foo: u64); + } + + struct RpcServer; + + impl Rpc for RpcServer { + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } + + fn notify(&self, foo: u64) { + println!("received {}", foo); + } + } + + #[test] + fn client_server_roundtrip() { + let mut handler = IoHandler::new(); + handler.extend_with(RpcServer.to_delegate()); + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .add(3, 4) + .and_then(move |res| client.notify(res).map(move |_| res)) + .join(rpc_client) + .map(|(res, ())| { + assert_eq!(res, 7); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } } -struct RpcServer; +mod named_params { + use super::*; + use jsonrpc_core::Params; + use serde_json::json; -impl Rpc for RpcServer { - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) + #[rpc(client, params = "named")] + pub trait Rpc { + #[rpc(name = "call_with_named")] + fn call_with_named(&self, number: u64, string: String, json: Value) -> Result; + + #[rpc(name = "notify", params = "raw")] + fn notify(&self, payload: Value); } - fn notify(&self, foo: u64) { - println!("received {}", foo); + #[test] + fn client_generates_correct_named_params_payload() { + let expected = json!({ // key names are derived from function parameter names in the trait + "number": 3, + "string": String::from("test string"), + "json": { + "key": ["value"] + } + }); + + let mut handler = IoHandler::new(); + handler.add_method("call_with_named", |params: Params| Ok(params.into())); + + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .call_with_named(3, String::from("test string"), json!({"key": ["value"]})) + .and_then(move |res| client.notify(res.clone()).map(move |_| res)) + .join(rpc_client) + .map(move |(res, ())| { + assert_eq!(res, expected); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); } } -#[test] -fn client_server_roundtrip() { - let mut handler = IoHandler::new(); - handler.extend_with(RpcServer.to_delegate()); - let (client, rpc_client) = local::connect::(handler); - let fut = client - .clone() - .add(3, 4) - .and_then(move |res| client.notify(res).map(move |_| res)) - .join(rpc_client) - .map(|(res, ())| { - assert_eq!(res, 7); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); +mod raw_params { + use super::*; + use jsonrpc_core::Params; + use serde_json::json; + + #[rpc(client)] + pub trait Rpc { + #[rpc(name = "call_raw", params = "raw")] + fn call_raw_single_param(&self, params: Value) -> Result; + + #[rpc(name = "notify", params = "raw")] + fn notify(&self, payload: Value); + } + + #[test] + fn client_generates_correct_raw_params_payload() { + let expected = json!({ + "sub_object": { + "key": ["value"] + } }); - tokio::run(fut); + + let mut handler = IoHandler::new(); + handler.add_method("call_raw", |params: Params| Ok(params.into())); + + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .call_raw_single_param(expected.clone()) + .and_then(move |res| client.notify(res.clone()).map(move |_| res)) + .join(rpc_client) + .map(move |(res, ())| { + assert_eq!(res, expected); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } } diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index 632c27a8c..1f4483672 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -27,7 +27,7 @@ pub trait Rpc { fn add(&self, a: u64, b: u64) -> Result; /// Retrieves and debug prints the underlying `Params` object. - #[rpc(name = "raw", raw_params)] + #[rpc(name = "raw", params = "raw")] fn raw(&self, params: Params) -> Result; /// Handles a notification. diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index 9bb48a1c7..6811f0c3e 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,4 +1,4 @@ -error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns' +error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns, params' --> $DIR/attr-invalid-name-values.rs:5:2 | 5 | /// Returns a protocol version diff --git a/derive/tests/ui/attr-named-params-on-server.rs b/derive/tests/ui/attr-named-params-on-server.rs new file mode 100644 index 000000000..074995642 --- /dev/null +++ b/derive/tests/ui/attr-named-params-on-server.rs @@ -0,0 +1,10 @@ +use jsonrpc_derive::rpc; + +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "add", params = "named")] + fn add(&self, a: u32, b: u32) -> Result; +} + +fn main() {} diff --git a/derive/tests/ui/attr-named-params-on-server.stderr b/derive/tests/ui/attr-named-params-on-server.stderr new file mode 100644 index 000000000..41ccc852a --- /dev/null +++ b/derive/tests/ui/attr-named-params-on-server.stderr @@ -0,0 +1,9 @@ +error: `params = "named"` can only be used to generate a client (on a trait annotated with #[rpc(client)]). At this time the server does not support named parameters. + --> $DIR/attr-named-params-on-server.rs:4:1 + | +4 | / pub trait Rpc { +5 | | /// Returns a protocol version +6 | | #[rpc(name = "add", params = "named")] +7 | | fn add(&self, a: u32, b: u32) -> Result; +8 | | } + | |_^ diff --git a/derive/tests/ui/trait-attr-named-params-on-server.rs b/derive/tests/ui/trait-attr-named-params-on-server.rs new file mode 100644 index 000000000..302768fcf --- /dev/null +++ b/derive/tests/ui/trait-attr-named-params-on-server.rs @@ -0,0 +1,7 @@ +use jsonrpc_derive::rpc; + +#[rpc(server, params = "named")] +pub trait Rpc { +} + +fn main() {} diff --git a/derive/tests/ui/trait-attr-named-params-on-server.stderr b/derive/tests/ui/trait-attr-named-params-on-server.stderr new file mode 100644 index 000000000..c44d44465 --- /dev/null +++ b/derive/tests/ui/trait-attr-named-params-on-server.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> $DIR/trait-attr-named-params-on-server.rs:3:1 + | +3 | #[rpc(server, params = "named")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Server code generation only supports `params = "positional"` (default) or `params = "raw" at this time. From 06375c3d31971f177bbf1a11c5d0216b03b106e7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 14 Apr 2020 11:01:43 +0100 Subject: [PATCH 113/149] 14.1.0 bump (#545) --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 18 files changed, 48 insertions(+), 48 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 8b6ccad6c..5848101df 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" categories = [ "asynchronous", @@ -26,7 +26,7 @@ ipc = ["jsonrpc-client-transports/ipc"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "14.0", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "14.1", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index d660c7ce0..355ad5401 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" categories = [ "asynchronous", @@ -38,9 +38,9 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "14.0", path = "../../core" } -jsonrpc-pubsub = { version = "14.0", path = "../../pubsub" } -jsonrpc-server-utils = { version = "14.0", path = "../../server-utils", optional = true } +jsonrpc-core = { version = "14.1", path = "../../core" } +jsonrpc-pubsub = { version = "14.1", path = "../../pubsub" } +jsonrpc-server-utils = { version = "14.1", path = "../../server-utils", optional = true } log = "0.4" parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } @@ -51,8 +51,8 @@ url = "1.7" [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "14.0", path = "../../http" } -jsonrpc-ipc-server = { version = "14.0", path = "../../ipc" } +jsonrpc-http-server = { version = "14.1", path = "../../http" } +jsonrpc-ipc-server = { version = "14.1", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index d5d9dd6a4..56f968672 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 8199f796a..63fe98300 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "=1.0.1" proc-macro-crate = "0.1.4" [dev-dependencies] -jsonrpc-core = { version = "14.0", path = "../core" } -jsonrpc-core-client = { version = "14.0", path = "../core-client" } -jsonrpc-pubsub = { version = "14.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "14.0", path = "../tcp" } +jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-core-client = { version = "14.1", path = "../core-client" } +jsonrpc-pubsub = { version = "14.1", path = "../pubsub" } +jsonrpc-tcp-server = { version = "14.1", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 43f53c8f7..8021fc307 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.6" +version = "14.1.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "14.0", path = "../core" } -jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } +jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index 57d6a1ca5..a713f3b2f 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "14.0" +jsonrpc-http-server = "14.1" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index fe5c86179..ee7d1f795 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.7" +version = "14.1.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "14.0", path = "../core" } -jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } +jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } parity-tokio-ipc = "0.4" parking_lot = "0.10.0" diff --git a/ipc/README.md b/ipc/README.md index e9e5bf6a2..8a9794962 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "14.0" +jsonrpc-ipc-server = "14.1" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index edced41e0..852371f22 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,16 +8,16 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.6" +version = "14.1.0" [dependencies] log = "0.4" parking_lot = "0.10.0" -jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-core = { version = "14.1", path = "../core" } serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "14.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "14.1", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 0010f3593..53ae02c7a 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "14.0", path = "../../core" } -jsonrpc-pubsub = { version = "14.0", path = "../" } -jsonrpc-ws-server = { version = "14.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "14.0", path = "../../ipc" } +jsonrpc-core = { version = "14.1", path = "../../core" } +jsonrpc-pubsub = { version = "14.1", path = "../" } +jsonrpc-ws-server = { version = "14.1", path = "../../ws" } +jsonrpc-ipc-server = { version = "14.1", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 9286c273e..5c9a2aa29 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-core = { version = "14.1", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.1.15" } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index e9d747da3..1b06423d5 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.5" +version = "14.1.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "14.0", path = "../core" } +jsonrpc-core = { version = "14.1", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index 7a737570f..eb6681870 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "14.0" +jsonrpc-stdio-server = "14.1" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 151769f4f..f5624baa4 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.6" +version = "14.1.0" [dependencies] log = "0.4" parking_lot = "0.10.0" tokio-service = "0.1" -jsonrpc-core = { version = "14.0", path = "../core" } -jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } +jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index 12caa7284..3b0581b4d 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "14.0" +jsonrpc-tcp-server = "14.1" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index 241142c32..5a878567d 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.0.5" +version = "14.1.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "14.0", path = "../core" } -jsonrpc-core-client = { version = "14.0", path = "../core-client" } -jsonrpc-pubsub = { version = "14.0", path = "../pubsub" } +jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-core-client = { version = "14.1", path = "../core-client" } +jsonrpc-pubsub = { version = "14.1", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "14.0", path = "../derive" } +jsonrpc-derive = { version = "14.1", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 40999299d..a60171d8d 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.0.6" +version = "14.1.0" [dependencies] -jsonrpc-core = { version = "14.0", path = "../core" } -jsonrpc-server-utils = { version = "14.0", path = "../server-utils" } +jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } log = "0.4" parking_lot = "0.10.0" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index b8211c66d..524e3b147 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "14.0" +jsonrpc-ws-server = "14.1" ``` `main.rs` From eed011e302563e6b209f9513878dfe80100a6630 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 14 Apr 2020 16:43:11 +0100 Subject: [PATCH 114/149] Wait for published version to be available (#546) * Wait for published version to be available * Sleep before publishing so the user has an opportunity to quit --- _automate/publish.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index e8d9829d8..b7fd813cf 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -23,8 +23,17 @@ for crate in ${ORDER[@]}; do cd $crate VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') echo "Publishing $crate@$VERSION" - sleep 5 + sleep 5 # give the user an opportunity to abort before publishing cargo publish $@ || read -p ">>>>> Publishing $crate failed. Press [enter] to continue. " + echo " Waiting for published version $VERSION to be available..." + CRATE_NAME=$(grep "^name" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') + LATEST_VERSION=0 + while [[ $LATEST_VERSION != $VERSION ]] + do + sleep 3 + LATEST_VERSION=$(cargo search "$CRATE_NAME" | grep "^$CRATE_NAME =" | sed -e 's/.*"\(.*\)".*/\1/') + echo " Latest available version: $LATEST_VERSION" + done cd - done From 5f1e693c212da27af0ddfcf2aecbd9f8cebe26a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 22 May 2020 11:51:36 +0200 Subject: [PATCH 115/149] Fix mac builds. (#550) --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f615f46d0..26d1a4967 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,9 +37,9 @@ test-mac-stable: <<: *test_and_build <<: *only tags: - - rust-mac + - osx -# check style +# check style checkstyle-linux-stable: image: parity/rust-builder:latest stage: checkstyle From 0d31c5031082f09fc0ed63846ad1587d531fbb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <123550+andresilva@users.noreply.github.com> Date: Fri, 22 May 2020 10:53:35 +0100 Subject: [PATCH 116/149] hosts: when binding to unspecified address allow requests from localhost (#549) --- server-utils/src/hosts.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/server-utils/src/hosts.rs b/server-utils/src/hosts.rs index 53f7a9702..ab2270ea6 100644 --- a/server-utils/src/hosts.rs +++ b/server-utils/src/hosts.rs @@ -160,11 +160,20 @@ pub fn is_host_valid(host: Option<&str>, allowed_hosts: &Option>) -> b /// Updates given list of hosts with the address. pub fn update(hosts: Option>, address: &SocketAddr) -> Option> { + use std::net::{IpAddr, Ipv4Addr}; + hosts.map(|current_hosts| { let mut new_hosts = current_hosts.into_iter().collect::>(); - let address = address.to_string(); - new_hosts.insert(address.clone().into()); - new_hosts.insert(address.replace("127.0.0.1", "localhost").into()); + let address_string = address.to_string(); + + if address.ip() == IpAddr::V4(Ipv4Addr::UNSPECIFIED) { + new_hosts.insert(address_string.replace("0.0.0.0", "127.0.0.1").into()); + new_hosts.insert(address_string.replace("0.0.0.0", "localhost").into()); + } else if address.ip() == IpAddr::V4(Ipv4Addr::LOCALHOST) { + new_hosts.insert(address_string.replace("127.0.0.1", "localhost").into()); + } + + new_hosts.insert(address_string.into()); new_hosts.into_iter().collect() }) } From 1779dbef95dd5189d73516782e861661b463cd3d Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 27 May 2020 12:31:48 -0400 Subject: [PATCH 117/149] Add a subscription manager (#548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP: Add a subscription manager. The idea is to use the `Subscriptions` struct from Substrate, which is used to drive subscription futures to completion, and modify it for "general" use. * Allow IdProvider::Id and SubscriptionId to work together Adds trait bounds that allow conversion between the two, removing the need for generics in SubscriptionId. * Update SubscriptionId tests * Rustfmt * Use `SubscriptionId` as the key for `active_subscriptions` * Add subscription ID providers. Adds two subscription ID providers which can be used by the SubscriptionManager. One provides a simple numeric ID, while the other provides a random string. * Add some documentation * Clean up comment and naming * Change the NumericIdProvider to use `u64` IDs Instead of providing a guarantee that we can convert between `usize` and `u64` we make the assumptions that it's unlikely that we're running on an architecture larger than 64-bits and we use a `u64` directly. * Add tests for IdProvider and SubscriptionManager Note: There's one test that doesn't pass yet which has to do with the `cancel()` function of the SubscriptionManager. * Restore RandomStringIdProvider as the default provider * Retain receiver.: * Make test executor a lazy static * Rustfmt * Add a comment to test * Remove `matches!` macro Our Windows CI runner isn't up to date and thinks this is still a nightly feature Co-authored-by: Tomasz Drwięga --- pubsub/Cargo.toml | 3 + pubsub/src/lib.rs | 1 + pubsub/src/manager.rs | 385 ++++++++++++++++++++++++++++++++++++++++++ pubsub/src/types.rs | 102 ++++++++--- 4 files changed, 464 insertions(+), 27 deletions(-) create mode 100644 pubsub/src/manager.rs diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 852371f22..f7ccbc046 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -15,9 +15,12 @@ log = "0.4" parking_lot = "0.10.0" jsonrpc-core = { version = "14.1", path = "../core" } serde = "1.0" +rand = "0.7" [dev-dependencies] jsonrpc-tcp-server = { version = "14.1", path = "../tcp" } +futures = { version = "0.3", features = ["compat", "thread-pool"] } +lazy_static = "1.4" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/src/lib.rs b/pubsub/src/lib.rs index a4f74eca5..145304486 100644 --- a/pubsub/src/lib.rs +++ b/pubsub/src/lib.rs @@ -9,6 +9,7 @@ extern crate log; mod delegates; mod handler; +pub mod manager; pub mod oneshot; mod subscription; pub mod typed; diff --git a/pubsub/src/manager.rs b/pubsub/src/manager.rs new file mode 100644 index 000000000..e8e725949 --- /dev/null +++ b/pubsub/src/manager.rs @@ -0,0 +1,385 @@ +//! The SubscriptionManager used to manage subscription based RPCs. +//! +//! The manager provides four main things in terms of functionality: +//! +//! 1. The ability to create unique subscription IDs through the +//! use of the `IdProvider` trait. Two implementations are availble +//! out of the box, a `NumericIdProvider` and a `RandomStringIdProvider`. +//! +//! 2. An executor with which to drive `Future`s to completion. +//! +//! 3. A way to add new subscriptions. Subscriptions should come in the form +//! of a `Stream`. These subscriptions will be transformed into notifications +//! by the manager, which can be consumed by the client. +//! +//! 4. A way to cancel any currently active subscription. + +use std::collections::HashMap; +use std::iter; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; + +use crate::core::futures::sync::oneshot; +use crate::core::futures::{future as future01, Future as Future01}; +use crate::{ + typed::{Sink, Subscriber}, + SubscriptionId, +}; + +use log::{error, warn}; +use parking_lot::Mutex; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; + +/// Alias for an implementation of `futures::future::Executor`. +pub type TaskExecutor = Arc + Send>> + Send + Sync>; + +type ActiveSubscriptions = Arc>>>; + +/// Trait used to provide unique subscription IDs. +pub trait IdProvider { + /// A unique ID used to identify a subscription. + type Id: Default + Into; + + /// Returns the next ID for the subscription. + fn next_id(&self) -> Self::Id; +} + +/// Provides a thread-safe incrementing integer which +/// can be used as a subscription ID. +#[derive(Debug)] +pub struct NumericIdProvider { + current_id: AtomicUsize, +} + +impl NumericIdProvider { + /// Create a new NumericIdProvider. + pub fn new() -> Self { + Default::default() + } + + /// Create a new NumericIdProvider starting from + /// the given ID. + pub fn with_id(id: AtomicUsize) -> Self { + Self { current_id: id } + } +} + +impl IdProvider for NumericIdProvider { + type Id = u64; + + fn next_id(&self) -> Self::Id { + self.current_id.fetch_add(1, Ordering::AcqRel) as u64 + } +} + +impl Default for NumericIdProvider { + fn default() -> Self { + NumericIdProvider { + current_id: AtomicUsize::new(1), + } + } +} + +/// Used to generate random strings for use as +/// subscription IDs. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct RandomStringIdProvider { + len: usize, +} + +impl RandomStringIdProvider { + /// Create a new RandomStringIdProvider. + pub fn new() -> Self { + Default::default() + } + + /// Create a new RandomStringIdProvider, which will generate + /// random id strings of the given length. + pub fn with_len(len: usize) -> Self { + Self { len } + } +} + +impl IdProvider for RandomStringIdProvider { + type Id = String; + + fn next_id(&self) -> Self::Id { + let mut rng = thread_rng(); + let id: String = iter::repeat(()) + .map(|()| rng.sample(Alphanumeric)) + .take(self.len) + .collect(); + id + } +} + +impl Default for RandomStringIdProvider { + fn default() -> Self { + Self { len: 16 } + } +} + +/// Subscriptions manager. +/// +/// Takes care of assigning unique subscription ids and +/// driving the sinks into completion. +#[derive(Clone)] +pub struct SubscriptionManager { + id_provider: I, + active_subscriptions: ActiveSubscriptions, + executor: TaskExecutor, +} + +impl SubscriptionManager { + /// Creates a new SubscriptionManager. + /// + /// Uses `RandomStringIdProvider` as the ID provider. + pub fn new(executor: TaskExecutor) -> Self { + Self { + id_provider: RandomStringIdProvider::default(), + active_subscriptions: Default::default(), + executor, + } + } +} + +impl SubscriptionManager { + /// Creates a new SubscriptionManager with the specified + /// ID provider. + pub fn with_id_provider(id_provider: I, executor: TaskExecutor) -> Self { + Self { + id_provider, + active_subscriptions: Default::default(), + executor, + } + } + + /// Borrows the internal task executor. + /// + /// This can be used to spawn additional tasks on the underlying event loop. + pub fn executor(&self) -> &TaskExecutor { + &self.executor + } + + /// Creates new subscription for given subscriber. + /// + /// Second parameter is a function that converts Subscriber Sink into a Future. + /// This future will be driven to completion by the underlying event loop + pub fn add(&self, subscriber: Subscriber, into_future: G) -> SubscriptionId + where + G: FnOnce(Sink) -> R, + R: future01::IntoFuture, + F: future01::Future + Send + 'static, + { + let id = self.id_provider.next_id(); + let subscription_id: SubscriptionId = id.into(); + if let Ok(sink) = subscriber.assign_id(subscription_id.clone()) { + let (tx, rx) = oneshot::channel(); + let future = into_future(sink) + .into_future() + .select(rx.map_err(|e| warn!("Error timing out: {:?}", e))) + .then(|_| Ok(())); + + self.active_subscriptions.lock().insert(subscription_id.clone(), tx); + if self.executor.execute(Box::new(future)).is_err() { + error!("Failed to spawn RPC subscription task"); + } + } + + subscription_id + } + + /// Cancel subscription. + /// + /// Returns true if subscription existed or false otherwise. + pub fn cancel(&self, id: SubscriptionId) -> bool { + if let Some(tx) = self.active_subscriptions.lock().remove(&id) { + let _ = tx.send(()); + return true; + } + + false + } +} + +impl SubscriptionManager { + /// Creates a new SubscriptionManager. + pub fn with_executor(executor: TaskExecutor) -> Self { + Self { + id_provider: Default::default(), + active_subscriptions: Default::default(), + executor, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::typed::Subscriber; + use futures::{compat::Future01CompatExt, executor, FutureExt}; + use futures::{stream, StreamExt, TryStreamExt}; + + use crate::core::futures::sink::Sink as Sink01; + use crate::core::futures::stream::Stream as Stream01; + + // Executor shared by all tests. + // + // This shared executor is used to prevent `Too many open files` errors + // on systems with a lot of cores. + lazy_static::lazy_static! { + static ref EXECUTOR: executor::ThreadPool = executor::ThreadPool::new() + .expect("Failed to create thread pool executor for tests"); + } + + pub struct TestTaskExecutor; + type Boxed01Future01 = Box + Send + 'static>; + + impl future01::Executor for TestTaskExecutor { + fn execute(&self, future: Boxed01Future01) -> std::result::Result<(), future01::ExecuteError> { + EXECUTOR.spawn_ok(future.compat().map(drop)); + Ok(()) + } + } + + #[test] + fn making_a_numeric_id_provider_works() { + let provider = NumericIdProvider::new(); + let expected_id = 1; + let actual_id = provider.next_id(); + + assert_eq!(actual_id, expected_id); + } + + #[test] + fn default_numeric_id_provider_works() { + let provider: NumericIdProvider = Default::default(); + let expected_id = 1; + let actual_id = provider.next_id(); + + assert_eq!(actual_id, expected_id); + } + + #[test] + fn numeric_id_provider_with_id_works() { + let provider = NumericIdProvider::with_id(AtomicUsize::new(5)); + let expected_id = 5; + let actual_id = provider.next_id(); + + assert_eq!(actual_id, expected_id); + } + + #[test] + fn random_string_provider_returns_id_with_correct_default_len() { + let provider = RandomStringIdProvider::new(); + let expected_len = 16; + let actual_len = provider.next_id().len(); + + assert_eq!(actual_len, expected_len); + } + + #[test] + fn random_string_provider_returns_id_with_correct_user_given_len() { + let expected_len = 10; + let provider = RandomStringIdProvider::with_len(expected_len); + let actual_len = provider.next_id().len(); + + assert_eq!(actual_len, expected_len); + } + + #[test] + fn new_subscription_manager_defaults_to_random_string_provider() { + let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor)); + let subscriber = Subscriber::::new_test("test_subTest").0; + let stream = stream::iter(vec![Ok(1)]).compat(); + + let id = manager.add(subscriber, |sink| { + let stream = stream.map(|res| Ok(res)); + + sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) + }); + + if let SubscriptionId::String(_) = id { + assert!(true) + } else { + assert!(false, "Expected SubscriptionId::String"); + } + } + + #[test] + fn new_subscription_manager_works_with_numeric_id_provider() { + let id_provider = NumericIdProvider::default(); + let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor)); + + let subscriber = Subscriber::::new_test("test_subTest").0; + let stream = stream::iter(vec![Ok(1)]).compat(); + + let id = manager.add(subscriber, |sink| { + let stream = stream.map(|res| Ok(res)); + + sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) + }); + + if let SubscriptionId::Number(_) = id { + assert!(true) + } else { + assert!(false, "Expected SubscriptionId::Number"); + } + } + + #[test] + fn new_subscription_manager_works_with_random_string_provider() { + let id_provider = RandomStringIdProvider::default(); + let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor)); + + let subscriber = Subscriber::::new_test("test_subTest").0; + let stream = stream::iter(vec![Ok(1)]).compat(); + + let id = manager.add(subscriber, |sink| { + let stream = stream.map(|res| Ok(res)); + + sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) + }); + + if let SubscriptionId::String(_) = id { + assert!(true) + } else { + assert!(false, "Expected SubscriptionId::String"); + } + } + + #[test] + fn subscription_is_canceled_if_it_existed() { + let manager = SubscriptionManager::::with_executor(Arc::new(TestTaskExecutor)); + // Need to bind receiver here (unlike the other tests) or else the subscriber + // will think the client has disconnected and not update `active_subscriptions` + let (subscriber, _recv, _) = Subscriber::::new_test("test_subTest"); + + let (mut tx, rx) = futures::channel::mpsc::channel(8); + tx.start_send(1).unwrap(); + let stream = rx.map(|v| Ok::<_, ()>(v)).compat(); + + let id = manager.add(subscriber, |sink| { + let stream = stream.map(|res| Ok(res)); + + sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) + }); + + let is_cancelled = manager.cancel(id); + assert!(is_cancelled); + } + + #[test] + fn subscription_is_not_canceled_because_it_didnt_exist() { + let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor)); + + let id: SubscriptionId = 23u32.into(); + let is_cancelled = manager.cancel(id); + let is_not_cancelled = !is_cancelled; + + assert!(is_not_cancelled); + } +} diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index be5fefc49..9e1437e74 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -35,12 +35,13 @@ impl PubSubMetadata for Option { } /// Unique subscription id. +/// /// NOTE Assigning same id to different requests will cause the previous request to be unsubscribed. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum SubscriptionId { - /// U64 number + /// A numerical ID, represented by a `u64`. Number(u64), - /// String + /// A non-numerical ID, for example a hash. String(String), } @@ -61,12 +62,6 @@ impl From for SubscriptionId { } } -impl From for SubscriptionId { - fn from(other: u64) -> Self { - SubscriptionId::Number(other) - } -} - impl From for core::Value { fn from(sub: SubscriptionId) -> Self { match sub { @@ -76,30 +71,83 @@ impl From for core::Value { } } +macro_rules! impl_from_num { + ($num:ty) => { + impl From<$num> for SubscriptionId { + fn from(other: $num) -> Self { + SubscriptionId::Number(other.into()) + } + } + }; +} + +impl_from_num!(u8); +impl_from_num!(u16); +impl_from_num!(u32); +impl_from_num!(u64); + #[cfg(test)] mod tests { use super::SubscriptionId; use crate::core::Value; #[test] - fn should_convert_between_value_and_subscription_id() { - // given - let val1 = Value::Number(5.into()); - let val2 = Value::String("asdf".into()); - let val3 = Value::Null; - - // when - let res1 = SubscriptionId::parse_value(&val1); - let res2 = SubscriptionId::parse_value(&val2); - let res3 = SubscriptionId::parse_value(&val3); - - // then - assert_eq!(res1, Some(SubscriptionId::Number(5))); - assert_eq!(res2, Some(SubscriptionId::String("asdf".into()))); - assert_eq!(res3, None); - - // and back - assert_eq!(Value::from(res1.unwrap()), val1); - assert_eq!(Value::from(res2.unwrap()), val2); + fn should_convert_between_number_value_and_subscription_id() { + let val = Value::Number(5.into()); + let res = SubscriptionId::parse_value(&val); + + assert_eq!(res, Some(SubscriptionId::Number(5))); + assert_eq!(Value::from(res.unwrap()), val); + } + + #[test] + fn should_convert_between_string_value_and_subscription_id() { + let val = Value::String("asdf".into()); + let res = SubscriptionId::parse_value(&val); + + assert_eq!(res, Some(SubscriptionId::String("asdf".into()))); + assert_eq!(Value::from(res.unwrap()), val); + } + + #[test] + fn should_convert_between_null_value_and_subscription_id() { + let val = Value::Null; + let res = SubscriptionId::parse_value(&val); + assert_eq!(res, None); + } + + #[test] + fn should_convert_from_u8_to_subscription_id() { + let val = 5u8; + let res: SubscriptionId = val.into(); + assert_eq!(res, SubscriptionId::Number(5)); + } + + #[test] + fn should_convert_from_u16_to_subscription_id() { + let val = 5u16; + let res: SubscriptionId = val.into(); + assert_eq!(res, SubscriptionId::Number(5)); + } + + #[test] + fn should_convert_from_u32_to_subscription_id() { + let val = 5u32; + let res: SubscriptionId = val.into(); + assert_eq!(res, SubscriptionId::Number(5)); + } + + #[test] + fn should_convert_from_u64_to_subscription_id() { + let val = 5u64; + let res: SubscriptionId = val.into(); + assert_eq!(res, SubscriptionId::Number(5)); + } + + #[test] + fn should_convert_from_string_to_subscription_id() { + let val = "String".to_string(); + let res: SubscriptionId = val.into(); + assert_eq!(res, SubscriptionId::String("String".to_string())); } } From e2569b96be12403ff1b7dbea332aa975d64503e2 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Tue, 2 Jun 2020 04:34:46 -0400 Subject: [PATCH 118/149] Place NumericIdProvider's Id behind an Arc (#554) Allows the Id to be moved safely between threads. Also has the advantage of allowing NumericIdProvider to derive `Clone`. --- pubsub/src/manager.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pubsub/src/manager.rs b/pubsub/src/manager.rs index e8e725949..bb1af8bf1 100644 --- a/pubsub/src/manager.rs +++ b/pubsub/src/manager.rs @@ -49,9 +49,9 @@ pub trait IdProvider { /// Provides a thread-safe incrementing integer which /// can be used as a subscription ID. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct NumericIdProvider { - current_id: AtomicUsize, + current_id: Arc, } impl NumericIdProvider { @@ -63,7 +63,9 @@ impl NumericIdProvider { /// Create a new NumericIdProvider starting from /// the given ID. pub fn with_id(id: AtomicUsize) -> Self { - Self { current_id: id } + Self { + current_id: Arc::new(id), + } } } @@ -78,7 +80,7 @@ impl IdProvider for NumericIdProvider { impl Default for NumericIdProvider { fn default() -> Self { NumericIdProvider { - current_id: AtomicUsize::new(1), + current_id: Arc::new(AtomicUsize::new(1)), } } } From ef92a481d76802da489f0f57dfdf4d459d7f7f76 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Tue, 2 Jun 2020 04:50:29 -0400 Subject: [PATCH 119/149] Use matches! macro (#553) Apparently our Windows runner has been updated, so this should work now. --- pubsub/src/manager.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/pubsub/src/manager.rs b/pubsub/src/manager.rs index bb1af8bf1..3c8d77ba2 100644 --- a/pubsub/src/manager.rs +++ b/pubsub/src/manager.rs @@ -304,11 +304,7 @@ mod tests { sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) }); - if let SubscriptionId::String(_) = id { - assert!(true) - } else { - assert!(false, "Expected SubscriptionId::String"); - } + assert!(matches!(id, SubscriptionId::String(_))) } #[test] @@ -325,11 +321,7 @@ mod tests { sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) }); - if let SubscriptionId::Number(_) = id { - assert!(true) - } else { - assert!(false, "Expected SubscriptionId::Number"); - } + assert!(matches!(id, SubscriptionId::Number(_))) } #[test] @@ -346,11 +338,7 @@ mod tests { sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) }); - if let SubscriptionId::String(_) = id { - assert!(true) - } else { - assert!(false, "Expected SubscriptionId::String"); - } + assert!(matches!(id, SubscriptionId::String(_))) } #[test] From e4548ba98293d9f9f9e1476a7482e5931c77fbf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 3 Jun 2020 12:50:26 +0200 Subject: [PATCH 120/149] Bump version. (#556) --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- http/README.md | 2 +- ipc/Cargo.toml | 6 +++--- ipc/README.md | 2 +- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- stdio/README.md | 2 +- tcp/Cargo.toml | 6 +++--- tcp/README.md | 2 +- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- ws/README.md | 2 +- 18 files changed, 48 insertions(+), 48 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 5848101df..fccda664a 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" categories = [ "asynchronous", @@ -26,7 +26,7 @@ ipc = ["jsonrpc-client-transports/ipc"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "14.1", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "14.2", path = "./transports", default-features = false } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 355ad5401..16b97f04c 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" categories = [ "asynchronous", @@ -38,9 +38,9 @@ failure = "0.1" futures = "0.1.26" hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "14.1", path = "../../core" } -jsonrpc-pubsub = { version = "14.1", path = "../../pubsub" } -jsonrpc-server-utils = { version = "14.1", path = "../../server-utils", optional = true } +jsonrpc-core = { version = "14.2", path = "../../core" } +jsonrpc-pubsub = { version = "14.2", path = "../../pubsub" } +jsonrpc-server-utils = { version = "14.2", path = "../../server-utils", optional = true } log = "0.4" parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } @@ -51,8 +51,8 @@ url = "1.7" [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "14.1", path = "../../http" } -jsonrpc-ipc-server = { version = "14.1", path = "../../ipc" } +jsonrpc-http-server = { version = "14.2", path = "../../http" } +jsonrpc-ipc-server = { version = "14.2", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" tokio = "0.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index 56f968672..f4bd56fec 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 63fe98300..c1009a064 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [lib] proc-macro = true @@ -19,10 +19,10 @@ quote = "=1.0.1" proc-macro-crate = "0.1.4" [dev-dependencies] -jsonrpc-core = { version = "14.1", path = "../core" } -jsonrpc-core-client = { version = "14.1", path = "../core-client" } -jsonrpc-pubsub = { version = "14.1", path = "../pubsub" } -jsonrpc-tcp-server = { version = "14.1", path = "../tcp" } +jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-core-client = { version = "14.2", path = "../core-client" } +jsonrpc-pubsub = { version = "14.2", path = "../pubsub" } +jsonrpc-tcp-server = { version = "14.2", path = "../tcp" } futures = "~0.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 8021fc307..7474084c6 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] hyper = "0.12" -jsonrpc-core = { version = "14.1", path = "../core" } -jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } +jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } log = "0.4" net2 = "0.2" unicase = "2.0" diff --git a/http/README.md b/http/README.md index a713f3b2f..257704042 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "14.1" +jsonrpc-http-server = "14.2" ``` `main.rs` diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index ee7d1f795..00ada6ec4 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "14.1", path = "../core" } -jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } +jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } parity-tokio-ipc = "0.4" parking_lot = "0.10.0" diff --git a/ipc/README.md b/ipc/README.md index 8a9794962..60561eb77 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "14.1" +jsonrpc-ipc-server = "14.2" ``` `main.rs` diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index f7ccbc046..589dd367d 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,17 +8,17 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] log = "0.4" parking_lot = "0.10.0" -jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-core = { version = "14.2", path = "../core" } serde = "1.0" rand = "0.7" [dev-dependencies] -jsonrpc-tcp-server = { version = "14.1", path = "../tcp" } +jsonrpc-tcp-server = { version = "14.2", path = "../tcp" } futures = { version = "0.3", features = ["compat", "thread-pool"] } lazy_static = "1.4" diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 53ae02c7a..bae1e4eb4 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "14.1", path = "../../core" } -jsonrpc-pubsub = { version = "14.1", path = "../" } -jsonrpc-ws-server = { version = "14.1", path = "../../ws" } -jsonrpc-ipc-server = { version = "14.1", path = "../../ipc" } +jsonrpc-core = { version = "14.2", path = "../../core" } +jsonrpc-pubsub = { version = "14.2", path = "../" } +jsonrpc-ws-server = { version = "14.2", path = "../../ws" } +jsonrpc-ipc-server = { version = "14.2", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 5c9a2aa29..72dcf1f11 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] bytes = "0.4" globset = "0.4" -jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-core = { version = "14.2", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.1.15" } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 1b06423d5..72bda0643 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] futures = "0.1.23" -jsonrpc-core = { version = "14.1", path = "../core" } +jsonrpc-core = { version = "14.2", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index eb6681870..c2660dfef 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "14.1" +jsonrpc-stdio-server = "14.2" ``` `main.rs` diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index f5624baa4..0a76fa921 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] log = "0.4" parking_lot = "0.10.0" tokio-service = "0.1" -jsonrpc-core = { version = "14.1", path = "../core" } -jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } +jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index 3b0581b4d..81fac2166 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "14.1" +jsonrpc-tcp-server = "14.2" ``` `main.rs` diff --git a/test/Cargo.toml b/test/Cargo.toml index 5a878567d..679ba3953 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.1.0" +version = "14.2.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "14.1", path = "../core" } -jsonrpc-core-client = { version = "14.1", path = "../core-client" } -jsonrpc-pubsub = { version = "14.1", path = "../pubsub" } +jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-core-client = { version = "14.2", path = "../core-client" } +jsonrpc-pubsub = { version = "14.2", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "14.1", path = "../derive" } +jsonrpc-derive = { version = "14.2", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index a60171d8d..0c9e0e627 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.1.0" +version = "14.2.0" [dependencies] -jsonrpc-core = { version = "14.1", path = "../core" } -jsonrpc-server-utils = { version = "14.1", path = "../server-utils" } +jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } log = "0.4" parking_lot = "0.10.0" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 524e3b147..948056bc7 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "14.1" +jsonrpc-ws-server = "14.2" ``` `main.rs` From a615225ea5b8c8c24fd68ffa77a76c3aa25d8f63 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 3 Jun 2020 12:09:57 -0400 Subject: [PATCH 121/149] Bump quote crate version (#558) There was an issue with quote v1.0.2 which is why we were pinned to v1.0.1. This issue has been fixed so we don't need to be pinned anymore. --- derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index c1009a064..aa76fc322 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -15,7 +15,7 @@ proc-macro = true [dependencies] syn = { version = "1.0", features = ["full", "extra-traits", "visit", "fold"] } proc-macro2 = "1.0" -quote = "=1.0.1" +quote = "1.0.6" proc-macro-crate = "0.1.4" [dev-dependencies] From bf8c33c042fe0862e9e2c57df60323b1cbb9f816 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 3 Jun 2020 13:36:37 -0400 Subject: [PATCH 122/149] Bump `derive` version to v14.2.1 (#559) * Bump version to v14.2.1 * Revert "Bump version to v14.2.1" This reverts commit e274975cff03b6212ddacf51dee942062ce8151a. * Only bump `derive` to v14.2.1 --- derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index aa76fc322..1d94d78f9 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "14.2.1" [lib] proc-macro = true From dfa6b98304f52a8fa9e3c4d03f68152dbb770f1d Mon Sep 17 00:00:00 2001 From: Denis Pisarev Date: Wed, 10 Jun 2020 15:45:10 +0200 Subject: [PATCH 123/149] change (ci): some optimizations; new image (#560) --- .gitlab-ci.yml | 63 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 26d1a4967..eef0e83ac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,12 @@ stages: - checkstyle - test +variables: &default-vars + GIT_STRATEGY: fetch + GIT_DEPTH: 100 + CARGO_INCREMENTAL: 0 -.test_and_build: &test_and_build +.test_and_build: &test_and_build script: - cargo build --all - cargo test --all @@ -16,37 +20,52 @@ stages: - web - /^[0-9]+$/ +.docker-env: &docker-env + image: paritytech/ci-linux:production + before_script: + - rustup show + - cargo --version + - sccache -s + variables: + <<: *default-vars + CARGO_TARGET_DIR: "/ci-cache/${CI_PROJECT_NAME}/targets/${CI_COMMIT_REF_NAME}/${CI_JOB_NAME}" + retry: + max: 2 + when: + - runner_system_failure + - unknown_failure + - api_failure + interruptible: true + tags: + - linux-docker + +# check style +checkstyle-linux-stable: + stage: checkstyle + <<: *only + <<: *docker-env + script: + - rustup component add rustfmt + - cargo fmt --all -- --check + allow_failure: true + # test rust stable test-linux-stable: - image: parity/rust-builder:latest stage: test - <<: *test_and_build - <<: *only - tags: - - linux-docker + <<: *docker-env + <<: *only + <<: *test_and_build test-windows-stable: stage: test - <<: *test_and_build - <<: *only + <<: *test_and_build + <<: *only tags: - rust-windows test-mac-stable: stage: test - <<: *test_and_build - <<: *only + <<: *test_and_build + <<: *only tags: - osx - -# check style -checkstyle-linux-stable: - image: parity/rust-builder:latest - stage: checkstyle - script: - - rustup component add rustfmt - - cargo fmt --all -- --check - allow_failure: true - <<: *only - tags: - - linux-docker From c056be4ca44e8fe5aa4715b5ae4d65b413544a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 22 Jul 2020 13:27:34 +0200 Subject: [PATCH 124/149] Allow skipping and retrying publishing. (#557) * Allow skipping and retrying publishing. * Update _automate/publish.sh Co-authored-by: David * Even more idempotenter. * Apply suggestions from code review Co-authored-by: David * Fetch tags before creating. * Replace main with jsonrpc Co-authored-by: David --- _automate/publish.sh | 115 +++++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 27 deletions(-) diff --git a/_automate/publish.sh b/_automate/publish.sh index b7fd813cf..53b9a8a71 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -4,11 +4,22 @@ set -eu ORDER=(core server-utils tcp ws http ipc stdio pubsub core-client/transports core-client derive test) -# First display the plan -for crate in ${ORDER[@]}; do - cd $crate > /dev/null +function read_toml () { + NAME="" + VERSION="" + NAME=$(grep "^name" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') - echo "$crate@$VERSION" +} +function remote_version () { + REMOTE_VERSION="" + REMOTE_VERSION=$(cargo search "$NAME" | grep "^$NAME =" | sed -e 's/.*"\(.*\)".*/\1/') +} + +# First display the plan +for CRATE_DIR in ${ORDER[@]}; do + cd $CRATE_DIR > /dev/null + read_toml + echo "$NAME@$VERSION" cd - > /dev/null done @@ -18,37 +29,87 @@ set -x cargo clean +set +x + # Then actually perform publishing. -for crate in ${ORDER[@]}; do - cd $crate - VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') - echo "Publishing $crate@$VERSION" - sleep 5 # give the user an opportunity to abort before publishing - cargo publish $@ || read -p ">>>>> Publishing $crate failed. Press [enter] to continue. " - echo " Waiting for published version $VERSION to be available..." - CRATE_NAME=$(grep "^name" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') - LATEST_VERSION=0 - while [[ $LATEST_VERSION != $VERSION ]] - do - sleep 3 - LATEST_VERSION=$(cargo search "$CRATE_NAME" | grep "^$CRATE_NAME =" | sed -e 's/.*"\(.*\)".*/\1/') - echo " Latest available version: $LATEST_VERSION" +for CRATE_DIR in ${ORDER[@]}; do + cd $CRATE_DIR > /dev/null + read_toml + remote_version + # Seems the latest version matches, skip by default. + if [ "$REMOTE_VERSION" = "$VERSION" ] || [[ "$REMOTE_VERSION" > "$VERSION" ]]; then + RET="" + echo "Seems like $NAME@$REMOTE_VERSION is already published. Continuing in 5s. " + read -t 5 -p ">>>> Type [r][enter] to retry, or [enter] to continue... " RET || true + if [ "$RET" != "r" ]; then + echo "Skipping $NAME@$VERSION" + cd - > /dev/null + continue + fi + fi + + # Attempt to publish (allow retries) + while : ; do + # give the user an opportunity to abort or skip before publishing + echo "🚀 Publishing $NAME@$VERSION..." + sleep 3 + + set +e && set -x + cargo publish $@ + RES=$? + set +x && set -e + # Check if it succeeded + if [ "$RES" != "0" ]; then + CHOICE="" + echo "##### Publishing $NAME failed" + read -p ">>>>> Type [s][enter] to skip, or [enter] to retry.. " CHOICE + if [ "$CHOICE" = "s" ]; then + break + fi + fi + done + + # Wait again to make sure that the new version is published and available. + echo "Waiting for $NAME@$VERSION to become available at the registry..." + while : ; do + sleep 3 + remote_version + if [ "$REMOTE_VERSION" = "$VERSION" ]; then + echo "🥳 $NAME@$VERSION published succesfully." + sleep 3 + break + else + echo "#### Got $NAME@$REMOTE_VERSION but expected $NAME@$VERSION. Retrying..." + fi done - cd - + cd - > /dev/null done # Make tags in one go -for crate in ${ORDER[@]}; do - cd $crate - VERSION=$(grep "^version" ./Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') - echo "Tagging $crate@$VERSION" - git tag -a "$crate-$VERSION" -m "$crate $VERSION" || true - cd - +set -x +git fetch --tags +set +x + +for CRATE_DIR in ${ORDER[@]}; do + cd $CRATE_DIR > /dev/null + read_toml + echo "Tagging $NAME@$VERSION" + set -x + git tag -a "$NAME-$VERSION" -m "$NAME $VERSION" || true + set +x + cd - > /dev/null done +set -x +sleep 3 git push --tags +set +x -VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/') -echo "Tagging main $VERSION" +cd core > /dev/null +read_toml +cd - > /dev/null +echo "Tagging jsonrpc@$VERSION" +set -x git tag -a v$VERSION -m "Version $VERSION" +sleep 3 git push --tags From 57575efe9c220c7e7c0addf3c90e9577e3296c80 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Mon, 27 Jul 2020 23:12:36 +0800 Subject: [PATCH 125/149] Add missing user specified trait bounds in [rpc] macro (#569) * Add missing user specified trait bounds in [rpc] Fix https://github.com/paritytech/jsonrpc/issues/567 * Nit * Add a test file that should pass the compilation --- derive/src/to_delegate.rs | 16 ++++++++ .../client_with_generic_trait_bounds.rs | 39 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 derive/tests/run-pass/client_with_generic_trait_bounds.rs diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 80c83ab31..8c77dcded 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -458,6 +458,8 @@ pub fn generate_where_clause_serialization_predicates( let mut visitor = FindTyParams::default(); visitor.visit_item_trait(item_trait); + let additional_where_clause = item_trait.generics.where_clause.clone(); + item_trait .generics .type_params() @@ -483,6 +485,20 @@ pub fn generate_where_clause_serialization_predicates( bounds.push(parse_quote!(_jsonrpc_core::serde::de::DeserializeOwned)) } } + + // add the trait bounds specified by the user in where clause. + if let Some(ref where_clause) = additional_where_clause { + for predicate in where_clause.predicates.iter() { + if let syn::WherePredicate::Type(where_ty) = predicate { + if let syn::Type::Path(ref predicate) = where_ty.bounded_ty { + if *predicate == ty_path { + bounds.extend(where_ty.bounds.clone().into_iter()); + } + } + } + } + } + syn::WherePredicate::Type(syn::PredicateType { lifetimes: None, bounded_ty: syn::Type::Path(ty_path), diff --git a/derive/tests/run-pass/client_with_generic_trait_bounds.rs b/derive/tests/run-pass/client_with_generic_trait_bounds.rs new file mode 100644 index 000000000..196f189ba --- /dev/null +++ b/derive/tests/run-pass/client_with_generic_trait_bounds.rs @@ -0,0 +1,39 @@ +use jsonrpc_core::futures::future::{self, FutureResult}; +use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_derive::rpc; +use std::collections::BTreeMap; + +#[rpc] +pub trait Rpc +where + One: Ord, + Two: Ord + Eq, +{ + /// Adds two numbers and returns a result + #[rpc(name = "setTwo")] + fn set_two(&self, a: Two) -> Result>; + + /// Performs asynchronous operation + #[rpc(name = "beFancy")] + fn call(&self, a: One) -> FutureResult<(One, Two), Error>; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn set_two(&self, x: String) -> Result> { + println!("{}", x); + Ok(Default::default()) + } + + fn call(&self, num: u64) -> FutureResult<(u64, String), Error> { + crate::future::finished((num + 999, "hello".into())) + } +} + +fn main() { + let mut io = IoHandler::new(); + let rpc = RpcImpl; + + io.extend_with(rpc.to_delegate()) +} From 99bd064d8f0c08aaa0626f7547e635009d1e8e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Jul 2020 12:48:10 +0200 Subject: [PATCH 126/149] Migrate core to futures=0.3 (#565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update to latest futures. * Rewrite core? * pubsub rewrite. * Fix pubsub tests & rewrite tcp. * Update stdio. * ipc & stdio & ws * Cargo check ✅ * Fixing tests 🤔 * Unify Result/BoxFuture for derive. * Rewrite clients for new futures. * Fix derive. * Fix client tests. * Remove unused functions. * Bump version. * cargo fmt --all * Addressing review grumbles. * Rewrite with async-await. * Add a issue number. * cargo fmt --all * Fix test. --- core-client/Cargo.toml | 15 +- core-client/src/lib.rs | 4 + core-client/transports/Cargo.toml | 29 +- core-client/transports/src/lib.rs | 297 ++++++++---------- .../transports/src/transports/duplex.rs | 152 +++++---- core-client/transports/src/transports/http.rs | 227 +++++-------- core-client/transports/src/transports/ipc.rs | 35 ++- .../transports/src/transports/local.rs | 124 +++++--- core-client/transports/src/transports/ws.rs | 18 +- core/Cargo.toml | 4 +- core/examples/async.rs | 17 +- core/examples/basic.rs | 2 +- core/examples/meta.rs | 5 +- core/examples/middlewares.rs | 10 +- core/examples/params.rs | 2 +- core/src/calls.rs | 58 +++- core/src/delegates.rs | 22 +- core/src/io.rs | 86 ++--- core/src/lib.rs | 20 +- core/src/middleware.rs | 35 ++- derive/Cargo.toml | 13 +- derive/examples/generic-trait-bounds.rs | 9 +- derive/examples/generic-trait.rs | 9 +- derive/examples/meta-macros.rs | 17 +- derive/examples/pubsub-macros.rs | 3 +- derive/examples/std.rs | 21 +- derive/src/lib.rs | 34 +- derive/src/rpc_trait.rs | 2 + derive/src/to_client.rs | 59 ++-- derive/src/to_delegate.rs | 19 +- derive/tests/client.rs | 53 ++-- derive/tests/pubsub-macros.rs | 4 +- derive/tests/run-pass/client_only.rs | 8 +- .../client_with_generic_trait_bounds.rs | 10 +- http/Cargo.toml | 10 +- http/README.md | 2 +- http/examples/http_async.rs | 2 +- http/examples/http_meta.rs | 4 +- http/examples/http_middleware.rs | 2 +- http/examples/server.rs | 2 +- http/src/handler.rs | 111 +++---- http/src/lib.rs | 33 +- http/src/tests.rs | 15 +- ipc/Cargo.toml | 8 +- ipc/README.md | 2 +- ipc/examples/ipc.rs | 2 +- ipc/src/meta.rs | 4 +- ipc/src/select_with_weak.rs | 4 +- ipc/src/server.rs | 82 ++--- pubsub/Cargo.toml | 14 +- pubsub/examples/pubsub.rs | 8 +- pubsub/examples/pubsub_simple.rs | 8 +- pubsub/more-examples/Cargo.toml | 10 +- pubsub/more-examples/examples/pubsub_ipc.rs | 12 +- pubsub/more-examples/examples/pubsub_ws.rs | 19 +- pubsub/src/delegates.rs | 21 +- pubsub/src/handler.rs | 15 +- pubsub/src/manager.rs | 82 +++-- pubsub/src/oneshot.rs | 27 +- pubsub/src/subscription.rs | 137 ++++---- pubsub/src/typed.rs | 67 ++-- pubsub/src/types.rs | 8 +- server-utils/Cargo.toml | 5 +- server-utils/src/lib.rs | 2 - server-utils/src/reactor.rs | 8 +- stdio/Cargo.toml | 6 +- stdio/README.md | 2 +- stdio/examples/stdio.rs | 2 +- stdio/src/lib.rs | 44 +-- tcp/Cargo.toml | 9 +- tcp/README.md | 2 +- tcp/examples/tcp.rs | 2 +- tcp/src/dispatch.rs | 22 +- tcp/src/lib.rs | 2 +- tcp/src/meta.rs | 5 +- tcp/src/server.rs | 19 +- tcp/src/service.rs | 18 +- tcp/src/tests.rs | 14 +- test/Cargo.toml | 10 +- test/src/lib.rs | 6 +- ws/Cargo.toml | 8 +- ws/README.md | 2 +- ws/examples/ws.rs | 2 +- ws/src/metadata.rs | 32 +- ws/src/server.rs | 6 +- ws/src/server_builder.rs | 12 +- ws/src/session.rs | 24 +- ws/src/tests.rs | 14 +- 88 files changed, 1211 insertions(+), 1201 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index fccda664a..d21f13b94 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" categories = [ "asynchronous", @@ -19,14 +19,17 @@ categories = [ ] [features] -tls = ["jsonrpc-client-transports/tls"] -http = ["jsonrpc-client-transports/http"] -ws = ["jsonrpc-client-transports/ws"] -ipc = ["jsonrpc-client-transports/ipc"] +tls = ["jsonrpc-client-transports/tls", "futures01"] +http = ["jsonrpc-client-transports/http", "futures01"] +ws = ["jsonrpc-client-transports/ws", "futures01"] +ipc = ["jsonrpc-client-transports/ipc", "futures01"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "14.2", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "15.0", path = "./transports", default-features = false } +# Only for client transports, should be removed when we fully transition to futures=0.3 +futures01 = { version = "0.1", package = "futures", optional = true } +futures = { version = "0.3", features = [ "compat" ] } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index b35f33af8..c7294c871 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -7,4 +7,8 @@ #![deny(missing_docs)] +pub use futures; pub use jsonrpc_client_transports::*; + +#[cfg(feature = "futures01")] +pub use futures01; diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 16b97f04c..6e19608a8 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" categories = [ "asynchronous", @@ -21,41 +21,44 @@ categories = [ [features] default = ["http", "tls", "ws"] tls = ["hyper-tls", "http"] -http = ["hyper"] +http = ["hyper", "futures01"] ws = [ "websocket", "tokio", + "futures01", ] ipc = [ "parity-tokio-ipc", "jsonrpc-server-utils", "tokio", + "futures01", ] arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dependencies] failure = "0.1" -futures = "0.1.26" -hyper = { version = "0.12", optional = true } -hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-core = { version = "14.2", path = "../../core" } -jsonrpc-pubsub = { version = "14.2", path = "../../pubsub" } -jsonrpc-server-utils = { version = "14.2", path = "../../server-utils", optional = true } +futures = { version = "0.3", features = [ "compat" ] } +jsonrpc-core = { version = "15.0", path = "../../core" } +jsonrpc-pubsub = { version = "15.0", path = "../../pubsub" } log = "0.4" -parity-tokio-ipc = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +url = "1.7" + +futures01 = { version = "0.1.26", package = "futures", optional = true } +hyper = { version = "0.12", optional = true } +hyper-tls = { version = "0.3.2", optional = true } +jsonrpc-server-utils = { version = "15.0", path = "../../server-utils", optional = true } +parity-tokio-ipc = { version = "0.2", optional = true } tokio = { version = "0.1", optional = true } websocket = { version = "0.24", optional = true } -url = "1.7" [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "14.2", path = "../../http" } -jsonrpc-ipc-server = { version = "14.2", path = "../../ipc" } +jsonrpc-http-server = { version = "15.0", path = "../../http" } +jsonrpc-ipc-server = { version = "15.0", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" -tokio = "0.1" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master" } diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index b6da7c7e2..c8cfd2cef 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -3,13 +3,18 @@ #![deny(missing_docs)] use failure::{format_err, Fail}; -use futures::sync::{mpsc, oneshot}; -use futures::{future, prelude::*}; +use jsonrpc_core::futures::channel::{mpsc, oneshot}; +use jsonrpc_core::futures::{ + self, + task::{Context, Poll}, + Future, Stream, StreamExt, +}; use jsonrpc_core::{Error, Params}; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::Value; use std::marker::PhantomData; +use std::pin::Pin; pub mod transports; @@ -39,6 +44,9 @@ impl From for RpcError { } } +/// A result returned by the client. +pub type RpcResult = Result; + /// An RPC call message. struct CallMessage { /// The RPC method name. @@ -47,7 +55,7 @@ struct CallMessage { params: Params, /// The oneshot channel to send the result of the rpc /// call to. - sender: oneshot::Sender>, + sender: oneshot::Sender>, } /// An RPC notification. @@ -75,7 +83,7 @@ struct SubscribeMessage { /// The subscription to subscribe to. subscription: Subscription, /// The channel to send notifications to. - sender: mpsc::Sender>, + sender: mpsc::UnboundedSender>, } /// A message sent to the `RpcClient`. @@ -108,76 +116,25 @@ impl From for RpcMessage { /// A channel to a `RpcClient`. #[derive(Clone)] -pub struct RpcChannel(mpsc::Sender); +pub struct RpcChannel(mpsc::UnboundedSender); impl RpcChannel { - fn send( - &self, - msg: RpcMessage, - ) -> impl Future, Error = mpsc::SendError> { - self.0.to_owned().send(msg) + fn send(&self, msg: RpcMessage) -> Result<(), mpsc::TrySendError> { + self.0.unbounded_send(msg) } } -impl From> for RpcChannel { - fn from(sender: mpsc::Sender) -> Self { +impl From> for RpcChannel { + fn from(sender: mpsc::UnboundedSender) -> Self { RpcChannel(sender) } } /// The future returned by the rpc call. -pub struct RpcFuture { - recv: oneshot::Receiver>, -} - -impl RpcFuture { - /// Creates a new `RpcFuture`. - pub fn new(recv: oneshot::Receiver>) -> Self { - RpcFuture { recv } - } -} - -impl Future for RpcFuture { - type Item = Value; - type Error = RpcError; - - fn poll(&mut self) -> Result, Self::Error> { - // TODO should timeout (#410) - match self.recv.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(error))) => Err(error), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(error) => Err(RpcError::Other(error.into())), - } - } -} +pub type RpcFuture = oneshot::Receiver>; /// The stream returned by a subscribe. -pub struct SubscriptionStream { - recv: mpsc::Receiver>, -} - -impl SubscriptionStream { - /// Crates a new `SubscriptionStream`. - pub fn new(recv: mpsc::Receiver>) -> Self { - SubscriptionStream { recv } - } -} - -impl Stream for SubscriptionStream { - type Item = Value; - type Error = RpcError; - - fn poll(&mut self) -> Result>, Self::Error> { - match self.recv.poll() { - Ok(Async::Ready(Some(Ok(value)))) => Ok(Async::Ready(Some(value))), - Ok(Async::Ready(Some(Err(error)))) => Err(error), - Ok(Async::Ready(None)) => Ok(Async::Ready(None)), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(()) => Err(RpcError::Other(format_err!("mpsc channel returned an error."))), - } - } -} +pub type SubscriptionStream = mpsc::UnboundedReceiver>; /// A typed subscription stream. pub struct TypedSubscriptionStream { @@ -197,19 +154,20 @@ impl TypedSubscriptionStream { } } -impl Stream for TypedSubscriptionStream { - type Item = T; - type Error = RpcError; - - fn poll(&mut self) -> Result>, Self::Error> { - let result = match self.stream.poll()? { - Async::Ready(Some(value)) => serde_json::from_value::(value) - .map(|result| Async::Ready(Some(result))) - .map_err(|error| RpcError::ParseError(self.returns.into(), error.into()))?, - Async::Ready(None) => Async::Ready(None), - Async::NotReady => Async::NotReady, - }; - Ok(result) +impl Stream for TypedSubscriptionStream { + type Item = RpcResult; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let result = futures::ready!(self.stream.poll_next_unpin(cx)); + match result { + Some(Ok(value)) => Some( + serde_json::from_value::(value) + .map_err(|error| RpcError::ParseError(self.returns.into(), error.into())), + ), + None => None, + Some(Err(err)) => Some(Err(err.into())), + } + .into() } } @@ -225,29 +183,31 @@ impl From for RawClient { impl RawClient { /// Call RPC method with raw JSON. - pub fn call_method(&self, method: &str, params: Params) -> impl Future { + pub fn call_method(&self, method: &str, params: Params) -> impl Future> { let (sender, receiver) = oneshot::channel(); let msg = CallMessage { method: method.into(), params, sender, }; - self.0 - .send(msg.into()) - .map_err(|error| RpcError::Other(error.into())) - .and_then(|_| RpcFuture::new(receiver)) + let result = self.0.send(msg.into()); + async move { + let () = result.map_err(|e| RpcError::Other(e.into()))?; + + receiver.await.map_err(|e| RpcError::Other(e.into()))? + } } /// Send RPC notification with raw JSON. - pub fn notify(&self, method: &str, params: Params) -> impl Future { + pub fn notify(&self, method: &str, params: Params) -> RpcResult<()> { let msg = NotifyMessage { method: method.into(), params, }; - self.0 - .send(msg.into()) - .map(|_| ()) - .map_err(|error| RpcError::Other(error.into())) + match self.0.send(msg.into()) { + Ok(()) => Ok(()), + Err(error) => Err(RpcError::Other(error.into())), + } } /// Subscribe to topic with raw JSON. @@ -257,8 +217,8 @@ impl RawClient { subscribe_params: Params, notification: &str, unsubscribe: &str, - ) -> impl Future { - let (sender, receiver) = mpsc::channel(0); + ) -> RpcResult { + let (sender, receiver) = mpsc::unbounded(); let msg = SubscribeMessage { subscription: Subscription { subscribe: subscribe.into(), @@ -268,10 +228,11 @@ impl RawClient { }, sender, }; + self.0 .send(msg.into()) - .map_err(|error| RpcError::Other(error.into())) - .map(|_| SubscriptionStream::new(receiver)) + .map(|()| receiver) + .map_err(|e| RpcError::Other(e.into())) } } @@ -292,48 +253,49 @@ impl TypedClient { } /// Call RPC with serialization of request and deserialization of response. - pub fn call_method( + pub fn call_method( &self, method: &str, - returns: &'static str, + returns: &str, args: T, - ) -> impl Future { + ) -> impl Future> { + let returns = returns.to_owned(); let args = serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC"); let params = match args { - Value::Array(vec) => Params::Array(vec), - Value::Null => Params::None, - Value::Object(map) => Params::Map(map), - _ => { - return future::Either::A(future::err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, JSON object or null" - )))) - } + Value::Array(vec) => Ok(Params::Array(vec)), + Value::Null => Ok(Params::None), + Value::Object(map) => Ok(Params::Map(map)), + _ => Err(RpcError::Other(format_err!( + "RPC params should serialize to a JSON array, JSON object or null" + ))), }; + let result = params.map(|params| self.0.call_method(method, params)); + + async move { + let value: Value = result?.await?; - future::Either::B(self.0.call_method(method, params).and_then(move |value: Value| { log::debug!("response: {:?}", value); - let result = - serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns.into(), error.into())); - future::done(result) - })) + + serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns, error.into())) + } } /// Call RPC with serialization of request only. - pub fn notify(&self, method: &str, args: T) -> impl Future { + pub fn notify(&self, method: &str, args: T) -> RpcResult<()> { let args = serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC"); let params = match args { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, _ => { - return future::Either::A(future::err(RpcError::Other(format_err!( + return Err(RpcError::Other(format_err!( "RPC params should serialize to a JSON array, or null" - )))) + ))) } }; - future::Either::B(self.0.notify(method, params)) + self.0.notify(method, params) } /// Subscribe with serialization of request and deserialization of response. @@ -344,7 +306,7 @@ impl TypedClient { topic: &str, unsubscribe: &str, returns: &'static str, - ) -> impl Future, Error = RpcError> { + ) -> RpcResult> { let args = serde_json::to_value(subscribe_params) .expect("Only types with infallible serialisation can be used for JSON-RPC"); @@ -352,17 +314,15 @@ impl TypedClient { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, _ => { - return future::Either::A(future::err(RpcError::Other(format_err!( + return Err(RpcError::Other(format_err!( "RPC params should serialize to a JSON array, or null" - )))) + ))) } }; - let typed_stream = self - .0 + self.0 .subscribe(subscribe, params, topic, unsubscribe) - .map(move |stream| TypedSubscriptionStream::new(stream, returns)); - future::Either::B(typed_stream) + .map(move |stream| TypedSubscriptionStream::new(stream, returns)) } } @@ -370,7 +330,8 @@ impl TypedClient { mod tests { use super::*; use crate::transports::local; - use crate::{RpcChannel, RpcError, TypedClient}; + use crate::{RpcChannel, TypedClient}; + use jsonrpc_core::futures::{future, FutureExt}; use jsonrpc_core::{self as core, IoHandler}; use jsonrpc_pubsub::{PubSubHandler, Subscriber, SubscriptionId}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -386,11 +347,11 @@ mod tests { } impl AddClient { - fn add(&self, a: u64, b: u64) -> impl Future { + fn add(&self, a: u64, b: u64) -> impl Future> { self.0.call_method("add", "u64", (a, b)) } - fn completed(&self, success: bool) -> impl Future { + fn completed(&self, success: bool) -> RpcResult<()> { self.0.notify("completed", (success,)) } } @@ -399,65 +360,61 @@ mod tests { fn test_client_terminates() { crate::logger::init_log(); let mut handler = IoHandler::new(); - handler.add_method("add", |params: Params| { + handler.add_sync_method("add", |params: Params| { let (a, b) = params.parse::<(u64, u64)>()?; let res = a + b; Ok(jsonrpc_core::to_value(res).unwrap()) }); + let (tx, rx) = std::sync::mpsc::channel(); let (client, rpc_client) = local::connect::(handler); - let fut = client - .clone() - .add(3, 4) - .and_then(move |res| client.add(res, 5)) - .join(rpc_client) - .map(|(res, ())| { - assert_eq!(res, 12); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); - }); - tokio::run(fut); + let fut = async move { + let res = client.add(3, 4).await?; + let res = client.add(res, 5).await?; + assert_eq!(res, 12); + tx.send(()).unwrap(); + Ok(()) as RpcResult<_> + }; + let pool = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + pool.spawn_ok(rpc_client.map(|x| x.unwrap())); + pool.spawn_ok(fut.map(|x| x.unwrap())); + rx.recv().unwrap() } #[test] fn should_send_notification() { crate::logger::init_log(); + let (tx, rx) = std::sync::mpsc::sync_channel(1); let mut handler = IoHandler::new(); - handler.add_notification("completed", |params: Params| { + handler.add_notification("completed", move |params: Params| { let (success,) = params.parse::<(bool,)>().expect("expected to receive one boolean"); assert_eq!(success, true); + tx.send(()).unwrap(); }); let (client, rpc_client) = local::connect::(handler); - let fut = client - .clone() - .completed(true) - .map(move |()| drop(client)) - .join(rpc_client) - .map(|_| ()) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); - }); - tokio::run(fut); + client.completed(true).unwrap(); + let pool = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + pool.spawn_ok(rpc_client.map(|x| x.unwrap())); + rx.recv().unwrap() } #[test] fn should_handle_subscription() { crate::logger::init_log(); // given + let (finish, finished) = std::sync::mpsc::sync_channel(1); let mut handler = PubSubHandler::::default(); let called = Arc::new(AtomicBool::new(false)); let called2 = called.clone(); handler.add_subscription( "hello", - ("subscribe_hello", |params, _meta, subscriber: Subscriber| { + ("subscribe_hello", move |params, _meta, subscriber: Subscriber| { assert_eq!(params, core::Params::None); let sink = subscriber .assign_id(SubscriptionId::Number(5)) .expect("assigned subscription id"); + let finish = finish.clone(); std::thread::spawn(move || { for i in 0..3 { std::thread::sleep(std::time::Duration::from_millis(100)); @@ -465,49 +422,45 @@ mod tests { "subscription": 5, "result": vec![i], }); - sink.notify(serde_json::from_value(value).unwrap()) - .wait() - .expect("sent notification"); + let _ = sink.notify(serde_json::from_value(value).unwrap()); } + finish.send(()).unwrap(); }); }), ("unsubscribe_hello", move |id, _meta| { // Should be called because session is dropped. called2.store(true, Ordering::SeqCst); assert_eq!(id, SubscriptionId::Number(5)); - future::ok(core::Value::Bool(true)) + future::ready(Ok(core::Value::Bool(true))) }), ); // when + let (tx, rx) = std::sync::mpsc::channel(); let (client, rpc_client) = local::connect_with_pubsub::(handler); let received = Arc::new(std::sync::Mutex::new(vec![])); let r2 = received.clone(); - let fut = client - .subscribe::<_, (u32,)>("subscribe_hello", (), "hello", "unsubscribe_hello", "u32") - .and_then(|stream| { - stream - .into_future() - .map(move |(result, _)| { - drop(client); - r2.lock().unwrap().push(result.unwrap()); - }) - .map_err(|_| { - panic!("Expected message not received."); - }) - }) - .join(rpc_client) - .map(|(res, _)| { - log::info!("ok {:?}", res); - }) - .map_err(|err| { - log::error!("err {:?}", err); - }); - tokio::run(fut); - assert_eq!(called.load(Ordering::SeqCst), true); + let fut = async move { + let mut stream = + client.subscribe::<_, (u32,)>("subscribe_hello", (), "hello", "unsubscribe_hello", "u32")?; + let result = stream.next().await; + r2.lock().unwrap().push(result.expect("Expected at least one item.")); + tx.send(()).unwrap(); + Ok(()) as RpcResult<_> + }; + + let pool = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + pool.spawn_ok(rpc_client.map(|_| ())); + pool.spawn_ok(fut.map(|x| x.unwrap())); + + rx.recv().unwrap(); assert!( !received.lock().unwrap().is_empty(), "Expected at least one received item." ); + // The session is being dropped only when another notification is received. + // TODO [ToDr] we should unsubscribe as soon as the stream is dropped instead! + finished.recv().unwrap(); + assert_eq!(called.load(Ordering::SeqCst), true, "Unsubscribe not called."); } } diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index 5465c37e4..1296cfd3d 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -1,17 +1,21 @@ //! Duplex transport use failure::format_err; -use futures::prelude::*; -use futures::sync::{mpsc, oneshot}; +use futures::channel::{mpsc, oneshot}; +use futures::{ + task::{Context, Poll}, + Future, Sink, Stream, StreamExt, +}; use jsonrpc_core::Id; use jsonrpc_pubsub::SubscriptionId; use log::debug; use serde_json::Value; use std::collections::HashMap; use std::collections::VecDeque; +use std::pin::Pin; use super::RequestBuilder; -use crate::{RpcChannel, RpcError, RpcMessage}; +use crate::{RpcChannel, RpcError, RpcMessage, RpcResult}; struct Subscription { /// Subscription id received when subscribing. @@ -21,11 +25,11 @@ struct Subscription { /// Rpc method to unsubscribe. unsubscribe: String, /// Where to send messages to. - channel: mpsc::Sender>, + channel: mpsc::UnboundedSender>, } impl Subscription { - fn new(channel: mpsc::Sender>, notification: String, unsubscribe: String) -> Self { + fn new(channel: mpsc::UnboundedSender>, notification: String, unsubscribe: String) -> Self { Subscription { id: None, notification, @@ -36,7 +40,7 @@ impl Subscription { } enum PendingRequest { - Call(oneshot::Sender>), + Call(oneshot::Sender>), Subscription(Subscription), } @@ -45,24 +49,24 @@ enum PendingRequest { pub struct Duplex { request_builder: RequestBuilder, /// Channel from the client. - channel: Option>, + channel: Option>, /// Requests that haven't received a response yet. pending_requests: HashMap, /// A map from the subscription name to the subscription. subscriptions: HashMap<(SubscriptionId, String), Subscription>, /// Incoming messages from the underlying transport. - stream: TStream, + stream: Pin>, /// Unprocessed incoming messages. - incoming: VecDeque<(Id, Result, Option, Option)>, + incoming: VecDeque<(Id, RpcResult, Option, Option)>, /// Unprocessed outgoing messages. outgoing: VecDeque, /// Outgoing messages from the underlying transport. - sink: TSink, + sink: Pin>, } impl Duplex { /// Creates a new `Duplex`. - fn new(sink: TSink, stream: TStream, channel: mpsc::Receiver) -> Self { + fn new(sink: Pin>, stream: Pin>, channel: mpsc::UnboundedReceiver) -> Self { log::debug!("open"); Duplex { request_builder: RequestBuilder::new(), @@ -78,21 +82,24 @@ impl Duplex { } /// Creates a new `Duplex`, along with a channel to communicate -pub fn duplex(sink: TSink, stream: TStream) -> (Duplex, RpcChannel) { - let (sender, receiver) = mpsc::channel(0); +pub fn duplex(sink: Pin>, stream: Pin>) -> (Duplex, RpcChannel) +where + TSink: Sink, + TStream: Stream, +{ + let (sender, receiver) = mpsc::unbounded(); let client = Duplex::new(sink, stream, receiver); (client, sender.into()) } impl Future for Duplex where - TSink: Sink, - TStream: Stream, + TSink: Sink, + TStream: Stream, { - type Item = (); - type Error = RpcError; + type Output = RpcResult<()>; - fn poll(&mut self) -> Result, Self::Error> { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { // Handle requests from the client. log::debug!("handle requests from client"); loop { @@ -101,16 +108,15 @@ where Some(channel) => channel, None => break, }; - let msg = match channel.poll() { - Ok(Async::Ready(Some(msg))) => msg, - Ok(Async::Ready(None)) => { + let msg = match channel.poll_next_unpin(cx) { + Poll::Ready(Some(msg)) => msg, + Poll::Ready(None) => { // When the channel is dropped we still need to finish // outstanding requests. self.channel.take(); break; } - Ok(Async::NotReady) => break, - Err(()) => continue, + Poll::Pending => break, }; let request_str = match msg { RpcMessage::Call(msg) => { @@ -154,17 +160,16 @@ where // Reads from stream and queues to incoming queue. log::debug!("handle stream"); loop { - let response_str = match self.stream.poll() { - Ok(Async::Ready(Some(response_str))) => response_str, - Ok(Async::Ready(None)) => { + let response_str = match self.stream.as_mut().poll_next(cx) { + Poll::Ready(Some(response_str)) => response_str, + Poll::Ready(None) => { // The websocket connection was closed so the client // can be shutdown. Reopening closed connections must // be handled by the transport. debug!("connection closed"); - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } - Ok(Async::NotReady) => break, - Err(err) => Err(err)?, + Poll::Pending => break, }; log::debug!("incoming: {}", response_str); // we only send one request at the time, so there can only be one response. @@ -220,21 +225,10 @@ where method, result, )); - match subscription.channel.poll_ready() { - Ok(Async::Ready(())) => { - subscription - .channel - .try_send(result) - .expect("The channel is ready; qed"); - } - Ok(Async::NotReady) => { - self.incoming.push_back((id, result, Some(method), sid)); - break; - } - Err(_) => { - log::warn!("{}, but the reply channel has closed.", err); - } - }; + + if subscription.channel.unbounded_send(result).is_err() { + log::warn!("{}, but the reply channel has closed.", err); + } } continue; } @@ -254,33 +248,21 @@ where }; if let Some(subscription) = self.subscriptions.get_mut(&sid_and_method) { - match subscription.channel.poll_ready() { - Ok(Async::Ready(())) => { - subscription - .channel - .try_send(result) - .expect("The channel is ready; qed"); - } - Ok(Async::NotReady) => { - let (sid, method) = sid_and_method; - self.incoming.push_back((id, result, Some(method), Some(sid))); - break; - } - Err(_) => { - let subscription = self - .subscriptions - .remove(&sid_and_method) - .expect("Subscription was just polled; qed"); - let sid = subscription.id.expect( - "Every subscription that ends up in `self.subscriptions` has id already \ - assigned; assignment happens during response to subscribe request.", - ); - let (_id, request_str) = - self.request_builder.unsubscribe_request(subscription.unsubscribe, sid); - log::debug!("outgoing: {}", request_str); - self.outgoing.push_back(request_str); - log::debug!("unsubscribed from {:?}", sid_and_method); - } + let res = subscription.channel.unbounded_send(result); + if res.is_err() { + let subscription = self + .subscriptions + .remove(&sid_and_method) + .expect("Subscription was just polled; qed"); + let sid = subscription.id.expect( + "Every subscription that ends up in `self.subscriptions` has id already \ + assigned; assignment happens during response to subscribe request.", + ); + let (_id, request_str) = + self.request_builder.unsubscribe_request(subscription.unsubscribe, sid); + log::debug!("outgoing: {}", request_str); + self.outgoing.push_back(request_str); + log::debug!("unsubscribed from {:?}", sid_and_method); } } else { log::warn!("Received unexpected subscription notification: {:?}", sid_and_method); @@ -294,21 +276,27 @@ where // Writes queued messages to sink. log::debug!("handle outgoing"); loop { + let err = || Err(RpcError::Other(failure::format_err!("closing"))); + match self.sink.as_mut().poll_ready(cx) { + Poll::Ready(Ok(())) => {} + Poll::Ready(Err(_)) => return err().into(), + _ => break, + } match self.outgoing.pop_front() { - Some(request) => match self.sink.start_send(request)? { - AsyncSink::Ready => {} - AsyncSink::NotReady(request) => { - self.outgoing.push_front(request); - break; + Some(request) => { + if let Err(_) = self.sink.as_mut().start_send(request) { + // the channel is disconnected. + return err().into(); } - }, + } None => break, } } log::debug!("handle sink"); - let sink_empty = match self.sink.poll_complete()? { - Async::Ready(()) => true, - Async::NotReady => false, + let sink_empty = match self.sink.as_mut().poll_flush(cx) { + Poll::Ready(Ok(())) => true, + Poll::Ready(Err(_)) => true, + Poll::Pending => false, }; log::debug!("{:?}", self); @@ -321,9 +309,9 @@ where && sink_empty { log::debug!("close"); - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } } diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index ba7d3f12a..e5d40d1b3 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -3,33 +3,49 @@ //! HTTPS support is enabled with the `tls` feature. use super::RequestBuilder; -use crate::{RpcChannel, RpcError, RpcMessage}; +use crate::{RpcChannel, RpcError, RpcMessage, RpcResult}; use failure::format_err; -use futures::{ - future::{ - self, - Either::{A, B}, - }, - sync::mpsc, - Future, Stream, -}; +use futures::{Future, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; use hyper::{http, rt, Client, Request, Uri}; /// Create a HTTP Client -pub fn connect(url: &str) -> impl Future +pub fn connect(url: &str) -> impl Future> where TClient: From, { + let (sender, receiver) = futures::channel::oneshot::channel(); + let url = url.to_owned(); + + std::thread::spawn(move || { + let connect = rt::lazy(move || { + do_connect(&url) + .map(|client| { + if sender.send(client).is_err() { + panic!("The caller did not wait for the server."); + } + Ok(()) + }) + .compat() + }); + rt::run(connect); + }); + + receiver.map(|res| res.expect("Server closed prematurely.").map(TClient::from)) +} + +fn do_connect(url: &str) -> impl Future> { + use futures::future::ready; + let max_parallel = 8; let url: Uri = match url.parse() { Ok(url) => url, - Err(e) => return A(future::err(RpcError::Other(e.into()))), + Err(e) => return ready(Err(RpcError::Other(e.into()))), }; #[cfg(feature = "tls")] let connector = match hyper_tls::HttpsConnector::new(4) { Ok(connector) => connector, - Err(e) => return A(future::err(RpcError::Other(e.into()))), + Err(e) => return ready(Err(RpcError::Other(e.into()))), }; #[cfg(feature = "tls")] let client = Client::builder().build::<_, hyper::Body>(connector); @@ -39,9 +55,12 @@ where let mut request_builder = RequestBuilder::new(); - let (sender, receiver) = mpsc::channel(max_parallel); + let (sender, receiver) = futures::channel::mpsc::unbounded(); + use futures01::{Future, Stream}; let fut = receiver + .map(Ok) + .compat() .filter_map(move |msg: RpcMessage| { let (request, sender) = match msg { RpcMessage::Call(call) => { @@ -71,6 +90,10 @@ where }) .buffer_unordered(max_parallel) .for_each(|(result, sender)| { + use futures01::future::{ + self, + Either::{A, B}, + }; let future = match result { Ok(ref res) if !res.status().is_success() => { log::trace!("http result status {}", res.status()); @@ -101,10 +124,8 @@ where }) }); - B(rt::lazy(move || { - rt::spawn(fut.map_err(|e| log::error!("RPC Client error: {:?}", e))); - Ok(TClient::from(sender.into())) - })) + rt::spawn(fut.map_err(|e: RpcError| log::error!("RPC Client error: {:?}", e))); + ready(Ok(sender.into())) } #[cfg(test)] @@ -112,11 +133,8 @@ mod tests { use super::*; use crate::*; use assert_matches::assert_matches; - use hyper::rt; use jsonrpc_core::{Error, ErrorCode, IoHandler, Params, Value}; use jsonrpc_http_server::*; - use std::net::SocketAddr; - use std::time::Duration; fn id(t: T) -> T { t @@ -124,7 +142,6 @@ mod tests { struct TestServer { uri: String, - socket_addr: SocketAddr, server: Option, } @@ -133,28 +150,14 @@ mod tests { let builder = ServerBuilder::new(io()).rest_api(RestApi::Unsecure); let server = alter(builder).start_http(&"127.0.0.1:0".parse().unwrap()).unwrap(); - let socket_addr = server.address().clone(); - let uri = format!("http://{}", socket_addr); + let uri = format!("http://{}", server.address()); TestServer { uri, - socket_addr, server: Some(server), } } - fn start(&mut self) { - if self.server.is_none() { - let server = ServerBuilder::new(io()) - .rest_api(RestApi::Unsecure) - .start_http(&self.socket_addr) - .unwrap(); - self.server = Some(server); - } else { - panic!("Server already running") - } - } - fn stop(&mut self) { let server = self.server.take(); if let Some(server) = server { @@ -165,11 +168,11 @@ mod tests { fn io() -> IoHandler { let mut io = IoHandler::default(); - io.add_method("hello", |params: Params| match params.parse::<(String,)>() { + io.add_sync_method("hello", |params: Params| match params.parse::<(String,)>() { Ok((msg,)) => Ok(Value::String(format!("hello {}", msg))), _ => Ok(Value::String("world".into())), }); - io.add_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); + io.add_sync_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); io.add_notification("notify", |params: Params| { let (value,) = params.parse::<(u64,)>().expect("expected one u64 as param"); assert_eq!(value, 12); @@ -188,13 +191,13 @@ mod tests { } impl TestClient { - fn hello(&self, msg: &'static str) -> impl Future { + fn hello(&self, msg: &'static str) -> impl Future> { self.0.call_method("hello", "String", (msg,)) } - fn fail(&self) -> impl Future { + fn fail(&self) -> impl Future> { self.0.call_method("fail", "()", ()) } - fn notify(&self, value: u64) -> impl Future { + fn notify(&self, value: u64) -> RpcResult<()> { self.0.notify("notify", (value,)) } } @@ -205,24 +208,18 @@ mod tests { // given let server = TestServer::serve(id); - let (tx, rx) = std::sync::mpsc::channel(); // when - let run = connect(&server.uri) - .and_then(|client: TestClient| { - client.hello("http").and_then(move |result| { - drop(client); - let _ = tx.send(result); - Ok(()) - }) - }) - .map_err(|e| log::error!("RPC Client error: {:?}", e)); + let run = async { + let client: TestClient = connect(&server.uri).await?; + let result = client.hello("http").await?; - rt::run(run); + // then + assert_eq!("hello http", result); + Ok(()) as RpcResult<_> + }; - // then - let result = rx.recv_timeout(Duration::from_secs(3)).unwrap(); - assert_eq!("hello http", result); + futures::executor::block_on(run).unwrap(); } #[test] @@ -234,20 +231,15 @@ mod tests { let (tx, rx) = std::sync::mpsc::channel(); // when - let run = connect(&server.uri) - .and_then(|client: TestClient| { - client.notify(12).and_then(move |result| { - drop(client); - let _ = tx.send(result); - Ok(()) - }) - }) - .map_err(|e| log::error!("RPC Client error: {:?}", e)); - - rt::run(run); - - // then - rx.recv_timeout(Duration::from_secs(3)).unwrap(); + let run = async move { + let client: TestClient = connect(&server.uri).await.unwrap(); + client.notify(12).unwrap(); + tx.send(()).unwrap(); + }; + + let pool = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + pool.spawn_ok(run); + rx.recv().unwrap(); } #[test] @@ -258,8 +250,7 @@ mod tests { let invalid_uri = "invalid uri"; // when - let run = connect(invalid_uri); // rx.recv_timeout(Duration::from_secs(3)).unwrap(); - let res: Result = run.wait(); + let res: RpcResult = futures::executor::block_on(connect(invalid_uri)); // then assert_matches!( @@ -275,22 +266,15 @@ mod tests { // given let server = TestServer::serve(id); - let (tx, rx) = std::sync::mpsc::channel(); // when - let run = connect(&server.uri) - .and_then(|client: TestClient| { - client.fail().then(move |res| { - let _ = tx.send(res); - Ok(()) - }) - }) - .map_err(|e| log::error!("RPC Client error: {:?}", e)); - rt::run(run); + let run = async { + let client: TestClient = connect(&server.uri).await?; + client.fail().await + }; + let res = futures::executor::block_on(run); // then - let res = rx.recv_timeout(Duration::from_secs(3)).unwrap(); - if let Err(RpcError::JsonRpcError(err)) = res { assert_eq!( err, @@ -311,75 +295,24 @@ mod tests { let mut server = TestServer::serve(id); // stop server so that we get a connection refused server.stop(); - let (tx, rx) = std::sync::mpsc::channel(); - - let client = connect(&server.uri); - - let call = client - .and_then(|client: TestClient| { - client.hello("http").then(move |res| { - let _ = tx.send(res); - Ok(()) - }) - }) - .map_err(|e| log::error!("RPC Client error: {:?}", e)); - rt::run(call); + let run = async { + let client: TestClient = connect(&server.uri).await?; + let res = client.hello("http").await; - // then - let res = rx.recv_timeout(Duration::from_secs(3)).unwrap(); - - if let Err(RpcError::Other(err)) = res { - if let Some(err) = err.downcast_ref::() { - assert!(err.is_connect(), format!("Expected Connection Error, got {:?}", err)) + if let Err(RpcError::Other(err)) = res { + if let Some(err) = err.downcast_ref::() { + assert!(err.is_connect(), format!("Expected Connection Error, got {:?}", err)) + } else { + panic!("Expected a hyper::Error") + } } else { - panic!("Expected a hyper::Error") + panic!("Expected JsonRpcError. Received {:?}", res) } - } else { - panic!("Expected JsonRpcError. Received {:?}", res) - } - } - #[test] - #[ignore] // todo: [AJ] make it pass - fn client_still_works_after_http_connect_error() { - // given - let mut server = TestServer::serve(id); - - // stop server so that we get a connection refused - server.stop(); - - let (tx, rx) = std::sync::mpsc::channel(); - let tx2 = tx.clone(); - - let client = connect(&server.uri); - - let call = client - .and_then(move |client: TestClient| { - client - .hello("http") - .then(move |res| { - let _ = tx.send(res); - Ok(()) - }) - .and_then(move |_| { - server.start(); // todo: make the server start on the main thread - client.hello("http2").then(move |res| { - let _ = tx2.send(res); - Ok(()) - }) - }) - }) - .map_err(|e| log::error!("RPC Client error: {:?}", e)); + Ok(()) as RpcResult<_> + }; - // when - rt::run(call); - - let res = rx.recv_timeout(Duration::from_secs(3)).unwrap(); - assert!(res.is_err()); - - // then - let result = rx.recv_timeout(Duration::from_secs(3)).unwrap().unwrap(); - assert_eq!("hello http", result); + futures::executor::block_on(run).unwrap(); } } diff --git a/core-client/transports/src/transports/ipc.rs b/core-client/transports/src/transports/ipc.rs index 8d3407489..4ad2ff429 100644 --- a/core-client/transports/src/transports/ipc.rs +++ b/core-client/transports/src/transports/ipc.rs @@ -3,30 +3,42 @@ use crate::transports::duplex::duplex; use crate::{RpcChannel, RpcError}; -use futures::prelude::*; +use futures::compat::{Sink01CompatExt, Stream01CompatExt}; +use futures::StreamExt; +use futures01::prelude::*; use jsonrpc_server_utils::codecs::StreamCodec; use parity_tokio_ipc::IpcConnection; -use std::io; use std::path::Path; use tokio::codec::Decoder; +use tokio::runtime::Runtime; /// Connect to a JSON-RPC IPC server. pub fn connect, Client: From>( path: P, - reactor: &tokio::reactor::Handle, -) -> Result, io::Error> { - let connection = IpcConnection::connect(path, reactor)?; - - Ok(futures::lazy(move || { +) -> impl futures::Future> { + let rt = Runtime::new().unwrap(); + #[allow(deprecated)] + let reactor = rt.reactor().clone(); + async move { + let connection = IpcConnection::connect(path, &reactor).map_err(|e| RpcError::Other(e.into()))?; let (sink, stream) = StreamCodec::stream_incoming().framed(connection).split(); let sink = sink.sink_map_err(|e| RpcError::Other(e.into())); - let stream = stream.map_err(|e| RpcError::Other(e.into())); + let stream = stream.map_err(|e| log::error!("IPC stream error: {}", e)); + + let (client, sender) = duplex( + Box::pin(sink.sink_compat()), + Box::pin( + stream + .compat() + .take_while(|x| futures::future::ready(x.is_ok())) + .map(|x| x.expect("Stream is closed upon first error.")), + ), + ); - let (client, sender) = duplex(sink, stream); + tokio::spawn(futures::compat::Compat::new(client).map_err(|_| unreachable!())); - tokio::spawn(client.map_err(|e| log::warn!("IPC client error: {:?}", e))); Ok(sender.into()) - })) + } } #[cfg(test)] @@ -37,7 +49,6 @@ mod tests { use jsonrpc_ipc_server::ServerBuilder; use parity_tokio_ipc::dummy_endpoint; use serde_json::map::Map; - use tokio::runtime::Runtime; #[test] fn should_call_one() { diff --git a/core-client/transports/src/transports/local.rs b/core-client/transports/src/transports/local.rs index 5dd1dc73c..aee50d895 100644 --- a/core-client/transports/src/transports/local.rs +++ b/core-client/transports/src/transports/local.rs @@ -1,20 +1,29 @@ //! Rpc client implementation for `Deref>`. -use crate::{RpcChannel, RpcError}; -use failure::format_err; -use futures::prelude::*; -use futures::sync::mpsc; -use jsonrpc_core::{MetaIoHandler, Metadata}; +use crate::{RpcChannel, RpcError, RpcResult}; +use futures::channel::mpsc; +use futures::{ + task::{Context, Poll}, + Future, Sink, SinkExt, Stream, StreamExt, +}; +use jsonrpc_core::{BoxFuture, MetaIoHandler, Metadata}; use jsonrpc_pubsub::Session; -use std::collections::VecDeque; use std::ops::Deref; +use std::pin::Pin; use std::sync::Arc; /// Implements a rpc client for `MetaIoHandler`. pub struct LocalRpc { handler: THandler, meta: TMetadata, - queue: VecDeque, + buffered: Buffered, + queue: (mpsc::UnboundedSender, mpsc::UnboundedReceiver), +} + +enum Buffered { + Request(BoxFuture>), + Response(String), + None, } impl LocalRpc @@ -35,45 +44,80 @@ where Self { handler, meta, - queue: Default::default(), + buffered: Buffered::None, + queue: mpsc::unbounded(), } } } impl Stream for LocalRpc where - TMetadata: Metadata, - THandler: Deref>, + TMetadata: Metadata + Unpin, + THandler: Deref> + Unpin, { type Item = String; - type Error = RpcError; - fn poll(&mut self) -> Result>, Self::Error> { - match self.queue.pop_front() { - Some(response) => Ok(Async::Ready(Some(response))), - None => Ok(Async::NotReady), + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.queue.1.poll_next_unpin(cx) + } +} + +impl LocalRpc +where + TMetadata: Metadata + Unpin, + THandler: Deref> + Unpin, +{ + fn poll_buffered(&mut self, cx: &mut Context) -> Poll> { + let response = match self.buffered { + Buffered::Request(ref mut r) => futures::ready!(r.as_mut().poll(cx)), + _ => None, + }; + if let Some(response) = response { + self.buffered = Buffered::Response(response); } + + self.send_response().into() + } + + fn send_response(&mut self) -> Result<(), RpcError> { + if let Buffered::Response(r) = std::mem::replace(&mut self.buffered, Buffered::None) { + self.queue.0.start_send(r).map_err(|e| RpcError::Other(e.into()))?; + } + Ok(()) } } -impl Sink for LocalRpc +impl Sink for LocalRpc where - TMetadata: Metadata, - THandler: Deref>, + TMetadata: Metadata + Unpin, + THandler: Deref> + Unpin, { - type SinkItem = String; - type SinkError = RpcError; + type Error = RpcError; - fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { - match self.handler.handle_request_sync(&request, self.meta.clone()) { - Some(response) => self.queue.push_back(response), - None => {} - }; - Ok(AsyncSink::Ready) + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + futures::ready!(self.poll_buffered(cx))?; + futures::ready!(self.queue.0.poll_ready(cx)) + .map_err(|e| RpcError::Other(e.into())) + .into() } - fn poll_complete(&mut self) -> Result, Self::SinkError> { - Ok(Async::Ready(())) + fn start_send(mut self: Pin<&mut Self>, item: String) -> Result<(), Self::Error> { + let future = self.handler.handle_request(&item, self.meta.clone()); + self.buffered = Buffered::Request(Box::pin(future)); + Ok(()) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + futures::ready!(self.poll_buffered(cx))?; + futures::ready!(self.queue.0.poll_flush_unpin(cx)) + .map_err(|e| RpcError::Other(e.into())) + .into() + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + futures::ready!(self.queue.0.poll_close_unpin(cx)) + .map_err(|e| RpcError::Other(e.into())) + .into() } } @@ -81,24 +125,24 @@ where pub fn connect_with_metadata( handler: THandler, meta: TMetadata, -) -> (TClient, impl Future) +) -> (TClient, impl Future>) where TClient: From, - THandler: Deref>, - TMetadata: Metadata, + THandler: Deref> + Unpin, + TMetadata: Metadata + Unpin, { let (sink, stream) = LocalRpc::with_metadata(handler, meta).split(); - let (rpc_client, sender) = crate::transports::duplex(sink, stream); + let (rpc_client, sender) = crate::transports::duplex(Box::pin(sink), Box::pin(stream)); let client = TClient::from(sender); (client, rpc_client) } /// Connects to a `Deref`. -pub fn connect(handler: THandler) -> (TClient, impl Future) +pub fn connect(handler: THandler) -> (TClient, impl Future>) where TClient: From, - THandler: Deref>, - TMetadata: Metadata + Default, + THandler: Deref> + Unpin, + TMetadata: Metadata + Default + Unpin, { connect_with_metadata(handler, Default::default()) } @@ -107,16 +151,16 @@ where pub type LocalMeta = Arc; /// Connects with pubsub. -pub fn connect_with_pubsub(handler: THandler) -> (TClient, impl Future) +pub fn connect_with_pubsub(handler: THandler) -> (TClient, impl Future>) where TClient: From, - THandler: Deref>, + THandler: Deref> + Unpin, { - let (tx, rx) = mpsc::channel(0); + let (tx, rx) = mpsc::unbounded(); let meta = Arc::new(Session::new(tx)); let (sink, stream) = LocalRpc::with_metadata(handler, meta).split(); - let stream = stream.select(rx.map_err(|_| RpcError::Other(format_err!("Pubsub channel returned an error")))); - let (rpc_client, sender) = crate::transports::duplex(sink, stream); + let stream = futures::stream::select(stream, rx); + let (rpc_client, sender) = crate::transports::duplex(Box::pin(sink), Box::pin(stream)); let client = TClient::from(sender); (client, rpc_client) } diff --git a/core-client/transports/src/transports/ws.rs b/core-client/transports/src/transports/ws.rs index e9f89531d..ea3ea8cf4 100644 --- a/core-client/transports/src/transports/ws.rs +++ b/core-client/transports/src/transports/ws.rs @@ -1,7 +1,7 @@ //! JSON-RPC websocket client implementation. use crate::{RpcChannel, RpcError}; use failure::Error; -use futures::prelude::*; +use futures01::prelude::*; use log::info; use std::collections::VecDeque; use websocket::{ClientBuilder, OwnedMessage}; @@ -22,12 +22,13 @@ where /// Connect to a JSON-RPC websocket server. /// /// Uses an unbuffered channel to queue outgoing rpc messages. -pub fn connect(url: &url::Url) -> impl Future +pub fn connect(url: &url::Url) -> impl futures::Future> where T: From, { let client_builder = ClientBuilder::from_url(url); - do_connect(client_builder) + let fut = do_connect(client_builder); + futures::compat::Compat01As03::new(fut) } fn do_connect(client_builder: ClientBuilder) -> impl Future @@ -37,10 +38,19 @@ where client_builder .async_connect(None) .map(|(client, _)| { + use futures::{StreamExt, TryFutureExt}; let (sink, stream) = client.split(); let (sink, stream) = WebsocketClient::new(sink, stream).split(); + let (sink, stream) = ( + Box::pin(futures::compat::Compat01As03Sink::new(sink)), + Box::pin( + futures::compat::Compat01As03::new(stream) + .take_while(|x| futures::future::ready(x.is_ok())) + .map(|x| x.expect("Stream is closed upon first error.")), + ), + ); let (rpc_client, sender) = super::duplex(sink, stream); - let rpc_client = rpc_client.map_err(|error| eprintln!("{:?}", error)); + let rpc_client = rpc_client.compat().map_err(|error| log::error!("{:?}", error)); tokio::spawn(rpc_client); sender.into() }) diff --git a/core/Cargo.toml b/core/Cargo.toml index f4bd56fec..c975839bb 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" categories = [ "asynchronous", @@ -20,7 +20,7 @@ categories = [ [dependencies] log = "0.4" -futures = "~0.1.6" +futures = "0.3" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" diff --git a/core/examples/async.rs b/core/examples/async.rs index 619cd3742..0e65b5b19 100644 --- a/core/examples/async.rs +++ b/core/examples/async.rs @@ -1,15 +1,16 @@ -use jsonrpc_core::futures::Future; use jsonrpc_core::*; fn main() { - let mut io = IoHandler::new(); + futures::executor::block_on(async { + let mut io = IoHandler::new(); - io.add_method("say_hello", |_: Params| { - futures::finished(Value::String("Hello World!".to_owned())) - }); + io.add_method("say_hello", |_: Params| async { + Ok(Value::String("Hello World!".to_owned())) + }); - let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; - assert_eq!(io.handle_request(request).wait().unwrap(), Some(response.to_owned())); + assert_eq!(io.handle_request(request).await, Some(response.to_owned())); + }); } diff --git a/core/examples/basic.rs b/core/examples/basic.rs index 24dbbca1b..f81c24b12 100644 --- a/core/examples/basic.rs +++ b/core/examples/basic.rs @@ -3,7 +3,7 @@ use jsonrpc_core::*; fn main() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_: Params| Ok(Value::String("Hello World!".to_owned()))); + io.add_sync_method("say_hello", |_: Params| Ok(Value::String("Hello World!".to_owned()))); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; diff --git a/core/examples/meta.rs b/core/examples/meta.rs index 3e19a4bdf..abc3c6b55 100644 --- a/core/examples/meta.rs +++ b/core/examples/meta.rs @@ -1,4 +1,3 @@ -use jsonrpc_core::futures::Future; use jsonrpc_core::*; #[derive(Clone, Default)] @@ -8,7 +7,7 @@ impl Metadata for Meta {} pub fn main() { let mut io = MetaIoHandler::default(); - io.add_method_with_meta("say_hello", |_params: Params, meta: Meta| { + io.add_method_with_meta("say_hello", |_params: Params, meta: Meta| async move { Ok(Value::String(format!("Hello World: {}", meta.0))) }); @@ -17,7 +16,7 @@ pub fn main() { let headers = 5; assert_eq!( - io.handle_request(request, Meta(headers)).wait().unwrap(), + io.handle_request_sync(request, Meta(headers)), Some(response.to_owned()) ); } diff --git a/core/examples/middlewares.rs b/core/examples/middlewares.rs index 48af3e595..37485c526 100644 --- a/core/examples/middlewares.rs +++ b/core/examples/middlewares.rs @@ -1,5 +1,5 @@ use jsonrpc_core::futures::future::Either; -use jsonrpc_core::futures::Future; +use jsonrpc_core::futures::{Future, FutureExt}; use jsonrpc_core::*; use std::sync::atomic::{self, AtomicUsize}; use std::time::Instant; @@ -17,13 +17,13 @@ impl Middleware for MyMiddleware { fn on_request(&self, request: Request, meta: Meta, next: F) -> Either where F: FnOnce(Request, Meta) -> X + Send, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { let start = Instant::now(); let request_number = self.0.fetch_add(1, atomic::Ordering::SeqCst); println!("Processing request {}: {:?}, {:?}", request_number, request, meta); - Either::A(Box::new(next(request, meta).map(move |res| { + Either::Left(Box::pin(next(request, meta).map(move |res| { println!("Processing took: {:?}", start.elapsed()); res }))) @@ -33,7 +33,7 @@ impl Middleware for MyMiddleware { pub fn main() { let mut io = MetaIoHandler::with_middleware(MyMiddleware::default()); - io.add_method_with_meta("say_hello", |_params: Params, meta: Meta| { + io.add_method_with_meta("say_hello", |_params: Params, meta: Meta| async move { Ok(Value::String(format!("Hello World: {}", meta.0))) }); @@ -42,7 +42,7 @@ pub fn main() { let headers = 5; assert_eq!( - io.handle_request(request, Meta(headers)).wait().unwrap(), + io.handle_request_sync(request, Meta(headers)), Some(response.to_owned()) ); } diff --git a/core/examples/params.rs b/core/examples/params.rs index d528b3578..353ab770a 100644 --- a/core/examples/params.rs +++ b/core/examples/params.rs @@ -9,7 +9,7 @@ struct HelloParams { fn main() { let mut io = IoHandler::new(); - io.add_method("say_hello", |params: Params| { + io.add_method("say_hello", |params: Params| async move { let parsed: HelloParams = params.parse().unwrap(); Ok(Value::String(format!("hello, {}", parsed.name))) }); diff --git a/core/src/calls.rs b/core/src/calls.rs index 335d75fb7..36718c94f 100644 --- a/core/src/calls.rs +++ b/core/src/calls.rs @@ -1,6 +1,6 @@ use crate::types::{Error, Params, Value}; use crate::BoxFuture; -use futures::{Future, IntoFuture}; +use futures::Future; use std::fmt; use std::sync::Arc; @@ -11,10 +11,34 @@ impl Metadata for Option {} impl Metadata for Box {} impl Metadata for Arc {} +/// A future-conversion trait. +pub trait WrapFuture { + /// Convert itself into a boxed future. + fn into_future(self) -> BoxFuture>; +} + +impl WrapFuture for Result { + fn into_future(self) -> BoxFuture> { + Box::pin(futures::future::ready(self)) + } +} + +impl WrapFuture for BoxFuture> { + fn into_future(self) -> BoxFuture> { + self + } +} + +/// A synchronous or asynchronous method. +pub trait RpcMethodSync: Send + Sync + 'static { + /// Call method + fn call(&self, params: Params) -> BoxFuture>; +} + /// Asynchronous Method pub trait RpcMethodSimple: Send + Sync + 'static { /// Output future - type Out: Future + Send; + type Out: Future> + Send; /// Call method fn call(&self, params: Params) -> Self::Out; } @@ -22,7 +46,7 @@ pub trait RpcMethodSimple: Send + Sync + 'static { /// Asynchronous Method with Metadata pub trait RpcMethod: Send + Sync + 'static { /// Call method - fn call(&self, params: Params, meta: T) -> BoxFuture; + fn call(&self, params: Params, meta: T) -> BoxFuture>; } /// Notification @@ -59,14 +83,23 @@ impl fmt::Debug for RemoteProcedure { } } -impl RpcMethodSimple for F +impl RpcMethodSimple for F where - F: Fn(Params) -> I, - X: Future, - I: IntoFuture, + F: Fn(Params) -> X, + X: Future>, { type Out = X; fn call(&self, params: Params) -> Self::Out { + self(params) + } +} + +impl RpcMethodSync for F +where + F: Fn(Params) -> X, + X: WrapFuture, +{ + fn call(&self, params: Params) -> BoxFuture> { self(params).into_future() } } @@ -80,15 +113,14 @@ where } } -impl RpcMethod for F +impl RpcMethod for F where T: Metadata, - F: Fn(Params, T) -> I, - I: IntoFuture, - X: Future, + F: Fn(Params, T) -> X, + X: Future>, { - fn call(&self, params: Params, meta: T) -> BoxFuture { - Box::new(self(params, meta).into_future()) + fn call(&self, params: Params, meta: T) -> BoxFuture> { + Box::pin(self(params, meta)) } } diff --git a/core/src/delegates.rs b/core/src/delegates.rs index 577d9d8d1..4d43152b3 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification}; use crate::types::{Error, Params, Value}; use crate::BoxFuture; -use futures::IntoFuture; +use futures::Future; struct DelegateAsyncMethod { delegate: Arc, @@ -17,14 +17,13 @@ impl RpcMethod for DelegateAsyncMethod where M: Metadata, F: Fn(&T, Params) -> I, - I: IntoFuture, + I: Future> + Send + 'static, T: Send + Sync + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { - fn call(&self, params: Params, _meta: M) -> BoxFuture { + fn call(&self, params: Params, _meta: M) -> BoxFuture> { let closure = &self.closure; - Box::new(closure(&self.delegate, params).into_future()) + Box::pin(closure(&self.delegate, params)) } } @@ -37,14 +36,13 @@ impl RpcMethod for DelegateMethodWithMeta where M: Metadata, F: Fn(&T, Params, M) -> I, - I: IntoFuture, + I: Future> + Send + 'static, T: Send + Sync + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { - fn call(&self, params: Params, meta: M) -> BoxFuture { + fn call(&self, params: Params, meta: M) -> BoxFuture> { let closure = &self.closure; - Box::new(closure(&self.delegate, params, meta).into_future()) + Box::pin(closure(&self.delegate, params, meta)) } } @@ -117,9 +115,8 @@ where pub fn add_method(&mut self, name: &str, method: F) where F: Fn(&T, Params) -> I, - I: IntoFuture, + I: Future> + Send + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { self.methods.insert( name.into(), @@ -134,9 +131,8 @@ where pub fn add_method_with_meta(&mut self, name: &str, method: F) where F: Fn(&T, Params, M) -> I, - I: IntoFuture, + I: Future> + Send + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { self.methods.insert( name.into(), diff --git a/core/src/io.rs b/core/src/io.rs index 65f67162b..153455be1 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -3,37 +3,40 @@ use std::collections::{ HashMap, }; use std::ops::{Deref, DerefMut}; +use std::pin::Pin; use std::sync::Arc; -use futures::{self, future, Future}; +use futures::{self, future, Future, FutureExt}; use serde_json; -use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple}; +use crate::calls::{ + Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcMethodSync, RpcNotification, RpcNotificationSimple, +}; use crate::middleware::{self, Middleware}; use crate::types::{Call, Output, Request, Response}; use crate::types::{Error, ErrorCode, Version}; /// A type representing middleware or RPC response before serialization. -pub type FutureResponse = Box, Error = ()> + Send>; +pub type FutureResponse = Pin> + Send>>; /// A type representing middleware or RPC call output. -pub type FutureOutput = Box, Error = ()> + Send>; +pub type FutureOutput = Pin> + Send>>; /// A type representing future string response. pub type FutureResult = future::Map< - future::Either, ()>, FutureRpcResult>, + future::Either>, FutureRpcResult>, fn(Option) -> Option, >; /// A type representing a result of a single method call. -pub type FutureRpcOutput = future::Either, ()>>>; +pub type FutureRpcOutput = future::Either>>>; /// A type representing an optional `Response` for RPC `Request`. pub type FutureRpcResult = future::Either< F, future::Either< future::Map, fn(Option) -> Option>, - future::Map>>, fn(Vec>) -> Option>, + future::Map>, fn(Vec>) -> Option>, >, >; @@ -139,7 +142,17 @@ impl> MetaIoHandler { self.methods.insert(alias.into(), RemoteProcedure::Alias(other.into())); } - /// Adds new supported asynchronous method + /// Adds new supported synchronous method. + /// + /// A backward-compatible wrapper. + pub fn add_sync_method(&mut self, name: &str, method: F) + where + F: RpcMethodSync, + { + self.add_method(name, move |params| method.call(params)) + } + + /// Adds new supported asynchronous method. pub fn add_method(&mut self, name: &str, method: F) where F: RpcMethodSimple, @@ -185,14 +198,12 @@ impl> MetaIoHandler { /// If you have any asynchronous methods in your RPC it is much wiser to use /// `handle_request` instead and deal with asynchronous requests in a non-blocking fashion. pub fn handle_request_sync(&self, request: &str, meta: T) -> Option { - self.handle_request(request, meta) - .wait() - .expect("Handler calls can never fail.") + futures::executor::block_on(self.handle_request(request, meta)) } /// Handle given request asynchronously. pub fn handle_request(&self, request: &str, meta: T) -> FutureResult { - use self::future::Either::{A, B}; + use self::future::Either::{Left, Right}; fn as_string(response: Option) -> Option { let res = response.map(write_response); debug!(target: "rpc", "Response: {}.", match res { @@ -205,11 +216,11 @@ impl> MetaIoHandler { trace!(target: "rpc", "Request: {}.", request); let request = read_request(request); let result = match request { - Err(error) => A(futures::finished(Some(Response::from( + Err(error) => Left(future::ready(Some(Response::from( error, self.compatibility.default_version(), )))), - Ok(request) => B(self.handle_rpc_request(request, meta)), + Ok(request) => Right(self.handle_rpc_request(request, meta)), }; result.map(as_string) @@ -217,7 +228,7 @@ impl> MetaIoHandler { /// Handle deserialized RPC request. pub fn handle_rpc_request(&self, request: Request, meta: T) -> FutureRpcResult { - use self::future::Either::{A, B}; + use self::future::Either::{Left, Right}; fn output_as_response(output: Option) -> Option { output.map(Response::Single) @@ -234,23 +245,25 @@ impl> MetaIoHandler { self.middleware .on_request(request, meta, |request, meta| match request { - Request::Single(call) => A(self - .handle_call(call, meta) - .map(output_as_response as fn(Option) -> Option)), + Request::Single(call) => Left( + self.handle_call(call, meta) + .map(output_as_response as fn(Option) -> Option), + ), Request::Batch(calls) => { let futures: Vec<_> = calls .into_iter() .map(move |call| self.handle_call(call, meta.clone())) .collect(); - B(futures::future::join_all(futures) - .map(outputs_as_batch as fn(Vec>) -> Option)) + Right( + future::join_all(futures).map(outputs_as_batch as fn(Vec>) -> Option), + ) } }) } /// Handle single call asynchronously. pub fn handle_call(&self, call: Call, meta: T) -> FutureRpcOutput { - use self::future::Either::{A, B}; + use self::future::Either::{Left, Right}; self.middleware.on_call(call, meta, |call, meta| match call { Call::MethodCall(method) => { @@ -259,10 +272,7 @@ impl> MetaIoHandler { let jsonrpc = method.jsonrpc; let valid_version = self.compatibility.is_version_valid(jsonrpc); - let call_method = |method: &Arc>| { - let method = method.clone(); - futures::lazy(move || method.call(params, meta)) - }; + let call_method = |method: &Arc>| method.call(params, meta); let result = match (valid_version, self.methods.get(&method.method)) { (false, _) => Err(Error::invalid_version()), @@ -275,17 +285,17 @@ impl> MetaIoHandler { }; match result { - Ok(result) => A(Box::new( - result.then(move |result| futures::finished(Some(Output::from(result, id, jsonrpc)))), + Ok(result) => Left(Box::pin( + result.then(move |result| future::ready(Some(Output::from(result, id, jsonrpc)))), ) as _), - Err(err) => B(futures::finished(Some(Output::from(Err(err), id, jsonrpc)))), + Err(err) => Right(future::ready(Some(Output::from(Err(err), id, jsonrpc)))), } } Call::Notification(notification) => { let params = notification.params; let jsonrpc = notification.jsonrpc; if !self.compatibility.is_version_valid(jsonrpc) { - return B(futures::finished(None)); + return Right(future::ready(None)); } match self.methods.get(¬ification.method) { @@ -300,9 +310,9 @@ impl> MetaIoHandler { _ => {} } - B(futures::finished(None)) + Right(future::ready(None)) } - Call::Invalid { id } => B(futures::finished(Some(Output::invalid_request( + Call::Invalid { id } => Right(future::ready(Some(Output::invalid_request( id, self.compatibility.default_version(), )))), @@ -475,13 +485,13 @@ fn write_response(response: Response) -> String { mod tests { use super::{Compatibility, IoHandler}; use crate::types::Value; - use futures; + use futures::future; #[test] fn test_io_handler() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| Ok(Value::String("hello".to_string()))); + io.add_method("say_hello", |_| async { Ok(Value::String("hello".to_string())) }); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; @@ -493,7 +503,7 @@ mod tests { fn test_io_handler_1dot0() { let mut io = IoHandler::with_compatibility(Compatibility::Both); - io.add_method("say_hello", |_| Ok(Value::String("hello".to_string()))); + io.add_method("say_hello", |_| async { Ok(Value::String("hello".to_string())) }); let request = r#"{"method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"result":"hello","id":1}"#; @@ -505,7 +515,7 @@ mod tests { fn test_async_io_handler() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| futures::finished(Value::String("hello".to_string()))); + io.add_method("say_hello", |_| future::ready(Ok(Value::String("hello".to_string())))); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; @@ -544,7 +554,7 @@ mod tests { #[test] fn test_method_alias() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| Ok(Value::String("hello".to_string()))); + io.add_method("say_hello", |_| async { Ok(Value::String("hello".to_string())) }); io.add_alias("say_hello_alias", "say_hello"); let request = r#"{"jsonrpc": "2.0", "method": "say_hello_alias", "params": [42, 23], "id": 1}"#; @@ -612,8 +622,8 @@ mod tests { struct Test; impl Test { - fn abc(&self, _p: crate::Params) -> Result { - Ok(5.into()) + fn abc(&self, _p: crate::Params) -> crate::BoxFuture> { + Box::pin(async { Ok(5.into()) }) } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 1e0f27fd1..dc1f4059f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,23 +4,24 @@ //! //! ```rust //! use jsonrpc_core::*; -//! use jsonrpc_core::futures::Future; //! //! fn main() { //! let mut io = IoHandler::new(); -//! io.add_method("say_hello", |_| { +//! io.add_sync_method("say_hello", |_| { //! Ok(Value::String("Hello World!".into())) //! }); //! //! let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; //! let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#; //! -//! assert_eq!(io.handle_request(request).wait().unwrap(), Some(response.to_string())); +//! assert_eq!(io.handle_request_sync(request), Some(response.to_string())); //! } //! ``` #![deny(missing_docs)] +use std::pin::Pin; + #[macro_use] extern crate log; #[macro_use] @@ -40,13 +41,16 @@ pub mod delegates; pub mod middleware; pub mod types; -/// A `Future` trait object. -pub type BoxFuture = Box + Send>; - /// A Result type. -pub type Result = ::std::result::Result; +pub type Result = std::result::Result; -pub use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcNotification, RpcNotificationSimple}; +/// A `Future` trait object. +pub type BoxFuture = Pin + Send>>; + +pub use crate::calls::{ + Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcMethodSync, RpcNotification, RpcNotificationSimple, + WrapFuture, +}; pub use crate::delegates::IoDelegate; pub use crate::io::{ Compatibility, FutureOutput, FutureResponse, FutureResult, FutureRpcResult, IoHandler, IoHandlerExtension, diff --git a/core/src/middleware.rs b/core/src/middleware.rs index a33ae4add..71dd725ee 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -3,14 +3,15 @@ use crate::calls::Metadata; use crate::types::{Call, Output, Request, Response}; use futures::{future::Either, Future}; +use std::pin::Pin; /// RPC middleware pub trait Middleware: Send + Sync + 'static { /// A returned request future. - type Future: Future, Error = ()> + Send + 'static; + type Future: Future> + Send + 'static; /// A returned call future. - type CallFuture: Future, Error = ()> + Send + 'static; + type CallFuture: Future> + Send + 'static; /// Method invoked on each request. /// Allows you to either respond directly (without executing RPC call) @@ -18,9 +19,9 @@ pub trait Middleware: Send + Sync + 'static { fn on_request(&self, request: Request, meta: M, next: F) -> Either where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { - Either::B(next(request, meta)) + Either::Right(next(request, meta)) } /// Method invoked on each call inside a request. @@ -29,16 +30,16 @@ pub trait Middleware: Send + Sync + 'static { fn on_call(&self, call: Call, meta: M, next: F) -> Either where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { - Either::B(next(call, meta)) + Either::Right(next(call, meta)) } } /// Dummy future used as a noop result of middleware. -pub type NoopFuture = Box, Error = ()> + Send>; +pub type NoopFuture = Pin> + Send>>; /// Dummy future used as a noop call result of middleware. -pub type NoopCallFuture = Box, Error = ()> + Send>; +pub type NoopCallFuture = Pin> + Send>>; /// No-op middleware implementation #[derive(Clone, Debug, Default)] @@ -55,7 +56,7 @@ impl, B: Middleware> Middleware for (A, B) { fn on_request(&self, request: Request, meta: M, process: F) -> Either where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { repack(self.0.on_request(request, meta, |request, meta| { self.1.on_request(request, meta, &process) @@ -65,7 +66,7 @@ impl, B: Middleware> Middleware for (A, B) { fn on_call(&self, call: Call, meta: M, process: F) -> Either where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { repack( self.0 @@ -81,7 +82,7 @@ impl, B: Middleware, C: Middleware> Middlewa fn on_request(&self, request: Request, meta: M, process: F) -> Either where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { repack(self.0.on_request(request, meta, |request, meta| { repack(self.1.on_request(request, meta, |request, meta| { @@ -93,7 +94,7 @@ impl, B: Middleware, C: Middleware> Middlewa fn on_call(&self, call: Call, meta: M, process: F) -> Either where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { repack(self.0.on_call(call, meta, |call, meta| { repack( @@ -113,7 +114,7 @@ impl, B: Middleware, C: Middleware, D: Middl fn on_request(&self, request: Request, meta: M, process: F) -> Either where F: Fn(Request, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { repack(self.0.on_request(request, meta, |request, meta| { repack(self.1.on_request(request, meta, |request, meta| { @@ -127,7 +128,7 @@ impl, B: Middleware, C: Middleware, D: Middl fn on_call(&self, call: Call, meta: M, process: F) -> Either where F: Fn(Call, M) -> X + Send + Sync, - X: Future, Error = ()> + Send + 'static, + X: Future> + Send + 'static, { repack(self.0.on_call(call, meta, |call, meta| { repack(self.1.on_call(call, meta, |call, meta| { @@ -143,8 +144,8 @@ impl, B: Middleware, C: Middleware, D: Middl #[inline(always)] fn repack(result: Either>) -> Either, X> { match result { - Either::A(a) => Either::A(Either::A(a)), - Either::B(Either::A(b)) => Either::A(Either::B(b)), - Either::B(Either::B(x)) => Either::B(x), + Either::Left(a) => Either::Left(Either::Left(a)), + Either::Right(Either::Left(b)) => Either::Left(Either::Right(b)), + Either::Right(Either::Right(x)) => Either::Right(x), } } diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 1d94d78f9..4e399764a 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.1" +version = "15.0.0" [lib] proc-macro = true @@ -19,12 +19,11 @@ quote = "1.0.6" proc-macro-crate = "0.1.4" [dev-dependencies] -jsonrpc-core = { version = "14.2", path = "../core" } -jsonrpc-core-client = { version = "14.2", path = "../core-client" } -jsonrpc-pubsub = { version = "14.2", path = "../pubsub" } -jsonrpc-tcp-server = { version = "14.2", path = "../tcp" } -futures = "~0.1.6" +assert_matches = "1.3" +jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-core-client = { version = "15.0", path = "../core-client" } +jsonrpc-pubsub = { version = "15.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "15.0", path = "../tcp" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tokio = "0.1" trybuild = "1.0" diff --git a/derive/examples/generic-trait-bounds.rs b/derive/examples/generic-trait-bounds.rs index 552a86a30..6ac09705c 100644 --- a/derive/examples/generic-trait-bounds.rs +++ b/derive/examples/generic-trait-bounds.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; -use jsonrpc_core::futures::future::{self, FutureResult}; -use jsonrpc_core::{Error, IoHandler, IoHandlerExtension, Result}; +use jsonrpc_core::{futures::future, BoxFuture, IoHandler, IoHandlerExtension, Result}; use jsonrpc_derive::rpc; // One is both parameter and a result so requires both Serialize and DeserializeOwned @@ -22,7 +21,7 @@ pub trait Rpc { /// Performs asynchronous operation #[rpc(name = "beFancy")] - fn call(&self, a: One) -> FutureResult<(One, u64), Error>; + fn call(&self, a: One) -> BoxFuture>; } struct RpcImpl; @@ -49,8 +48,8 @@ impl Rpc for RpcImpl { Ok(Out {}) } - fn call(&self, num: InAndOut) -> FutureResult<(InAndOut, u64), Error> { - crate::future::finished((InAndOut { foo: num.foo + 999 }, num.foo)) + fn call(&self, num: InAndOut) -> BoxFuture> { + Box::pin(future::ready(Ok((InAndOut { foo: num.foo + 999 }, num.foo)))) } } diff --git a/derive/examples/generic-trait.rs b/derive/examples/generic-trait.rs index 6cf867b31..8e0972bc8 100644 --- a/derive/examples/generic-trait.rs +++ b/derive/examples/generic-trait.rs @@ -1,7 +1,6 @@ use jsonrpc_core; -use jsonrpc_core::futures::future::{self, FutureResult}; -use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_core::{BoxFuture, IoHandler, Result}; use jsonrpc_derive::rpc; #[rpc] @@ -16,7 +15,7 @@ pub trait Rpc { /// Performs asynchronous operation #[rpc(name = "beFancy")] - fn call(&self, a: One) -> FutureResult<(One, Two), Error>; + fn call(&self, a: One) -> BoxFuture>; } struct RpcImpl; @@ -31,8 +30,8 @@ impl Rpc for RpcImpl { Ok(()) } - fn call(&self, num: u64) -> FutureResult<(u64, String), Error> { - crate::future::finished((num + 999, "hello".into())) + fn call(&self, num: u64) -> BoxFuture> { + Box::pin(jsonrpc_core::futures::future::ready(Ok((num + 999, "hello".into())))) } } diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 8d7a64d7f..9ea7ea69e 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -1,8 +1,7 @@ use std::collections::BTreeMap; -use jsonrpc_core::futures::future::FutureResult; -use jsonrpc_core::types::params::Params; -use jsonrpc_core::{futures, Error, MetaIoHandler, Metadata, Result, Value}; +use jsonrpc_core::futures::future; +use jsonrpc_core::{BoxFuture, MetaIoHandler, Metadata, Params, Result, Value}; use jsonrpc_derive::rpc; #[derive(Clone)] @@ -31,11 +30,11 @@ pub trait Rpc { /// Performs an asynchronous operation. #[rpc(name = "callAsync")] - fn call(&self, a: u64) -> FutureResult; + fn call(&self, a: u64) -> BoxFuture>; /// Performs an asynchronous operation with meta. #[rpc(meta, name = "callAsyncMeta", alias("callAsyncMetaAlias"))] - fn call_meta(&self, a: Self::Metadata, b: BTreeMap) -> FutureResult; + fn call_meta(&self, a: Self::Metadata, b: BTreeMap) -> BoxFuture>; /// Handles a notification. #[rpc(name = "notify")] @@ -62,12 +61,12 @@ impl Rpc for RpcImpl { Ok(format!("Got: {:?}", params)) } - fn call(&self, x: u64) -> FutureResult { - futures::finished(format!("OK: {}", x)) + fn call(&self, x: u64) -> BoxFuture> { + Box::pin(future::ready(Ok(format!("OK: {}", x)))) } - fn call_meta(&self, meta: Self::Metadata, map: BTreeMap) -> FutureResult { - futures::finished(format!("From: {}, got: {:?}", meta.0, map)) + fn call_meta(&self, meta: Self::Metadata, map: BTreeMap) -> BoxFuture> { + Box::pin(future::ready(Ok(format!("From: {}, got: {:?}", meta.0, map)))) } fn notify(&self, a: u64) { diff --git a/derive/examples/pubsub-macros.rs b/derive/examples/pubsub-macros.rs index 5346e1068..ec8479a27 100644 --- a/derive/examples/pubsub-macros.rs +++ b/derive/examples/pubsub-macros.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::sync::{atomic, Arc, RwLock}; use std::thread; -use jsonrpc_core::futures::Future; use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_derive::rpc; use jsonrpc_pubsub::typed; @@ -70,7 +69,7 @@ fn main() { { let subscribers = active_subscriptions.read().unwrap(); for sink in subscribers.values() { - let _ = sink.notify(Ok("Hello World!".into())).wait(); + let _ = sink.notify(Ok("Hello World!".into())); } } thread::sleep(::std::time::Duration::from_secs(1)); diff --git a/derive/examples/std.rs b/derive/examples/std.rs index 401bb05c5..3c2c1e640 100644 --- a/derive/examples/std.rs +++ b/derive/examples/std.rs @@ -1,7 +1,7 @@ //! A simple example #![deny(missing_docs)] -use jsonrpc_core::futures::future::{self, Future, FutureResult}; -use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_core::futures::{self, future, TryFutureExt}; +use jsonrpc_core::{BoxFuture, IoHandler, Result}; use jsonrpc_core_client::transports::local; use jsonrpc_derive::rpc; @@ -18,7 +18,7 @@ pub trait Rpc { /// Performs asynchronous operation. #[rpc(name = "callAsync")] - fn call(&self, a: u64) -> FutureResult; + fn call(&self, a: u64) -> BoxFuture>; /// Handles a notification. #[rpc(name = "notify")] @@ -36,8 +36,8 @@ impl Rpc for RpcImpl { Ok(a + b) } - fn call(&self, _: u64) -> FutureResult { - future::ok("OK".to_owned()) + fn call(&self, _: u64) -> BoxFuture> { + Box::pin(future::ready(Ok("OK".to_owned()))) } fn notify(&self, a: u64) { @@ -49,9 +49,10 @@ fn main() { let mut io = IoHandler::new(); io.extend_with(RpcImpl.to_delegate()); - let fut = { - let (client, server) = local::connect::(io); - client.add(5, 6).map(|res| println!("5 + 6 = {}", res)).join(server) - }; - fut.wait().unwrap(); + let (client, server) = local::connect::(io); + let fut = client.add(5, 6).map_ok(|res| println!("5 + 6 = {}", res)); + + futures::executor::block_on(async move { futures::join!(fut, server) }) + .0 + .unwrap(); } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index fa8d9fdfe..13cb0fe7b 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -6,8 +6,8 @@ //! //! ``` //! use jsonrpc_derive::rpc; -//! use jsonrpc_core::{IoHandler, Error, Result}; -//! use jsonrpc_core::futures::future::{self, FutureResult}; +//! use jsonrpc_core::{IoHandler, Result, BoxFuture}; +//! use jsonrpc_core::futures::future; //! //! #[rpc(server)] //! pub trait Rpc { @@ -18,7 +18,7 @@ //! fn add(&self, a: u64, b: u64) -> Result; //! //! #[rpc(name = "callAsync")] -//! fn call(&self, a: u64) -> FutureResult; +//! fn call(&self, a: u64) -> BoxFuture>; //! } //! //! struct RpcImpl; @@ -31,8 +31,8 @@ //! Ok(a + b) //! } //! -//! fn call(&self, _: u64) -> FutureResult { -//! future::ok("OK".to_owned()).into() +//! fn call(&self, _: u64) -> BoxFuture> { +//! Box::pin(future::ready(Ok("OK".to_owned()).into())) //! } //! } //! @@ -56,7 +56,6 @@ //! use std::collections::HashMap; //! //! use jsonrpc_core::{Error, ErrorCode, Result}; -//! use jsonrpc_core::futures::Future; //! use jsonrpc_derive::rpc; //! use jsonrpc_pubsub::{Session, PubSubHandler, SubscriptionId, typed::{Subscriber, Sink}}; //! @@ -128,8 +127,8 @@ //! //! ``` //! use jsonrpc_core_client::transports::local; -//! use jsonrpc_core::futures::future::{self, Future, FutureResult}; -//! use jsonrpc_core::{Error, IoHandler, Result}; +//! use jsonrpc_core::futures::{self, future}; +//! use jsonrpc_core::{IoHandler, Result, BoxFuture}; //! use jsonrpc_derive::rpc; //! //! /// Rpc trait @@ -145,7 +144,7 @@ //! //! /// Performs asynchronous operation //! #[rpc(name = "callAsync")] -//! fn call(&self, a: u64) -> FutureResult; +//! fn call(&self, a: u64) -> BoxFuture>; //! } //! //! struct RpcImpl; @@ -159,20 +158,23 @@ //! Ok(a + b) //! } //! -//! fn call(&self, _: u64) -> FutureResult { -//! future::ok("OK".to_owned()) +//! fn call(&self, _: u64) -> BoxFuture> { +//! Box::pin(future::ready(Ok("OK".to_owned()))) //! } //! } //! //! fn main() { +//! let exec = futures::executor::ThreadPool::new().unwrap(); +//! exec.spawn_ok(run()) +//! } +//! async fn run() { //! let mut io = IoHandler::new(); //! io.extend_with(RpcImpl.to_delegate()); //! -//! let fut = { -//! let (client, server) = local::connect::(io); -//! client.add(5, 6).map(|res| println!("5 + 6 = {}", res)).join(server) -//! }; -//! fut.wait().unwrap(); +//! let (client, server) = local::connect::(io); +//! let res = client.add(5, 6).await.unwrap(); +//! println!("5 + 6 = {}", res); +//! server.await.unwrap() //! } //! //! ``` diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 7550ade4f..a95be556b 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -258,8 +258,10 @@ pub fn rpc_impl(input: syn::Item, options: &DeriveOptions) -> Result Result> { @@ -126,7 +127,7 @@ fn generate_client_methods(methods: &[MethodRegistration], options: &DeriveOptio let client_method = syn::parse_quote! { #(#attrs)* - pub fn #name(&self, #args) -> impl Future { + pub fn #name(&self, #args) -> impl Future> { let args = #args_serialized; self.inner.call_method(#rpc_name, #returns_str, args) } @@ -150,7 +151,7 @@ fn generate_client_methods(methods: &[MethodRegistration], options: &DeriveOptio let unsubscribe = unsubscribe.name(); let client_method = syn::parse_quote!( #(#attrs)* - pub fn #name(&self, #args) -> impl Future, Error=RpcError> { + pub fn #name(&self, #args) -> RpcResult> { let args_tuple = (#(#arg_names,)*); self.inner.subscribe(#subscribe, args_tuple, #subscription, #unsubscribe, #returns_str) } @@ -166,7 +167,7 @@ fn generate_client_methods(methods: &[MethodRegistration], options: &DeriveOptio let arg_names = compute_arg_identifiers(&args)?; let client_method = syn::parse_quote! { #(#attrs)* - pub fn #name(&self, #args) -> impl Future { + pub fn #name(&self, #args) -> RpcResult<()> { let args_tuple = (#(#arg_names,)*); self.inner.notify(#rpc_name, args_tuple) } @@ -264,22 +265,38 @@ fn compute_returns(method: &syn::TraitItemMethod, returns: &Option) -> R } fn try_infer_returns(output: &syn::ReturnType) -> Option { + let extract_path_segments = |ty: &syn::Type| match ty { + syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) => Some(segments.clone()), + _ => None, + }; + match output { - syn::ReturnType::Type(_, ty) => match &**ty { - syn::Type::Path(syn::TypePath { - path: syn::Path { segments, .. }, - .. - }) => match &segments[0] { + syn::ReturnType::Type(_, ty) => { + let segments = extract_path_segments(&**ty)?; + let check_segment = |seg: &syn::PathSegment| match seg { syn::PathSegment { ident, arguments, .. } => { - if ident.to_string().ends_with("Result") { - get_first_type_argument(arguments) + let id = ident.to_string(); + let inner = get_first_type_argument(arguments); + if id.ends_with("Result") { + Ok(inner) } else { - None + Err(inner) } } - }, - _ => None, - }, + }; + // Try out first argument (Result) or nested types like: + // BoxFuture> + match check_segment(&segments[0]) { + Ok(returns) => Some(returns?), + Err(inner) => { + let segments = extract_path_segments(&inner?)?; + check_segment(&segments[0]).ok().flatten() + } + } + } _ => None, } } @@ -287,9 +304,9 @@ fn try_infer_returns(output: &syn::ReturnType) -> Option { fn get_first_type_argument(args: &syn::PathArguments) -> Option { match args { syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }) => { - if args.len() > 0 { + if !args.is_empty() { match &args[0] { - syn::GenericArgument::Type(ty) => Some(ty.to_owned()), + syn::GenericArgument::Type(ty) => Some(ty.clone()), _ => None, } } else { diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 8c77dcded..384e92d39 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -53,9 +53,11 @@ impl MethodRegistration { let unsub_method_ident = unsubscribe.ident(); let unsub_closure = quote! { move |base, id, meta| { - use self::_futures::{Future, IntoFuture}; - Self::#unsub_method_ident(base, meta, id).into_future() - .map(|value| _jsonrpc_core::to_value(value) + use self::_futures::{FutureExt, TryFutureExt}; + self::_jsonrpc_core::WrapFuture::into_future( + Self::#unsub_method_ident(base, meta, id) + ) + .map_ok(|value| _jsonrpc_core::to_value(value) .expect("Expected always-serializable type; qed")) .map_err(Into::into) } @@ -278,15 +280,14 @@ impl RpcMethod { } else { quote! { Ok((#(#tuple_fields, )*)) => { - use self::_futures::{Future, IntoFuture}; - let fut = (method)#method_call - .into_future() - .map(|value| _jsonrpc_core::to_value(value) + use self::_futures::{FutureExt, TryFutureExt}; + let fut = self::_jsonrpc_core::WrapFuture::into_future((method)#method_call) + .map_ok(|value| _jsonrpc_core::to_value(value) .expect("Expected always-serializable type; qed")) .map_err(Into::into as fn(_) -> _jsonrpc_core::Error); - _futures::future::Either::A(fut) + _futures::future::Either::Left(fut) }, - Err(e) => _futures::future::Either::B(_futures::failed(e)), + Err(e) => _futures::future::Either::Right(_futures::future::ready(Err(e))), } }; diff --git a/derive/tests/client.rs b/derive/tests/client.rs index f5eff5ee8..317c3f995 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -1,4 +1,5 @@ -use futures::prelude::*; +use assert_matches::assert_matches; +use jsonrpc_core::futures::{self, FutureExt, TryFutureExt}; use jsonrpc_core::{IoHandler, Result}; use jsonrpc_core_client::transports::local; use jsonrpc_derive::rpc; @@ -35,16 +36,14 @@ mod client_server { let fut = client .clone() .add(3, 4) - .and_then(move |res| client.notify(res).map(move |_| res)) - .join(rpc_client) - .map(|(res, ())| { - assert_eq!(res, 7); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); + .map_ok(move |res| client.notify(res).map(move |_| res)) + .map(|res| { + assert_matches!(res, Ok(Ok(7))); }); - tokio::run(fut); + let exec = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + exec.spawn_ok(async move { + futures::join!(fut, rpc_client).1.unwrap(); + }); } } @@ -64,6 +63,8 @@ mod named_params { #[test] fn client_generates_correct_named_params_payload() { + use jsonrpc_core::futures::{FutureExt, TryFutureExt}; + let expected = json!({ // key names are derived from function parameter names in the trait "number": 3, "string": String::from("test string"), @@ -73,22 +74,18 @@ mod named_params { }); let mut handler = IoHandler::new(); - handler.add_method("call_with_named", |params: Params| Ok(params.into())); + handler.add_sync_method("call_with_named", |params: Params| Ok(params.into())); let (client, rpc_client) = local::connect::(handler); let fut = client .clone() .call_with_named(3, String::from("test string"), json!({"key": ["value"]})) - .and_then(move |res| client.notify(res.clone()).map(move |_| res)) - .join(rpc_client) - .map(move |(res, ())| { - assert_eq!(res, expected); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); + .map_ok(move |res| client.notify(res.clone()).map(move |_| res)) + .map(move |res| { + assert_matches!(res, Ok(Ok(x)) if x == expected); }); - tokio::run(fut); + let exec = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + exec.spawn_ok(async move { futures::join!(fut, rpc_client).1.unwrap() }); } } @@ -115,21 +112,17 @@ mod raw_params { }); let mut handler = IoHandler::new(); - handler.add_method("call_raw", |params: Params| Ok(params.into())); + handler.add_sync_method("call_raw", |params: Params| Ok(params.into())); let (client, rpc_client) = local::connect::(handler); let fut = client .clone() .call_raw_single_param(expected.clone()) - .and_then(move |res| client.notify(res.clone()).map(move |_| res)) - .join(rpc_client) - .map(move |(res, ())| { - assert_eq!(res, expected); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); + .map_ok(move |res| client.notify(res.clone()).map(move |_| res)) + .map(move |res| { + assert_matches!(res, Ok(Ok(x)) if x == expected); }); - tokio::run(fut); + let exec = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); + exec.spawn_ok(async move { futures::join!(fut, rpc_client).1.unwrap() }); } } diff --git a/derive/tests/pubsub-macros.rs b/derive/tests/pubsub-macros.rs index 0b5c88912..71f7615fd 100644 --- a/derive/tests/pubsub-macros.rs +++ b/derive/tests/pubsub-macros.rs @@ -4,7 +4,7 @@ use serde_json; #[macro_use] extern crate jsonrpc_derive; -use jsonrpc_core::futures::sync::mpsc; +use jsonrpc_core::futures::channel::mpsc; use jsonrpc_pubsub::typed::Subscriber; use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; use std::sync::Arc; @@ -75,7 +75,7 @@ struct Metadata; impl jsonrpc_core::Metadata for Metadata {} impl PubSubMetadata for Metadata { fn session(&self) -> Option> { - let (tx, _rx) = mpsc::channel(1); + let (tx, _rx) = mpsc::unbounded(); Some(Arc::new(Session::new(tx))) } } diff --git a/derive/tests/run-pass/client_only.rs b/derive/tests/run-pass/client_only.rs index ff86ef140..25f7712bb 100644 --- a/derive/tests/run-pass/client_only.rs +++ b/derive/tests/run-pass/client_only.rs @@ -4,7 +4,7 @@ extern crate jsonrpc_core_client; extern crate jsonrpc_derive; use jsonrpc_core::IoHandler; -use jsonrpc_core::futures::future::Future; +use jsonrpc_core::futures::{self, TryFutureExt}; use jsonrpc_core_client::transports::local; #[rpc(client)] @@ -24,9 +24,7 @@ fn main() { let (client, _rpc_client) = local::connect::(handler); client .add(5, 6) - .map(|res| println!("5 + 6 = {}", res)) + .map_ok(|res| println!("5 + 6 = {}", res)) }; - fut - .wait() - .ok(); + let _ = futures::executor::block_on(fut); } diff --git a/derive/tests/run-pass/client_with_generic_trait_bounds.rs b/derive/tests/run-pass/client_with_generic_trait_bounds.rs index 196f189ba..b0e780c19 100644 --- a/derive/tests/run-pass/client_with_generic_trait_bounds.rs +++ b/derive/tests/run-pass/client_with_generic_trait_bounds.rs @@ -1,5 +1,5 @@ -use jsonrpc_core::futures::future::{self, FutureResult}; -use jsonrpc_core::{Error, IoHandler, Result}; +use jsonrpc_core::futures::future; +use jsonrpc_core::{IoHandler, Result, BoxFuture}; use jsonrpc_derive::rpc; use std::collections::BTreeMap; @@ -15,7 +15,7 @@ where /// Performs asynchronous operation #[rpc(name = "beFancy")] - fn call(&self, a: One) -> FutureResult<(One, Two), Error>; + fn call(&self, a: One) -> BoxFuture>; } struct RpcImpl; @@ -26,8 +26,8 @@ impl Rpc for RpcImpl { Ok(Default::default()) } - fn call(&self, num: u64) -> FutureResult<(u64, String), Error> { - crate::future::finished((num + 999, "hello".into())) + fn call(&self, num: u64) -> BoxFuture> { + Box::pin(future::ready(Ok((num + 999, "hello".into())))) } } diff --git a/http/Cargo.toml b/http/Cargo.toml index 7474084c6..f881710e0 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,16 +8,18 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] +futures01 = { version = "0.1", package = "futures" } +futures03 = { version = "0.3", package = "futures", features = ["compat"] } hyper = "0.12" -jsonrpc-core = { version = "14.2", path = "../core" } -jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } +jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } log = "0.4" net2 = "0.2" -unicase = "2.0" parking_lot = "0.10.0" +unicase = "2.0" [dev-dependencies] env_logger = "0.7" diff --git a/http/README.md b/http/README.md index 257704042..c80a2e65c 100644 --- a/http/README.md +++ b/http/README.md @@ -9,7 +9,7 @@ Rust http server using JSON-RPC 2.0. ``` [dependencies] -jsonrpc-http-server = "14.2" +jsonrpc-http-server = "15.0" ``` `main.rs` diff --git a/http/examples/http_async.rs b/http/examples/http_async.rs index 44704f5ea..c243bb875 100644 --- a/http/examples/http_async.rs +++ b/http/examples/http_async.rs @@ -4,7 +4,7 @@ use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBui fn main() { let mut io = IoHandler::default(); io.add_method("say_hello", |_params| { - futures::finished(Value::String("hello".to_owned())) + futures::future::ready(Ok(Value::String("hello".to_owned()))) }); let server = ServerBuilder::new(io) diff --git a/http/examples/http_meta.rs b/http/examples/http_meta.rs index b3b40a407..50d412148 100644 --- a/http/examples/http_meta.rs +++ b/http/examples/http_meta.rs @@ -13,13 +13,13 @@ fn main() { io.add_method_with_meta("say_hello", |_params: Params, meta: Meta| { let auth = meta.auth.unwrap_or_else(String::new); - if auth.as_str() == "let-me-in" { + futures::future::ready(if auth.as_str() == "let-me-in" { Ok(Value::String("Hello World!".to_owned())) } else { Ok(Value::String( "Please send a valid Bearer token in Authorization header.".to_owned(), )) - } + }) }); let server = ServerBuilder::new(io) diff --git a/http/examples/http_middleware.rs b/http/examples/http_middleware.rs index 47d03cd06..68629d78c 100644 --- a/http/examples/http_middleware.rs +++ b/http/examples/http_middleware.rs @@ -5,7 +5,7 @@ use jsonrpc_http_server::{hyper, AccessControlAllowOrigin, DomainsValidation, Re fn main() { let mut io = IoHandler::default(); io.add_method("say_hello", |_params| { - futures::finished(Value::String("hello".to_owned())) + futures::future::ready(Ok(Value::String("hello".to_owned()))) }); let server = ServerBuilder::new(io) diff --git a/http/examples/server.rs b/http/examples/server.rs index 2e9ebc5e0..a8bdfce5d 100644 --- a/http/examples/server.rs +++ b/http/examples/server.rs @@ -5,7 +5,7 @@ fn main() { env_logger::init(); let mut io = IoHandler::default(); - io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); let server = ServerBuilder::new(io) .threads(3) diff --git a/http/src/handler.rs b/http/src/handler.rs index 6f72a345b..b6abb796f 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -6,11 +6,11 @@ use std::{fmt, mem, str}; use hyper::header::{self, HeaderMap, HeaderValue}; use hyper::{self, service::Service, Body, Method}; -use crate::jsonrpc::futures::{future, Async, Future, Poll, Stream}; use crate::jsonrpc::serde_json; -use crate::jsonrpc::{self as core, middleware, FutureResult, Metadata, Middleware}; +use crate::jsonrpc::{self as core, middleware, Metadata, Middleware}; use crate::response::Response; use crate::server_utils::cors; +use futures01::{Async, Future, Poll, Stream}; use crate::{utils, AllowedHosts, CorsDomains, RequestMiddleware, RequestMiddlewareAction, RestApi}; @@ -57,7 +57,11 @@ impl> ServerHandler { } } -impl> Service for ServerHandler { +impl> Service for ServerHandler +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ type ReqBody = Body; type ResBody = Body; type Error = hyper::Error; @@ -117,7 +121,11 @@ pub enum Handler> { Middleware(Box, Error = hyper::Error> + Send>), } -impl> Future for Handler { +impl> Future for Handler +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ type Item = hyper::Response; type Error = hyper::Error; @@ -135,21 +143,13 @@ impl> Future for Handler { } } -enum RpcPollState -where - F: Future, Error = ()>, - G: Future, Error = ()>, -{ - Ready(RpcHandlerState), - NotReady(RpcHandlerState), +enum RpcPollState { + Ready(RpcHandlerState), + NotReady(RpcHandlerState), } -impl RpcPollState -where - F: Future, Error = ()>, - G: Future, Error = ()>, -{ - fn decompose(self) -> (RpcHandlerState, bool) { +impl RpcPollState { + fn decompose(self) -> (RpcHandlerState, bool) { use self::RpcPollState::*; match self { Ready(handler) => (handler, true), @@ -158,16 +158,7 @@ where } } -type FutureResponse = future::Map< - future::Either, ()>, core::FutureRpcResult>, - fn(Option) -> Response, ->; - -enum RpcHandlerState -where - F: Future, Error = ()>, - G: Future, Error = ()>, -{ +enum RpcHandlerState { ReadingHeaders { request: hyper::Request, cors_domains: CorsDomains, @@ -190,16 +181,12 @@ where metadata: M, }, Writing(Response), - Waiting(FutureResult), - WaitingForResponse(FutureResponse), + Waiting(Box, Error = ()> + Send>), + WaitingForResponse(Box + Send>), Done, } -impl fmt::Debug for RpcHandlerState -where - F: Future, Error = ()>, - G: Future, Error = ()>, -{ +impl fmt::Debug for RpcHandlerState { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { use self::RpcHandlerState::*; @@ -218,7 +205,7 @@ where pub struct RpcHandler> { jsonrpc_handler: WeakRpc, - state: RpcHandlerState, + state: RpcHandlerState, is_options: bool, cors_allow_origin: cors::AllowCors, cors_allow_headers: cors::AllowCors>, @@ -229,7 +216,11 @@ pub struct RpcHandler> { keep_alive: bool, } -impl> Future for RpcHandler { +impl> Future for RpcHandler +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ type Item = hyper::Response; type Error = hyper::Error; @@ -337,12 +328,12 @@ impl From for BodyError { } } -impl> RpcHandler { - fn read_headers( - &self, - request: hyper::Request, - continue_on_invalid_cors: bool, - ) -> RpcHandlerState { +impl> RpcHandler +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ + fn read_headers(&self, request: hyper::Request, continue_on_invalid_cors: bool) -> RpcHandlerState { if self.cors_allow_origin == cors::AllowCors::Invalid && !continue_on_invalid_cors { return RpcHandlerState::Writing(Response::invalid_allow_origin()); } @@ -401,12 +392,9 @@ impl> RpcHandler { } } - fn process_health( - &self, - method: String, - metadata: M, - ) -> Result, hyper::Error> { + fn process_health(&self, method: String, metadata: M) -> Result, hyper::Error> { use self::core::types::{Call, Failure, Id, MethodCall, Output, Params, Request, Success, Version}; + use futures03::{FutureExt, TryFutureExt}; // Create a request let call = Request::Single(Call::MethodCall(MethodCall { @@ -420,9 +408,10 @@ impl> RpcHandler { Some(h) => h.handler.handle_rpc_request(call, metadata), None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), }; + let response = response.map(Ok).compat(); - Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse( - future::Either::B(response).map(|res| match res { + Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse(Box::new( + response.map(|res| match res { Some(core::Response::Single(Output::Success(Success { result, .. }))) => { let result = serde_json::to_string(&result).expect("Serialization of result is infallible;qed"); @@ -435,15 +424,12 @@ impl> RpcHandler { } e => Response::internal_error(format!("Invalid response for health request: {:?}", e)), }), - ))) + )))) } - fn process_rest( - &self, - uri: hyper::Uri, - metadata: M, - ) -> Result, hyper::Error> { + fn process_rest(&self, uri: hyper::Uri, metadata: M) -> Result, hyper::Error> { use self::core::types::{Call, Id, MethodCall, Params, Request, Value, Version}; + use futures03::{FutureExt, TryFutureExt}; // skip the initial / let mut it = uri.path().split('/').skip(1); @@ -470,12 +456,11 @@ impl> RpcHandler { Some(h) => h.handler.handle_rpc_request(call, metadata), None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), }; + let response = response.map(Ok).compat(); - Ok(RpcPollState::Ready(RpcHandlerState::Waiting( - future::Either::B(response).map(|res| { - res.map(|x| serde_json::to_string(&x).expect("Serialization of response is infallible;qed")) - }), - ))) + Ok(RpcPollState::Ready(RpcHandlerState::Waiting(Box::new(response.map( + |res| res.map(|x| serde_json::to_string(&x).expect("Serialization of response is infallible;qed")), + ))))) } fn process_body( @@ -484,7 +469,7 @@ impl> RpcHandler { mut request: Vec, uri: Option, metadata: M, - ) -> Result, BodyError> { + ) -> Result, BodyError> { loop { match body.poll()? { Async::Ready(Some(chunk)) => { @@ -499,6 +484,7 @@ impl> RpcHandler { request.extend_from_slice(&*chunk) } Async::Ready(None) => { + use futures03::{FutureExt, TryFutureExt}; if let (Some(uri), true) = (uri, request.is_empty()) { return Ok(RpcPollState::Ready(RpcHandlerState::ProcessRest { uri, metadata })); } @@ -517,7 +503,8 @@ impl> RpcHandler { }; // Content is ready - return Ok(RpcPollState::Ready(RpcHandlerState::Waiting(response))); + let response = response.map(Ok).compat(); + return Ok(RpcPollState::Ready(RpcHandlerState::Waiting(Box::new(response)))); } Async::NotReady => { return Ok(RpcPollState::NotReady(RpcHandlerState::ReadingBody { diff --git a/http/src/lib.rs b/http/src/lib.rs index 7cc40bedf..9fc4a2a7c 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -6,7 +6,7 @@ //! //! fn main() { //! let mut io = IoHandler::new(); -//! io.add_method("say_hello", |_: Params| { +//! io.add_sync_method("say_hello", |_: Params| { //! Ok(Value::String("hello".to_string())) //! }); //! @@ -42,10 +42,10 @@ use std::thread; use parking_lot::Mutex; -use crate::jsonrpc::futures::sync::oneshot; -use crate::jsonrpc::futures::{self, Future, Stream}; use crate::jsonrpc::MetaIoHandler; use crate::server_utils::reactor::{Executor, UninitializedExecutor}; +use futures01::sync::oneshot; +use futures01::{future, Future, Stream}; use hyper::{server, Body}; use jsonrpc_core as jsonrpc; @@ -79,7 +79,7 @@ impl From for RequestMiddlewareAction { fn from(o: Response) -> Self { RequestMiddlewareAction::Respond { should_validate_hosts: true, - response: Box::new(futures::future::ok(o.into())), + response: Box::new(future::ok(o.into())), } } } @@ -88,7 +88,7 @@ impl From> for RequestMiddlewareAction { fn from(response: hyper::Response) -> Self { RequestMiddlewareAction::Respond { should_validate_hosts: true, - response: Box::new(futures::future::ok(response)), + response: Box::new(future::ok(response)), } } } @@ -247,7 +247,11 @@ pub struct ServerBuilder = max_request_body_size: usize, } -impl> ServerBuilder { +impl> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new `ServerBuilder` for given `IoHandler`. /// /// By default: @@ -261,7 +265,11 @@ impl> ServerBuilder> ServerBuilder { +impl> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new `ServerBuilder` for given `IoHandler`. /// /// By default: @@ -524,7 +532,10 @@ fn serve>( keep_alive: bool, reuse_port: bool, max_request_body_size: usize, -) { +) where + S::Future: Unpin, + S::CallFuture: Unpin, +{ let (shutdown_signal, local_addr_tx, done_tx) = signals; executor.spawn({ let handle = tokio::reactor::Handle::default(); @@ -551,13 +562,13 @@ fn serve>( Ok((listener, local_addr)) => { // Send local address match local_addr_tx.send(Ok(local_addr)) { - Ok(_) => futures::future::ok((listener, local_addr)), + Ok(_) => future::ok((listener, local_addr)), Err(_) => { warn!( "Thread {:?} unable to reach receiver, closing server", thread::current().name() ); - futures::future::err(()) + future::err(()) } } } @@ -565,7 +576,7 @@ fn serve>( // Send error let _send_result = local_addr_tx.send(Err(err)); - futures::future::err(()) + future::err(()) } }; diff --git a/http/src/tests.rs b/http/src/tests.rs index 31664a6bc..302642599 100644 --- a/http/src/tests.rs +++ b/http/src/tests.rs @@ -6,7 +6,7 @@ use std::net::TcpStream; use std::str::Lines; use std::time::Duration; -use self::jsonrpc_core::futures::{self, Future}; +use self::jsonrpc_core::futures; use super::*; fn serve_hosts(hosts: Vec) -> Server { @@ -38,7 +38,7 @@ fn serve ServerBuilder>(alter: F) -> Server { fn serve_allow_headers(cors_allow_headers: cors::AccessControlAllowHeaders) -> Server { let mut io = IoHandler::default(); - io.add_method("hello", |params: Params| match params.parse::<(u64,)>() { + io.add_sync_method("hello", |params: Params| match params.parse::<(u64,)>() { Ok((num,)) => Ok(Value::String(format!("world: {}", num))), _ => Ok(Value::String("world".into())), }); @@ -54,16 +54,17 @@ fn serve_allow_headers(cors_allow_headers: cors::AccessControlAllowHeaders) -> S fn io() -> IoHandler { let mut io = IoHandler::default(); - io.add_method("hello", |params: Params| match params.parse::<(u64,)>() { + io.add_sync_method("hello", |params: Params| match params.parse::<(u64,)>() { Ok((num,)) => Ok(Value::String(format!("world: {}", num))), _ => Ok(Value::String("world".into())), }); - io.add_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); + io.add_sync_method("fail", |_: Params| Err(Error::new(ErrorCode::ServerError(-34)))); io.add_method("hello_async", |_params: Params| { - futures::finished(Value::String("world".into())) + futures::future::ready(Ok(Value::String("world".into()))) }); io.add_method("hello_async2", |_params: Params| { - let (c, p) = futures::oneshot(); + use futures::TryFutureExt; + let (c, p) = futures::channel::oneshot::channel(); thread::spawn(move || { thread::sleep(Duration::from_millis(10)); c.send(Value::String("world".into())).unwrap(); @@ -1503,7 +1504,7 @@ fn should_drop_io_handler_when_server_is_closed() { let my_ref = Arc::new(Mutex::new(5)); let weak = Arc::downgrade(&my_ref); let mut io = IoHandler::default(); - io.add_method("hello", move |_| { + io.add_sync_method("hello", move |_| { Ok(Value::String(format!("{}", my_ref.lock().unwrap()))) }); let server = ServerBuilder::new(io) diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 00ada6ec4..fa4783dd8 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,13 +7,15 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] +futures01 = { version = "0.1", package = "futures" } +futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "14.2", path = "../core" } -jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } +jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } parity-tokio-ipc = "0.4" parking_lot = "0.10.0" diff --git a/ipc/README.md b/ipc/README.md index 60561eb77..b709f8071 100644 --- a/ipc/README.md +++ b/ipc/README.md @@ -9,7 +9,7 @@ IPC server (Windows & Linux) for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ipc-server = "14.2" +jsonrpc-ipc-server = "15.0" ``` `main.rs` diff --git a/ipc/examples/ipc.rs b/ipc/examples/ipc.rs index 5a94bce0f..483794887 100644 --- a/ipc/examples/ipc.rs +++ b/ipc/examples/ipc.rs @@ -4,7 +4,7 @@ use jsonrpc_ipc_server::jsonrpc_core::*; fn main() { let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); let _server = jsonrpc_ipc_server::ServerBuilder::new(io) .start("/tmp/parity-example.ipc") .expect("Server should start ok"); diff --git a/ipc/src/meta.rs b/ipc/src/meta.rs index 8eff47a0e..a1ae788d4 100644 --- a/ipc/src/meta.rs +++ b/ipc/src/meta.rs @@ -1,4 +1,4 @@ -use crate::jsonrpc::futures::sync::mpsc; +use crate::jsonrpc::futures::channel::mpsc; use crate::jsonrpc::Metadata; use crate::server_utils::session; @@ -9,7 +9,7 @@ pub struct RequestContext<'a> { /// Remote UDS endpoint pub endpoint_addr: &'a ::parity_tokio_ipc::RemoteId, /// Direct pipe sender - pub sender: mpsc::Sender, + pub sender: mpsc::UnboundedSender, } /// Metadata extractor (per session) diff --git a/ipc/src/select_with_weak.rs b/ipc/src/select_with_weak.rs index 7bfec7c7c..409aa46cd 100644 --- a/ipc/src/select_with_weak.rs +++ b/ipc/src/select_with_weak.rs @@ -1,5 +1,5 @@ -use crate::jsonrpc::futures::stream::{Fuse, Stream}; -use crate::jsonrpc::futures::{Async, Poll}; +use futures01::stream::{Fuse, Stream}; +use futures01::{Async, Poll}; pub trait SelectWithWeakExt: Stream { fn select_with_weak(self, other: S) -> SelectWithWeak diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 933ad8648..47aaa9f29 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -1,8 +1,12 @@ use std::sync::Arc; -use crate::jsonrpc::futures::sync::{mpsc, oneshot}; -use crate::jsonrpc::futures::{future, Future, Sink, Stream}; -use crate::jsonrpc::{middleware, FutureResult, MetaIoHandler, Metadata, Middleware}; +use crate::jsonrpc::futures::channel::mpsc; +use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; +use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; +use crate::select_with_weak::SelectWithWeakExt; +use futures01::{future, sync::oneshot, Future, Sink, Stream}; +use parity_tokio_ipc::Endpoint; +use parking_lot::Mutex; use tokio_service::{self, Service as TokioService}; use crate::server_utils::{ @@ -10,11 +14,7 @@ use crate::server_utils::{ tokio::{reactor::Handle, runtime::TaskExecutor}, tokio_codec::Framed, }; -use parking_lot::Mutex; -use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; -use crate::select_with_weak::SelectWithWeakExt; -use parity_tokio_ipc::Endpoint; pub use parity_tokio_ipc::SecurityAttributes; /// IPC server session @@ -30,17 +30,22 @@ impl> Service { } } -impl> tokio_service::Service for Service { +impl> tokio_service::Service for Service +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ type Request = String; type Response = Option; type Error = (); - type Future = FutureResult; + type Future = Box, Error = ()> + Send>; fn call(&self, req: Self::Request) -> Self::Future { + use futures03::{FutureExt, TryFutureExt}; trace!(target: "ipc", "Received request: {}", req); - self.handler.handle_request(&req, self.meta.clone()) + Box::new(self.handler.handle_request(&req, self.meta.clone()).map(Ok).compat()) } } @@ -57,7 +62,11 @@ pub struct ServerBuilder = middleware::Noop> client_buffer_size: usize, } -impl> ServerBuilder { +impl> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new IPC server build given the `IoHandler`. pub fn new(io_handler: T) -> ServerBuilder where @@ -67,7 +76,11 @@ impl> ServerBuilder { } } -impl> ServerBuilder { +impl> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new IPC server build given the `IoHandler` and metadata extractor. pub fn with_meta_extractor(io_handler: T, extractor: E) -> ServerBuilder where @@ -189,7 +202,7 @@ impl> ServerBuilder { stats.open_session(session_id) } - let (sender, receiver) = mpsc::channel(16); + let (sender, receiver) = mpsc::unbounded(); let meta = meta_extractor.extract(&RequestContext { endpoint_addr: &remote_id, session_id, @@ -215,10 +228,7 @@ impl> ServerBuilder { .filter_map(|x| x) // we use `select_with_weak` here, instead of `select`, to close the stream // as soon as the ipc pipe is closed - .select_with_weak(receiver.map_err(|e| { - warn!(target: "ipc", "Notification error: {:?}", e); - std::io::ErrorKind::Other.into() - })); + .select_with_weak(futures03::TryStreamExt::compat(futures03::StreamExt::map(receiver, Ok))); let writer = writer.send_all(responses).then(move |_| { trace!(target: "ipc", "Peer: service finished"); @@ -330,29 +340,19 @@ impl CloseHandle { #[cfg(test)] #[cfg(not(windows))] mod tests { - use tokio_uds; - - use self::tokio_uds::UnixStream; - use super::SecurityAttributes; - use super::{Server, ServerBuilder}; - use crate::jsonrpc::futures::sync::{mpsc, oneshot}; - use crate::jsonrpc::futures::{future, Future, Sink, Stream}; - use crate::jsonrpc::{MetaIoHandler, Value}; - use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; - use crate::server_utils::codecs; - use crate::server_utils::{ - tokio::{self, timer::Delay}, - tokio_codec::Decoder, - }; - use parking_lot::Mutex; - use std::sync::Arc; + use super::*; + + use futures01::{Future, Sink, Stream}; + use jsonrpc_core::Value; + use jsonrpc_server_utils::tokio::{self, timer::Delay}; + use jsonrpc_server_utils::tokio_codec::Decoder; use std::thread; - use std::time; - use std::time::{Duration, Instant}; + use std::time::{self, Duration, Instant}; + use tokio_uds::UnixStream; fn server_builder() -> ServerBuilder { let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); ServerBuilder::new(io) } @@ -381,7 +381,7 @@ mod tests { crate::logger::init_log(); let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); let server = ServerBuilder::new(io); let _server = server @@ -430,7 +430,7 @@ mod tests { crate::logger::init_log(); let path = "/tmp/test-ipc-45000"; let server = run(path); - let (stop_signal, stop_receiver) = mpsc::channel(400); + let (stop_signal, stop_receiver) = futures01::sync::mpsc::channel(400); let mut handles = Vec::new(); for _ in 0..4 { @@ -505,7 +505,7 @@ mod tests { let path = "/tmp/test-ipc-60000"; let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_huge_hello", |_params| Ok(Value::String(huge_response_test_str()))); + io.add_sync_method("say_huge_hello", |_params| Ok(Value::String(huge_response_test_str()))); let builder = ServerBuilder::new(io); let server = builder.start(path).expect("Server must run with no issues"); @@ -547,7 +547,7 @@ mod tests { } struct SessionEndExtractor { - drop_receivers: Arc>>>, + drop_receivers: Arc>>>, } impl MetaExtractor> for SessionEndExtractor { @@ -563,7 +563,7 @@ mod tests { crate::logger::init_log(); let path = "/tmp/test-ipc-30009"; - let (signal, receiver) = mpsc::channel(16); + let (signal, receiver) = futures01::sync::mpsc::channel(16); let session_metadata_extractor = SessionEndExtractor { drop_receivers: Arc::new(Mutex::new(signal)), }; diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 589dd367d..6a6301400 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,19 +8,19 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] +futures = { version = "0.3", features = ["thread-pool"] } +jsonrpc-core = { version = "15.0", path = "../core" } +lazy_static = "1.4" log = "0.4" -parking_lot = "0.10.0" -jsonrpc-core = { version = "14.2", path = "../core" } -serde = "1.0" +parking_lot = "0.11.0" rand = "0.7" +serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "14.2", path = "../tcp" } -futures = { version = "0.3", features = ["compat", "thread-pool"] } -lazy_static = "1.4" +jsonrpc-tcp-server = { version = "15.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/examples/pubsub.rs b/pubsub/examples/pubsub.rs index 6e78a20ce..b44896afa 100644 --- a/pubsub/examples/pubsub.rs +++ b/pubsub/examples/pubsub.rs @@ -5,8 +5,6 @@ use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; use jsonrpc_tcp_server::{RequestContext, ServerBuilder}; -use jsonrpc_core::futures::Future; - /// To test the server: /// /// ```bash @@ -16,7 +14,7 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); let is_done = Arc::new(atomic::AtomicBool::default()); let is_done2 = is_done.clone(); @@ -36,7 +34,7 @@ fn main() { let is_done = is_done.clone(); thread::spawn(move || { - let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); // or subscriber.reject(Error {} ); // or drop(subscriber) @@ -46,7 +44,7 @@ fn main() { } thread::sleep(time::Duration::from_millis(100)); - match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { + match sink.notify(Params::Array(vec![Value::Number(10.into())])) { Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); diff --git a/pubsub/examples/pubsub_simple.rs b/pubsub/examples/pubsub_simple.rs index bfd8fadac..bfd5bae1d 100644 --- a/pubsub/examples/pubsub_simple.rs +++ b/pubsub/examples/pubsub_simple.rs @@ -5,8 +5,6 @@ use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; use jsonrpc_tcp_server::{RequestContext, ServerBuilder}; -use jsonrpc_core::futures::Future; - /// To test the server: /// /// ```bash @@ -18,7 +16,7 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); io.add_subscription( "hello", @@ -35,13 +33,13 @@ fn main() { } thread::spawn(move || { - let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); // or subscriber.reject(Error {} ); // or drop(subscriber) loop { thread::sleep(time::Duration::from_millis(100)); - match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { + match sink.notify(Params::Array(vec![Value::Number(10.into())])) { Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index bae1e4eb4..e7e479a80 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "14.2", path = "../../core" } -jsonrpc-pubsub = { version = "14.2", path = "../" } -jsonrpc-ws-server = { version = "14.2", path = "../../ws" } -jsonrpc-ipc-server = { version = "14.2", path = "../../ipc" } +jsonrpc-core = { version = "15.0", path = "../../core" } +jsonrpc-pubsub = { version = "15.0", path = "../" } +jsonrpc-ws-server = { version = "15.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "15.0", path = "../../ipc" } diff --git a/pubsub/more-examples/examples/pubsub_ipc.rs b/pubsub/more-examples/examples/pubsub_ipc.rs index bdd805287..d8798c971 100644 --- a/pubsub/more-examples/examples/pubsub_ipc.rs +++ b/pubsub/more-examples/examples/pubsub_ipc.rs @@ -9,8 +9,6 @@ use jsonrpc_core::*; use jsonrpc_ipc_server::{RequestContext, ServerBuilder, SessionId, SessionStats}; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; -use jsonrpc_core::futures::Future; - /// To test the server: /// /// ```bash @@ -20,7 +18,7 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); io.add_subscription( "hello", @@ -37,13 +35,13 @@ fn main() { } thread::spawn(move || { - let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); // or subscriber.reject(Error {} ); // or drop(subscriber) loop { thread::sleep(time::Duration::from_millis(100)); - match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { + match sink.notify(Params::Array(vec![Value::Number(10.into())])) { Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); @@ -53,9 +51,9 @@ fn main() { } }); }), - ("remove_hello", |_id: SubscriptionId, _meta| -> Result { + ("remove_hello", |_id: SubscriptionId, _meta| { println!("Closing subscription"); - Ok(Value::Bool(true)) + futures::future::ready(Ok(Value::Bool(true))) }), ); diff --git a/pubsub/more-examples/examples/pubsub_ws.rs b/pubsub/more-examples/examples/pubsub_ws.rs index 5b0de4405..463bb1182 100644 --- a/pubsub/more-examples/examples/pubsub_ws.rs +++ b/pubsub/more-examples/examples/pubsub_ws.rs @@ -9,8 +9,6 @@ use jsonrpc_core::*; use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; use jsonrpc_ws_server::{RequestContext, ServerBuilder}; -use jsonrpc_core::futures::Future; - /// Use following node.js code to test: /// /// ```js @@ -36,7 +34,7 @@ use jsonrpc_core::futures::Future; /// ``` fn main() { let mut io = PubSubHandler::new(MetaIoHandler::default()); - io.add_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params: Params| Ok(Value::String("hello".to_string()))); io.add_subscription( "hello", @@ -53,13 +51,13 @@ fn main() { } thread::spawn(move || { - let sink = subscriber.assign_id_async(SubscriptionId::Number(5)).wait().unwrap(); + let sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); // or subscriber.reject(Error {} ); // or drop(subscriber) loop { thread::sleep(time::Duration::from_millis(1000)); - match sink.notify(Params::Array(vec![Value::Number(10.into())])).wait() { + match sink.notify(Params::Array(vec![Value::Number(10.into())])) { Ok(_) => {} Err(_) => { println!("Subscription has ended, finishing."); @@ -69,10 +67,13 @@ fn main() { } }); }), - ("remove_hello", |_id: SubscriptionId, _meta| -> BoxFuture { - println!("Closing subscription"); - Box::new(futures::future::ok(Value::Bool(true))) - }), + ( + "remove_hello", + |_id: SubscriptionId, _meta| -> BoxFuture> { + println!("Closing subscription"); + Box::pin(futures::future::ready(Ok(Value::Bool(true)))) + }, + ), ); let server = diff --git a/pubsub/src/delegates.rs b/pubsub/src/delegates.rs index d2da169fe..7df77e079 100644 --- a/pubsub/src/delegates.rs +++ b/pubsub/src/delegates.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; use std::sync::Arc; -use crate::core::futures::IntoFuture; -use crate::core::{self, Error, Metadata, Params, RemoteProcedure, RpcMethod, Value}; +use crate::core::futures::Future; +use crate::core::{self, Metadata, Params, RemoteProcedure, RpcMethod, Value}; use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; use crate::subscription::{new_subscription, Subscriber}; use crate::types::{PubSubMetadata, SubscriptionId}; @@ -29,15 +29,14 @@ impl UnsubscribeRpcMethod for DelegateSubscription where M: PubSubMetadata, F: Fn(&T, SubscriptionId, Option) -> I, - I: IntoFuture, + I: Future> + Send + 'static, T: Send + Sync + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { - type Out = I::Future; + type Out = I; fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out { let closure = &self.closure; - closure(&self.delegate, id, meta).into_future() + closure(&self.delegate, id, meta) } } @@ -72,9 +71,8 @@ where Sub: Fn(&T, Params, M, Subscriber), Sub: Send + Sync + 'static, Unsub: Fn(&T, SubscriptionId, Option) -> I, - I: IntoFuture, + I: Future> + Send + 'static, Unsub: Send + Sync + 'static, - I::Future: Send + 'static, { let (sub, unsub) = new_subscription( name, @@ -98,13 +96,13 @@ where self.inner.add_alias(from, to) } + // TODO [ToDr] Consider sync? /// Adds async method to the delegate. pub fn add_method(&mut self, name: &str, method: F) where F: Fn(&T, Params) -> I, - I: IntoFuture, + I: Future> + Send + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { self.inner.add_method(name, method) } @@ -113,9 +111,8 @@ where pub fn add_method_with_meta(&mut self, name: &str, method: F) where F: Fn(&T, Params, M) -> I, - I: IntoFuture, + I: Future> + Send + 'static, F: Send + Sync + 'static, - I::Future: Send + 'static, { self.inner.add_method_with_meta(name, method) } diff --git a/pubsub/src/handler.rs b/pubsub/src/handler.rs index 1b3567579..912f7701f 100644 --- a/pubsub/src/handler.rs +++ b/pubsub/src/handler.rs @@ -1,5 +1,5 @@ use crate::core; -use crate::core::futures::{Future, IntoFuture}; +use crate::core::futures::Future; use crate::subscription::{new_subscription, Subscriber}; use crate::types::{PubSubMetadata, SubscriptionId}; @@ -23,7 +23,7 @@ where /// Unsubscribe handler pub trait UnsubscribeRpcMethod: Send + Sync + 'static { /// Output type - type Out: Future + Send + 'static; + type Out: Future> + Send + 'static; /// Called when client is requesting to cancel existing subscription. /// /// Metadata is not available if the session was closed without unsubscribing. @@ -33,12 +33,11 @@ pub trait UnsubscribeRpcMethod: Send + Sync + 'static { impl UnsubscribeRpcMethod for F where F: Fn(SubscriptionId, Option) -> I + Send + Sync + 'static, - I: IntoFuture, - I::Future: Send + 'static, + I: Future> + Send + 'static, { - type Out = I::Future; + type Out = I; fn call(&self, id: SubscriptionId, meta: Option) -> Self::Out { - (*self)(id, meta).into_future() + (*self)(id, meta) } } @@ -99,8 +98,8 @@ mod tests { use std::sync::Arc; use crate::core; + use crate::core::futures::channel::mpsc; use crate::core::futures::future; - use crate::core::futures::sync::mpsc; use crate::subscription::{Session, Subscriber}; use crate::types::{PubSubMetadata, SubscriptionId}; @@ -136,7 +135,7 @@ mod tests { ); // when - let (tx, _rx) = mpsc::channel(1); + let (tx, _rx) = mpsc::unbounded(); let meta = Metadata(Arc::new(Session::new(tx))); let req = r#"{"jsonrpc":"2.0","id":1,"method":"subscribe_hello","params":null}"#; let res = handler.handle_request_sync(req, meta); diff --git a/pubsub/src/manager.rs b/pubsub/src/manager.rs index 3c8d77ba2..b20da7272 100644 --- a/pubsub/src/manager.rs +++ b/pubsub/src/manager.rs @@ -21,8 +21,8 @@ use std::sync::{ Arc, }; -use crate::core::futures::sync::oneshot; -use crate::core::futures::{future as future01, Future as Future01}; +use crate::core::futures::channel::oneshot; +use crate::core::futures::{self, task, Future, FutureExt, TryFutureExt}; use crate::{ typed::{Sink, Subscriber}, SubscriptionId, @@ -33,8 +33,8 @@ use parking_lot::Mutex; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; -/// Alias for an implementation of `futures::future::Executor`. -pub type TaskExecutor = Arc + Send>> + Send + Sync>; +/// Cloneable `Spawn` handle. +pub type TaskExecutor = Arc; type ActiveSubscriptions = Arc>>>; @@ -170,23 +170,28 @@ impl SubscriptionManager { /// /// Second parameter is a function that converts Subscriber Sink into a Future. /// This future will be driven to completion by the underlying event loop - pub fn add(&self, subscriber: Subscriber, into_future: G) -> SubscriptionId + pub fn add(&self, subscriber: Subscriber, into_future: G) -> SubscriptionId where - G: FnOnce(Sink) -> R, - R: future01::IntoFuture, - F: future01::Future + Send + 'static, + G: FnOnce(Sink) -> F, + F: Future + Send + 'static, { let id = self.id_provider.next_id(); let subscription_id: SubscriptionId = id.into(); if let Ok(sink) = subscriber.assign_id(subscription_id.clone()) { let (tx, rx) = oneshot::channel(); - let future = into_future(sink) - .into_future() - .select(rx.map_err(|e| warn!("Error timing out: {:?}", e))) - .then(|_| Ok(())); + let f = into_future(sink).fuse(); + let rx = rx.map_err(|e| warn!("Error timing out: {:?}", e)).fuse(); + let future = async move { + futures::pin_mut!(f); + futures::pin_mut!(rx); + futures::select! { + a = f => a, + _ = rx => (), + } + }; self.active_subscriptions.lock().insert(subscription_id.clone(), tx); - if self.executor.execute(Box::new(future)).is_err() { + if self.executor.spawn_obj(task::FutureObj::new(Box::pin(future))).is_err() { error!("Failed to spawn RPC subscription task"); } } @@ -222,11 +227,8 @@ impl SubscriptionManager { mod tests { use super::*; use crate::typed::Subscriber; - use futures::{compat::Future01CompatExt, executor, FutureExt}; - use futures::{stream, StreamExt, TryStreamExt}; - - use crate::core::futures::sink::Sink as Sink01; - use crate::core::futures::stream::Stream as Stream01; + use futures::{executor, stream}; + use futures::{FutureExt, StreamExt}; // Executor shared by all tests. // @@ -238,12 +240,13 @@ mod tests { } pub struct TestTaskExecutor; - type Boxed01Future01 = Box + Send + 'static>; + impl task::Spawn for TestTaskExecutor { + fn spawn_obj(&self, future: task::FutureObj<'static, ()>) -> Result<(), task::SpawnError> { + EXECUTOR.spawn_obj(future) + } - impl future01::Executor for TestTaskExecutor { - fn execute(&self, future: Boxed01Future01) -> std::result::Result<(), future01::ExecuteError> { - EXECUTOR.spawn_ok(future.compat().map(drop)); - Ok(()) + fn status(&self) -> Result<(), task::SpawnError> { + EXECUTOR.status() } } @@ -296,13 +299,9 @@ mod tests { fn new_subscription_manager_defaults_to_random_string_provider() { let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor)); let subscriber = Subscriber::::new_test("test_subTest").0; - let stream = stream::iter(vec![Ok(1)]).compat(); + let stream = stream::iter(vec![Ok(Ok(1))]); - let id = manager.add(subscriber, |sink| { - let stream = stream.map(|res| Ok(res)); - - sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) - }); + let id = manager.add(subscriber, move |sink| stream.forward(sink).map(|_| ())); assert!(matches!(id, SubscriptionId::String(_))) } @@ -313,13 +312,9 @@ mod tests { let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor)); let subscriber = Subscriber::::new_test("test_subTest").0; - let stream = stream::iter(vec![Ok(1)]).compat(); - - let id = manager.add(subscriber, |sink| { - let stream = stream.map(|res| Ok(res)); + let stream = stream::iter(vec![Ok(Ok(1))]); - sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) - }); + let id = manager.add(subscriber, move |sink| stream.forward(sink).map(|_| ())); assert!(matches!(id, SubscriptionId::Number(_))) } @@ -330,13 +325,9 @@ mod tests { let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor)); let subscriber = Subscriber::::new_test("test_subTest").0; - let stream = stream::iter(vec![Ok(1)]).compat(); - - let id = manager.add(subscriber, |sink| { - let stream = stream.map(|res| Ok(res)); + let stream = stream::iter(vec![Ok(Ok(1))]); - sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) - }); + let id = manager.add(subscriber, move |sink| stream.forward(sink).map(|_| ())); assert!(matches!(id, SubscriptionId::String(_))) } @@ -350,12 +341,9 @@ mod tests { let (mut tx, rx) = futures::channel::mpsc::channel(8); tx.start_send(1).unwrap(); - let stream = rx.map(|v| Ok::<_, ()>(v)).compat(); - - let id = manager.add(subscriber, |sink| { - let stream = stream.map(|res| Ok(res)); - - sink.sink_map_err(|_| ()).send_all(stream).map(|_| ()) + let id = manager.add(subscriber, move |sink| { + let rx = rx.map(|v| Ok(Ok(v))); + rx.forward(sink).map(|_| ()) }); let is_cancelled = manager.cancel(id); diff --git a/pubsub/src/oneshot.rs b/pubsub/src/oneshot.rs index 2d72f11ef..2ab4208d3 100644 --- a/pubsub/src/oneshot.rs +++ b/pubsub/src/oneshot.rs @@ -1,12 +1,12 @@ //! A futures oneshot channel that can be used for rendezvous. -use crate::core::futures::{self, future, sync::oneshot, Future}; +use crate::core::futures::{self, channel::oneshot, future, Future, FutureExt, TryFutureExt}; use std::ops::{Deref, DerefMut}; /// Create a new future-base rendezvous channel. /// /// The returned `Sender` and `Receiver` objects are wrapping -/// the regular `futures::sync::oneshot` counterparts and have the same functionality. +/// the regular `futures::channel::oneshot` counterparts and have the same functionality. /// Additionaly `Sender::send_and_wait` allows you to send a message to the channel /// and get a future that resolves when the message is consumed. pub fn channel() -> (Sender, Receiver) { @@ -49,14 +49,14 @@ impl Sender { /// to send the message as that happens synchronously. /// The future resolves to error in case the receiving end was dropped before /// being able to process the message. - pub fn send_and_wait(self, t: T) -> impl Future { + pub fn send_and_wait(self, t: T) -> impl Future> { let Self { sender, receipt } = self; if let Err(_) = sender.send(t) { - return future::Either::A(future::err(())); + return future::Either::Left(future::ready(Err(()))); } - future::Either::B(receipt.map_err(|_| ())) + future::Either::Right(receipt.map_err(|_| ())) } } @@ -88,18 +88,13 @@ pub struct Receiver { } impl Future for Receiver { - type Item = as Future>::Item; - type Error = as Future>::Error; + type Output = as Future>::Output; - fn poll(&mut self) -> futures::Poll { - match self.receiver.poll() { - Ok(futures::Async::Ready(r)) => { - if let Some(receipt) = self.receipt.take() { - let _ = receipt.send(()); - } - Ok(futures::Async::Ready(r)) - } - e => e, + fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut futures::task::Context) -> futures::task::Poll { + let r = futures::ready!(self.receiver.poll_unpin(cx))?; + if let Some(receipt) = self.receipt.take() { + let _ = receipt.send(()); } + Ok(r).into() } } diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 473f0c47d..8c02512a4 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -3,15 +3,25 @@ use parking_lot::Mutex; use std::collections::HashMap; use std::fmt; +use std::pin::Pin; use std::sync::Arc; -use crate::core::futures::sync::mpsc; -use crate::core::futures::{self, future, Future, Sink as FuturesSink}; +use crate::core::futures::channel::mpsc; +use crate::core::futures::{ + self, future, + task::{Context, Poll}, + Future, Sink as FuturesSink, TryFutureExt, +}; use crate::core::{self, BoxFuture}; use crate::handler::{SubscribeRpcMethod, UnsubscribeRpcMethod}; use crate::types::{PubSubMetadata, SinkResult, SubscriptionId, TransportError, TransportSender}; +lazy_static::lazy_static! { + static ref UNSUBSCRIBE_POOL: futures::executor::ThreadPool = futures::executor::ThreadPool::new() + .expect("Unable to spawn background pool for unsubscribe tasks."); +} + /// RPC client session /// Keeps track of active subscriptions and unsubscribes from them upon dropping. pub struct Session { @@ -101,40 +111,37 @@ impl Sink { /// Sends a notification to a client. pub fn notify(&self, val: core::Params) -> SinkResult { let val = self.params_to_string(val); - self.transport.clone().send(val.0) + self.transport.clone().unbounded_send(val) } - fn params_to_string(&self, val: core::Params) -> (String, core::Params) { + fn params_to_string(&self, val: core::Params) -> String { let notification = core::Notification { jsonrpc: Some(core::Version::V2), method: self.notification.clone(), params: val, }; - ( - core::to_string(¬ification).expect("Notification serialization never fails."), - notification.params, - ) + core::to_string(¬ification).expect("Notification serialization never fails.") } } -impl FuturesSink for Sink { - type SinkItem = core::Params; - type SinkError = TransportError; +impl FuturesSink for Sink { + type Error = TransportError; - fn start_send(&mut self, item: Self::SinkItem) -> futures::StartSend { - let (val, params) = self.params_to_string(item); - self.transport.start_send(val).map(|result| match result { - futures::AsyncSink::Ready => futures::AsyncSink::Ready, - futures::AsyncSink::NotReady(_) => futures::AsyncSink::NotReady(params), - }) + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.transport).poll_ready(cx) + } + + fn start_send(mut self: Pin<&mut Self>, item: core::Params) -> Result<(), Self::Error> { + let val = self.params_to_string(item); + Pin::new(&mut self.transport).start_send(val) } - fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { - self.transport.poll_complete() + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.transport).poll_flush(cx) } - fn close(&mut self) -> futures::Poll<(), Self::SinkError> { - self.transport.close() + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.transport).poll_close(cx) } } @@ -156,10 +163,10 @@ impl Subscriber { ) -> ( Self, crate::oneshot::Receiver>, - mpsc::Receiver, + mpsc::UnboundedReceiver, ) { let (sender, id_receiver) = crate::oneshot::channel(); - let (transport, transport_receiver) = mpsc::channel(1); + let (transport, transport_receiver) = mpsc::unbounded(); let subscriber = Subscriber { notification: method.into(), @@ -192,19 +199,16 @@ impl Subscriber { /// /// The returned `Future` resolves when the subscriber receives subscription id. /// Resolves to `Err` if request has already terminated. - pub fn assign_id_async(self, id: SubscriptionId) -> impl Future { + pub fn assign_id_async(self, id: SubscriptionId) -> impl Future> { let Self { notification, transport, sender, } = self; - sender - .send_and_wait(Ok(id)) - .map(|_| Sink { - notification, - transport, - }) - .map_err(|_| ()) + sender.send_and_wait(Ok(id)).map_ok(|_| Sink { + notification, + transport, + }) } /// Rejects this subscription request with given error. @@ -218,8 +222,8 @@ impl Subscriber { /// /// The returned `Future` resolves when the rejection is sent to the client. /// Resolves to `Err` if request has already terminated. - pub fn reject_async(self, error: core::Error) -> impl Future { - self.sender.send_and_wait(Err(error)).map(|_| ()).map_err(|_| ()) + pub fn reject_async(self, error: core::Error) -> impl Future> { + self.sender.send_and_wait(Err(error)).map_ok(|_| ()).map_err(|_| ()) } } @@ -274,7 +278,7 @@ where F: SubscribeRpcMethod, G: UnsubscribeRpcMethod, { - fn call(&self, params: core::Params, meta: M) -> BoxFuture { + fn call(&self, params: core::Params, meta: M) -> BoxFuture> { match meta.session() { Some(session) => { let (tx, rx) = crate::oneshot::channel(); @@ -290,19 +294,25 @@ where let unsub = self.unsubscribe.clone(); let notification = self.notification.clone(); let subscribe_future = rx.map_err(|_| subscription_rejected()).and_then(move |result| { - futures::done(match result { + futures::future::ready(match result { Ok(id) => { session.add_subscription(¬ification, &id, move |id| { - let _ = unsub.call(id, None).wait(); + // TODO [#570] [ToDr] We currently run unsubscribe tasks on a shared thread pool. + // In the future we should use some kind of `::spawn` method + // that spawns a task on an existing executor or pass the spawner handle here. + let f = unsub.call(id, None); + UNSUBSCRIBE_POOL.spawn_ok(async move { + let _ = f.await; + }); }); Ok(id.into()) } Err(e) => Err(e), }) }); - Box::new(subscribe_future) + Box::pin(subscribe_future) } - None => Box::new(future::err(subscriptions_unavailable())), + None => Box::pin(future::err(subscriptions_unavailable())), } } } @@ -318,7 +328,7 @@ where M: PubSubMetadata, G: UnsubscribeRpcMethod, { - fn call(&self, params: core::Params, meta: M) -> BoxFuture { + fn call(&self, params: core::Params, meta: M) -> BoxFuture> { let id = match params { core::Params::Array(ref vec) if vec.len() == 1 => SubscriptionId::parse_value(&vec[0]), _ => None, @@ -326,10 +336,10 @@ where match (meta.session(), id) { (Some(session), Some(id)) => { session.remove_subscription(&self.notification, &id); - Box::new(self.unsubscribe.call(id, Some(meta))) + Box::pin(self.unsubscribe.call(id, Some(meta))) } - (Some(_), None) => Box::new(future::err(core::Error::invalid_params("Expected subscription id."))), - _ => Box::new(future::err(subscriptions_unavailable())), + (Some(_), None) => Box::pin(future::err(core::Error::invalid_params("Expected subscription id."))), + _ => Box::pin(future::err(subscriptions_unavailable())), } } } @@ -337,8 +347,7 @@ where #[cfg(test)] mod tests { use crate::core; - use crate::core::futures::sync::mpsc; - use crate::core::futures::{Async, Future, Stream}; + use crate::core::futures::channel::mpsc; use crate::core::RpcMethod; use crate::types::{PubSubMetadata, SubscriptionId}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -346,8 +355,8 @@ mod tests { use super::{new_subscription, Session, Sink, Subscriber}; - fn session() -> (Session, mpsc::Receiver) { - let (tx, rx) = mpsc::channel(1); + fn session() -> (Session, mpsc::UnboundedReceiver) { + let (tx, rx) = mpsc::unbounded(); (Session::new(tx), rx) } @@ -412,7 +421,7 @@ mod tests { #[test] fn should_send_notification_to_the_transport() { // given - let (tx, mut rx) = mpsc::channel(1); + let (tx, mut rx) = mpsc::unbounded(); let sink = Sink { notification: "test".into(), transport: tx, @@ -420,21 +429,18 @@ mod tests { // when sink.notify(core::Params::Array(vec![core::Value::Number(10.into())])) - .wait() .unwrap(); + let val = rx.try_next().unwrap(); // then - assert_eq!( - rx.poll().unwrap(), - Async::Ready(Some(r#"{"jsonrpc":"2.0","method":"test","params":[10]}"#.into())) - ); + assert_eq!(val, Some(r#"{"jsonrpc":"2.0","method":"test","params":[10]}"#.into())); } #[test] fn should_assign_id() { // given - let (transport, _) = mpsc::channel(1); - let (tx, mut rx) = crate::oneshot::channel(); + let (transport, _) = mpsc::unbounded(); + let (tx, rx) = crate::oneshot::channel(); let subscriber = Subscriber { notification: "test".into(), transport, @@ -445,16 +451,19 @@ mod tests { let sink = subscriber.assign_id_async(SubscriptionId::Number(5)); // then - assert_eq!(rx.poll().unwrap(), Async::Ready(Ok(SubscriptionId::Number(5)))); - let sink = sink.wait().unwrap(); - assert_eq!(sink.notification, "test".to_owned()); + futures::executor::block_on(async move { + let id = rx.await; + assert_eq!(id, Ok(Ok(SubscriptionId::Number(5)))); + let sink = sink.await.unwrap(); + assert_eq!(sink.notification, "test".to_owned()); + }) } #[test] fn should_reject() { // given - let (transport, _) = mpsc::channel(1); - let (tx, mut rx) = crate::oneshot::channel(); + let (transport, _) = mpsc::unbounded(); + let (tx, rx) = crate::oneshot::channel(); let subscriber = Subscriber { notification: "test".into(), transport, @@ -470,8 +479,10 @@ mod tests { let reject = subscriber.reject_async(error.clone()); // then - assert_eq!(rx.poll().unwrap(), Async::Ready(Err(error))); - reject.wait().unwrap(); + futures::executor::block_on(async move { + assert_eq!(rx.await.unwrap(), Err(error)); + reject.await.unwrap(); + }); } #[derive(Clone, Default)] @@ -494,7 +505,7 @@ mod tests { assert_eq!(params, core::Params::None); called2.store(true, Ordering::SeqCst); }, - |_id, _meta| Ok(core::Value::Bool(true)), + |_id, _meta| async { Ok(core::Value::Bool(true)) }, ); let meta = Metadata; @@ -504,7 +515,7 @@ mod tests { // then assert_eq!(called.load(Ordering::SeqCst), true); assert_eq!( - result.wait(), + futures::executor::block_on(result), Err(core::Error { code: core::ErrorCode::ServerError(-32091), message: "Subscription rejected".into(), diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs index a2c5804a2..e64b6f81e 100644 --- a/pubsub/src/typed.rs +++ b/pubsub/src/typed.rs @@ -1,12 +1,14 @@ //! PUB-SUB auto-serializing structures. use std::marker::PhantomData; +use std::pin::Pin; use crate::subscription; use crate::types::{SinkResult, SubscriptionId, TransportError}; use serde; -use crate::core::futures::{self, sync, Future, Sink as FuturesSink}; +use crate::core::futures::task::{Context, Poll}; +use crate::core::futures::{self, channel}; use crate::core::{self, Error, Params, Value}; /// New PUB-SUB subscriber. @@ -31,7 +33,7 @@ impl Subscriber { ) -> ( Self, crate::oneshot::Receiver>, - sync::mpsc::Receiver, + channel::mpsc::UnboundedReceiver, ) { let (subscriber, id, subscription) = subscription::Subscriber::new_test(method); (Subscriber::new(subscriber), id, subscription) @@ -45,18 +47,18 @@ impl Subscriber { /// Reject subscription with given error. /// /// The returned future will resolve when the response is sent to the client. - pub fn reject_async(self, error: Error) -> impl Future { - self.subscriber.reject_async(error) + pub async fn reject_async(self, error: Error) -> Result<(), ()> { + self.subscriber.reject_async(error).await } /// Assign id to this subscriber. /// This method consumes `Subscriber` and returns `Sink` /// if the connection is still open or error otherwise. pub fn assign_id(self, id: SubscriptionId) -> Result, ()> { - self.subscriber.assign_id(id.clone()).map(|sink| Sink { + let sink = self.subscriber.assign_id(id.clone())?; + Ok(Sink { id, sink, - buffered: None, _data: PhantomData, }) } @@ -64,11 +66,11 @@ impl Subscriber { /// Assign id to this subscriber. /// This method consumes `Subscriber` and resolves to `Sink` /// if the connection is still open and the id has been sent or to error otherwise. - pub fn assign_id_async(self, id: SubscriptionId) -> impl Future, Error = ()> { - self.subscriber.assign_id_async(id.clone()).map(|sink| Sink { + pub async fn assign_id_async(self, id: SubscriptionId) -> Result, ()> { + let sink = self.subscriber.assign_id_async(id.clone()).await?; + Ok(Sink { id, sink, - buffered: None, _data: PhantomData, }) } @@ -79,7 +81,6 @@ impl Subscriber { pub struct Sink { sink: subscription::Sink, id: SubscriptionId, - buffered: Option, _data: PhantomData<(T, E)>, } @@ -112,49 +113,25 @@ impl Sink { .collect(), ) } - - fn poll(&mut self) -> futures::Poll<(), TransportError> { - if let Some(item) = self.buffered.take() { - let result = self.sink.start_send(item)?; - if let futures::AsyncSink::NotReady(item) = result { - self.buffered = Some(item); - } - } - - if self.buffered.is_some() { - Ok(futures::Async::NotReady) - } else { - Ok(futures::Async::Ready(())) - } - } } -impl futures::sink::Sink for Sink { - type SinkItem = Result; - type SinkError = TransportError; +impl futures::sink::Sink> for Sink { + type Error = TransportError; - fn start_send(&mut self, item: Self::SinkItem) -> futures::StartSend { - // Make sure to always try to process the buffered entry. - // Since we're just a proxy to real `Sink` we don't need - // to schedule a `Task` wakeup. It will be done downstream. - if self.poll()?.is_not_ready() { - return Ok(futures::AsyncSink::NotReady(item)); - } + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.sink).poll_ready(cx) + } + fn start_send(mut self: Pin<&mut Self>, item: Result) -> Result<(), Self::Error> { let val = self.val_to_params(item); - self.buffered = Some(val); - self.poll()?; - - Ok(futures::AsyncSink::Ready) + Pin::new(&mut self.sink).start_send(val) } - fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { - self.poll()?; - self.sink.poll_complete() + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.sink).poll_flush(cx) } - fn close(&mut self) -> futures::Poll<(), Self::SinkError> { - self.poll()?; - self.sink.close() + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.sink).poll_close(cx) } } diff --git a/pubsub/src/types.rs b/pubsub/src/types.rs index 9e1437e74..f6606801c 100644 --- a/pubsub/src/types.rs +++ b/pubsub/src/types.rs @@ -1,15 +1,15 @@ use crate::core; -use crate::core::futures::sync::mpsc; +use crate::core::futures::channel::mpsc; use std::sync::Arc; use crate::subscription::Session; /// Raw transport sink for specific client. -pub type TransportSender = mpsc::Sender; +pub type TransportSender = mpsc::UnboundedSender; /// Raw transport error. -pub type TransportError = mpsc::SendError; +pub type TransportError = mpsc::SendError; /// Subscription send result. -pub type SinkResult = core::futures::sink::Send; +pub type SinkResult = Result<(), mpsc::TrySendError>; /// Metadata extension for pub-sub method handling. /// diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 72dcf1f11..c21a0eca7 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,12 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] bytes = "0.4" +futures01 = { version = "0.1", package = "futures" } globset = "0.4" -jsonrpc-core = { version = "14.2", path = "../core" } +jsonrpc-core = { version = "15.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.1.15" } diff --git a/server-utils/src/lib.rs b/server-utils/src/lib.rs index 4a1e2a0e1..e13342007 100644 --- a/server-utils/src/lib.rs +++ b/server-utils/src/lib.rs @@ -8,8 +8,6 @@ extern crate log; #[macro_use] extern crate lazy_static; -use jsonrpc_core as core; - pub use tokio; pub use tokio_codec; diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index f66c73c12..1d1917db3 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -8,7 +8,7 @@ use std::io; use tokio; -use crate::core::futures::{self, Future}; +use futures01::Future; /// Possibly uninitialized event loop executor. #[derive(Debug)] @@ -83,7 +83,7 @@ impl Executor { #[derive(Debug)] pub struct RpcEventLoop { executor: tokio::runtime::TaskExecutor, - close: Option>, + close: Option>, handle: Option, } @@ -101,7 +101,7 @@ impl RpcEventLoop { /// Spawns a new named thread with the `EventLoop`. pub fn with_name(name: Option) -> io::Result { - let (stop, stopped) = futures::oneshot(); + let (stop, stopped) = futures01::oneshot(); let mut tb = tokio::runtime::Builder::new(); tb.core_threads(1); @@ -112,7 +112,7 @@ impl RpcEventLoop { let mut runtime = tb.build()?; let executor = runtime.executor(); - let terminate = futures::empty().select(stopped).map(|_| ()).map_err(|_| ()); + let terminate = futures01::empty().select(stopped).map(|_| ()).map_err(|_| ()); runtime.spawn(terminate); let handle = runtime.shutdown_on_idle(); diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 72bda0643..96241bc4f 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] -futures = "0.1.23" -jsonrpc-core = { version = "14.2", path = "../core" } +futures = { version = "0.3", features = [ "compat" ] } +jsonrpc-core = { version = "15.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/stdio/README.md b/stdio/README.md index c2660dfef..bd8152526 100644 --- a/stdio/README.md +++ b/stdio/README.md @@ -10,7 +10,7 @@ Takes one request per line and outputs each response on a new line. ``` [dependencies] -jsonrpc-stdio-server = "14.2" +jsonrpc-stdio-server = "15.0" ``` `main.rs` diff --git a/stdio/examples/stdio.rs b/stdio/examples/stdio.rs index 5ba4ba31c..974ea9df4 100644 --- a/stdio/examples/stdio.rs +++ b/stdio/examples/stdio.rs @@ -3,7 +3,7 @@ use jsonrpc_stdio_server::ServerBuilder; fn main() { let mut io = IoHandler::default(); - io.add_method("say_hello", |_params| Ok(Value::String("hello".to_owned()))); + io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_owned()))); ServerBuilder::new(io).build(); } diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index 3982abc0a..9d118f563 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -7,7 +7,7 @@ //! //! fn main() { //! let mut io = IoHandler::default(); -//! io.add_method("say_hello", |_params| { +//! io.add_sync_method("say_hello", |_params| { //! Ok(Value::String("hello".to_owned())) //! }); //! @@ -24,22 +24,24 @@ extern crate log; pub use jsonrpc_core; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{MetaIoHandler, Metadata, Middleware}; use std::sync::Arc; use tokio::prelude::{Future, Stream}; use tokio_codec::{FramedRead, FramedWrite, LinesCodec}; /// Stdio server builder -pub struct ServerBuilder { - handler: Arc, +pub struct ServerBuilder = jsonrpc_core::NoopMiddleware> { + handler: Arc>, } -impl ServerBuilder { +impl> ServerBuilder +where + M: Default, + T::Future: Unpin, + T::CallFuture: Unpin, +{ /// Returns a new server instance - pub fn new(handler: T) -> Self - where - T: Into, - { + pub fn new(handler: impl Into>) -> Self { ServerBuilder { handler: Arc::new(handler.into()), } @@ -57,22 +59,24 @@ impl ServerBuilder { let handler = self.handler.clone(); let future = framed_stdin - .and_then(move |line| process(&handler, line).map_err(|_| unreachable!())) + .and_then(move |line| Self::process(&handler, line).map_err(|_| unreachable!())) .forward(framed_stdout) .map(|_| ()) .map_err(|e| panic!("{:?}", e)); tokio::run(future); } -} -/// Process a request asynchronously -fn process(io: &Arc, input: String) -> impl Future + Send { - io.handle_request(&input).map(move |result| match result { - Some(res) => res, - None => { - info!("JSON RPC request produced no response: {:?}", input); - String::from("") - } - }) + /// Process a request asynchronously + fn process(io: &Arc>, input: String) -> impl Future + Send { + use jsonrpc_core::futures::{FutureExt, TryFutureExt}; + let f = io.handle_request(&input, Default::default()); + f.map(Ok).compat().map(move |result| match result { + Some(res) => res, + None => { + info!("JSON RPC request produced no response: {:?}", input); + String::from("") + } + }) + } } diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 0a76fa921..7f41efcff 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,17 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] +futures01 = { version = "0.1", package = "futures" } +# TODO remove when we no longer need compat (use jsonrpc-core re-export instead) +futures03 = { version = "0.3", features = ["compat"], package = "futures" } +jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } log = "0.4" parking_lot = "0.10.0" tokio-service = "0.1" -jsonrpc-core = { version = "14.2", path = "../core" } -jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/README.md b/tcp/README.md index 81fac2166..7e12cff57 100644 --- a/tcp/README.md +++ b/tcp/README.md @@ -9,7 +9,7 @@ TCP server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-tcp-server = "14.2" +jsonrpc-tcp-server = "15.0" ``` `main.rs` diff --git a/tcp/examples/tcp.rs b/tcp/examples/tcp.rs index 58570cb1f..0c51a5c17 100644 --- a/tcp/examples/tcp.rs +++ b/tcp/examples/tcp.rs @@ -5,7 +5,7 @@ use jsonrpc_tcp_server::ServerBuilder; fn main() { env_logger::init(); let mut io = IoHandler::default(); - io.add_method("say_hello", |_params| { + io.add_sync_method("say_hello", |_params| { println!("Processing"); Ok(Value::String("hello".to_owned())) }); diff --git a/tcp/src/dispatch.rs b/tcp/src/dispatch.rs index 8af9cc210..f12121a51 100644 --- a/tcp/src/dispatch.rs +++ b/tcp/src/dispatch.rs @@ -3,24 +3,25 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; -use crate::jsonrpc::futures::sync::mpsc; -use crate::jsonrpc::futures::{Async, Future, Poll, Sink, Stream}; +use crate::jsonrpc::futures::{self as futures03, channel::mpsc, StreamExt}; +use futures01::{Async, Poll, Stream}; use parking_lot::Mutex; -pub type SenderChannels = Mutex>>; +pub type SenderChannels = Mutex>>; pub struct PeerMessageQueue { up: S, - receiver: Option>, + receiver: Option + Send>>, _addr: SocketAddr, } impl PeerMessageQueue { - pub fn new(response_stream: S, receiver: mpsc::Receiver, addr: SocketAddr) -> Self { + pub fn new(response_stream: S, receiver: mpsc::UnboundedReceiver, addr: SocketAddr) -> Self { + let receiver = futures03::compat::Compat::new(receiver.map(|v| Ok(v))); PeerMessageQueue { up: response_stream, - receiver: Some(receiver), + receiver: Some(Box::new(receiver)), _addr: addr, } } @@ -32,11 +33,11 @@ pub enum PushMessageError { /// Invalid peer NoSuchPeer, /// Send error - Send(mpsc::SendError), + Send(mpsc::TrySendError), } -impl From> for PushMessageError { - fn from(send_err: mpsc::SendError) -> Self { +impl From> for PushMessageError { + fn from(send_err: mpsc::TrySendError) -> Self { PushMessageError::Send(send_err) } } @@ -59,8 +60,7 @@ impl Dispatcher { match channels.get_mut(peer_addr) { Some(channel) => { - // todo: maybe async here later? - channel.send(msg).wait().map_err(PushMessageError::from)?; + channel.unbounded_send(msg).map_err(PushMessageError::from)?; Ok(()) } None => Err(PushMessageError::NoSuchPeer), diff --git a/tcp/src/lib.rs b/tcp/src/lib.rs index 3c0c1c847..5eda8e7e1 100644 --- a/tcp/src/lib.rs +++ b/tcp/src/lib.rs @@ -6,7 +6,7 @@ //! //! fn main() { //! let mut io = IoHandler::default(); -//! io.add_method("say_hello", |_params| { +//! io.add_sync_method("say_hello", |_params| { //! Ok(Value::String("hello".to_string())) //! }); //! let server = ServerBuilder::new(io) diff --git a/tcp/src/meta.rs b/tcp/src/meta.rs index c057e733c..7cf2d4586 100644 --- a/tcp/src/meta.rs +++ b/tcp/src/meta.rs @@ -1,14 +1,13 @@ use std::net::SocketAddr; -use crate::jsonrpc::futures::sync::mpsc; -use crate::jsonrpc::Metadata; +use crate::jsonrpc::{futures::channel::mpsc, Metadata}; /// Request context pub struct RequestContext { /// Peer Address pub peer_addr: SocketAddr, /// Peer Sender channel - pub sender: mpsc::Sender, + pub sender: mpsc::UnboundedSender, } /// Metadata extractor (per session) diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 9af74d038..6e7d3693d 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -4,8 +4,9 @@ use std::sync::Arc; use tokio_service::Service as TokioService; -use crate::jsonrpc::futures::sync::{mpsc, oneshot}; -use crate::jsonrpc::futures::{future, Future, Sink, Stream}; +use futures01::sync::oneshot; +use futures01::{future, Future, Sink, Stream}; + use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; use crate::server_utils::{codecs, reactor, tokio, tokio_codec::Framed, SuspendableStream}; @@ -23,7 +24,11 @@ pub struct ServerBuilder = middleware::Noop> outgoing_separator: codecs::Separator, } -impl + 'static> ServerBuilder { +impl + 'static> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new `ServerBuilder` wih given `IoHandler` pub fn new(handler: T) -> Self where @@ -33,7 +38,11 @@ impl + 'static> ServerBuilder { } } -impl + 'static> ServerBuilder { +impl + 'static> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new `ServerBuilder` wih given `IoHandler` pub fn with_meta_extractor(handler: T, extractor: E) -> Self where @@ -96,7 +105,7 @@ impl + 'static> ServerBuilder { } }; trace!(target: "tcp", "Accepted incoming connection from {}", &peer_addr); - let (sender, receiver) = mpsc::channel(65536); + let (sender, receiver) = crate::jsonrpc::futures::channel::mpsc::unbounded(); let context = RequestContext { peer_addr, diff --git a/tcp/src/service.rs b/tcp/src/service.rs index 9254981fc..cb0f4b7b2 100644 --- a/tcp/src/service.rs +++ b/tcp/src/service.rs @@ -1,9 +1,9 @@ use std::net::SocketAddr; use std::sync::Arc; -use tokio_service; - -use crate::jsonrpc::{middleware, FutureResult, MetaIoHandler, Metadata, Middleware}; +use crate::jsonrpc::futures::FutureExt; +use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; +use futures01::Future; pub struct Service = middleware::Noop> { handler: Arc>, @@ -21,7 +21,11 @@ impl> Service { } } -impl> tokio_service::Service for Service { +impl> tokio_service::Service for Service +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ // These types must match the corresponding protocol types: type Request = String; type Response = Option; @@ -30,11 +34,13 @@ impl> tokio_service::Service for Service { type Error = (); // The future for computing the response; box it for simplicity. - type Future = FutureResult; + type Future = Box, Error = ()> + Send>; // Produce a future for computing a response from a request. fn call(&self, req: Self::Request) -> Self::Future { trace!(target: "tcp", "Accepted request from peer {}: {}", &self.peer_addr, req); - self.handler.handle_request(&req, self.meta.clone()) + Box::new(futures03::compat::Compat::new( + self.handler.handle_request(&req, self.meta.clone()).map(|v| Ok(v)), + )) } } diff --git a/tcp/src/tests.rs b/tcp/src/tests.rs index 71b819646..f06293847 100644 --- a/tcp/src/tests.rs +++ b/tcp/src/tests.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use std::sync::Arc; use std::time::{Duration, Instant}; -use crate::jsonrpc::futures::{self, future, Future}; -use crate::jsonrpc::{MetaIoHandler, Metadata, Value}; +use futures01::{future, Future}; +use jsonrpc_core::{MetaIoHandler, Metadata, Value}; use crate::server_utils::tokio::{self, io, net::TcpStream, timer::Delay}; @@ -16,7 +16,7 @@ use crate::ServerBuilder; fn casual_server() -> ServerBuilder { let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); ServerBuilder::new(io) } @@ -25,7 +25,7 @@ fn doc_test() { crate::logger::init_log(); let mut io = MetaIoHandler::<()>::default(); - io.add_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); + io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_string()))); let server = ServerBuilder::new(io); server @@ -71,7 +71,7 @@ fn disconnect() { } fn dummy_request(addr: &SocketAddr, data: Vec) -> Vec { - let (ret_tx, ret_rx) = futures::sync::oneshot::channel(); + let (ret_tx, ret_rx) = futures01::sync::oneshot::channel(); let stream = TcpStream::connect(addr) .and_then(move |stream| io::write_all(stream, data)) @@ -180,7 +180,7 @@ impl MetaExtractor for PeerMetaExtractor { fn meta_server() -> ServerBuilder { let mut io = MetaIoHandler::::default(); io.add_method_with_meta("say_hello", |_params, meta: SocketMetadata| { - future::ok(Value::String(format!("hello, {}", meta.addr()))) + jsonrpc_core::futures::future::ready(Ok(Value::String(format!("hello, {}", meta.addr())))) }); ServerBuilder::new(io).session_meta_extractor(PeerMetaExtractor) } @@ -223,7 +223,7 @@ fn message() { let addr: SocketAddr = "127.0.0.1:17790".parse().unwrap(); let mut io = MetaIoHandler::::default(); io.add_method_with_meta("say_hello", |_params, _: SocketMetadata| { - future::ok(Value::String("hello".to_owned())) + jsonrpc_core::futures::future::ready(Ok(Value::String("hello".to_owned()))) }); let extractor = PeerListMetaExtractor::default(); let peer_list = extractor.peers.clone(); diff --git a/test/Cargo.toml b/test/Cargo.toml index 679ba3953..7436abb28 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "14.2.0" +version = "15.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "14.2", path = "../core" } -jsonrpc-core-client = { version = "14.2", path = "../core-client" } -jsonrpc-pubsub = { version = "14.2", path = "../pubsub" } +jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-core-client = { version = "15.0", path = "../core-client" } +jsonrpc-pubsub = { version = "15.0", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "14.2", path = "../derive" } +jsonrpc-derive = { version = "15.0", path = "../derive" } diff --git a/test/src/lib.rs b/test/src/lib.rs index 985709fdb..5cc963ab9 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -30,7 +30,7 @@ //! // You can also test RPC created without macros: //! let rpc = { //! let mut io = IoHandler::new(); -//! io.add_method("rpc_test_method", |_| { +//! io.add_sync_method("rpc_test_method", |_| { //! Err(Error::internal_error()) //! }); //! test::Rpc::from(io) @@ -147,7 +147,7 @@ mod tests { // given let rpc = { let mut io = rpc::IoHandler::new(); - io.add_method("test_method", |_| Ok(rpc::Value::Array(vec![5.into(), 10.into()]))); + io.add_sync_method("test_method", |_| Ok(rpc::Value::Array(vec![5.into(), 10.into()]))); Rpc::from(io) }; @@ -160,7 +160,7 @@ mod tests { // given let rpc = { let mut io = rpc::IoHandler::new(); - io.add_method("test_method", |_| Ok(rpc::Value::Array(vec![5.into(), 10.into()]))); + io.add_sync_method("test_method", |_| Ok(rpc::Value::Array(vec![5.into(), 10.into()]))); Rpc::from(io) }; diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 0c9e0e627..deabf5854 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,11 +7,13 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "14.2.0" +version = "15.0.0" [dependencies] -jsonrpc-core = { version = "14.2", path = "../core" } -jsonrpc-server-utils = { version = "14.2", path = "../server-utils" } +futures01 = { version = "0.1", package = "futures" } +futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } +jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } log = "0.4" parking_lot = "0.10.0" slab = "0.4" diff --git a/ws/README.md b/ws/README.md index 948056bc7..fed02adcd 100644 --- a/ws/README.md +++ b/ws/README.md @@ -9,7 +9,7 @@ WebSockets server for JSON-RPC 2.0. ``` [dependencies] -jsonrpc-ws-server = "14.2" +jsonrpc-ws-server = "15.0" ``` `main.rs` diff --git a/ws/examples/ws.rs b/ws/examples/ws.rs index ea6a9087f..69fd9a494 100644 --- a/ws/examples/ws.rs +++ b/ws/examples/ws.rs @@ -3,7 +3,7 @@ use jsonrpc_ws_server::ServerBuilder; fn main() { let mut io = IoHandler::default(); - io.add_method("say_hello", |_params| { + io.add_sync_method("say_hello", |_params| { println!("Processing"); Ok(Value::String("hello".to_owned())) }); diff --git a/ws/src/metadata.rs b/ws/src/metadata.rs index 07befcfe9..624be5830 100644 --- a/ws/src/metadata.rs +++ b/ws/src/metadata.rs @@ -1,8 +1,8 @@ use std::fmt; use std::sync::{atomic, Arc}; -use crate::core::futures::sync::mpsc; -use crate::core::{self, futures}; +use crate::core; +use crate::core::futures::channel::mpsc; use crate::server_utils::{session, tokio::runtime::TaskExecutor}; use crate::ws; @@ -78,10 +78,12 @@ pub struct RequestContext { impl RequestContext { /// Get this session as a `Sink` spawning a new future /// in the underlying event loop. - pub fn sender(&self) -> mpsc::Sender { + pub fn sender(&self) -> mpsc::UnboundedSender { + use futures03::{StreamExt, TryStreamExt}; let out = self.out.clone(); - let (sender, receiver) = mpsc::channel(1); - self.executor.spawn(SenderFuture(out, receiver)); + let (sender, receiver) = mpsc::unbounded(); + let receiver = receiver.map(Ok).compat(); + self.executor.spawn(SenderFuture(out, Box::new(receiver))); sender } } @@ -121,27 +123,27 @@ impl MetaExtractor for NoopExtractor { } } -struct SenderFuture(Sender, mpsc::Receiver); -impl futures::Future for SenderFuture { +struct SenderFuture(Sender, Box + Send>); +impl futures01::Future for SenderFuture { type Item = (); type Error = (); - fn poll(&mut self) -> futures::Poll { - use self::futures::Stream; + fn poll(&mut self) -> futures01::Poll { + use futures01::Stream; loop { let item = self.1.poll()?; match item { - futures::Async::NotReady => { - return Ok(futures::Async::NotReady); + futures01::Async::NotReady => { + return Ok(futures01::Async::NotReady); } - futures::Async::Ready(None) => { - return Ok(futures::Async::Ready(())); + futures01::Async::Ready(None) => { + return Ok(futures01::Async::Ready(())); } - futures::Async::Ready(Some(val)) => { + futures01::Async::Ready(Some(val)) => { if let Err(e) = self.0.send(val) { warn!("Error sending a subscription update: {:?}", e); - return Ok(futures::Async::Ready(())); + return Ok(futures01::Async::Ready(())); } } } diff --git a/ws/src/server.rs b/ws/src/server.rs index 4fb83d67a..ca758e580 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -58,7 +58,11 @@ impl Server { executor: UninitializedExecutor, max_connections: usize, max_payload_bytes: usize, - ) -> Result { + ) -> Result + where + S::Future: Unpin, + S::CallFuture: Unpin, + { let config = { let mut config = ws::Settings::default(); config.max_connections = max_connections; diff --git a/ws/src/server_builder.rs b/ws/src/server_builder.rs index 1af8e6763..535c1f321 100644 --- a/ws/src/server_builder.rs +++ b/ws/src/server_builder.rs @@ -26,7 +26,11 @@ pub struct ServerBuilder> { max_payload_bytes: usize, } -impl> ServerBuilder { +impl> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new `ServerBuilder` pub fn new(handler: T) -> Self where @@ -36,7 +40,11 @@ impl> ServerBuilder { } } -impl> ServerBuilder { +impl> ServerBuilder +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ /// Creates new `ServerBuilder` pub fn with_meta_extractor(handler: T, extractor: E) -> Self where diff --git a/ws/src/session.rs b/ws/src/session.rs index 63c40acb8..65d76fd38 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -2,8 +2,8 @@ use std; use std::sync::{atomic, Arc}; use crate::core; -use crate::core::futures::sync::oneshot; -use crate::core::futures::{Async, Future, Poll}; +use futures01::sync::oneshot; +use futures01::{Async, Future, Poll}; use parking_lot::Mutex; use slab::Slab; @@ -205,7 +205,11 @@ impl> Session { } } -impl> ws::Handler for Session { +impl> ws::Handler for Session +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ fn on_request(&mut self, req: &ws::Request) -> ws::Result { // Run middleware let action = if let Some(ref middleware) = self.request_middleware { @@ -264,9 +268,11 @@ impl> ws::Handler for Session { let poll_liveness = LivenessPoll::create(self.task_slab.clone()); let active_lock = self.active.clone(); - let future = self - .handler - .handle_request(req, metadata) + let response = self.handler.handle_request(req, metadata); + + use futures03::{FutureExt, TryFutureExt}; + let response = response.map(Ok).compat(); + let future = response .map(move |response| { if !active_lock.load(atomic::Ordering::SeqCst) { return; @@ -328,7 +334,11 @@ impl> Factory { } } -impl> ws::Factory for Factory { +impl> ws::Factory for Factory +where + S::Future: Unpin, + S::CallFuture: Unpin, +{ type Handler = Session; fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { diff --git a/ws/src/tests.rs b/ws/src/tests.rs index d46e978f7..b416c9384 100644 --- a/ws/src/tests.rs +++ b/ws/src/tests.rs @@ -7,7 +7,6 @@ use std::thread; use std::time::Duration; use crate::core; -use crate::core::futures::Future; use crate::server_utils::hosts::DomainsValidation; use crate::ws; @@ -61,28 +60,27 @@ fn request(server: Server, request: &str) -> Response { } fn serve(port: u16) -> (Server, Arc) { - use crate::core::futures::sync::oneshot; - + use futures03::{channel::oneshot, future, FutureExt}; let pending = Arc::new(AtomicUsize::new(0)); let counter = pending.clone(); let mut io = core::IoHandler::default(); - io.add_method("hello", |_params: core::Params| Ok(core::Value::String("world".into()))); + io.add_sync_method("hello", |_params: core::Params| Ok(core::Value::String("world".into()))); io.add_method("hello_async", |_params: core::Params| { - core::futures::finished(core::Value::String("world".into())) + future::ready(Ok(core::Value::String("world".into()))) }); io.add_method("record_pending", move |_params: core::Params| { counter.fetch_add(1, Ordering::SeqCst); let (send, recv) = oneshot::channel(); - ::std::thread::spawn(move || { - ::std::thread::sleep(Duration::from_millis(500)); + std::thread::spawn(move || { + std::thread::sleep(Duration::from_millis(500)); let _ = send.send(()); }); let counter = counter.clone(); - recv.then(move |res| { + recv.map(move |res| { if res.is_ok() { counter.fetch_sub(1, Ordering::SeqCst); } From 254b630d7d7ce7f589d4448f5480f10295e59201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Jul 2020 12:48:38 +0200 Subject: [PATCH 127/149] Update readme. (#568) --- README.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/README.md b/README.md index 5496985d1..61cd89680 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,7 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` **New!** Support for [clients](#Client-support). -[![Build Status][travis-image]][travis-url] -[![Build Status][appveyor-image]][appveyor-url] - -[travis-image]: https://travis-ci.org/paritytech/jsonrpc.svg?branch=master -[travis-url]: https://travis-ci.org/paritytech/jsonrpc -[appveyor-image]: https://ci.appveyor.com/api/projects/status/github/paritytech/jsonrpc?svg=true -[appveyor-url]: https://ci.appveyor.com/project/paritytech/jsonrpc/branch/master - -[Documentation](http://paritytech.github.io/jsonrpc/) +[Documentation](https://docs.rs/jsonrpc-core/) ## Sub-projects - [jsonrpc-core](./core) [![crates.io][core-image]][core-url] From 8e7ddc96a89ddeedab01f4e149e8a86c4e93346f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Jul 2020 11:27:17 +0200 Subject: [PATCH 128/149] Make sure manager is Sync+Send (#573) --- pubsub/src/manager.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pubsub/src/manager.rs b/pubsub/src/manager.rs index b20da7272..1948dde10 100644 --- a/pubsub/src/manager.rs +++ b/pubsub/src/manager.rs @@ -34,7 +34,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; /// Cloneable `Spawn` handle. -pub type TaskExecutor = Arc; +pub type TaskExecutor = Arc; type ActiveSubscriptions = Arc>>>; @@ -360,4 +360,11 @@ mod tests { assert!(is_not_cancelled); } + + #[test] + fn is_send_sync() { + fn send_sync() {} + + send_sync::(); + } } From b165facb6c1c2666fc85a8037a823a23d734784e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 28 Aug 2020 15:17:19 +0200 Subject: [PATCH 129/149] Remove failure dependency. (#579) * Remove failure dependency. * Remove needless format!. * cargo fmt --all --- core-client/transports/Cargo.toml | 2 +- core-client/transports/src/lib.rs | 59 +++++++++++-------- .../transports/src/transports/duplex.rs | 11 ++-- core-client/transports/src/transports/http.rs | 11 ++-- .../transports/src/transports/local.rs | 8 +-- core-client/transports/src/transports/mod.rs | 2 +- core-client/transports/src/transports/ws.rs | 21 +++---- 7 files changed, 62 insertions(+), 52 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 6e19608a8..73a311bcb 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -36,7 +36,7 @@ ipc = [ arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dependencies] -failure = "0.1" +derive_more = "0.99" futures = { version = "0.3", features = [ "compat" ] } jsonrpc-core = { version = "15.0", path = "../../core" } jsonrpc-pubsub = { version = "15.0", path = "../../pubsub" } diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index c8cfd2cef..001177a33 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -2,7 +2,6 @@ #![deny(missing_docs)] -use failure::{format_err, Fail}; use jsonrpc_core::futures::channel::{mpsc, oneshot}; use jsonrpc_core::futures::{ self, @@ -22,20 +21,34 @@ pub mod transports; mod logger; /// The errors returned by the client. -#[derive(Debug, Fail)] +#[derive(Debug, derive_more::Display)] pub enum RpcError { /// An error returned by the server. - #[fail(display = "Server returned rpc error {}", _0)] + #[display(fmt = "Server returned rpc error {}", _0)] JsonRpcError(Error), /// Failure to parse server response. - #[fail(display = "Failed to parse server response as {}: {}", _0, _1)] - ParseError(String, failure::Error), + #[display(fmt = "Failed to parse server response as {}: {}", _0, _1)] + ParseError(String, Box), /// Request timed out. - #[fail(display = "Request timed out")] + #[display(fmt = "Request timed out")] Timeout, + /// A general client error. + #[display(fmt = "Client error: {}", _0)] + Client(String), /// Not rpc specific errors. - #[fail(display = "{}", _0)] - Other(failure::Error), + #[display(fmt = "{}", _0)] + Other(Box), +} + +impl std::error::Error for RpcError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::JsonRpcError(ref e) => Some(e), + Self::ParseError(_, ref e) => Some(&**e), + Self::Other(ref e) => Some(&**e), + _ => None, + } + } } impl From for RpcError { @@ -162,7 +175,7 @@ impl Stream for TypedSubscriptionStream Some( serde_json::from_value::(value) - .map_err(|error| RpcError::ParseError(self.returns.into(), error.into())), + .map_err(|error| RpcError::ParseError(self.returns.into(), Box::new(error))), ), None => None, Some(Err(err)) => Some(Err(err.into())), @@ -192,9 +205,9 @@ impl RawClient { }; let result = self.0.send(msg.into()); async move { - let () = result.map_err(|e| RpcError::Other(e.into()))?; + let () = result.map_err(|e| RpcError::Other(Box::new(e)))?; - receiver.await.map_err(|e| RpcError::Other(e.into()))? + receiver.await.map_err(|e| RpcError::Other(Box::new(e)))? } } @@ -206,7 +219,7 @@ impl RawClient { }; match self.0.send(msg.into()) { Ok(()) => Ok(()), - Err(error) => Err(RpcError::Other(error.into())), + Err(error) => Err(RpcError::Other(Box::new(error))), } } @@ -232,7 +245,7 @@ impl RawClient { self.0 .send(msg.into()) .map(|()| receiver) - .map_err(|e| RpcError::Other(e.into())) + .map_err(|e| RpcError::Other(Box::new(e))) } } @@ -266,9 +279,9 @@ impl TypedClient { Value::Array(vec) => Ok(Params::Array(vec)), Value::Null => Ok(Params::None), Value::Object(map) => Ok(Params::Map(map)), - _ => Err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, JSON object or null" - ))), + _ => Err(RpcError::Client( + "RPC params should serialize to a JSON array, JSON object or null".into(), + )), }; let result = params.map(|params| self.0.call_method(method, params)); @@ -277,7 +290,7 @@ impl TypedClient { log::debug!("response: {:?}", value); - serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns, error.into())) + serde_json::from_value::(value).map_err(|error| RpcError::ParseError(returns, Box::new(error))) } } @@ -289,9 +302,9 @@ impl TypedClient { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, _ => { - return Err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, or null" - ))) + return Err(RpcError::Client( + "RPC params should serialize to a JSON array, or null".into(), + )) } }; @@ -314,9 +327,9 @@ impl TypedClient { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, _ => { - return Err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, or null" - ))) + return Err(RpcError::Client( + "RPC params should serialize to a JSON array, or null".into(), + )) } }; diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index 1296cfd3d..c92cd1fe8 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -1,6 +1,5 @@ //! Duplex transport -use failure::format_err; use futures::channel::{mpsc, oneshot}; use futures::{ task::{Context, Poll}, @@ -195,7 +194,7 @@ where // It's a regular Req-Res call, so just answer. Some(PendingRequest::Call(tx)) => { tx.send(result) - .map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?; + .map_err(|_| RpcError::Client("oneshot channel closed".into()))?; continue; } // It was a subscription request, @@ -219,11 +218,9 @@ where ); } } else { - let err = RpcError::Other(format_err!( + let err = RpcError::Client(format!( "Subscription {:?} ({:?}) rejected: {:?}", - id, - method, - result, + id, method, result, )); if subscription.channel.unbounded_send(result).is_err() { @@ -276,7 +273,7 @@ where // Writes queued messages to sink. log::debug!("handle outgoing"); loop { - let err = || Err(RpcError::Other(failure::format_err!("closing"))); + let err = || Err(RpcError::Client("closing".into())); match self.sink.as_mut().poll_ready(cx) { Poll::Ready(Ok(())) => {} Poll::Ready(Err(_)) => return err().into(), diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index e5d40d1b3..b4b7cb9ae 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -4,7 +4,6 @@ use super::RequestBuilder; use crate::{RpcChannel, RpcError, RpcMessage, RpcResult}; -use failure::format_err; use futures::{Future, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; use hyper::{http, rt, Client, Request, Uri}; @@ -39,13 +38,13 @@ fn do_connect(url: &str) -> impl Future> { let max_parallel = 8; let url: Uri = match url.parse() { Ok(url) => url, - Err(e) => return ready(Err(RpcError::Other(e.into()))), + Err(e) => return ready(Err(RpcError::Other(Box::new(e)))), }; #[cfg(feature = "tls")] let connector = match hyper_tls::HttpsConnector::new(4) { Ok(connector) => connector, - Err(e) => return ready(Err(RpcError::Other(e.into()))), + Err(e) => return ready(Err(RpcError::Other(Box::new(e)))), }; #[cfg(feature = "tls")] let client = Client::builder().build::<_, hyper::Body>(connector); @@ -97,16 +96,16 @@ fn do_connect(url: &str) -> impl Future> { let future = match result { Ok(ref res) if !res.status().is_success() => { log::trace!("http result status {}", res.status()); - A(future::err(RpcError::Other(format_err!( + A(future::err(RpcError::Client(format!( "Unexpected response status code: {}", res.status() )))) } Ok(res) => B(res .into_body() - .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) + .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e))) .concat2()), - Err(err) => A(future::err(RpcError::Other(err.into()))), + Err(err) => A(future::err(RpcError::Other(Box::new(err)))), }; future.then(|result| { if let Some(sender) = sender { diff --git a/core-client/transports/src/transports/local.rs b/core-client/transports/src/transports/local.rs index aee50d895..e4d91eaef 100644 --- a/core-client/transports/src/transports/local.rs +++ b/core-client/transports/src/transports/local.rs @@ -81,7 +81,7 @@ where fn send_response(&mut self) -> Result<(), RpcError> { if let Buffered::Response(r) = std::mem::replace(&mut self.buffered, Buffered::None) { - self.queue.0.start_send(r).map_err(|e| RpcError::Other(e.into()))?; + self.queue.0.start_send(r).map_err(|e| RpcError::Other(Box::new(e)))?; } Ok(()) } @@ -97,7 +97,7 @@ where fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { futures::ready!(self.poll_buffered(cx))?; futures::ready!(self.queue.0.poll_ready(cx)) - .map_err(|e| RpcError::Other(e.into())) + .map_err(|e| RpcError::Other(Box::new(e))) .into() } @@ -110,13 +110,13 @@ where fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { futures::ready!(self.poll_buffered(cx))?; futures::ready!(self.queue.0.poll_flush_unpin(cx)) - .map_err(|e| RpcError::Other(e.into())) + .map_err(|e| RpcError::Other(Box::new(e))) .into() } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { futures::ready!(self.queue.0.poll_close_unpin(cx)) - .map_err(|e| RpcError::Other(e.into())) + .map_err(|e| RpcError::Other(Box::new(e))) .into() } } diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index fdf54d74a..00b6f0600 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -82,7 +82,7 @@ pub fn parse_response( response: &str, ) -> Result<(Id, Result, Option, Option), RpcError> { jsonrpc_core::serde_from_str::(response) - .map_err(|e| RpcError::ParseError(e.to_string(), e.into())) + .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e))) .map(|response| { let id = response.id().unwrap_or(Id::Null); let sid = response.subscription_id(); diff --git a/core-client/transports/src/transports/ws.rs b/core-client/transports/src/transports/ws.rs index ea3ea8cf4..d74d5df44 100644 --- a/core-client/transports/src/transports/ws.rs +++ b/core-client/transports/src/transports/ws.rs @@ -1,6 +1,5 @@ //! JSON-RPC websocket client implementation. use crate::{RpcChannel, RpcError}; -use failure::Error; use futures01::prelude::*; use log::info; use std::collections::VecDeque; @@ -11,11 +10,11 @@ use websocket::{ClientBuilder, OwnedMessage}; /// Uses an unbuffered channel to queue outgoing rpc messages. /// /// Returns `Err` if the `url` is invalid. -pub fn try_connect(url: &str) -> Result, Error> +pub fn try_connect(url: &str) -> Result, RpcError> where T: From, { - let client_builder = ClientBuilder::new(url)?; + let client_builder = ClientBuilder::new(url).map_err(|e| RpcError::Other(Box::new(e)))?; Ok(do_connect(client_builder)) } @@ -54,7 +53,7 @@ where tokio::spawn(rpc_client); sender.into() }) - .map_err(|error| RpcError::Other(error.into())) + .map_err(|error| RpcError::Other(Box::new(error))) } struct WebsocketClient { @@ -67,7 +66,7 @@ impl WebsocketClient where TSink: Sink, TStream: Stream, - TError: Into, + TError: std::error::Error + Send + 'static, { pub fn new(sink: TSink, stream: TStream) -> Self { Self { @@ -82,7 +81,7 @@ impl Sink for WebsocketClient where TSink: Sink, TStream: Stream, - TError: Into, + TError: std::error::Error + Send + 'static, { type SinkItem = String; type SinkError = RpcError; @@ -101,12 +100,14 @@ where self.queue.push_front(request); break; } - Err(error) => return Err(RpcError::Other(error.into())), + Err(error) => return Err(RpcError::Other(Box::new(error))), }, None => break, } } - self.sink.poll_complete().map_err(|error| RpcError::Other(error.into())) + self.sink + .poll_complete() + .map_err(|error| RpcError::Other(Box::new(error))) } } @@ -114,7 +115,7 @@ impl Stream for WebsocketClient where TSink: Sink, TStream: Stream, - TError: Into, + TError: std::error::Error + Send + 'static, { type Item = String; type Error = RpcError; @@ -134,7 +135,7 @@ where return Ok(Async::Ready(None)); } Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(error) => return Err(RpcError::Other(error.into())), + Err(error) => return Err(RpcError::Other(Box::new(error))), } } } From 92e3c14a1ebc1f3dc066e9721ffcd95b8f7cbc3f Mon Sep 17 00:00:00 2001 From: Maciej Hirsz <1096222+maciejhirsz@users.noreply.github.com> Date: Fri, 18 Sep 2020 21:00:19 +0200 Subject: [PATCH 130/149] Use patched ws-rs (#584) * Use patched ws-rs * Update settings * Bump versions to 16.0.0 --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- ipc/Cargo.toml | 6 +++--- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- tcp/Cargo.toml | 6 +++--- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 8 ++++---- ws/src/lib.rs | 2 +- ws/src/server.rs | 2 ++ 15 files changed, 47 insertions(+), 45 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index d21f13b94..7bf00a1aa 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" categories = [ "asynchronous", @@ -26,7 +26,7 @@ ipc = ["jsonrpc-client-transports/ipc", "futures01"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "15.0", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "16.0", path = "./transports", default-features = false } # Only for client transports, should be removed when we fully transition to futures=0.3 futures01 = { version = "0.1", package = "futures", optional = true } futures = { version = "0.3", features = [ "compat" ] } diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 73a311bcb..a1804b8c1 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" categories = [ "asynchronous", @@ -38,8 +38,8 @@ arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary [dependencies] derive_more = "0.99" futures = { version = "0.3", features = [ "compat" ] } -jsonrpc-core = { version = "15.0", path = "../../core" } -jsonrpc-pubsub = { version = "15.0", path = "../../pubsub" } +jsonrpc-core = { version = "16.0", path = "../../core" } +jsonrpc-pubsub = { version = "16.0", path = "../../pubsub" } log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -48,15 +48,15 @@ url = "1.7" futures01 = { version = "0.1.26", package = "futures", optional = true } hyper = { version = "0.12", optional = true } hyper-tls = { version = "0.3.2", optional = true } -jsonrpc-server-utils = { version = "15.0", path = "../../server-utils", optional = true } +jsonrpc-server-utils = { version = "16.0", path = "../../server-utils", optional = true } parity-tokio-ipc = { version = "0.2", optional = true } tokio = { version = "0.1", optional = true } websocket = { version = "0.24", optional = true } [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "15.0", path = "../../http" } -jsonrpc-ipc-server = { version = "15.0", path = "../../ipc" } +jsonrpc-http-server = { version = "16.0", path = "../../http" } +jsonrpc-ipc-server = { version = "16.0", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" diff --git a/core/Cargo.toml b/core/Cargo.toml index c975839bb..22fac8938 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 4e399764a..5d586c0a6 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [lib] proc-macro = true @@ -20,10 +20,10 @@ proc-macro-crate = "0.1.4" [dev-dependencies] assert_matches = "1.3" -jsonrpc-core = { version = "15.0", path = "../core" } -jsonrpc-core-client = { version = "15.0", path = "../core-client" } -jsonrpc-pubsub = { version = "15.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "15.0", path = "../tcp" } +jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-core-client = { version = "16.0", path = "../core-client" } +jsonrpc-pubsub = { version = "16.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "16.0", path = "../tcp" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" trybuild = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index f881710e0..864c3e8b3 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,14 +8,14 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] futures01 = { version = "0.1", package = "futures" } futures03 = { version = "0.3", package = "futures", features = ["compat"] } hyper = "0.12" -jsonrpc-core = { version = "15.0", path = "../core" } -jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } +jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" net2 = "0.2" parking_lot = "0.10.0" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index fa4783dd8..3bc1cb1fb 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,15 +7,15 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] futures01 = { version = "0.1", package = "futures" } futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } log = "0.4" tokio-service = "0.1" -jsonrpc-core = { version = "15.0", path = "../core" } -jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } +jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } parity-tokio-ipc = "0.4" parking_lot = "0.10.0" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 6a6301400..0386764f1 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,11 +8,11 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] futures = { version = "0.3", features = ["thread-pool"] } -jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-core = { version = "16.0", path = "../core" } lazy_static = "1.4" log = "0.4" parking_lot = "0.11.0" @@ -20,7 +20,7 @@ rand = "0.7" serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "15.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "16.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index e7e479a80..ea99adc26 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "15.0", path = "../../core" } -jsonrpc-pubsub = { version = "15.0", path = "../" } -jsonrpc-ws-server = { version = "15.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "15.0", path = "../../ipc" } +jsonrpc-core = { version = "16.0", path = "../../core" } +jsonrpc-pubsub = { version = "16.0", path = "../" } +jsonrpc-ws-server = { version = "16.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "16.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index c21a0eca7..4324500ab 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] bytes = "0.4" futures01 = { version = "0.1", package = "futures" } globset = "0.4" -jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-core = { version = "16.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.1.15" } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 96241bc4f..ef90c2093 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] futures = { version = "0.3", features = [ "compat" ] } -jsonrpc-core = { version = "15.0", path = "../core" } +jsonrpc-core = { version = "16.0", path = "../core" } log = "0.4" tokio = "0.1.7" tokio-codec = "0.1.0" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 7f41efcff..d3b8e9eb7 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] futures01 = { version = "0.1", package = "futures" } # TODO remove when we no longer need compat (use jsonrpc-core re-export instead) futures03 = { version = "0.3", features = ["compat"], package = "futures" } -jsonrpc-core = { version = "15.0", path = "../core" } -jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } +jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" parking_lot = "0.10.0" tokio-service = "0.1" diff --git a/test/Cargo.toml b/test/Cargo.toml index 7436abb28..a9cfd280b 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "15.0.0" +version = "16.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "15.0", path = "../core" } -jsonrpc-core-client = { version = "15.0", path = "../core-client" } -jsonrpc-pubsub = { version = "15.0", path = "../pubsub" } +jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-core-client = { version = "16.0", path = "../core-client" } +jsonrpc-pubsub = { version = "16.0", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "15.0", path = "../derive" } +jsonrpc-derive = { version = "16.0", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index deabf5854..81d4eb34e 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,17 +7,17 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "15.0.0" +version = "16.0.0" [dependencies] futures01 = { version = "0.1", package = "futures" } futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } -jsonrpc-core = { version = "15.0", path = "../core" } -jsonrpc-server-utils = { version = "15.0", path = "../server-utils" } +jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" parking_lot = "0.10.0" slab = "0.4" -ws = "0.9" +parity-ws = "0.10" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ws/src/lib.rs b/ws/src/lib.rs index aeae08a5d..5c1cfc3ca 100644 --- a/ws/src/lib.rs +++ b/ws/src/lib.rs @@ -5,7 +5,7 @@ use jsonrpc_server_utils as server_utils; pub use jsonrpc_core; -pub use ws; +pub use parity_ws as ws; #[macro_use] extern crate log; diff --git a/ws/src/server.rs b/ws/src/server.rs index ca758e580..3e51ef208 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -68,6 +68,8 @@ impl Server { config.max_connections = max_connections; // don't accept super large requests config.max_fragment_size = max_payload_bytes; + config.max_in_buffer_capacity = max_payload_bytes; + config.max_out_buffer_capacity = max_payload_bytes; // don't grow non-final fragments (to prevent DOS) config.fragments_grow = false; config.fragments_capacity = cmp::max(1, max_payload_bytes / config.fragment_size); From 7a4bf1ef4894e1ed725511b7ba67ef5aeb0c91a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 30 Sep 2020 14:10:17 +0200 Subject: [PATCH 131/149] Fix publishing script. (#593) --- _automate/publish.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_automate/publish.sh b/_automate/publish.sh index 53b9a8a71..ec389b923 100755 --- a/_automate/publish.sh +++ b/_automate/publish.sh @@ -66,6 +66,8 @@ for CRATE_DIR in ${ORDER[@]}; do if [ "$CHOICE" = "s" ]; then break fi + else + break fi done From 07a002add315b69bf4baeca908e70ef814649d81 Mon Sep 17 00:00:00 2001 From: Qinxuan Chen Date: Wed, 30 Sep 2020 21:05:03 +0800 Subject: [PATCH 132/149] update parking_lot to v0.11 (#591) Signed-off-by: koushiro --- http/Cargo.toml | 2 +- ipc/Cargo.toml | 2 +- tcp/Cargo.toml | 2 +- ws/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/http/Cargo.toml b/http/Cargo.toml index 864c3e8b3..fdccef242 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -18,7 +18,7 @@ jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" net2 = "0.2" -parking_lot = "0.10.0" +parking_lot = "0.11.0" unicase = "2.0" [dev-dependencies] diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 3bc1cb1fb..ff38dee14 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -17,7 +17,7 @@ tokio-service = "0.1" jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } parity-tokio-ipc = "0.4" -parking_lot = "0.10.0" +parking_lot = "0.11.0" [dev-dependencies] env_logger = "0.7" diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index d3b8e9eb7..365d8667a 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -16,7 +16,7 @@ futures03 = { version = "0.3", features = ["compat"], package = "futures" } jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" -parking_lot = "0.10.0" +parking_lot = "0.11.0" tokio-service = "0.1" [dev-dependencies] diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 81d4eb34e..2247d1098 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -15,7 +15,7 @@ futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" -parking_lot = "0.10.0" +parking_lot = "0.11.0" slab = "0.4" parity-ws = "0.10" From 2cb9752b15400c1270643dd9abd60811fa3cb5b4 Mon Sep 17 00:00:00 2001 From: Zeke Mostov <32168567+emostov@users.noreply.github.com> Date: Mon, 5 Oct 2020 02:25:02 -0700 Subject: [PATCH 133/149] Expose direct configuration of max in and out buffer capacity (#594) * Expose direct configuration of max in and out buffer capacity * Correct argument duplicate typo" * Add server builder tests * rustfmt ws/src/server_builder.rs --- ws/src/server.rs | 6 ++-- ws/src/server_builder.rs | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/ws/src/server.rs b/ws/src/server.rs index 3e51ef208..55717dc7e 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -58,6 +58,8 @@ impl Server { executor: UninitializedExecutor, max_connections: usize, max_payload_bytes: usize, + max_in_buffer_capacity: usize, + max_out_buffer_capacity: usize, ) -> Result where S::Future: Unpin, @@ -68,8 +70,8 @@ impl Server { config.max_connections = max_connections; // don't accept super large requests config.max_fragment_size = max_payload_bytes; - config.max_in_buffer_capacity = max_payload_bytes; - config.max_out_buffer_capacity = max_payload_bytes; + config.max_in_buffer_capacity = max_in_buffer_capacity; + config.max_out_buffer_capacity = max_out_buffer_capacity; // don't grow non-final fragments (to prevent DOS) config.fragments_grow = false; config.fragments_capacity = cmp::max(1, max_payload_bytes / config.fragment_size); diff --git a/ws/src/server_builder.rs b/ws/src/server_builder.rs index 535c1f321..e10978f34 100644 --- a/ws/src/server_builder.rs +++ b/ws/src/server_builder.rs @@ -24,6 +24,8 @@ pub struct ServerBuilder> { executor: UninitializedExecutor, max_connections: usize, max_payload_bytes: usize, + max_in_buffer_capacity: usize, + max_out_buffer_capacity: usize, } impl> ServerBuilder @@ -61,6 +63,8 @@ where executor: UninitializedExecutor::Unspawned, max_connections: 100, max_payload_bytes: 5 * 1024 * 1024, + max_in_buffer_capacity: 10 * 1024 * 1024, + max_out_buffer_capacity: 10 * 1024 * 1024, } } @@ -115,6 +119,20 @@ where self } + /// The maximum size to which the incoming buffer can grow. + /// Default: 10,485,760 + pub fn max_in_buffer_capacity(mut self, max_in_buffer_capacity: usize) -> Self { + self.max_in_buffer_capacity = max_in_buffer_capacity; + self + } + + /// The maximum size to which the outgoing buffer can grow. + /// Default: 10,485,760 + pub fn max_out_buffer_capacity(mut self, max_out_buffer_capacity: usize) -> Self { + self.max_out_buffer_capacity = max_out_buffer_capacity; + self + } + /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. pub fn start(self, addr: &SocketAddr) -> Result { @@ -129,6 +147,51 @@ where self.executor, self.max_connections, self.max_payload_bytes, + self.max_in_buffer_capacity, + self.max_out_buffer_capacity, ) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn basic_server_builder() -> ServerBuilder<(), jsonrpc_core::middleware::Noop> { + let io = core::IoHandler::default(); + ServerBuilder::new(io) + } + #[test] + fn config_usize_vals_have_correct_defaults() { + let server = basic_server_builder(); + + assert_eq!(server.max_connections, 100); + assert_eq!(server.max_payload_bytes, 5 * 1024 * 1024); + assert_eq!(server.max_in_buffer_capacity, 10 * 1024 * 1024); + assert_eq!(server.max_out_buffer_capacity, 10 * 1024 * 1024); + } + + #[test] + fn config_usize_vals_can_be_set() { + let server = basic_server_builder(); + + // We can set them individually + let server = server.max_connections(10); + assert_eq!(server.max_connections, 10); + + let server = server.max_payload(29); + assert_eq!(server.max_payload_bytes, 29); + + let server = server.max_in_buffer_capacity(38); + assert_eq!(server.max_in_buffer_capacity, 38); + + let server = server.max_out_buffer_capacity(47); + assert_eq!(server.max_out_buffer_capacity, 47); + + // Setting values consecutively does not impact other values + assert_eq!(server.max_connections, 10); + assert_eq!(server.max_payload_bytes, 29); + assert_eq!(server.max_in_buffer_capacity, 38); + assert_eq!(server.max_out_buffer_capacity, 47); + } +} From 454cf5d19a6a7ca66d34a7c5888699534bef4c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 6 Nov 2020 20:46:50 +0100 Subject: [PATCH 134/149] Update examples in top-level readme. (#588) * Update examples in top-level readme. * cargo fmt --all * Added docs. --- README.md | 10 +++--- core/README.md | 59 ------------------------------- derive/examples/client-local.rs | 62 +++++++++++++++++++++++++++++++++ derive/src/lib.rs | 17 ++++++++- 4 files changed, 82 insertions(+), 66 deletions(-) delete mode 100644 core/README.md create mode 100644 derive/examples/client-local.rs diff --git a/README.md b/README.md index 61cd89680..45a018116 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,12 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets` ```rust use jsonrpc_http_server::jsonrpc_core::{IoHandler, Value, Params}; -use jsonrpc_http_server::{ServerBuilder}; +use jsonrpc_http_server::ServerBuilder; fn main() { - let mut io = IoHandler::new(); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".to_string())) + let mut io = IoHandler::default(); + io.add_method("say_hello", |_params: Params| async { + Ok(Value::String("hello".to_owned())) }); let server = ServerBuilder::new(io) @@ -97,7 +97,6 @@ fn main() { ```rust use jsonrpc_core_client::transports::local; -use jsonrpc_core::futures::future::{self, Future, FutureResult}; use jsonrpc_core::{Error, IoHandler, Result}; use jsonrpc_derive::rpc; @@ -143,5 +142,4 @@ fn main() { }; fut.wait().unwrap(); } - ``` diff --git a/core/README.md b/core/README.md deleted file mode 100644 index 13f5e9f64..000000000 --- a/core/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# jsonrpc-core -Transport agnostic rust implementation of JSON-RPC 2.0 Specification. - -[Documentation](http://paritytech.github.io/jsonrpc/jsonrpc_core/index.html) - -- [x] - server side -- [x] - client side - -## Example - -`Cargo.toml` - - -``` -[dependencies] -jsonrpc-core = "4.0" -``` - -`main.rs` - -```rust -use jsonrpc_core::*; - -fn main() { - let mut io = IoHandler::default(); - io.add_method("say_hello", |_params: Params| { - Ok(Value::String("hello".into())) - }); - - let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; - - assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); -} -``` - -### Asynchronous responses - -`main.rs` - -```rust -use jsonrpc_core::*; -use jsonrpc_core::futures::Future; - -fn main() { - let io = IoHandler::new(); - io.add_async_method("say_hello", |_params: Params| { - futures::finished(Value::String("hello".into())) - }); - - let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; - - assert_eq!(io.handle_request(request).wait().unwrap(), Some(response.to_owned())); -} -``` - -### Publish-Subscribe -See examples directory. diff --git a/derive/examples/client-local.rs b/derive/examples/client-local.rs new file mode 100644 index 000000000..09a8c0148 --- /dev/null +++ b/derive/examples/client-local.rs @@ -0,0 +1,62 @@ +use jsonrpc_core::{ + futures::{self, FutureExt}, + BoxFuture, IoHandler, Result, +}; +use jsonrpc_core_client::transports::local; +use jsonrpc_derive::rpc; + +/// Rpc trait +#[rpc] +pub trait Rpc { + /// Returns a protocol version + #[rpc(name = "protocolVersion")] + fn protocol_version(&self) -> Result; + + /// Adds two numbers and returns a result + #[rpc(name = "add", alias("callAsyncMetaAlias"))] + fn add(&self, a: u64, b: u64) -> Result; + + /// Performs asynchronous operation + #[rpc(name = "callAsync")] + fn call(&self, a: u64) -> BoxFuture>; +} + +struct RpcImpl; + +impl Rpc for RpcImpl { + fn protocol_version(&self) -> Result { + Ok("version1".into()) + } + + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } + + fn call(&self, _: u64) -> BoxFuture> { + Box::pin(futures::future::ready(Ok("OK".to_owned()))) + } +} + +fn main() { + futures::executor::block_on(async { + let mut io = IoHandler::new(); + io.extend_with(RpcImpl.to_delegate()); + println!("Starting local server"); + let (client, server) = local::connect(io); + let client = use_client(client).fuse(); + let server = server.fuse(); + + futures::pin_mut!(client); + futures::pin_mut!(server); + + futures::select! { + server = server => {}, + client = client => {}, + } + }); +} + +async fn use_client(client: RpcClient) { + let res = client.add(5, 6).await.unwrap(); + println!("5 + 6 = {}", res); +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 13cb0fe7b..9f0f50657 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -120,7 +120,22 @@ //! } //! } //! -//! # fn main() {} +//! fn main() { +//! let mut io = jsonrpc_core::MetaIoHandler::default(); +//! io.extend_with(RpcImpl::default().to_delegate()); +//! +//! let server_builder = jsonrpc_tcp_server::ServerBuilder::with_meta_extractor( +//! io, +//! |request: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(request.sender.clone())) +//! ); +//! let server = server_builder +//! .start(&"127.0.0.1:3030".parse().unwrap()) +//! .expect("Unable to start TCP server"); +//! +//! // The server spawns a separate thread. Dropping the `server` handle causes it to close. +//! // Uncomment the line below to keep the server running in your example. +//! // server.wait(); +//! } //! ``` //! //! Client Example From 40eec2045f85c76de48fdeb8aad1a0ecadd5821f Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Wed, 9 Dec 2020 13:04:28 +0200 Subject: [PATCH 135/149] Fix typos (#587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix typos * Fix request.sender * cargo fmt --all Co-authored-by: Tomasz Drwięga --- pubsub/examples/pubsub.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubsub/examples/pubsub.rs b/pubsub/examples/pubsub.rs index b44896afa..60d17cde6 100644 --- a/pubsub/examples/pubsub.rs +++ b/pubsub/examples/pubsub.rs @@ -39,7 +39,7 @@ fn main() { // or drop(subscriber) loop { - if is_done.load(atomic::Ordering::AcqRel) { + if is_done.load(atomic::Ordering::SeqCst) { return; } @@ -56,7 +56,7 @@ fn main() { }), ("remove_hello", move |_id: SubscriptionId, _| { println!("Closing subscription"); - is_done2.store(true, atomic::Ordering::AcqRel); + is_done2.store(true, atomic::Ordering::SeqCst); futures::future::ok(Value::Bool(true)) }), ); From 61ea7c65a64bb977c2e54ed1833d8d2190bb4007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 9 Dec 2020 12:05:06 +0100 Subject: [PATCH 136/149] [forwardport] Update `local` client to support middleware (Kudos Seun) (#589) (#600) * Update `local` client to support middleware (Kudos Seun) (#589) * v15.0.1 * v15.0.1 => v15.1.0 * v15.0.1 => v15.1.0 * cargo fmt * fix tests * adds *_with_middleware methods * remove Unpin bound, add documentation, cargo fmt * 15.0.1 * bump to 15.1.0 Co-authored-by: Seun Lanlege Co-authored-by: Niklas * cargo fmt --all Co-authored-by: Seun Lanlege Co-authored-by: Niklas --- .../transports/src/transports/local.rs | 77 +++++++++++++++---- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/core-client/transports/src/transports/local.rs b/core-client/transports/src/transports/local.rs index e4d91eaef..da91a554b 100644 --- a/core-client/transports/src/transports/local.rs +++ b/core-client/transports/src/transports/local.rs @@ -6,7 +6,7 @@ use futures::{ task::{Context, Poll}, Future, Sink, SinkExt, Stream, StreamExt, }; -use jsonrpc_core::{BoxFuture, MetaIoHandler, Metadata}; +use jsonrpc_core::{BoxFuture, MetaIoHandler, Metadata, Middleware}; use jsonrpc_pubsub::Session; use std::ops::Deref; use std::pin::Pin; @@ -26,10 +26,11 @@ enum Buffered { None, } -impl LocalRpc +impl LocalRpc where TMetadata: Metadata, - THandler: Deref>, + TMiddleware: Middleware, + THandler: Deref>, { /// Creates a new `LocalRpc` with default metadata. pub fn new(handler: THandler) -> Self @@ -50,10 +51,11 @@ where } } -impl Stream for LocalRpc +impl Stream for LocalRpc where TMetadata: Metadata + Unpin, - THandler: Deref> + Unpin, + TMiddleware: Middleware + Unpin, + THandler: Deref> + Unpin, { type Item = String; @@ -62,10 +64,11 @@ where } } -impl LocalRpc +impl LocalRpc where TMetadata: Metadata + Unpin, - THandler: Deref> + Unpin, + TMiddleware: Middleware + Unpin, + THandler: Deref> + Unpin, { fn poll_buffered(&mut self, cx: &mut Context) -> Poll> { let response = match self.buffered { @@ -87,10 +90,11 @@ where } } -impl Sink for LocalRpc +impl Sink for LocalRpc where TMetadata: Metadata + Unpin, - THandler: Deref> + Unpin, + TMiddleware: Middleware + Unpin, + THandler: Deref> + Unpin, { type Error = RpcError; @@ -121,14 +125,15 @@ where } } -/// Connects to a `Deref`. -pub fn connect_with_metadata( +/// Connects to a `Deref` specifying a custom middleware implementation. +pub fn connect_with_metadata_and_middleware( handler: THandler, meta: TMetadata, ) -> (TClient, impl Future>) where TClient: From, - THandler: Deref> + Unpin, + TMiddleware: Middleware + Unpin, + THandler: Deref> + Unpin, TMetadata: Metadata + Unpin, { let (sink, stream) = LocalRpc::with_metadata(handler, meta).split(); @@ -137,24 +142,53 @@ where (client, rpc_client) } +/// Connects to a `Deref`. +pub fn connect_with_metadata( + handler: THandler, + meta: TMetadata, +) -> (TClient, impl Future>) +where + TClient: From, + TMetadata: Metadata + Unpin, + THandler: Deref> + Unpin, +{ + connect_with_metadata_and_middleware(handler, meta) +} + +/// Connects to a `Deref` specifying a custom middleware implementation. +pub fn connect_with_middleware( + handler: THandler, +) -> (TClient, impl Future>) +where + TClient: From, + TMetadata: Metadata + Default + Unpin, + TMiddleware: Middleware + Unpin, + THandler: Deref> + Unpin, +{ + connect_with_metadata_and_middleware(handler, Default::default()) +} + /// Connects to a `Deref`. pub fn connect(handler: THandler) -> (TClient, impl Future>) where TClient: From, - THandler: Deref> + Unpin, TMetadata: Metadata + Default + Unpin, + THandler: Deref> + Unpin, { - connect_with_metadata(handler, Default::default()) + connect_with_middleware(handler) } /// Metadata for LocalRpc. pub type LocalMeta = Arc; -/// Connects with pubsub. -pub fn connect_with_pubsub(handler: THandler) -> (TClient, impl Future>) +/// Connects with pubsub specifying a custom middleware implementation. +pub fn connect_with_pubsub_and_middleware( + handler: THandler, +) -> (TClient, impl Future>) where TClient: From, - THandler: Deref> + Unpin, + TMiddleware: Middleware + Unpin, + THandler: Deref> + Unpin, { let (tx, rx) = mpsc::unbounded(); let meta = Arc::new(Session::new(tx)); @@ -164,3 +198,12 @@ where let client = TClient::from(sender); (client, rpc_client) } + +/// Connects with pubsub. +pub fn connect_with_pubsub(handler: THandler) -> (TClient, impl Future>) +where + TClient: From, + THandler: Deref> + Unpin, +{ + connect_with_pubsub_and_middleware(handler) +} From 91cf903aa8a083ebf05018952c33a961ba590c4f Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 11 Jan 2021 15:49:45 +0100 Subject: [PATCH 137/149] Migrate to Tokio 0.2 (#603) --- core-client/Cargo.toml | 10 +- core-client/src/lib.rs | 3 - core-client/transports/Cargo.toml | 16 +- core-client/transports/src/transports/http.rs | 132 ++++---- core-client/transports/src/transports/ipc.rs | 152 ++++------ core-client/transports/src/transports/ws.rs | 178 +++++++---- derive/examples/client-local.rs | 4 +- http/Cargo.toml | 5 +- http/src/handler.rs | 160 +++++----- http/src/lib.rs | 149 +++++---- ipc/Cargo.toml | 11 +- ipc/src/meta.rs | 4 +- ipc/src/select_with_weak.rs | 48 +-- ipc/src/server.rs | 282 +++++++++--------- server-utils/Cargo.toml | 9 +- server-utils/src/lib.rs | 2 +- server-utils/src/reactor.rs | 61 ++-- server-utils/src/stream_codec.rs | 10 +- server-utils/src/suspendable_stream.rs | 50 ++-- stdio/Cargo.toml | 9 +- stdio/examples/stdio.rs | 6 +- stdio/src/lib.rs | 60 ++-- tcp/Cargo.toml | 5 +- tcp/src/dispatch.rs | 50 ++-- tcp/src/lib.rs | 2 + tcp/src/server.rs | 105 +++---- tcp/src/service.rs | 23 +- tcp/src/tests.rs | 166 +++++------ ws/Cargo.toml | 3 +- ws/src/metadata.rs | 37 ++- ws/src/server_builder.rs | 5 +- ws/src/session.rs | 60 ++-- ws/src/tests.rs | 2 +- 33 files changed, 924 insertions(+), 895 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index 7bf00a1aa..a0e7953fe 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -19,16 +19,14 @@ categories = [ ] [features] -tls = ["jsonrpc-client-transports/tls", "futures01"] -http = ["jsonrpc-client-transports/http", "futures01"] -ws = ["jsonrpc-client-transports/ws", "futures01"] -ipc = ["jsonrpc-client-transports/ipc", "futures01"] +tls = ["jsonrpc-client-transports/tls"] +http = ["jsonrpc-client-transports/http"] +ws = ["jsonrpc-client-transports/ws"] +ipc = ["jsonrpc-client-transports/ipc"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] jsonrpc-client-transports = { version = "16.0", path = "./transports", default-features = false } -# Only for client transports, should be removed when we fully transition to futures=0.3 -futures01 = { version = "0.1", package = "futures", optional = true } futures = { version = "0.3", features = [ "compat" ] } [badges] diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index c7294c871..81b8b462e 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -9,6 +9,3 @@ pub use futures; pub use jsonrpc_client_transports::*; - -#[cfg(feature = "futures01")] -pub use futures01; diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index a1804b8c1..2cad8f8ad 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -21,23 +21,22 @@ categories = [ [features] default = ["http", "tls", "ws"] tls = ["hyper-tls", "http"] -http = ["hyper", "futures01"] +http = ["hyper", "tokio/full"] ws = [ "websocket", "tokio", - "futures01", + "futures/compat" ] ipc = [ "parity-tokio-ipc", "jsonrpc-server-utils", "tokio", - "futures01", ] arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dependencies] derive_more = "0.99" -futures = { version = "0.3", features = [ "compat" ] } +futures = "0.3" jsonrpc-core = { version = "16.0", path = "../../core" } jsonrpc-pubsub = { version = "16.0", path = "../../pubsub" } log = "0.4" @@ -45,12 +44,11 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" url = "1.7" -futures01 = { version = "0.1.26", package = "futures", optional = true } -hyper = { version = "0.12", optional = true } -hyper-tls = { version = "0.3.2", optional = true } +hyper = { version = "0.13", optional = true } +hyper-tls = { version = "0.4", optional = true } jsonrpc-server-utils = { version = "16.0", path = "../../server-utils", optional = true } -parity-tokio-ipc = { version = "0.2", optional = true } -tokio = { version = "0.1", optional = true } +parity-tokio-ipc = { version = "0.8", optional = true } +tokio = { version = "0.2", optional = true } websocket = { version = "0.24", optional = true } [dev-dependencies] diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index b4b7cb9ae..df518340a 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -4,75 +4,52 @@ use super::RequestBuilder; use crate::{RpcChannel, RpcError, RpcMessage, RpcResult}; -use futures::{Future, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; -use hyper::{http, rt, Client, Request, Uri}; +use futures::{future, Future, FutureExt, StreamExt, TryFutureExt}; +use hyper::{http, Client, Request, Uri}; /// Create a HTTP Client -pub fn connect(url: &str) -> impl Future> +pub async fn connect(url: &str) -> RpcResult where TClient: From, { - let (sender, receiver) = futures::channel::oneshot::channel(); - let url = url.to_owned(); - - std::thread::spawn(move || { - let connect = rt::lazy(move || { - do_connect(&url) - .map(|client| { - if sender.send(client).is_err() { - panic!("The caller did not wait for the server."); - } - Ok(()) - }) - .compat() - }); - rt::run(connect); - }); + let url: Uri = url.parse().map_err(|e| RpcError::Other(Box::new(e)))?; - receiver.map(|res| res.expect("Server closed prematurely.").map(TClient::from)) -} + let (client_api, client_worker) = do_connect(url).await; + tokio::spawn(client_worker); -fn do_connect(url: &str) -> impl Future> { - use futures::future::ready; + Ok(TClient::from(client_api)) +} +async fn do_connect(url: Uri) -> (RpcChannel, impl Future) { let max_parallel = 8; - let url: Uri = match url.parse() { - Ok(url) => url, - Err(e) => return ready(Err(RpcError::Other(Box::new(e)))), - }; #[cfg(feature = "tls")] - let connector = match hyper_tls::HttpsConnector::new(4) { - Ok(connector) => connector, - Err(e) => return ready(Err(RpcError::Other(Box::new(e)))), - }; + let connector = hyper_tls::HttpsConnector::new(); #[cfg(feature = "tls")] let client = Client::builder().build::<_, hyper::Body>(connector); #[cfg(not(feature = "tls"))] let client = Client::new(); - + // Keep track of internal request IDs when building subsequent requests let mut request_builder = RequestBuilder::new(); let (sender, receiver) = futures::channel::mpsc::unbounded(); - use futures01::{Future, Stream}; let fut = receiver - .map(Ok) - .compat() .filter_map(move |msg: RpcMessage| { - let (request, sender) = match msg { + future::ready(match msg { RpcMessage::Call(call) => { let (_, request) = request_builder.call_request(&call); - (request, Some(call.sender)) + Some((request, Some(call.sender))) } - RpcMessage::Notify(notify) => (request_builder.notification(¬ify), None), + RpcMessage::Notify(notify) => Some((request_builder.notification(¬ify), None)), RpcMessage::Subscribe(_) => { log::warn!("Unsupported `RpcMessage` type `Subscribe`."); - return None; + None } - }; - + }) + }) + .map(move |(request, sender)| { let request = Request::post(&url) .header( http::header::CONTENT_TYPE, @@ -85,46 +62,42 @@ fn do_connect(url: &str) -> impl Future> { .body(request.into()) .expect("Uri and request headers are valid; qed"); - Some(client.request(request).then(move |response| Ok((response, sender)))) + client + .request(request) + .then(|response| async move { (response, sender) }) }) .buffer_unordered(max_parallel) - .for_each(|(result, sender)| { - use futures01::future::{ - self, - Either::{A, B}, - }; - let future = match result { + .for_each(|(response, sender)| async { + let result = match response { Ok(ref res) if !res.status().is_success() => { log::trace!("http result status {}", res.status()); - A(future::err(RpcError::Client(format!( + Err(RpcError::Client(format!( "Unexpected response status code: {}", res.status() - )))) + ))) + } + Err(err) => Err(RpcError::Other(Box::new(err))), + Ok(res) => { + hyper::body::to_bytes(res.into_body()) + .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e))) + .await } - Ok(res) => B(res - .into_body() - .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e))) - .concat2()), - Err(err) => A(future::err(RpcError::Other(Box::new(err)))), }; - future.then(|result| { - if let Some(sender) = sender { - let response = result - .and_then(|response| { - let response_str = String::from_utf8_lossy(response.as_ref()).into_owned(); - super::parse_response(&response_str) - }) - .and_then(|r| r.1); - if let Err(err) = sender.send(response) { - log::warn!("Error resuming asynchronous request: {:?}", err); - } + + if let Some(sender) = sender { + let response = result + .and_then(|response| { + let response_str = String::from_utf8_lossy(response.as_ref()).into_owned(); + super::parse_response(&response_str) + }) + .and_then(|r| r.1); + if let Err(err) = sender.send(response) { + log::warn!("Error resuming asynchronous request: {:?}", err); } - Ok(()) - }) + } }); - rt::spawn(fut.map_err(|e: RpcError| log::error!("RPC Client error: {:?}", e))); - ready(Ok(sender.into())) + (sender.into(), fut) } #[cfg(test)] @@ -218,7 +191,7 @@ mod tests { Ok(()) as RpcResult<_> }; - futures::executor::block_on(run).unwrap(); + tokio::runtime::Runtime::new().unwrap().block_on(run).unwrap(); } #[test] @@ -227,18 +200,16 @@ mod tests { // given let server = TestServer::serve(id); - let (tx, rx) = std::sync::mpsc::channel(); // when - let run = async move { + let run = async { let client: TestClient = connect(&server.uri).await.unwrap(); client.notify(12).unwrap(); - tx.send(()).unwrap(); }; - let pool = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); - pool.spawn_ok(run); - rx.recv().unwrap(); + tokio::runtime::Runtime::new().unwrap().block_on(run); + // Ensure that server has not been moved into runtime + drop(server); } #[test] @@ -249,7 +220,8 @@ mod tests { let invalid_uri = "invalid uri"; // when - let res: RpcResult = futures::executor::block_on(connect(invalid_uri)); + let fut = connect(invalid_uri); + let res: RpcResult = tokio::runtime::Runtime::new().unwrap().block_on(fut); // then assert_matches!( @@ -271,7 +243,7 @@ mod tests { let client: TestClient = connect(&server.uri).await?; client.fail().await }; - let res = futures::executor::block_on(run); + let res = tokio::runtime::Runtime::new().unwrap().block_on(run); // then if let Err(RpcError::JsonRpcError(err)) = res { @@ -312,6 +284,6 @@ mod tests { Ok(()) as RpcResult<_> }; - futures::executor::block_on(run).unwrap(); + tokio::runtime::Runtime::new().unwrap().block_on(run).unwrap(); } } diff --git a/core-client/transports/src/transports/ipc.rs b/core-client/transports/src/transports/ipc.rs index 4ad2ff429..d50022597 100644 --- a/core-client/transports/src/transports/ipc.rs +++ b/core-client/transports/src/transports/ipc.rs @@ -3,42 +3,34 @@ use crate::transports::duplex::duplex; use crate::{RpcChannel, RpcError}; -use futures::compat::{Sink01CompatExt, Stream01CompatExt}; -use futures::StreamExt; -use futures01::prelude::*; +use futures::{SinkExt, StreamExt, TryStreamExt}; use jsonrpc_server_utils::codecs::StreamCodec; -use parity_tokio_ipc::IpcConnection; +use jsonrpc_server_utils::tokio; +use jsonrpc_server_utils::tokio_util::codec::Decoder as _; +use parity_tokio_ipc::Endpoint; use std::path::Path; -use tokio::codec::Decoder; -use tokio::runtime::Runtime; /// Connect to a JSON-RPC IPC server. -pub fn connect, Client: From>( - path: P, -) -> impl futures::Future> { - let rt = Runtime::new().unwrap(); - #[allow(deprecated)] - let reactor = rt.reactor().clone(); - async move { - let connection = IpcConnection::connect(path, &reactor).map_err(|e| RpcError::Other(e.into()))?; - let (sink, stream) = StreamCodec::stream_incoming().framed(connection).split(); - let sink = sink.sink_map_err(|e| RpcError::Other(e.into())); - let stream = stream.map_err(|e| log::error!("IPC stream error: {}", e)); - - let (client, sender) = duplex( - Box::pin(sink.sink_compat()), - Box::pin( - stream - .compat() - .take_while(|x| futures::future::ready(x.is_ok())) - .map(|x| x.expect("Stream is closed upon first error.")), - ), - ); - - tokio::spawn(futures::compat::Compat::new(client).map_err(|_| unreachable!())); - - Ok(sender.into()) - } +pub async fn connect, Client: From>(path: P) -> Result { + let connection = Endpoint::connect(path) + .await + .map_err(|e| RpcError::Other(Box::new(e)))?; + let (sink, stream) = StreamCodec::stream_incoming().framed(connection).split(); + let sink = sink.sink_map_err(|e| RpcError::Other(Box::new(e))); + let stream = stream.map_err(|e| log::error!("IPC stream error: {}", e)); + + let (client, sender) = duplex( + Box::pin(sink), + Box::pin( + stream + .take_while(|x| futures::future::ready(x.is_ok())) + .map(|x| x.expect("Stream is closed upon first error.")), + ), + ); + + tokio::spawn(client); + + Ok(sender.into()) } #[cfg(test)] @@ -52,13 +44,10 @@ mod tests { #[test] fn should_call_one() { - let mut rt = Runtime::new().unwrap(); - #[allow(deprecated)] - let reactor = rt.reactor().clone(); let sock_path = dummy_endpoint(); let mut io = IoHandler::new(); - io.add_method("greeting", |params| { + io.add_method("greeting", |params| async { let map_obj = match params { Params::Map(obj) => obj, _ => return Err(Error::invalid_params("missing object")), @@ -69,66 +58,57 @@ mod tests { }; Ok(Value::String(format!("Hello {}!", name))) }); - let builder = ServerBuilder::new(io).event_loop_executor(rt.executor()); - let server = builder.start(&sock_path).expect("Couldn't open socket"); - - let client: RawClient = rt.block_on(connect(sock_path, &reactor).unwrap()).unwrap(); - let mut map = Map::new(); - map.insert("name".to_string(), "Jeffry".into()); - let fut = client.call_method("greeting", Params::Map(map)); - - // FIXME: it seems that IPC server on Windows won't be polled with - // default I/O reactor, work around with sending stop signal which polls - // the server (https://github.com/paritytech/jsonrpc/pull/459) - server.close(); - - match rt.block_on(fut) { - Ok(val) => assert_eq!(&val, "Hello Jeffry!"), - Err(err) => panic!("IPC RPC call failed: {}", err), - } - rt.shutdown_now().wait().unwrap(); + let builder = ServerBuilder::new(io); + let _server = builder.start(&sock_path).expect("Couldn't open socket"); + + let client_fut = async move { + let client: RawClient = connect(sock_path).await.unwrap(); + let mut map = Map::new(); + map.insert("name".to_string(), "Jeffry".into()); + let fut = client.call_method("greeting", Params::Map(map)); + + match fut.await { + Ok(val) => assert_eq!(&val, "Hello Jeffry!"), + Err(err) => panic!("IPC RPC call failed: {}", err), + } + }; + tokio::runtime::Runtime::new().unwrap().block_on(client_fut); } #[test] fn should_fail_without_server() { - let rt = Runtime::new().unwrap(); - #[allow(deprecated)] - let reactor = rt.reactor(); - - match connect::<_, RawClient>(dummy_endpoint(), reactor) { - Err(..) => {} - Ok(..) => panic!("Should not be able to connect to an IPC socket that's not open"), - } - rt.shutdown_now().wait().unwrap(); + let test_fut = async move { + match connect::<_, RawClient>(dummy_endpoint()).await { + Err(..) => {} + Ok(..) => panic!("Should not be able to connect to an IPC socket that's not open"), + } + }; + + tokio::runtime::Runtime::new().unwrap().block_on(test_fut); } #[test] fn should_handle_server_error() { - let mut rt = Runtime::new().unwrap(); - #[allow(deprecated)] - let reactor = rt.reactor().clone(); let sock_path = dummy_endpoint(); let mut io = IoHandler::new(); - io.add_method("greeting", |_params| Err(Error::invalid_params("test error"))); - let builder = ServerBuilder::new(io).event_loop_executor(rt.executor()); - let server = builder.start(&sock_path).expect("Couldn't open socket"); - - let client: RawClient = rt.block_on(connect(sock_path, &reactor).unwrap()).unwrap(); - let mut map = Map::new(); - map.insert("name".to_string(), "Jeffry".into()); - let fut = client.call_method("greeting", Params::Map(map)); - - // FIXME: it seems that IPC server on Windows won't be polled with - // default I/O reactor, work around with sending stop signal which polls - // the server (https://github.com/paritytech/jsonrpc/pull/459) - server.close(); - - match rt.block_on(fut) { - Err(RpcError::JsonRpcError(err)) => assert_eq!(err.code, ErrorCode::InvalidParams), - Ok(_) => panic!("Expected the call to fail"), - _ => panic!("Unexpected error type"), - } - rt.shutdown_now().wait().unwrap(); + io.add_method("greeting", |_params| async { Err(Error::invalid_params("test error")) }); + let builder = ServerBuilder::new(io); + let _server = builder.start(&sock_path).expect("Couldn't open socket"); + + let client_fut = async move { + let client: RawClient = connect(sock_path).await.unwrap(); + let mut map = Map::new(); + map.insert("name".to_string(), "Jeffry".into()); + let fut = client.call_method("greeting", Params::Map(map)); + + match fut.await { + Err(RpcError::JsonRpcError(err)) => assert_eq!(err.code, ErrorCode::InvalidParams), + Ok(_) => panic!("Expected the call to fail"), + _ => panic!("Unexpected error type"), + } + }; + + tokio::runtime::Runtime::new().unwrap().block_on(client_fut); } } diff --git a/core-client/transports/src/transports/ws.rs b/core-client/transports/src/transports/ws.rs index d74d5df44..976d48748 100644 --- a/core-client/transports/src/transports/ws.rs +++ b/core-client/transports/src/transports/ws.rs @@ -1,16 +1,18 @@ //! JSON-RPC websocket client implementation. -use crate::{RpcChannel, RpcError}; -use futures01::prelude::*; -use log::info; use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::{RpcChannel, RpcError}; use websocket::{ClientBuilder, OwnedMessage}; /// Connect to a JSON-RPC websocket server. /// -/// Uses an unbuffered channel to queue outgoing rpc messages. +/// Uses an unbounded channel to queue outgoing rpc messages. /// /// Returns `Err` if the `url` is invalid. -pub fn try_connect(url: &str) -> Result, RpcError> +pub fn try_connect(url: &str) -> Result>, RpcError> where T: From, { @@ -20,40 +22,47 @@ where /// Connect to a JSON-RPC websocket server. /// -/// Uses an unbuffered channel to queue outgoing rpc messages. -pub fn connect(url: &url::Url) -> impl futures::Future> +/// Uses an unbounded channel to queue outgoing rpc messages. +pub fn connect(url: &url::Url) -> impl Future> where T: From, { let client_builder = ClientBuilder::from_url(url); - let fut = do_connect(client_builder); - futures::compat::Compat01As03::new(fut) + do_connect(client_builder) } -fn do_connect(client_builder: ClientBuilder) -> impl Future +fn do_connect(client_builder: ClientBuilder) -> impl Future> where T: From, { + use futures::compat::{Future01CompatExt, Sink01CompatExt, Stream01CompatExt}; + use futures::{SinkExt, StreamExt, TryFutureExt, TryStreamExt}; + use websocket::futures::Stream; + client_builder .async_connect(None) - .map(|(client, _)| { - use futures::{StreamExt, TryFutureExt}; + .compat() + .map_err(|error| RpcError::Other(Box::new(error))) + .map_ok(|(client, _)| { let (sink, stream) = client.split(); + + let sink = sink.sink_compat().sink_map_err(|e| RpcError::Other(Box::new(e))); + let stream = stream.compat().map_err(|e| RpcError::Other(Box::new(e))); let (sink, stream) = WebsocketClient::new(sink, stream).split(); let (sink, stream) = ( - Box::pin(futures::compat::Compat01As03Sink::new(sink)), + Box::pin(sink), Box::pin( - futures::compat::Compat01As03::new(stream) + stream .take_while(|x| futures::future::ready(x.is_ok())) .map(|x| x.expect("Stream is closed upon first error.")), ), ); let (rpc_client, sender) = super::duplex(sink, stream); - let rpc_client = rpc_client.compat().map_err(|error| log::error!("{:?}", error)); + let rpc_client = rpc_client.map_err(|error| log::error!("{:?}", error)); tokio::spawn(rpc_client); + sender.into() }) - .map_err(|error| RpcError::Other(Box::new(error))) } struct WebsocketClient { @@ -64,8 +73,8 @@ struct WebsocketClient { impl WebsocketClient where - TSink: Sink, - TStream: Stream, + TSink: futures::Sink + Unpin, + TStream: futures::Stream> + Unpin, TError: std::error::Error + Send + 'static, { pub fn new(sink: TSink, stream: TStream) -> Self { @@ -75,67 +84,116 @@ where queue: VecDeque::new(), } } + + // Drains the internal buffer and attempts to forward as much of the items + // as possible to the underlying sink + fn try_empty_buffer(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = Pin::into_inner(self); + + match Pin::new(&mut this.sink).poll_ready(cx) { + Poll::Ready(value) => value?, + Poll::Pending => return Poll::Pending, + } + + while let Some(item) = this.queue.pop_front() { + Pin::new(&mut this.sink).start_send(item)?; + + if !this.queue.is_empty() { + match Pin::new(&mut this.sink).poll_ready(cx) { + Poll::Ready(value) => value?, + Poll::Pending => return Poll::Pending, + } + } + } + + Poll::Ready(Ok(())) + } } -impl Sink for WebsocketClient +// This mostly forwards to the underlying sink but also adds an unbounded queue +// for when the underlying sink is incapable of receiving more items. +// See https://docs.rs/futures-util/0.3.8/futures_util/sink/struct.Buffer.html +// for the variant with a fixed-size buffer. +impl futures::Sink for WebsocketClient where - TSink: Sink, - TStream: Stream, - TError: std::error::Error + Send + 'static, + TSink: futures::Sink + Unpin, + TStream: futures::Stream> + Unpin, { - type SinkItem = String; - type SinkError = RpcError; + type Error = RpcError; - fn start_send(&mut self, request: Self::SinkItem) -> Result, Self::SinkError> { - self.queue.push_back(OwnedMessage::Text(request)); - Ok(AsyncSink::Ready) + fn start_send(mut self: Pin<&mut Self>, request: String) -> Result<(), Self::Error> { + let request = OwnedMessage::Text(request); + + if self.queue.is_empty() { + let this = Pin::into_inner(self); + Pin::new(&mut this.sink).start_send(request) + } else { + self.queue.push_back(request); + Ok(()) + } } - fn poll_complete(&mut self) -> Result, Self::SinkError> { - loop { - match self.queue.pop_front() { - Some(request) => match self.sink.start_send(request) { - Ok(AsyncSink::Ready) => continue, - Ok(AsyncSink::NotReady(request)) => { - self.queue.push_front(request); - break; - } - Err(error) => return Err(RpcError::Other(Box::new(error))), - }, - None => break, - } + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = Pin::into_inner(self); + + if this.queue.is_empty() { + return Pin::new(&mut this.sink).poll_ready(cx); + } + + let _ = Pin::new(this).try_empty_buffer(cx)?; + + Poll::Ready(Ok(())) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = Pin::into_inner(self); + + match Pin::new(&mut *this).try_empty_buffer(cx) { + Poll::Ready(value) => value?, + Poll::Pending => return Poll::Pending, + } + debug_assert!(this.queue.is_empty()); + + Pin::new(&mut this.sink).poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = Pin::into_inner(self); + + match Pin::new(&mut *this).try_empty_buffer(cx) { + Poll::Ready(value) => value?, + Poll::Pending => return Poll::Pending, } - self.sink - .poll_complete() - .map_err(|error| RpcError::Other(Box::new(error))) + debug_assert!(this.queue.is_empty()); + + Pin::new(&mut this.sink).poll_close(cx) } } -impl Stream for WebsocketClient +impl futures::Stream for WebsocketClient where - TSink: Sink, - TStream: Stream, - TError: std::error::Error + Send + 'static, + TSink: futures::Sink + Unpin, + TStream: futures::Stream> + Unpin, { - type Item = String; - type Error = RpcError; + type Item = Result; - fn poll(&mut self) -> Result>, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = Pin::into_inner(self); loop { - match self.stream.poll() { - Ok(Async::Ready(Some(message))) => match message { - OwnedMessage::Text(data) => return Ok(Async::Ready(Some(data))), - OwnedMessage::Binary(data) => info!("server sent binary data {:?}", data), - OwnedMessage::Ping(p) => self.queue.push_front(OwnedMessage::Pong(p)), + match Pin::new(&mut this.stream).poll_next(cx) { + Poll::Ready(Some(Ok(message))) => match message { + OwnedMessage::Text(data) => return Poll::Ready(Some(Ok(data))), + OwnedMessage::Binary(data) => log::info!("server sent binary data {:?}", data), + OwnedMessage::Ping(p) => this.queue.push_front(OwnedMessage::Pong(p)), OwnedMessage::Pong(_) => {} - OwnedMessage::Close(c) => self.queue.push_front(OwnedMessage::Close(c)), + OwnedMessage::Close(c) => this.queue.push_front(OwnedMessage::Close(c)), }, - Ok(Async::Ready(None)) => { + Poll::Ready(None) => { // TODO try to reconnect (#411). - return Ok(Async::Ready(None)); + return Poll::Ready(None); } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(error) => return Err(RpcError::Other(Box::new(error))), + Poll::Pending => return Poll::Pending, + Poll::Ready(Some(Err(error))) => return Poll::Ready(Some(Err(RpcError::Other(Box::new(error))))), } } } diff --git a/derive/examples/client-local.rs b/derive/examples/client-local.rs index 09a8c0148..a0fdf6fe1 100644 --- a/derive/examples/client-local.rs +++ b/derive/examples/client-local.rs @@ -50,8 +50,8 @@ fn main() { futures::pin_mut!(server); futures::select! { - server = server => {}, - client = client => {}, + _server = server => {}, + _client = client => {}, } }); } diff --git a/http/Cargo.toml b/http/Cargo.toml index fdccef242..e4356f294 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -11,9 +11,8 @@ repository = "https://github.com/paritytech/jsonrpc" version = "16.0.0" [dependencies] -futures01 = { version = "0.1", package = "futures" } -futures03 = { version = "0.3", package = "futures", features = ["compat"] } -hyper = "0.12" +futures = "0.3" +hyper = "0.13" jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" diff --git a/http/src/handler.rs b/http/src/handler.rs index b6abb796f..0466ee844 100644 --- a/http/src/handler.rs +++ b/http/src/handler.rs @@ -1,6 +1,9 @@ use crate::WeakRpc; +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; +use std::task::{self, Poll}; use std::{fmt, mem, str}; use hyper::header::{self, HeaderMap, HeaderValue}; @@ -10,7 +13,6 @@ use crate::jsonrpc::serde_json; use crate::jsonrpc::{self as core, middleware, Metadata, Middleware}; use crate::response::Response; use crate::server_utils::cors; -use futures01::{Async, Future, Poll, Stream}; use crate::{utils, AllowedHosts, CorsDomains, RequestMiddleware, RequestMiddlewareAction, RestApi}; @@ -57,17 +59,21 @@ impl> ServerHandler { } } -impl> Service for ServerHandler +impl> Service> for ServerHandler where S::Future: Unpin, S::CallFuture: Unpin, + M: Unpin, { - type ReqBody = Body; - type ResBody = Body; + type Response = hyper::Response; type Error = hyper::Error; type Future = Handler; - fn call(&mut self, request: hyper::Request) -> Self::Future { + fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> task::Poll> { + task::Poll::Ready(Ok(())) + } + + fn call(&mut self, request: hyper::Request) -> Self::Future { let is_host_allowed = utils::is_host_allowed(&request, &self.allowed_hosts); let action = self.middleware.on_request(request); @@ -118,27 +124,25 @@ where pub enum Handler> { Rpc(RpcHandler), Err(Option), - Middleware(Box, Error = hyper::Error> + Send>), + Middleware(Pin>> + Send>>), } impl> Future for Handler where S::Future: Unpin, S::CallFuture: Unpin, + M: Unpin, { - type Item = hyper::Response; - type Error = hyper::Error; - - fn poll(&mut self) -> Poll { - match *self { - Handler::Rpc(ref mut handler) => handler.poll(), - Handler::Middleware(ref mut middleware) => middleware.poll(), - Handler::Err(ref mut response) => Ok(Async::Ready( - response - .take() - .expect("Response always Some initialy. Returning `Ready` so will never be polled again; qed") - .into(), - )), + type Output = hyper::Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + match Pin::into_inner(self) { + Handler::Rpc(ref mut handler) => Pin::new(handler).poll(cx), + Handler::Middleware(ref mut middleware) => Pin::new(middleware).poll(cx), + Handler::Err(ref mut response) => Poll::Ready(Ok(response + .take() + .expect("Response always Some initialy. Returning `Ready` so will never be polled again; qed") + .into())), } } } @@ -181,8 +185,8 @@ enum RpcHandlerState { metadata: M, }, Writing(Response), - Waiting(Box, Error = ()> + Send>), - WaitingForResponse(Box + Send>), + Waiting(Pin> + Send>>), + WaitingForResponse(Pin + Send>>), Done, } @@ -220,12 +224,14 @@ impl> Future for RpcHandler where S::Future: Unpin, S::CallFuture: Unpin, + M: Unpin, { - type Item = hyper::Response; - type Error = hyper::Error; + type Output = hyper::Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + let this = Pin::into_inner(self); - fn poll(&mut self) -> Poll { - let new_state = match mem::replace(&mut self.state, RpcHandlerState::Done) { + let new_state = match mem::replace(&mut this.state, RpcHandlerState::Done) { RpcHandlerState::ReadingHeaders { request, cors_domains, @@ -234,19 +240,19 @@ where keep_alive, } => { // Read cors header - self.cors_allow_origin = utils::cors_allow_origin(&request, &cors_domains); - self.cors_allow_headers = utils::cors_allow_headers(&request, &cors_headers); - self.keep_alive = utils::keep_alive(&request, keep_alive); - self.is_options = *request.method() == Method::OPTIONS; + this.cors_allow_origin = utils::cors_allow_origin(&request, &cors_domains); + this.cors_allow_headers = utils::cors_allow_headers(&request, &cors_headers); + this.keep_alive = utils::keep_alive(&request, keep_alive); + this.is_options = *request.method() == Method::OPTIONS; // Read other headers - RpcPollState::Ready(self.read_headers(request, continue_on_invalid_cors)) + RpcPollState::Ready(this.read_headers(request, continue_on_invalid_cors)) } RpcHandlerState::ReadingBody { body, request, metadata, uri, - } => match self.process_body(body, request, uri, metadata) { + } => match this.process_body(body, request, uri, metadata, cx) { Err(BodyError::Utf8(ref e)) => { let mesg = format!("utf-8 encoding error at byte {} in request body", e.valid_up_to()); let resp = Response::bad_request(mesg); @@ -256,19 +262,18 @@ where let resp = Response::too_large("request body size exceeds allowed maximum"); RpcPollState::Ready(RpcHandlerState::Writing(resp)) } - Err(BodyError::Hyper(e)) => return Err(e), + Err(BodyError::Hyper(e)) => return Poll::Ready(Err(e)), Ok(state) => state, }, - RpcHandlerState::ProcessRest { uri, metadata } => self.process_rest(uri, metadata)?, - RpcHandlerState::ProcessHealth { method, metadata } => self.process_health(method, metadata)?, - RpcHandlerState::WaitingForResponse(mut waiting) => match waiting.poll() { - Ok(Async::Ready(response)) => RpcPollState::Ready(RpcHandlerState::Writing(response)), - Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::WaitingForResponse(waiting)), - Err(e) => RpcPollState::Ready(RpcHandlerState::Writing(Response::internal_error(format!("{:?}", e)))), + RpcHandlerState::ProcessRest { uri, metadata } => this.process_rest(uri, metadata)?, + RpcHandlerState::ProcessHealth { method, metadata } => this.process_health(method, metadata)?, + RpcHandlerState::WaitingForResponse(mut waiting) => match Pin::new(&mut waiting).poll(cx) { + Poll::Ready(response) => RpcPollState::Ready(RpcHandlerState::Writing(response)), + Poll::Pending => RpcPollState::NotReady(RpcHandlerState::WaitingForResponse(waiting)), }, RpcHandlerState::Waiting(mut waiting) => { - match waiting.poll() { - Ok(Async::Ready(response)) => { + match Pin::new(&mut waiting).poll(cx) { + Poll::Ready(response) => { RpcPollState::Ready(RpcHandlerState::Writing(match response { // Notification, just return empty response. None => Response::ok(String::new()), @@ -276,10 +281,7 @@ where Some(result) => Response::ok(format!("{}\n", result)), })) } - Ok(Async::NotReady) => RpcPollState::NotReady(RpcHandlerState::Waiting(waiting)), - Err(e) => { - RpcPollState::Ready(RpcHandlerState::Writing(Response::internal_error(format!("{:?}", e)))) - } + Poll::Pending => RpcPollState::NotReady(RpcHandlerState::Waiting(waiting)), } } state => RpcPollState::NotReady(state), @@ -289,25 +291,25 @@ where match new_state { RpcHandlerState::Writing(res) => { let mut response: hyper::Response = res.into(); - let cors_allow_origin = mem::replace(&mut self.cors_allow_origin, cors::AllowCors::Invalid); - let cors_allow_headers = mem::replace(&mut self.cors_allow_headers, cors::AllowCors::Invalid); + let cors_allow_origin = mem::replace(&mut this.cors_allow_origin, cors::AllowCors::Invalid); + let cors_allow_headers = mem::replace(&mut this.cors_allow_headers, cors::AllowCors::Invalid); Self::set_response_headers( response.headers_mut(), - self.is_options, - self.cors_max_age, + this.is_options, + this.cors_max_age, cors_allow_origin.into(), cors_allow_headers.into(), - self.keep_alive, + this.keep_alive, ); - Ok(Async::Ready(response)) + Poll::Ready(Ok(response)) } state => { - self.state = state; + this.state = state; if is_ready { - self.poll() + Pin::new(this).poll(cx) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -394,7 +396,6 @@ where fn process_health(&self, method: String, metadata: M) -> Result, hyper::Error> { use self::core::types::{Call, Failure, Id, MethodCall, Output, Params, Request, Success, Version}; - use futures03::{FutureExt, TryFutureExt}; // Create a request let call = Request::Single(Call::MethodCall(MethodCall { @@ -408,28 +409,28 @@ where Some(h) => h.handler.handle_rpc_request(call, metadata), None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), }; - let response = response.map(Ok).compat(); - Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse(Box::new( - response.map(|res| match res { - Some(core::Response::Single(Output::Success(Success { result, .. }))) => { - let result = serde_json::to_string(&result).expect("Serialization of result is infallible;qed"); + Ok(RpcPollState::Ready(RpcHandlerState::WaitingForResponse(Box::pin( + async { + match response.await { + Some(core::Response::Single(Output::Success(Success { result, .. }))) => { + let result = serde_json::to_string(&result).expect("Serialization of result is infallible;qed"); - Response::ok(result) - } - Some(core::Response::Single(Output::Failure(Failure { error, .. }))) => { - let result = serde_json::to_string(&error).expect("Serialization of error is infallible;qed"); + Response::ok(result) + } + Some(core::Response::Single(Output::Failure(Failure { error, .. }))) => { + let result = serde_json::to_string(&error).expect("Serialization of error is infallible;qed"); - Response::service_unavailable(result) + Response::service_unavailable(result) + } + e => Response::internal_error(format!("Invalid response for health request: {:?}", e)), } - e => Response::internal_error(format!("Invalid response for health request: {:?}", e)), - }), + }, )))) } fn process_rest(&self, uri: hyper::Uri, metadata: M) -> Result, hyper::Error> { use self::core::types::{Call, Id, MethodCall, Params, Request, Value, Version}; - use futures03::{FutureExt, TryFutureExt}; // skip the initial / let mut it = uri.path().split('/').skip(1); @@ -456,11 +457,12 @@ where Some(h) => h.handler.handle_rpc_request(call, metadata), None => return Ok(RpcPollState::Ready(RpcHandlerState::Writing(Response::closing()))), }; - let response = response.map(Ok).compat(); - Ok(RpcPollState::Ready(RpcHandlerState::Waiting(Box::new(response.map( - |res| res.map(|x| serde_json::to_string(&x).expect("Serialization of response is infallible;qed")), - ))))) + Ok(RpcPollState::Ready(RpcHandlerState::Waiting(Box::pin(async { + response + .await + .map(|x| serde_json::to_string(&x).expect("Serialization of response is infallible;qed")) + })))) } fn process_body( @@ -469,10 +471,14 @@ where mut request: Vec, uri: Option, metadata: M, + cx: &mut task::Context<'_>, ) -> Result, BodyError> { + use futures::Stream; + loop { - match body.poll()? { - Async::Ready(Some(chunk)) => { + let pinned_body = Pin::new(&mut body); + match pinned_body.poll_next(cx)? { + Poll::Ready(Some(chunk)) => { if request .len() .checked_add(chunk.len()) @@ -483,8 +489,7 @@ where } request.extend_from_slice(&*chunk) } - Async::Ready(None) => { - use futures03::{FutureExt, TryFutureExt}; + Poll::Ready(None) => { if let (Some(uri), true) = (uri, request.is_empty()) { return Ok(RpcPollState::Ready(RpcHandlerState::ProcessRest { uri, metadata })); } @@ -503,10 +508,9 @@ where }; // Content is ready - let response = response.map(Ok).compat(); - return Ok(RpcPollState::Ready(RpcHandlerState::Waiting(Box::new(response)))); + return Ok(RpcPollState::Ready(RpcHandlerState::Waiting(Box::pin(response)))); } - Async::NotReady => { + Poll::Pending => { return Ok(RpcPollState::NotReady(RpcHandlerState::ReadingBody { body, request, diff --git a/http/src/lib.rs b/http/src/lib.rs index 9fc4a2a7c..a229ae509 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -35,8 +35,11 @@ mod response; mod tests; mod utils; +use std::convert::Infallible; +use std::future::Future; use std::io; use std::net::SocketAddr; +use std::pin::Pin; use std::sync::{mpsc, Arc, Weak}; use std::thread; @@ -44,15 +47,15 @@ use parking_lot::Mutex; use crate::jsonrpc::MetaIoHandler; use crate::server_utils::reactor::{Executor, UninitializedExecutor}; -use futures01::sync::oneshot; -use futures01::{future, Future, Stream}; -use hyper::{server, Body}; +use futures::{channel::oneshot, future}; +use hyper::Body; use jsonrpc_core as jsonrpc; pub use crate::handler::ServerHandler; pub use crate::response::Response; pub use crate::server_utils::cors::{self, AccessControlAllowOrigin, AllowCors, Origin}; pub use crate::server_utils::hosts::{DomainsValidation, Host}; +pub use crate::server_utils::reactor::TaskExecutor; pub use crate::server_utils::{tokio, SuspendableStream}; pub use crate::utils::{cors_allow_headers, cors_allow_origin, is_host_allowed}; @@ -71,7 +74,7 @@ pub enum RequestMiddlewareAction { /// Should standard hosts validation be performed? should_validate_hosts: bool, /// a future for server response - response: Box, Error = hyper::Error> + Send>, + response: Pin>> + Send>>, }, } @@ -79,7 +82,7 @@ impl From for RequestMiddlewareAction { fn from(o: Response) -> Self { RequestMiddlewareAction::Respond { should_validate_hosts: true, - response: Box::new(future::ok(o.into())), + response: Box::pin(async { Ok(o.into()) }), } } } @@ -88,7 +91,7 @@ impl From> for RequestMiddlewareAction { fn from(response: hyper::Response) -> Self { RequestMiddlewareAction::Respond { should_validate_hosts: true, - response: Box::new(future::ok(response)), + response: Box::pin(async { Ok(response) }), } } } @@ -251,6 +254,7 @@ impl> ServerBuilder> ServerBuilder where S::Future: Unpin, S::CallFuture: Unpin, + M: Unpin, { /// Creates new `ServerBuilder` for given `IoHandler`. /// @@ -300,7 +305,7 @@ where /// Utilize existing event loop executor to poll RPC results. /// /// Applies only to 1 of the threads. Other threads will spawn their own Event Loops. - pub fn event_loop_executor(mut self, executor: tokio::runtime::TaskExecutor) -> Self { + pub fn event_loop_executor(mut self, executor: TaskExecutor) -> Self { self.executor = UninitializedExecutor::Shared(executor); self } @@ -519,7 +524,7 @@ fn serve>( mpsc::Sender>, oneshot::Sender<()>, ), - executor: tokio::runtime::TaskExecutor, + executor: TaskExecutor, addr: SocketAddr, cors_domains: CorsDomains, cors_max_age: Option, @@ -535,11 +540,10 @@ fn serve>( ) where S::Future: Unpin, S::CallFuture: Unpin, + M: Unpin, { let (shutdown_signal, local_addr_tx, done_tx) = signals; - executor.spawn({ - let handle = tokio::reactor::Handle::default(); - + executor.spawn(async move { let bind = move || { let listener = match addr { SocketAddr::V4(_) => net2::TcpBuilder::new_v4()?, @@ -549,26 +553,37 @@ fn serve>( listener.reuse_address(true)?; listener.bind(&addr)?; let listener = listener.listen(1024)?; - let listener = tokio::net::TcpListener::from_std(listener, &handle)?; + let local_addr = listener.local_addr()?; + + // NOTE: Future-proof by explicitly setting the listener socket to + // non-blocking mode of operation (future Tokio/Hyper versions + // require for the callers to do that manually) + listener.set_nonblocking(true)?; + // HACK: See below. + #[cfg(windows)] + let raw_socket = std::os::windows::io::AsRawSocket::as_raw_socket(&listener); + #[cfg(not(windows))] + let raw_socket = (); + + let server_builder = + hyper::Server::from_tcp(listener).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; // Add current host to allowed headers. // NOTE: we need to use `l.local_addr()` instead of `addr` // it might be different! - let local_addr = listener.local_addr()?; - - Ok((listener, local_addr)) + Ok((server_builder, local_addr, raw_socket)) }; let bind_result = match bind() { - Ok((listener, local_addr)) => { + Ok((server_builder, local_addr, raw_socket)) => { // Send local address match local_addr_tx.send(Ok(local_addr)) { - Ok(_) => future::ok((listener, local_addr)), + Ok(_) => Ok((server_builder, local_addr, raw_socket)), Err(_) => { warn!( "Thread {:?} unable to reach receiver, closing server", thread::current().name() ); - future::err(()) + Err(()) } } } @@ -576,54 +591,55 @@ fn serve>( // Send error let _send_result = local_addr_tx.send(Err(err)); - future::err(()) + Err(()) } }; - bind_result - .and_then(move |(listener, local_addr)| { - let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); - - let mut http = server::conn::Http::new(); - http.keep_alive(keep_alive); - let tcp_stream = SuspendableStream::new(listener.incoming()); - - tcp_stream - .map(move |socket| { - let service = ServerHandler::new( - jsonrpc_handler.downgrade(), - cors_domains.clone(), - cors_max_age, - allowed_headers.clone(), - allowed_hosts.clone(), - request_middleware.clone(), - rest_api, - health_api.clone(), - max_request_body_size, - keep_alive, - ); + let (server_builder, local_addr, _raw_socket) = bind_result?; + + let allowed_hosts = server_utils::hosts::update(allowed_hosts, &local_addr); + + let server_builder = server_builder + .http1_keepalive(keep_alive) + .tcp_nodelay(true) + // Explicitly attempt to recover from accept errors (e.g. too many + // files opened) instead of erroring out the entire server. + .tcp_sleep_on_accept_errors(true); + + let service_fn = hyper::service::make_service_fn(move |_addr_stream| { + let service = ServerHandler::new( + jsonrpc_handler.downgrade(), + cors_domains.clone(), + cors_max_age, + allowed_headers.clone(), + allowed_hosts.clone(), + request_middleware.clone(), + rest_api, + health_api.clone(), + max_request_body_size, + keep_alive, + ); + async { Ok::<_, Infallible>(service) } + }); + + let server = server_builder.serve(service_fn).with_graceful_shutdown(async { + if let Err(err) = shutdown_signal.await { + debug!("Shutdown signaller dropped, closing server: {:?}", err); + } + }); - tokio::spawn( - http.serve_connection(socket, service) - .map_err(|e| error!("Error serving connection: {:?}", e)) - .then(|_| Ok(())), - ) - }) - .for_each(|_| Ok(())) - .map_err(|e| { - warn!("Incoming streams error, closing sever: {:?}", e); - }) - .select(shutdown_signal.map_err(|e| { - debug!("Shutdown signaller dropped, closing server: {:?}", e); - })) - .map_err(|_| ()) - }) - .and_then(|(_, server)| { - // We drop the server first to prevent a situation where main thread terminates - // before the server is properly dropped (see #504 for more details) - drop(server); - done_tx.send(()) - }) + if let Err(err) = server.await { + error!("Error running HTTP server: {:?}", err); + } + + // FIXME: Work around TCP listener socket not being properly closed + // in mio v0.6. This runs the std::net::TcpListener's destructor, + // which closes the underlying OS socket. + // Remove this once we migrate to Tokio 1.0. + #[cfg(windows)] + let _: std::net::TcpListener = unsafe { std::os::windows::io::FromRawSocket::from_raw_socket(_raw_socket) }; + + done_tx.send(()) }); } @@ -654,8 +670,9 @@ impl CloseHandle { pub fn close(self) { if let Some(executors) = self.0.lock().take() { for (executor, closer) in executors { - executor.close(); + // First send shutdown signal so we can proceed with underlying select let _ = closer.send(()); + executor.close(); } } } @@ -692,9 +709,9 @@ impl Server { fn wait_internal(&mut self) { if let Some(receivers) = self.done.take() { - for receiver in receivers { - let _ = receiver.wait(); - } + // NOTE: Gracefully handle the case where we may wait on a *nested* + // local task pool (for now, wait on a dedicated, spawned thread) + let _ = std::thread::spawn(move || futures::executor::block_on(future::try_join_all(receivers))).join(); } } } diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index ff38dee14..63678c33c 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -10,13 +10,12 @@ repository = "https://github.com/paritytech/jsonrpc" version = "16.0.0" [dependencies] -futures01 = { version = "0.1", package = "futures" } -futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } +futures = "0.3" log = "0.4" -tokio-service = "0.1" +tower-service = "0.3" jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } -parity-tokio-ipc = "0.4" +jsonrpc-server-utils = { version = "16.0", path = "../server-utils", default-features = false } +parity-tokio-ipc = "0.8" parking_lot = "0.11.0" [dev-dependencies] @@ -24,7 +23,7 @@ env_logger = "0.7" lazy_static = "1.0" [target.'cfg(not(windows))'.dev-dependencies] -tokio-uds = "0.2" +tokio = { version = "0.2", default-features = false, features = ["uds", "time", "rt-threaded", "io-driver"] } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ipc/src/meta.rs b/ipc/src/meta.rs index a1ae788d4..497eaf086 100644 --- a/ipc/src/meta.rs +++ b/ipc/src/meta.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use crate::jsonrpc::futures::channel::mpsc; use crate::jsonrpc::Metadata; use crate::server_utils::session; @@ -7,7 +9,7 @@ pub struct RequestContext<'a> { /// Session ID pub session_id: session::SessionId, /// Remote UDS endpoint - pub endpoint_addr: &'a ::parity_tokio_ipc::RemoteId, + pub endpoint_addr: &'a Path, /// Direct pipe sender pub sender: mpsc::UnboundedSender, } diff --git a/ipc/src/select_with_weak.rs b/ipc/src/select_with_weak.rs index 409aa46cd..5a68aa258 100644 --- a/ipc/src/select_with_weak.rs +++ b/ipc/src/select_with_weak.rs @@ -1,10 +1,13 @@ -use futures01::stream::{Fuse, Stream}; -use futures01::{Async, Poll}; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; + +use futures::stream::{Fuse, Stream}; pub trait SelectWithWeakExt: Stream { fn select_with_weak(self, other: S) -> SelectWithWeak where - S: Stream, + S: Stream, Self: Sized; } @@ -14,7 +17,7 @@ where { fn select_with_weak(self, other: S) -> SelectWithWeak where - S: Stream, + S: Stream, Self: Sized, { new(self, other) @@ -39,8 +42,9 @@ pub struct SelectWithWeak { fn new(stream1: S1, stream2: S2) -> SelectWithWeak where S1: Stream, - S2: Stream, + S2: Stream, { + use futures::StreamExt; SelectWithWeak { strong: stream1.fuse(), weak: stream2.fuse(), @@ -50,36 +54,36 @@ where impl Stream for SelectWithWeak where - S1: Stream, - S2: Stream, + S1: Stream + Unpin, + S2: Stream + Unpin, { type Item = S1::Item; - type Error = S1::Error; - fn poll(&mut self) -> Poll, S1::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = Pin::into_inner(self); let mut checked_strong = false; loop { - if self.use_strong { - match self.strong.poll()? { - Async::Ready(Some(item)) => { - self.use_strong = false; - return Ok(Some(item).into()); + if this.use_strong { + match Pin::new(&mut this.strong).poll_next(cx) { + Poll::Ready(Some(item)) => { + this.use_strong = false; + return Poll::Ready(Some(item)); } - Async::Ready(None) => return Ok(None.into()), - Async::NotReady => { + Poll::Ready(None) => return Poll::Ready(None), + Poll::Pending => { if !checked_strong { - self.use_strong = false; + this.use_strong = false; } else { - return Ok(Async::NotReady); + return Poll::Pending; } } } checked_strong = true; } else { - self.use_strong = true; - match self.weak.poll()? { - Async::Ready(Some(item)) => return Ok(Some(item).into()), - Async::Ready(None) | Async::NotReady => (), + this.use_strong = true; + match Pin::new(&mut this.strong).poll_next(cx) { + Poll::Ready(Some(item)) => return Poll::Ready(Some(item)), + Poll::Ready(None) | Poll::Pending => (), } } } diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 47aaa9f29..107150304 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -1,19 +1,19 @@ +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; +use std::task::{Context, Poll}; use crate::jsonrpc::futures::channel::mpsc; use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; use crate::select_with_weak::SelectWithWeakExt; -use futures01::{future, sync::oneshot, Future, Sink, Stream}; +use futures::channel::oneshot; +use futures::StreamExt; use parity_tokio_ipc::Endpoint; use parking_lot::Mutex; -use tokio_service::{self, Service as TokioService}; +use tower_service::Service as _; -use crate::server_utils::{ - codecs, reactor, session, - tokio::{reactor::Handle, runtime::TaskExecutor}, - tokio_codec::Framed, -}; +use crate::server_utils::{codecs, reactor, reactor::TaskExecutor, session, tokio_util}; pub use parity_tokio_ipc::SecurityAttributes; @@ -30,22 +30,24 @@ impl> Service { } } -impl> tokio_service::Service for Service +impl> tower_service::Service for Service where S::Future: Unpin, S::CallFuture: Unpin, { - type Request = String; type Response = Option; - type Error = (); - type Future = Box, Error = ()> + Send>; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } - fn call(&self, req: Self::Request) -> Self::Future { - use futures03::{FutureExt, TryFutureExt}; + fn call(&mut self, req: String) -> Self::Future { + use futures::FutureExt; trace!(target: "ipc", "Received request: {}", req); - Box::new(self.handler.handle_request(&req, self.meta.clone()).map(Ok).compat()) + Box::pin(self.handler.handle_request(&req, self.meta.clone()).map(Ok)) } } @@ -55,7 +57,6 @@ pub struct ServerBuilder = middleware::Noop> meta_extractor: Arc>, session_stats: Option>, executor: reactor::UninitializedExecutor, - reactor: Option, incoming_separator: codecs::Separator, outgoing_separator: codecs::Separator, security_attributes: SecurityAttributes, @@ -92,7 +93,6 @@ where meta_extractor: Arc::new(extractor), session_stats: None, executor: reactor::UninitializedExecutor::Unspawned, - reactor: None, incoming_separator: codecs::Separator::Empty, outgoing_separator: codecs::Separator::default(), security_attributes: SecurityAttributes::empty(), @@ -106,12 +106,6 @@ where self } - /// Sets different event loop I/O reactor. - pub fn event_loop_reactor(mut self, reactor: Handle) -> Self { - self.reactor = Some(reactor); - self - } - /// Sets session metadata extractor. pub fn session_meta_extractor(mut self, meta_extractor: X) -> Self where @@ -149,7 +143,6 @@ where /// Creates a new server from the given endpoint. pub fn start(self, path: &str) -> std::io::Result { let executor = self.executor.initialize()?; - let reactor = self.reactor; let rpc_handler = self.handler; let endpoint_addr = path.to_owned(); let meta_extractor = self.meta_extractor; @@ -157,12 +150,13 @@ where let incoming_separator = self.incoming_separator; let outgoing_separator = self.outgoing_separator; let (stop_signal, stop_receiver) = oneshot::channel(); - let (start_signal, start_receiver) = oneshot::channel(); - let (wait_signal, wait_receiver) = oneshot::channel(); + // NOTE: These channels are only waited upon in synchronous fashion + let (start_signal, start_receiver) = std::sync::mpsc::channel(); + let (wait_signal, wait_receiver) = std::sync::mpsc::channel(); let security_attributes = self.security_attributes; let client_buffer_size = self.client_buffer_size; - executor.spawn(future::lazy(move || { + let fut = async move { let mut endpoint = Endpoint::new(endpoint_addr); endpoint.set_security_attributes(security_attributes); @@ -173,27 +167,21 @@ where } } - // Make sure to construct Handle::default() inside Tokio runtime - let reactor = if cfg!(windows) { - #[allow(deprecated)] - reactor.unwrap_or_else(Handle::current) - } else { - reactor.unwrap_or_else(Handle::default) - }; - - let connections = match endpoint.incoming(&reactor) { + let endpoint_addr = endpoint.path().to_owned(); + let connections = match endpoint.incoming() { Ok(connections) => connections, Err(e) => { start_signal .send(Err(e)) .expect("Cannot fail since receiver never dropped before receiving"); - return future::Either::A(future::ok(())); + return; } }; let mut id = 0u64; - let server = connections.map(move |(io_stream, remote_id)| { + use futures::TryStreamExt; + let server = connections.map_ok(move |io_stream| { id = id.wrapping_add(1); let session_id = id; let session_stats = session_stats.clone(); @@ -204,61 +192,56 @@ where let (sender, receiver) = mpsc::unbounded(); let meta = meta_extractor.extract(&RequestContext { - endpoint_addr: &remote_id, + endpoint_addr: endpoint_addr.as_ref(), session_id, sender, }); - let service = Service::new(rpc_handler.clone(), meta); - let (writer, reader) = Framed::new( - io_stream, - codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()), - ) - .split(); + let mut service = Service::new(rpc_handler.clone(), meta); + let codec = codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()); + let framed = tokio_util::codec::Decoder::framed(codec, io_stream); + let (writer, reader) = futures::StreamExt::split(framed); + let responses = reader - .map(move |req| { + .map_ok(move |req| { service .call(req) - .then(|result| match result { - Err(_) => future::ok(None), - Ok(some_result) => future::ok(some_result), - }) - .map_err(|_: ()| std::io::ErrorKind::Other.into()) + // Ignore service errors + .map(|x| Ok(x.ok().flatten())) }) - .buffer_unordered(client_buffer_size) - .filter_map(|x| x) + .try_buffer_unordered(client_buffer_size) + // Filter out previously ignored service errors as `None`s + .try_filter_map(|x| futures::future::ok(x)) // we use `select_with_weak` here, instead of `select`, to close the stream // as soon as the ipc pipe is closed - .select_with_weak(futures03::TryStreamExt::compat(futures03::StreamExt::map(receiver, Ok))); + .select_with_weak(receiver.map(Ok)); - let writer = writer.send_all(responses).then(move |_| { + responses.forward(writer).then(move |_| { trace!(target: "ipc", "Peer: service finished"); if let Some(stats) = session_stats.as_ref() { stats.close_session(session_id) } - Ok(()) - }); - writer + async { Ok(()) } + }) }); start_signal .send(Ok(())) .expect("Cannot fail since receiver never dropped before receiving"); + let stop = stop_receiver.map_err(|_| std::io::ErrorKind::Interrupted); + let stop = Box::pin(stop); - let stop = stop_receiver.map_err(|_| std::io::ErrorKind::Interrupted.into()); - future::Either::B( - server - .buffer_unordered(1024) - .for_each(|_| Ok(())) - .select(stop) - .map(|(_, server)| { - // We drop the server first to prevent a situation where main thread terminates - // before the server is properly dropped (see #504 for more details) - drop(server); - let _ = wait_signal.send(()); - }) - .map_err(|_| ()), - ) - })); + let server = server.try_buffer_unordered(1024).for_each(|_| async {}); + + let result = futures::future::select(Box::pin(server), stop).await; + // We drop the server first to prevent a situation where main thread terminates + // before the server is properly dropped (see #504 for more details) + drop(result); + let _ = wait_signal.send(()); + }; + + use futures::FutureExt; + let fut = Box::pin(fut.map(drop)); + executor.executor().spawn(fut); let handle = InnerHandles { executor: Some(executor), @@ -266,7 +249,8 @@ where path: path.to_owned(), }; - match start_receiver.wait().expect("Message should always be sent") { + use futures::TryFutureExt; + match start_receiver.recv().expect("Message should always be sent") { Ok(()) => Ok(Server { handles: Arc::new(Mutex::new(handle)), wait_handle: Some(wait_receiver), @@ -280,7 +264,7 @@ where #[derive(Debug)] pub struct Server { handles: Arc>, - wait_handle: Option>, + wait_handle: Option>, } impl Server { @@ -298,7 +282,9 @@ impl Server { /// Wait for the server to finish pub fn wait(mut self) { - self.wait_handle.take().map(|wait_receiver| wait_receiver.wait()); + if let Some(wait_receiver) = self.wait_handle.take() { + let _ = wait_receiver.recv(); + } } } @@ -342,13 +328,10 @@ impl CloseHandle { mod tests { use super::*; - use futures01::{Future, Sink, Stream}; use jsonrpc_core::Value; - use jsonrpc_server_utils::tokio::{self, timer::Delay}; - use jsonrpc_server_utils::tokio_codec::Decoder; + use std::os::unix::net::UnixStream; use std::thread; - use std::time::{self, Duration, Instant}; - use tokio_uds::UnixStream; + use std::time::{self, Duration}; fn server_builder() -> ServerBuilder { let mut io = MetaIoHandler::<()>::default(); @@ -363,17 +346,22 @@ mod tests { } fn dummy_request_str(path: &str, data: &str) -> String { - let stream_future = UnixStream::connect(path); - let reply = stream_future.and_then(|stream| { - let stream = codecs::StreamCodec::stream_incoming().framed(stream); - let reply = stream - .send(data.to_owned()) - .and_then(move |stream| stream.into_future().map_err(|(err, _)| err)) - .and_then(|(reply, _)| future::ok(reply.expect("there should be one reply"))); - reply - }); + use futures::SinkExt; + + let reply = async move { + use tokio::net::UnixStream; - reply.wait().expect("wait for reply") + let stream: UnixStream = UnixStream::connect(path).await?; + let codec = codecs::StreamCodec::stream_incoming(); + let mut stream = tokio_util::codec::Decoder::framed(codec, stream); + stream.send(data.to_owned()).await?; + let (reply, _) = stream.into_future().await; + + reply.expect("there should be one reply") + }; + + let mut rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(reply).expect("wait for reply") } #[test] @@ -395,7 +383,7 @@ mod tests { let path = "/tmp/test-ipc-30000"; let _server = run(path); - UnixStream::connect(path).wait().expect("Socket should connect"); + UnixStream::connect(path).expect("Socket should connect"); } #[test] @@ -403,7 +391,7 @@ mod tests { crate::logger::init_log(); let path = "/tmp/test-ipc-40000"; let server = run(path); - let (stop_signal, stop_receiver) = oneshot::channel(); + let (stop_signal, stop_receiver) = std::sync::mpsc::channel(); let t = thread::spawn(move || { let result = dummy_request_str( @@ -414,15 +402,13 @@ mod tests { }); t.join().unwrap(); - let _ = stop_receiver - .map(move |result: String| { - assert_eq!( - result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", - "Response does not exactly match the expected response", - ); - server.close(); - }) - .wait(); + let result = stop_receiver.recv().unwrap(); + + assert_eq!( + result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", + "Response does not exactly match the expected response", + ); + server.close(); } #[test] @@ -430,7 +416,7 @@ mod tests { crate::logger::init_log(); let path = "/tmp/test-ipc-45000"; let server = run(path); - let (stop_signal, stop_receiver) = futures01::sync::mpsc::channel(400); + let (stop_signal, stop_receiver) = futures::channel::mpsc::channel(400); let mut handles = Vec::new(); for _ in 0..4 { @@ -451,16 +437,20 @@ mod tests { handle.join().unwrap(); } - let _ = stop_receiver - .map(|result| { - assert_eq!( - result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", - "Response does not exactly match the expected response", - ); - }) - .take(400) - .collect() - .wait(); + thread::spawn(move || { + let fut = stop_receiver + .map(|result| { + assert_eq!( + result, "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}", + "Response does not exactly match the expected response", + ); + }) + .take(400) + .for_each(|_| async {}); + futures::executor::block_on(fut); + }) + .join() + .unwrap(); server.close(); } @@ -476,7 +466,7 @@ mod tests { "There should be no socket file left" ); assert!( - UnixStream::connect(path).wait().is_err(), + UnixStream::connect(path).is_err(), "Connection to the closed socket should fail" ); } @@ -521,16 +511,19 @@ mod tests { }); t.join().unwrap(); - let _ = stop_receiver - .map(move |result: String| { + thread::spawn(move || { + futures::executor::block_on(async move { + let result = stop_receiver.await.unwrap(); assert_eq!( result, huge_response_test_json(), "Response does not exactly match the expected response", ); server.close(); - }) - .wait(); + }); + }) + .join() + .unwrap(); } #[test] @@ -547,7 +540,7 @@ mod tests { } struct SessionEndExtractor { - drop_receivers: Arc>>>, + drop_receivers: Arc>>>, } impl MetaExtractor> for SessionEndExtractor { @@ -563,7 +556,7 @@ mod tests { crate::logger::init_log(); let path = "/tmp/test-ipc-30009"; - let (signal, receiver) = futures01::sync::mpsc::channel(16); + let (signal, receiver) = futures::channel::mpsc::channel(16); let session_metadata_extractor = SessionEndExtractor { drop_receivers: Arc::new(Mutex::new(signal)), }; @@ -572,15 +565,17 @@ mod tests { let builder = ServerBuilder::with_meta_extractor(io, session_metadata_extractor); let server = builder.start(path).expect("Server must run with no issues"); { - let _ = UnixStream::connect(path).wait().expect("Socket should connect"); + let _ = UnixStream::connect(path).expect("Socket should connect"); } - receiver - .into_future() - .map_err(|_| ()) - .and_then(|drop_receiver| drop_receiver.0.unwrap().map_err(|_| ())) - .wait() - .unwrap(); + thread::spawn(move || { + futures::executor::block_on(async move { + let (drop_receiver, ..) = receiver.into_future().await; + drop_receiver.unwrap().await.unwrap(); + }); + }) + .join() + .unwrap(); server.close(); } @@ -592,7 +587,7 @@ mod tests { let handle = server.close_handle(); handle.close(); assert!( - UnixStream::connect(path).wait().is_err(), + UnixStream::connect(path).is_err(), "Connection to the closed socket should fail" ); } @@ -614,23 +609,24 @@ mod tests { tx.send(true).expect("failed to report that the server has stopped"); }); - let delay = Delay::new(Instant::now() + Duration::from_millis(500)) - .map(|_| false) - .map_err(|err| panic!("{:?}", err)); - - let result_fut = rx.map_err(|_| ()).select(delay).then(move |result| match result { - Ok((result, _)) => { - assert_eq!(result, true, "Wait timeout exceeded"); - assert!( - UnixStream::connect(path).wait().is_err(), - "Connection to the closed socket should fail" - ); - Ok(()) + let mut rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async move { + let timeout = tokio::time::delay_for(Duration::from_millis(500)); + + match futures::future::select(rx, timeout).await { + futures::future::Either::Left((result, _)) => { + assert!(result.is_ok(), "Rx failed"); + assert_eq!(result, Ok(true), "Wait timeout exceeded"); + assert!( + UnixStream::connect(path).is_err(), + "Connection to the closed socket should fail" + ); + Ok(()) + } + futures::future::Either::Right(_) => Err("timed out"), } - Err(_) => Err(()), - }); - - tokio::run(result_fut); + }) + .unwrap(); } #[test] diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 4324500ab..e40481835 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -11,14 +11,15 @@ repository = "https://github.com/paritytech/jsonrpc" version = "16.0.0" [dependencies] -bytes = "0.4" -futures01 = { version = "0.1", package = "futures" } +bytes = "0.5" +futures = "0.3" globset = "0.4" jsonrpc-core = { version = "16.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" -tokio = { version = "0.1.15" } -tokio-codec = { version = "0.1" } +tokio = { version = "0.2", features = ["rt-threaded", "io-driver", "io-util", "time", "tcp"] } +tokio-util = { version = "0.3", features = ["codec"] } + unicase = "2.0" [badges] diff --git a/server-utils/src/lib.rs b/server-utils/src/lib.rs index e13342007..5c9f52d1a 100644 --- a/server-utils/src/lib.rs +++ b/server-utils/src/lib.rs @@ -9,7 +9,7 @@ extern crate log; extern crate lazy_static; pub use tokio; -pub use tokio_codec; +pub use tokio_util; pub mod cors; pub mod hosts; diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index 1d1917db3..df8afd408 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -6,15 +6,16 @@ //! that `tokio::runtime` can be multi-threaded. use std::io; -use tokio; -use futures01::Future; +use tokio::runtime; +/// Task executor for Tokio 0.2 runtime. +pub type TaskExecutor = tokio::runtime::Handle; /// Possibly uninitialized event loop executor. #[derive(Debug)] pub enum UninitializedExecutor { /// Shared instance of executor. - Shared(tokio::runtime::TaskExecutor), + Shared(TaskExecutor), /// Event Loop should be spawned by the transport. Unspawned, } @@ -42,28 +43,20 @@ impl UninitializedExecutor { #[derive(Debug)] pub enum Executor { /// Shared instance - Shared(tokio::runtime::TaskExecutor), + Shared(TaskExecutor), /// Spawned Event Loop Spawned(RpcEventLoop), } impl Executor { /// Get tokio executor associated with this event loop. - pub fn executor(&self) -> tokio::runtime::TaskExecutor { - match *self { + pub fn executor(&self) -> TaskExecutor { + match self { Executor::Shared(ref executor) => executor.clone(), Executor::Spawned(ref eloop) => eloop.executor(), } } - /// Spawn a future onto the Tokio runtime. - pub fn spawn(&self, future: F) - where - F: Future + Send + 'static, - { - self.executor().spawn(future) - } - /// Closes underlying event loop (if any!). pub fn close(self) { if let Executor::Spawned(eloop) = self { @@ -82,9 +75,9 @@ impl Executor { /// A handle to running event loop. Dropping the handle will cause event loop to finish. #[derive(Debug)] pub struct RpcEventLoop { - executor: tokio::runtime::TaskExecutor, - close: Option>, - handle: Option, + executor: TaskExecutor, + close: Option>, + runtime: Option, } impl Drop for RpcEventLoop { @@ -101,36 +94,46 @@ impl RpcEventLoop { /// Spawns a new named thread with the `EventLoop`. pub fn with_name(name: Option) -> io::Result { - let (stop, stopped) = futures01::oneshot(); + let (stop, stopped) = futures::channel::oneshot::channel(); - let mut tb = tokio::runtime::Builder::new(); + let mut tb = runtime::Builder::new(); tb.core_threads(1); + tb.threaded_scheduler(); + tb.enable_all(); if let Some(name) = name { - tb.name_prefix(name); + tb.thread_name(name); } - let mut runtime = tb.build()?; - let executor = runtime.executor(); - let terminate = futures01::empty().select(stopped).map(|_| ()).map_err(|_| ()); - runtime.spawn(terminate); - let handle = runtime.shutdown_on_idle(); + let runtime = tb.build()?; + let executor = runtime.handle().to_owned(); + + runtime.spawn(async { + let _ = stopped.await; + }); Ok(RpcEventLoop { executor, close: Some(stop), - handle: Some(handle), + runtime: Some(runtime), }) } /// Get executor for this event loop. - pub fn executor(&self) -> tokio::runtime::TaskExecutor { - self.executor.clone() + pub fn executor(&self) -> runtime::Handle { + self.runtime + .as_ref() + .expect("Runtime is only None if we're being dropped; qed") + .handle() + .clone() } /// Blocks current thread and waits until the event loop is finished. pub fn wait(mut self) -> Result<(), ()> { - self.handle.take().ok_or(())?.wait() + // Dropping Tokio 0.2 runtime waits for all spawned tasks to terminate + let runtime = self.runtime.take().ok_or(())?; + drop(runtime); + Ok(()) } /// Finishes this event loop. diff --git a/server-utils/src/stream_codec.rs b/server-utils/src/stream_codec.rs index d7cb268b9..4edef5add 100644 --- a/server-utils/src/stream_codec.rs +++ b/server-utils/src/stream_codec.rs @@ -1,6 +1,5 @@ use bytes::BytesMut; use std::{io, str}; -use tokio_codec::{Decoder, Encoder}; /// Separator for enveloping messages in streaming codecs #[derive(Debug, Clone)] @@ -48,7 +47,7 @@ fn is_whitespace(byte: u8) -> bool { } } -impl Decoder for StreamCodec { +impl tokio_util::codec::Decoder for StreamCodec { type Item = String; type Error = io::Error; @@ -56,7 +55,7 @@ impl Decoder for StreamCodec { if let Separator::Byte(separator) = self.incoming_separator { if let Some(i) = buf.as_ref().iter().position(|&b| b == separator) { let line = buf.split_to(i); - buf.split_to(1); + let _ = buf.split_to(1); match str::from_utf8(&line.as_ref()) { Ok(s) => Ok(Some(s.to_string())), @@ -108,8 +107,7 @@ impl Decoder for StreamCodec { } } -impl Encoder for StreamCodec { - type Item = String; +impl tokio_util::codec::Encoder for StreamCodec { type Error = io::Error; fn encode(&mut self, msg: String, buf: &mut BytesMut) -> io::Result<()> { @@ -127,7 +125,7 @@ mod tests { use super::StreamCodec; use bytes::{BufMut, BytesMut}; - use tokio_codec::Decoder; + use tokio_util::codec::Decoder; #[test] fn simple_encode() { diff --git a/server-utils/src/suspendable_stream.rs b/server-utils/src/suspendable_stream.rs index f563cdebe..8d3179da9 100644 --- a/server-utils/src/suspendable_stream.rs +++ b/server-utils/src/suspendable_stream.rs @@ -1,7 +1,10 @@ +use std::future::Future; use std::io; -use std::time::{Duration, Instant}; -use tokio::prelude::*; -use tokio::timer::Delay; +use std::pin::Pin; +use std::task::Poll; +use std::time::Duration; + +use tokio::time::Delay; /// `Incoming` is a stream of incoming sockets /// Polling the stream may return a temporary io::Error (for instance if we can't open the connection because of "too many open files" limit) @@ -33,38 +36,37 @@ impl SuspendableStream { } } -impl Stream for SuspendableStream +impl futures::Stream for SuspendableStream where - S: Stream, + S: futures::Stream> + Unpin, { type Item = I; - type Error = (); - fn poll(&mut self) -> Result>, ()> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { loop { - if let Some(mut timeout) = self.timeout.take() { - match timeout.poll() { - Ok(Async::Ready(_)) => {} - Ok(Async::NotReady) => { - self.timeout = Some(timeout); - return Ok(Async::NotReady); - } - Err(err) => { - warn!("Timeout error {:?}", err); - task::current().notify(); - return Ok(Async::NotReady); - } + if let Some(timeout) = self.timeout.as_mut() { + match Pin::new(timeout).poll(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(()) => {} } } - match self.stream.poll() { - Ok(item) => { + match Pin::new(&mut self.stream).poll_next(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => { + if self.next_delay > self.initial_delay { + self.next_delay = self.initial_delay; + } + return Poll::Ready(None); + } + Poll::Ready(Some(Ok(item))) => { if self.next_delay > self.initial_delay { self.next_delay = self.initial_delay; } - return Ok(item); + + return Poll::Ready(Some(item)); } - Err(ref err) => { + Poll::Ready(Some(Err(ref err))) => { if connection_error(err) { warn!("Connection Error: {:?}", err); continue; @@ -76,7 +78,7 @@ where }; debug!("Error accepting connection: {}", err); debug!("The server will stop accepting connections for {:?}", self.next_delay); - self.timeout = Some(Delay::new(Instant::now() + self.next_delay)); + self.timeout = Some(tokio::time::delay_for(self.next_delay)); } } } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index ef90c2093..44fcf9592 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -10,15 +10,14 @@ repository = "https://github.com/paritytech/jsonrpc" version = "16.0.0" [dependencies] -futures = { version = "0.3", features = [ "compat" ] } +futures = "0.3" jsonrpc-core = { version = "16.0", path = "../core" } log = "0.4" -tokio = "0.1.7" -tokio-codec = "0.1.0" -tokio-io = "0.1.7" -tokio-stdin-stdout = "0.1.4" +tokio = { version = "0.2", features = ["io-std", "io-driver", "io-util"] } +tokio-util = { version = "0.3", features = ["codec"] } [dev-dependencies] +tokio = { version = "0.2", features = ["rt-core", "macros"] } lazy_static = "1.0" env_logger = "0.7" diff --git a/stdio/examples/stdio.rs b/stdio/examples/stdio.rs index 974ea9df4..bd2bc2caa 100644 --- a/stdio/examples/stdio.rs +++ b/stdio/examples/stdio.rs @@ -1,9 +1,11 @@ use jsonrpc_stdio_server::jsonrpc_core::*; use jsonrpc_stdio_server::ServerBuilder; -fn main() { +#[tokio::main] +async fn main() { let mut io = IoHandler::default(); io.add_sync_method("say_hello", |_params| Ok(Value::String("hello".to_owned()))); - ServerBuilder::new(io).build(); + let server = ServerBuilder::new(io).build(); + server.await; } diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index 9d118f563..79918c44a 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -5,29 +5,31 @@ //! use jsonrpc_stdio_server::ServerBuilder; //! use jsonrpc_stdio_server::jsonrpc_core::*; //! -//! fn main() { +//! #[tokio::main] +//! async fn main() { //! let mut io = IoHandler::default(); //! io.add_sync_method("say_hello", |_params| { //! Ok(Value::String("hello".to_owned())) //! }); //! -//! ServerBuilder::new(io).build(); +//! let server = ServerBuilder::new(io).build(); +//! server.await; //! } //! ``` #![deny(missing_docs)] -use tokio; -use tokio_stdin_stdout; +use std::future::Future; +use std::sync::Arc; + #[macro_use] extern crate log; pub use jsonrpc_core; +pub use tokio; use jsonrpc_core::{MetaIoHandler, Metadata, Middleware}; -use std::sync::Arc; -use tokio::prelude::{Future, Stream}; -use tokio_codec::{FramedRead, FramedWrite, LinesCodec}; +use tokio_util::codec::{FramedRead, LinesCodec}; /// Stdio server builder pub struct ServerBuilder = jsonrpc_core::NoopMiddleware> { @@ -47,31 +49,45 @@ where } } + /// Returns a server future that needs to be polled in order to make progress. + /// /// Will block until EOF is read or until an error occurs. /// The server reads from STDIN line-by-line, one request is taken /// per line and each response is written to STDOUT on a new line. - pub fn build(&self) { - let stdin = tokio_stdin_stdout::stdin(0); - let stdout = tokio_stdin_stdout::stdout(0).make_sendable(); + pub fn build(&self) -> impl Future + 'static { + let handler = self.handler.clone(); - let framed_stdin = FramedRead::new(stdin, LinesCodec::new()); - let framed_stdout = FramedWrite::new(stdout, LinesCodec::new()); + async move { + let stdin = tokio::io::stdin(); + let mut stdout = tokio::io::stdout(); - let handler = self.handler.clone(); - let future = framed_stdin - .and_then(move |line| Self::process(&handler, line).map_err(|_| unreachable!())) - .forward(framed_stdout) - .map(|_| ()) - .map_err(|e| panic!("{:?}", e)); + let mut framed_stdin = FramedRead::new(stdin, LinesCodec::new()); - tokio::run(future); + use futures::StreamExt; + while let Some(request) = framed_stdin.next().await { + match request { + Ok(line) => { + let res = Self::process(&handler, line).await; + let mut sanitized = res.replace('\n', ""); + sanitized.push('\n'); + use tokio::io::AsyncWriteExt; + if let Err(e) = stdout.write_all(sanitized.as_bytes()).await { + log::warn!("Error writing response: {:?}", e); + } + } + Err(e) => { + log::warn!("Error reading line: {:?}", e); + } + } + } + } } /// Process a request asynchronously - fn process(io: &Arc>, input: String) -> impl Future + Send { - use jsonrpc_core::futures::{FutureExt, TryFutureExt}; + fn process(io: &Arc>, input: String) -> impl Future + Send { + use jsonrpc_core::futures::FutureExt; let f = io.handle_request(&input, Default::default()); - f.map(Ok).compat().map(move |result| match result { + f.map(move |result| match result { Some(res) => res, None => { info!("JSON RPC request produced no response: {:?}", input); diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 365d8667a..cda8542d2 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -10,14 +10,11 @@ repository = "https://github.com/paritytech/jsonrpc" version = "16.0.0" [dependencies] -futures01 = { version = "0.1", package = "futures" } -# TODO remove when we no longer need compat (use jsonrpc-core re-export instead) -futures03 = { version = "0.3", features = ["compat"], package = "futures" } jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" -tokio-service = "0.1" +tower-service = "0.3" [dev-dependencies] lazy_static = "1.0" diff --git a/tcp/src/dispatch.rs b/tcp/src/dispatch.rs index f12121a51..664e97a33 100644 --- a/tcp/src/dispatch.rs +++ b/tcp/src/dispatch.rs @@ -1,27 +1,26 @@ -use std; use std::collections::HashMap; use std::net::SocketAddr; +use std::pin::Pin; use std::sync::Arc; +use std::task::Poll; -use crate::jsonrpc::futures::{self as futures03, channel::mpsc, StreamExt}; -use futures01::{Async, Poll, Stream}; +use crate::futures::{channel::mpsc, Stream}; use parking_lot::Mutex; pub type SenderChannels = Mutex>>; -pub struct PeerMessageQueue { +pub struct PeerMessageQueue { up: S, - receiver: Option + Send>>, + receiver: Option>, _addr: SocketAddr, } -impl PeerMessageQueue { +impl PeerMessageQueue { pub fn new(response_stream: S, receiver: mpsc::UnboundedReceiver, addr: SocketAddr) -> Self { - let receiver = futures03::compat::Compat::new(receiver.map(|v| Ok(v))); PeerMessageQueue { up: response_stream, - receiver: Some(Box::new(receiver)), + receiver: Some(receiver), _addr: addr, } } @@ -78,9 +77,8 @@ impl Dispatcher { } } -impl> Stream for PeerMessageQueue { - type Item = String; - type Error = std::io::Error; +impl> + Unpin> Stream for PeerMessageQueue { + type Item = std::io::Result; // The receiver will never return `Ok(Async::Ready(None))` // Because the sender is kept in `SenderChannels` and it will never be dropped until `the stream` is resolved. @@ -90,32 +88,32 @@ impl> Stream for PeerMessageQue // However, it is possible to have a race between `poll` and `push_work` if the connection is dropped. // Therefore, the receiver is then dropped when the connection is dropped and an error is propagated when // a `send` attempt is made on that channel. - fn poll(&mut self) -> Poll, std::io::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { // check if we have response pending + let this = Pin::into_inner(self); - let up_closed = match self.up.poll() { - Ok(Async::Ready(Some(item))) => return Ok(Async::Ready(Some(item))), - Ok(Async::Ready(None)) => true, - Ok(Async::NotReady) => false, - err => return err, + let up_closed = match Pin::new(&mut this.up).poll_next(cx) { + Poll::Ready(Some(Ok(item))) => return Poll::Ready(Some(Ok(item))), + Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err))), + Poll::Ready(None) => true, + Poll::Pending => false, }; - let rx = match &mut self.receiver { + let mut rx = match &mut this.receiver { None => { debug_assert!(up_closed); - return Ok(Async::Ready(None)); + return Poll::Ready(None); } Some(rx) => rx, }; - match rx.poll() { - Ok(Async::Ready(Some(item))) => Ok(Async::Ready(Some(item))), - Ok(Async::Ready(None)) | Ok(Async::NotReady) if up_closed => { - self.receiver = None; - Ok(Async::Ready(None)) + match Pin::new(&mut rx).poll_next(cx) { + Poll::Ready(Some(item)) => Poll::Ready(Some(Ok(item))), + Poll::Ready(None) | Poll::Pending if up_closed => { + this.receiver = None; + Poll::Ready(None) } - Ok(Async::Ready(None)) | Ok(Async::NotReady) => Ok(Async::NotReady), - Err(_) => Err(std::io::Error::new(std::io::ErrorKind::Other, "MPSC error")), + Poll::Ready(None) | Poll::Pending => Poll::Pending, } } } diff --git a/tcp/src/lib.rs b/tcp/src/lib.rs index 5eda8e7e1..b78e4be54 100644 --- a/tcp/src/lib.rs +++ b/tcp/src/lib.rs @@ -42,6 +42,8 @@ mod tests; use jsonrpc_core as jsonrpc; +pub(crate) use crate::jsonrpc::futures; + pub use self::server_utils::{codecs::Separator, tokio}; pub use crate::dispatch::{Dispatcher, PushMessageError}; pub use crate::meta::{MetaExtractor, RequestContext}; diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 6e7d3693d..55a2b8d4a 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -1,14 +1,13 @@ -use std; +use std::io; use std::net::SocketAddr; +use std::pin::Pin; use std::sync::Arc; -use tokio_service::Service as TokioService; - -use futures01::sync::oneshot; -use futures01::{future, Future, Sink, Stream}; +use tower_service::Service as _; +use crate::futures::{self, future}; use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; -use crate::server_utils::{codecs, reactor, tokio, tokio_codec::Framed, SuspendableStream}; +use crate::server_utils::{codecs, reactor, tokio, tokio_util::codec::Framed, SuspendableStream}; use crate::dispatch::{Dispatcher, PeerMessageQueue, SenderChannels}; use crate::meta::{MetaExtractor, NoopExtractor, RequestContext}; @@ -60,7 +59,7 @@ where } /// Utilize existing event loop executor. - pub fn event_loop_executor(mut self, handle: tokio::runtime::TaskExecutor) -> Self { + pub fn event_loop_executor(mut self, handle: reactor::TaskExecutor) -> Self { self.executor = reactor::UninitializedExecutor::Shared(handle); self } @@ -79,7 +78,7 @@ where } /// Starts a new server - pub fn start(self, addr: &SocketAddr) -> std::io::Result { + pub fn start(self, addr: &SocketAddr) -> io::Result { let meta_extractor = self.meta_extractor.clone(); let rpc_handler = self.handler.clone(); let channels = self.channels.clone(); @@ -87,25 +86,26 @@ where let outgoing_separator = self.outgoing_separator; let address = addr.to_owned(); let (tx, rx) = std::sync::mpsc::channel(); - let (stop_tx, stop_rx) = oneshot::channel(); + let (stop_tx, stop_rx) = futures::channel::oneshot::channel(); let executor = self.executor.initialize()?; - executor.spawn(future::lazy(move || { - let start = move || { - let listener = tokio::net::TcpListener::bind(&address)?; - let connections = SuspendableStream::new(listener.incoming()); + use futures::{FutureExt, SinkExt, StreamExt, TryStreamExt}; + executor.executor().spawn(async move { + let start = async { + let listener = tokio::net::TcpListener::bind(&address).await?; + let connections = SuspendableStream::new(listener); - let server = connections.map(move |socket| { + let server = connections.map(|socket| { let peer_addr = match socket.peer_addr() { Ok(addr) => addr, Err(e) => { warn!(target: "tcp", "Unable to determine socket peer address, ignoring connection {}", e); - return future::Either::A(future::ok(())); + return future::Either::Left(async { io::Result::Ok(()) }); } }; trace!(target: "tcp", "Accepted incoming connection from {}", &peer_addr); - let (sender, receiver) = crate::jsonrpc::futures::channel::mpsc::unbounded(); + let (sender, receiver) = futures::channel::mpsc::unbounded(); let context = RequestContext { peer_addr, @@ -113,31 +113,33 @@ where }; let meta = meta_extractor.extract(&context); - let service = Service::new(peer_addr, rpc_handler.clone(), meta); - let (writer, reader) = Framed::new( + let mut service = Service::new(peer_addr, rpc_handler.clone(), meta); + let (mut writer, reader) = Framed::new( socket, codecs::StreamCodec::new(incoming_separator.clone(), outgoing_separator.clone()), ) .split(); - let responses = reader.and_then(move |req| { - service.call(req).then(|response| match response { - Err(e) => { - warn!(target: "tcp", "Error while processing request: {:?}", e); - future::ok(String::new()) - } - Ok(None) => { - trace!(target: "tcp", "JSON RPC request produced no response"); - future::ok(String::new()) - } - Ok(Some(response_data)) => { - trace!(target: "tcp", "Sent response: {}", &response_data); - future::ok(response_data) - } - }) - }); - - let peer_message_queue = { + // Work around https://github.com/rust-lang/rust/issues/64552 by boxing the stream type + let responses: Pin> + Send>> = + Box::pin(reader.and_then(move |req| { + service.call(req).then(|response| match response { + Err(e) => { + warn!(target: "tcp", "Error while processing request: {:?}", e); + future::ok(String::new()) + } + Ok(None) => { + trace!(target: "tcp", "JSON RPC request produced no response"); + future::ok(String::new()) + } + Ok(Some(response_data)) => { + trace!(target: "tcp", "Sent response: {}", &response_data); + future::ok(response_data) + } + }) + })); + + let mut peer_message_queue = { let mut channels = channels.lock(); channels.insert(peer_addr, sender.clone()); @@ -145,42 +147,33 @@ where }; let shared_channels = channels.clone(); - let writer = writer.send_all(peer_message_queue).then(move |_| { + let writer = async move { + writer.send_all(&mut peer_message_queue).await?; trace!(target: "tcp", "Peer {}: service finished", peer_addr); let mut channels = shared_channels.lock(); channels.remove(&peer_addr); Ok(()) - }); + }; - future::Either::B(writer) + future::Either::Right(writer) }); Ok(server) }; - let stop = stop_rx.map_err(|_| ()); - match start() { + match start.await { Ok(server) => { tx.send(Ok(())).expect("Rx is blocking parent thread."); - future::Either::A( - server - .buffer_unordered(1024) - .for_each(|_| Ok(())) - .select(stop) - .map(|_| ()) - .map_err(|(e, _)| { - error!("Error while executing the server: {:?}", e); - }), - ) + let server = server.buffer_unordered(1024).for_each(|_| async { () }); + + future::select(Box::pin(server), stop_rx).await; } Err(e) => { tx.send(Err(e)).expect("Rx is blocking parent thread."); - future::Either::B(stop.map_err(|e| { - error!("Error while executing the server: {:?}", e); - })) + let _ = stop_rx.await; } } - })); + }); let res = rx.recv().expect("Response is always sent before tx is dropped."); @@ -199,7 +192,7 @@ where /// TCP Server handle pub struct Server { executor: Option, - stop: Option>, + stop: Option>, } impl Server { diff --git a/tcp/src/service.rs b/tcp/src/service.rs index cb0f4b7b2..558b2feba 100644 --- a/tcp/src/service.rs +++ b/tcp/src/service.rs @@ -1,9 +1,11 @@ +use std::future::Future; use std::net::SocketAddr; +use std::pin::Pin; use std::sync::Arc; +use std::task::{Context, Poll}; -use crate::jsonrpc::futures::FutureExt; +use crate::futures; use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; -use futures01::Future; pub struct Service = middleware::Noop> { handler: Arc>, @@ -21,26 +23,27 @@ impl> Service { } } -impl> tokio_service::Service for Service +impl> tower_service::Service for Service where S::Future: Unpin, S::CallFuture: Unpin, { // These types must match the corresponding protocol types: - type Request = String; type Response = Option; - // For non-streaming protocols, service errors are always io::Error type Error = (); // The future for computing the response; box it for simplicity. - type Future = Box, Error = ()> + Send>; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } // Produce a future for computing a response from a request. - fn call(&self, req: Self::Request) -> Self::Future { + fn call(&mut self, req: String) -> Self::Future { + use futures::FutureExt; trace!(target: "tcp", "Accepted request from peer {}: {}", &self.peer_addr, req); - Box::new(futures03::compat::Compat::new( - self.handler.handle_request(&req, self.meta.clone()).map(|v| Ok(v)), - )) + Box::pin(self.handler.handle_request(&req, self.meta.clone()).map(Ok)) } } diff --git a/tcp/src/tests.rs b/tcp/src/tests.rs index f06293847..b95e1b288 100644 --- a/tcp/src/tests.rs +++ b/tcp/src/tests.rs @@ -1,12 +1,14 @@ use std::net::{Shutdown, SocketAddr}; use std::str::FromStr; use std::sync::Arc; -use std::time::{Duration, Instant}; +use std::time::Duration; -use futures01::{future, Future}; use jsonrpc_core::{MetaIoHandler, Metadata, Value}; +use tokio::io::AsyncReadExt; +use tokio::io::AsyncWriteExt; -use crate::server_utils::tokio::{self, io, net::TcpStream, timer::Delay}; +use crate::futures; +use crate::server_utils::tokio::{self, net::TcpStream}; use parking_lot::Mutex; @@ -20,6 +22,11 @@ fn casual_server() -> ServerBuilder { ServerBuilder::new(io) } +fn run_future(fut: impl std::future::Future + Send) -> O { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(fut) +} + #[test] fn doc_test() { crate::logger::init_log(); @@ -41,11 +48,7 @@ fn doc_test_connect() { let server = casual_server(); let _server = server.start(&addr).expect("Server must run with no issues"); - let stream = TcpStream::connect(&addr) - .and_then(move |_stream| Ok(())) - .map_err(|err| panic!("Server connection error: {:?}", err)); - - tokio::run(stream); + run_future(async move { TcpStream::connect(&addr).await }).expect("Server connection error"); } #[test] @@ -56,14 +59,11 @@ fn disconnect() { let dispatcher = server.dispatcher(); let _server = server.start(&addr).expect("Server must run with no issues"); - let stream = TcpStream::connect(&addr) - .and_then(move |stream| { - assert_eq!(stream.peer_addr().unwrap(), addr); - stream.shutdown(::std::net::Shutdown::Both) - }) - .map_err(|err| panic!("Error disconnecting: {:?}", err)); - - tokio::run(stream); + run_future(async move { + let stream = TcpStream::connect(&addr).await.unwrap(); + assert_eq!(stream.peer_addr().unwrap(), addr); + stream.shutdown(::std::net::Shutdown::Both).unwrap(); + }); ::std::thread::sleep(::std::time::Duration::from_millis(50)); @@ -71,19 +71,22 @@ fn disconnect() { } fn dummy_request(addr: &SocketAddr, data: Vec) -> Vec { - let (ret_tx, ret_rx) = futures01::sync::oneshot::channel(); - - let stream = TcpStream::connect(addr) - .and_then(move |stream| io::write_all(stream, data)) - .and_then(|(stream, _data)| { - stream.shutdown(Shutdown::Write).unwrap(); - io::read_to_end(stream, vec![]) - }) - .and_then(move |(_stream, read_buf)| ret_tx.send(read_buf).map_err(|err| panic!("Unable to send {:?}", err))) - .map_err(|err| panic!("Error connecting or closing connection: {:?}", err)); - - tokio::run(stream); - ret_rx.wait().expect("Unable to receive result") + let (ret_tx, ret_rx) = std::sync::mpsc::channel(); + + let stream = async move { + let mut stream = TcpStream::connect(addr).await?; + stream.write_all(&data).await?; + stream.shutdown(Shutdown::Write)?; + let mut read_buf = vec![]; + let _ = stream.read_to_end(&mut read_buf).await; + + let _ = ret_tx.send(read_buf).map_err(|err| panic!("Unable to send {:?}", err)); + + Ok::<(), Box>(()) + }; + + run_future(stream).unwrap(); + ret_rx.recv().expect("Unable to receive result") } fn dummy_request_str(addr: &SocketAddr, data: Vec) -> String { @@ -232,67 +235,62 @@ fn message() { let _server = server.start(&addr).expect("Server must run with no issues"); - let delay = Delay::new(Instant::now() + Duration::from_millis(500)).map_err(|err| panic!("{:?}", err)); - let message = "ping"; let executed_dispatch = Arc::new(Mutex::new(false)); let executed_request = Arc::new(Mutex::new(false)); let executed_dispatch_move = executed_dispatch.clone(); let executed_request_move = executed_request.clone(); - // CLIENT RUN - let stream = TcpStream::connect(&addr) - .and_then(|stream| future::ok(stream).join(delay)) - .and_then(move |stream| { - let peer_addr = peer_list.lock()[0].clone(); - dispatcher - .push_message(&peer_addr, message.to_owned()) - .expect("Should be sent with no errors"); - trace!(target: "tcp", "Dispatched message for {}", peer_addr); - future::ok(stream) - }) - .and_then(move |(stream, _)| { - // Read message plus newline appended by codec. - io::read_exact(stream, vec![0u8; message.len() + 1]) - }) - .and_then(move |(stream, read_buf)| { - trace!(target: "tcp", "Read ping message"); - let ping_signal = read_buf[..].to_vec(); - - assert_eq!( - format!("{}\n", message), - String::from_utf8(ping_signal).expect("String should be utf-8"), - "Sent request does not match received by the peer", - ); - // ensure that the above assert was actually triggered - *executed_dispatch_move.lock() = true; - - future::ok(stream) - }) - .and_then(|stream| { - // make request AFTER message dispatches - let data = b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"; - io::write_all(stream, &data[..]) - }) - .and_then(|(stream, _)| { - stream.shutdown(Shutdown::Write).unwrap(); - io::read_to_end(stream, Vec::new()) - }) - .and_then(move |(_, read_buf)| { - trace!(target: "tcp", "Read response message"); - let response_signal = read_buf[..].to_vec(); - assert_eq!( - "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", - String::from_utf8(response_signal).expect("String should be utf-8"), - "Response does not match the expected handling", - ); - *executed_request_move.lock() = true; - - future::ok(()) - }) - .map_err(|err| panic!("Dispach message error: {:?}", err)); - - tokio::run(stream); + let client = async move { + let stream = TcpStream::connect(&addr); + let delay = tokio::time::delay_for(Duration::from_millis(500)); + let (stream, _) = futures::join!(stream, delay); + let mut stream = stream?; + + let peer_addr = peer_list.lock()[0].clone(); + dispatcher + .push_message(&peer_addr, message.to_owned()) + .expect("Should be sent with no errors"); + trace!(target: "tcp", "Dispatched message for {}", peer_addr); + + // Read message plus newline appended by codec. + let mut read_buf = vec![0u8; message.len() + 1]; + let _ = stream.read_exact(&mut read_buf).await?; + + trace!(target: "tcp", "Read ping message"); + let ping_signal = read_buf[..].to_vec(); + + assert_eq!( + format!("{}\n", message), + String::from_utf8(ping_signal).expect("String should be utf-8"), + "Sent request does not match received by the peer", + ); + // ensure that the above assert was actually triggered + *executed_dispatch_move.lock() = true; + + // make request AFTER message dispatches + let data = b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"; + stream.write_all(&data[..]).await?; + + stream.shutdown(Shutdown::Write).unwrap(); + let mut read_buf = vec![]; + let _ = stream.read_to_end(&mut read_buf).await?; + + trace!(target: "tcp", "Read response message"); + let response_signal = read_buf[..].to_vec(); + assert_eq!( + "{\"jsonrpc\":\"2.0\",\"result\":\"hello\",\"id\":1}\n", + String::from_utf8(response_signal).expect("String should be utf-8"), + "Response does not match the expected handling", + ); + *executed_request_move.lock() = true; + + // delay + Ok::<(), Box>(()) + }; + + run_future(client).unwrap(); + assert!(*executed_dispatch.lock()); assert!(*executed_request.lock()); } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 2247d1098..37324fbe3 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -10,8 +10,7 @@ repository = "https://github.com/paritytech/jsonrpc" version = "16.0.0" [dependencies] -futures01 = { version = "0.1", package = "futures" } -futures03 = { version = "0.3", package = "futures", features = [ "compat" ] } +futures = "0.3" jsonrpc-core = { version = "16.0", path = "../core" } jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } log = "0.4" diff --git a/ws/src/metadata.rs b/ws/src/metadata.rs index 624be5830..25b1bed82 100644 --- a/ws/src/metadata.rs +++ b/ws/src/metadata.rs @@ -1,9 +1,12 @@ use std::fmt; +use std::future::Future; +use std::pin::Pin; use std::sync::{atomic, Arc}; +use std::task::{Context, Poll}; use crate::core; use crate::core::futures::channel::mpsc; -use crate::server_utils::{session, tokio::runtime::TaskExecutor}; +use crate::server_utils::{reactor::TaskExecutor, session}; use crate::ws; use crate::error; @@ -79,10 +82,8 @@ impl RequestContext { /// Get this session as a `Sink` spawning a new future /// in the underlying event loop. pub fn sender(&self) -> mpsc::UnboundedSender { - use futures03::{StreamExt, TryStreamExt}; let out = self.out.clone(); let (sender, receiver) = mpsc::unbounded(); - let receiver = receiver.map(Ok).compat(); self.executor.spawn(SenderFuture(out, Box::new(receiver))); sender } @@ -123,27 +124,23 @@ impl MetaExtractor for NoopExtractor { } } -struct SenderFuture(Sender, Box + Send>); -impl futures01::Future for SenderFuture { - type Item = (); - type Error = (); +struct SenderFuture(Sender, Box + Send + Unpin>); - fn poll(&mut self) -> futures01::Poll { - use futures01::Stream; +impl Future for SenderFuture { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures::Stream; + + let this = Pin::into_inner(self); loop { - let item = self.1.poll()?; - match item { - futures01::Async::NotReady => { - return Ok(futures01::Async::NotReady); - } - futures01::Async::Ready(None) => { - return Ok(futures01::Async::Ready(())); - } - futures01::Async::Ready(Some(val)) => { - if let Err(e) = self.0.send(val) { + match Pin::new(&mut this.1).poll_next(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(val)) => { + if let Err(e) = this.0.send(val) { warn!("Error sending a subscription update: {:?}", e); - return Ok(futures01::Async::Ready(())); + return Poll::Ready(()); } } } diff --git a/ws/src/server_builder.rs b/ws/src/server_builder.rs index e10978f34..2bfdcaadc 100644 --- a/ws/src/server_builder.rs +++ b/ws/src/server_builder.rs @@ -2,10 +2,9 @@ use std::net::SocketAddr; use std::sync::Arc; use crate::core; -use crate::server_utils; use crate::server_utils::cors::Origin; use crate::server_utils::hosts::{DomainsValidation, Host}; -use crate::server_utils::reactor::UninitializedExecutor; +use crate::server_utils::reactor::{self, UninitializedExecutor}; use crate::server_utils::session::SessionStats; use crate::error::Result; @@ -69,7 +68,7 @@ where } /// Utilize existing event loop executor to poll RPC results. - pub fn event_loop_executor(mut self, executor: server_utils::tokio::runtime::TaskExecutor) -> Self { + pub fn event_loop_executor(mut self, executor: reactor::TaskExecutor) -> Self { self.executor = UninitializedExecutor::Shared(executor); self } diff --git a/ws/src/session.rs b/ws/src/session.rs index 65d76fd38..eef7b0e9e 100644 --- a/ws/src/session.rs +++ b/ws/src/session.rs @@ -1,17 +1,20 @@ -use std; +use std::future::Future; +use std::pin::Pin; use std::sync::{atomic, Arc}; +use std::task::{Context, Poll}; use crate::core; -use futures01::sync::oneshot; -use futures01::{Async, Future, Poll}; +use futures::channel::oneshot; +use futures::future; +use futures::FutureExt; use parking_lot::Mutex; use slab::Slab; use crate::server_utils::cors::Origin; use crate::server_utils::hosts::Host; +use crate::server_utils::reactor::TaskExecutor; use crate::server_utils::session::{SessionId, SessionStats}; -use crate::server_utils::tokio::runtime::TaskExecutor; use crate::server_utils::Pattern; use crate::ws; @@ -123,16 +126,16 @@ impl LivenessPoll { } impl Future for LivenessPoll { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = Pin::into_inner(self); // if the future resolves ok then we've been signalled to return. // it should never be cancelled, but if it was the session definitely // isn't live. - match self.rx.poll() { - Ok(Async::Ready(_)) | Err(_) => Ok(Async::Ready(())), - Ok(Async::NotReady) => Ok(Async::NotReady), + match Pin::new(&mut this.rx).poll(cx) { + Poll::Ready(_) => Poll::Ready(()), + Poll::Pending => Poll::Pending, } } } @@ -270,30 +273,25 @@ where let active_lock = self.active.clone(); let response = self.handler.handle_request(req, metadata); - use futures03::{FutureExt, TryFutureExt}; - let response = response.map(Ok).compat(); - let future = response - .map(move |response| { - if !active_lock.load(atomic::Ordering::SeqCst) { - return; - } - if let Some(result) = response { - let res = out.send(result); - match res { - Err(error::Error::ConnectionClosed) => { - active_lock.store(false, atomic::Ordering::SeqCst); - } - Err(e) => { - warn!("Error while sending response: {:?}", e); - } - _ => {} + let future = response.map(move |response| { + if !active_lock.load(atomic::Ordering::SeqCst) { + return; + } + if let Some(result) = response { + let res = out.send(result); + match res { + Err(error::Error::ConnectionClosed) => { + active_lock.store(false, atomic::Ordering::SeqCst); } + Err(e) => { + warn!("Error while sending response: {:?}", e); + } + _ => {} } - }) - .select(poll_liveness) - .map(|_| ()) - .map_err(|_| ()); + } + }); + let future = future::select(future, poll_liveness); self.executor.spawn(future); Ok(()) diff --git a/ws/src/tests.rs b/ws/src/tests.rs index b416c9384..0d2928460 100644 --- a/ws/src/tests.rs +++ b/ws/src/tests.rs @@ -60,7 +60,7 @@ fn request(server: Server, request: &str) -> Response { } fn serve(port: u16) -> (Server, Arc) { - use futures03::{channel::oneshot, future, FutureExt}; + use futures::{channel::oneshot, future, FutureExt}; let pending = Arc::new(AtomicUsize::new(0)); let counter = pending.clone(); From f4180301ca69aa045e8bd11f9e05a475a4c3ed63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 20 Jan 2021 11:56:53 +0100 Subject: [PATCH 138/149] Bump version. (#608) --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- ipc/Cargo.toml | 6 +++--- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- tcp/Cargo.toml | 6 +++--- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- 13 files changed, 43 insertions(+), 43 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index a0e7953fe..ac64b4094 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" categories = [ "asynchronous", @@ -26,7 +26,7 @@ ipc = ["jsonrpc-client-transports/ipc"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "16.0", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "17.0", path = "./transports", default-features = false } futures = { version = "0.3", features = [ "compat" ] } [badges] diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index 2cad8f8ad..f92978f51 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" categories = [ "asynchronous", @@ -37,8 +37,8 @@ arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary [dependencies] derive_more = "0.99" futures = "0.3" -jsonrpc-core = { version = "16.0", path = "../../core" } -jsonrpc-pubsub = { version = "16.0", path = "../../pubsub" } +jsonrpc-core = { version = "17.0", path = "../../core" } +jsonrpc-pubsub = { version = "17.0", path = "../../pubsub" } log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -46,15 +46,15 @@ url = "1.7" hyper = { version = "0.13", optional = true } hyper-tls = { version = "0.4", optional = true } -jsonrpc-server-utils = { version = "16.0", path = "../../server-utils", optional = true } +jsonrpc-server-utils = { version = "17.0", path = "../../server-utils", optional = true } parity-tokio-ipc = { version = "0.8", optional = true } tokio = { version = "0.2", optional = true } websocket = { version = "0.24", optional = true } [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "16.0", path = "../../http" } -jsonrpc-ipc-server = { version = "16.0", path = "../../ipc" } +jsonrpc-http-server = { version = "17.0", path = "../../http" } +jsonrpc-ipc-server = { version = "17.0", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" diff --git a/core/Cargo.toml b/core/Cargo.toml index 22fac8938..48681a436 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 5d586c0a6..c07ee212c 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [lib] proc-macro = true @@ -20,10 +20,10 @@ proc-macro-crate = "0.1.4" [dev-dependencies] assert_matches = "1.3" -jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-core-client = { version = "16.0", path = "../core-client" } -jsonrpc-pubsub = { version = "16.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "16.0", path = "../tcp" } +jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-core-client = { version = "17.0", path = "../core-client" } +jsonrpc-pubsub = { version = "17.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "17.0", path = "../tcp" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" trybuild = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index e4356f294..b837fc444 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] futures = "0.3" hyper = "0.13" -jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } +jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-server-utils = { version = "17.0", path = "../server-utils" } log = "0.4" net2 = "0.2" parking_lot = "0.11.0" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 63678c33c..6c35dfafa 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] futures = "0.3" log = "0.4" tower-service = "0.3" -jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-server-utils = { version = "16.0", path = "../server-utils", default-features = false } +jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-server-utils = { version = "17.0", path = "../server-utils", default-features = false } parity-tokio-ipc = "0.8" parking_lot = "0.11.0" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 0386764f1..566990b3b 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,11 +8,11 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] futures = { version = "0.3", features = ["thread-pool"] } -jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-core = { version = "17.0", path = "../core" } lazy_static = "1.4" log = "0.4" parking_lot = "0.11.0" @@ -20,7 +20,7 @@ rand = "0.7" serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "16.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "17.0", path = "../tcp" } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index ea99adc26..b222ba108 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "16.0", path = "../../core" } -jsonrpc-pubsub = { version = "16.0", path = "../" } -jsonrpc-ws-server = { version = "16.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "16.0", path = "../../ipc" } +jsonrpc-core = { version = "17.0", path = "../../core" } +jsonrpc-pubsub = { version = "17.0", path = "../" } +jsonrpc-ws-server = { version = "17.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "17.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index e40481835..4adbda3c6 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] bytes = "0.5" futures = "0.3" globset = "0.4" -jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-core = { version = "17.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.2", features = ["rt-threaded", "io-driver", "io-util", "time", "tcp"] } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 44fcf9592..ba8725c71 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] futures = "0.3" -jsonrpc-core = { version = "16.0", path = "../core" } +jsonrpc-core = { version = "17.0", path = "../core" } log = "0.4" tokio = { version = "0.2", features = ["io-std", "io-driver", "io-util"] } tokio-util = { version = "0.3", features = ["codec"] } diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index cda8542d2..990ef7816 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] -jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } +jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-server-utils = { version = "17.0", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" tower-service = "0.3" diff --git a/test/Cargo.toml b/test/Cargo.toml index a9cfd280b..2dc6cfedd 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "16.0.0" +version = "17.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-core-client = { version = "16.0", path = "../core-client" } -jsonrpc-pubsub = { version = "16.0", path = "../pubsub" } +jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-core-client = { version = "17.0", path = "../core-client" } +jsonrpc-pubsub = { version = "17.0", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "16.0", path = "../derive" } +jsonrpc-derive = { version = "17.0", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index 37324fbe3..bfbf74237 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,12 +7,12 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "16.0.0" +version = "17.0.0" [dependencies] futures = "0.3" -jsonrpc-core = { version = "16.0", path = "../core" } -jsonrpc-server-utils = { version = "16.0", path = "../server-utils" } +jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-server-utils = { version = "17.0", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" slab = "0.4" From 5d6ba669892cfe88bf19dffb380daee330e89ceb Mon Sep 17 00:00:00 2001 From: lerencao Date: Wed, 27 Jan 2021 19:13:16 +0800 Subject: [PATCH 139/149] fix typo in select_with_weak future (#610) --- ipc/src/select_with_weak.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/src/select_with_weak.rs b/ipc/src/select_with_weak.rs index 5a68aa258..43c90b783 100644 --- a/ipc/src/select_with_weak.rs +++ b/ipc/src/select_with_weak.rs @@ -81,7 +81,7 @@ where checked_strong = true; } else { this.use_strong = true; - match Pin::new(&mut this.strong).poll_next(cx) { + match Pin::new(&mut this.weak).poll_next(cx) { Poll::Ready(Some(item)) => return Poll::Ready(Some(item)), Poll::Ready(None) | Poll::Pending => (), } From 9d4b2618414e70101fe4d564c04cc36175426ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 29 Jan 2021 21:49:14 +0100 Subject: [PATCH 140/149] Bump bugfix release of IPC. (#613) --- ipc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 6c35dfafa..cf2b9bad4 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.0.1" [dependencies] futures = "0.3" From 9c8110de7b049145f4d0a129bb204160d87af721 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 8 Mar 2021 19:06:05 +0000 Subject: [PATCH 141/149] core: Allow for using only futures-util dependency (#609) * core: Prefer standard Future over futures' variant * core: Make futures::executor convenience support optional * core: Use only futures-util and (optionally) futures-executor * core: Keep futures re-export for backcompat --- core/Cargo.toml | 8 +++++++- core/examples/async.rs | 2 +- core/examples/middlewares.rs | 4 ++-- core/src/calls.rs | 4 ++-- core/src/delegates.rs | 2 +- core/src/io.rs | 10 ++++++---- core/src/lib.rs | 6 +++++- core/src/middleware.rs | 3 ++- 8 files changed, 26 insertions(+), 13 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 48681a436..08536ae8c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,12 +20,18 @@ categories = [ [dependencies] log = "0.4" -futures = "0.3" +# FIXME: Currently a lot of jsonrpc-* crates depend on entire `futures` being +# re-exported but it's not strictly required for this crate. Either adapt the +# remaining crates or settle for this re-export to be a single, common dependency +futures = { version = "0.3", optional = true } +futures-util = { version = "0.3", default-features = false, features = ["std"] } +futures-executor = { version = "0.3", optional = true } serde = "1.0" serde_json = "1.0" serde_derive = "1.0" [features] +default = ["futures-executor", "futures"] arbitrary_precision = ["serde_json/arbitrary_precision"] [badges] diff --git a/core/examples/async.rs b/core/examples/async.rs index 0e65b5b19..b6397b240 100644 --- a/core/examples/async.rs +++ b/core/examples/async.rs @@ -1,7 +1,7 @@ use jsonrpc_core::*; fn main() { - futures::executor::block_on(async { + futures_executor::block_on(async { let mut io = IoHandler::new(); io.add_method("say_hello", |_: Params| async { diff --git a/core/examples/middlewares.rs b/core/examples/middlewares.rs index 37485c526..6d25352af 100644 --- a/core/examples/middlewares.rs +++ b/core/examples/middlewares.rs @@ -1,6 +1,6 @@ -use jsonrpc_core::futures::future::Either; -use jsonrpc_core::futures::{Future, FutureExt}; +use jsonrpc_core::futures_util::{future::Either, FutureExt}; use jsonrpc_core::*; +use std::future::Future; use std::sync::atomic::{self, AtomicUsize}; use std::time::Instant; diff --git a/core/src/calls.rs b/core/src/calls.rs index 36718c94f..dfc8dcb16 100644 --- a/core/src/calls.rs +++ b/core/src/calls.rs @@ -1,7 +1,7 @@ use crate::types::{Error, Params, Value}; use crate::BoxFuture; -use futures::Future; use std::fmt; +use std::future::Future; use std::sync::Arc; /// Metadata trait @@ -19,7 +19,7 @@ pub trait WrapFuture { impl WrapFuture for Result { fn into_future(self) -> BoxFuture> { - Box::pin(futures::future::ready(self)) + Box::pin(async { self }) } } diff --git a/core/src/delegates.rs b/core/src/delegates.rs index 4d43152b3..15b60e65a 100644 --- a/core/src/delegates.rs +++ b/core/src/delegates.rs @@ -1,12 +1,12 @@ //! Delegate rpc calls use std::collections::HashMap; +use std::future::Future; use std::sync::Arc; use crate::calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification}; use crate::types::{Error, Params, Value}; use crate::BoxFuture; -use futures::Future; struct DelegateAsyncMethod { delegate: Arc, diff --git a/core/src/io.rs b/core/src/io.rs index 153455be1..713b47f97 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -2,11 +2,12 @@ use std::collections::{ hash_map::{IntoIter, Iter}, HashMap, }; +use std::future::Future; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::sync::Arc; -use futures::{self, future, Future, FutureExt}; +use futures_util::{self, future, FutureExt}; use serde_json; use crate::calls::{ @@ -197,8 +198,9 @@ impl> MetaIoHandler { /// Handle given request synchronously - will block until response is available. /// If you have any asynchronous methods in your RPC it is much wiser to use /// `handle_request` instead and deal with asynchronous requests in a non-blocking fashion. + #[cfg(feature = "futures-executor")] pub fn handle_request_sync(&self, request: &str, meta: T) -> Option { - futures::executor::block_on(self.handle_request(request, meta)) + futures_executor::block_on(self.handle_request(request, meta)) } /// Handle given request asynchronously. @@ -441,6 +443,7 @@ impl IoHandler { /// Handle given request synchronously - will block until response is available. /// If you have any asynchronous methods in your RPC it is much wiser to use /// `handle_request` instead and deal with asynchronous requests in a non-blocking fashion. + #[cfg(feature = "futures-executor")] pub fn handle_request_sync(&self, request: &str) -> Option { self.0.handle_request_sync(request, M::default()) } @@ -485,7 +488,6 @@ fn write_response(response: Response) -> String { mod tests { use super::{Compatibility, IoHandler}; use crate::types::Value; - use futures::future; #[test] fn test_io_handler() { @@ -515,7 +517,7 @@ mod tests { fn test_async_io_handler() { let mut io = IoHandler::new(); - io.add_method("say_hello", |_| future::ready(Ok(Value::String("hello".to_string())))); + io.add_method("say_hello", |_| async { Ok(Value::String("hello".to_string())) }); let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#; diff --git a/core/src/lib.rs b/core/src/lib.rs index dc1f4059f..736363c3d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -27,7 +27,11 @@ extern crate log; #[macro_use] extern crate serde_derive; +#[cfg(feature = "futures")] pub use futures; +#[cfg(feature = "futures-executor")] +pub use futures_executor; +pub use futures_util; #[doc(hidden)] pub extern crate serde; @@ -45,7 +49,7 @@ pub mod types; pub type Result = std::result::Result; /// A `Future` trait object. -pub type BoxFuture = Pin + Send>>; +pub type BoxFuture = Pin + Send>>; pub use crate::calls::{ Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcMethodSync, RpcNotification, RpcNotificationSimple, diff --git a/core/src/middleware.rs b/core/src/middleware.rs index 71dd725ee..308a787c4 100644 --- a/core/src/middleware.rs +++ b/core/src/middleware.rs @@ -2,7 +2,8 @@ use crate::calls::Metadata; use crate::types::{Call, Output, Request, Response}; -use futures::{future::Either, Future}; +use futures_util::future::Either; +use std::future::Future; use std::pin::Pin; /// RPC middleware From 135d9290820bd9ae6920dbbb20c93de29ad0a8c8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 16 Mar 2021 17:18:29 +0900 Subject: [PATCH 142/149] Fix link to documentation (#620) --- core-client/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-client/src/lib.rs b/core-client/src/lib.rs index 81b8b462e..0ffd970b7 100644 --- a/core-client/src/lib.rs +++ b/core-client/src/lib.rs @@ -3,7 +3,7 @@ //! By default this crate does not implement any transports, //! use corresponding features (`tls`, `http` or `ws`) to opt-in for them. //! -//! See documentation of [`jsonrpc-client-transports`](../jsonrpc_client_transports/) for more details. +//! See documentation of [`jsonrpc-client-transports`](https://docs.rs/jsonrpc-client-transports) for more details. #![deny(missing_docs)] From 014b1ec2141e31736ba7da073919939a535ae24c Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Wed, 31 Mar 2021 18:28:12 +0800 Subject: [PATCH 143/149] Return error when unsubscribing from invalid subscription id (#619) --- pubsub/Cargo.toml | 1 + pubsub/src/subscription.rs | 107 +++++++++++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 16 deletions(-) diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 566990b3b..f4d0f2e26 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -21,6 +21,7 @@ serde = "1.0" [dev-dependencies] jsonrpc-tcp-server = { version = "17.0", path = "../tcp" } +serde_json = "1.0" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 8c02512a4..14e6ddcbb 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -81,8 +81,11 @@ impl Session { } /// Removes existing subscription. - fn remove_subscription(&self, name: &str, id: &SubscriptionId) { - self.active_subscriptions.lock().remove(&(id.clone(), name.into())); + fn remove_subscription(&self, name: &str, id: &SubscriptionId) -> bool { + self.active_subscriptions + .lock() + .remove(&(id.clone(), name.into())) + .is_some() } } @@ -335,8 +338,11 @@ where }; match (meta.session(), id) { (Some(session), Some(id)) => { - session.remove_subscription(&self.notification, &id); - Box::pin(self.unsubscribe.call(id, Some(meta))) + if session.remove_subscription(&self.notification, &id) { + Box::pin(self.unsubscribe.call(id, Some(meta))) + } else { + Box::pin(future::err(core::Error::invalid_params("Invalid subscription id."))) + } } (Some(_), None) => Box::pin(future::err(core::Error::invalid_params("Expected subscription id."))), _ => Box::pin(future::err(subscriptions_unavailable())), @@ -392,13 +398,36 @@ mod tests { }); // when - session.remove_subscription("test", &id); + let removed = session.remove_subscription("test", &id); drop(session); // then + assert_eq!(removed, true); assert_eq!(called.load(Ordering::SeqCst), false); } + #[test] + fn should_not_remove_subscription_if_invalid() { + // given + let id = SubscriptionId::Number(1); + let called = Arc::new(AtomicBool::new(false)); + let called2 = called.clone(); + let other_session = session().0; + let session = session().0; + session.add_subscription("test", &id, move |id| { + assert_eq!(id, SubscriptionId::Number(1)); + called2.store(true, Ordering::SeqCst); + }); + + // when + let removed = other_session.remove_subscription("test", &id); + drop(session); + + // then + assert_eq!(removed, false); + assert_eq!(called.load(Ordering::SeqCst), true); + } + #[test] fn should_unregister_in_case_of_collision() { // given @@ -485,40 +514,86 @@ mod tests { }); } - #[derive(Clone, Default)] - struct Metadata; + #[derive(Clone)] + struct Metadata(Arc); impl core::Metadata for Metadata {} impl PubSubMetadata for Metadata { fn session(&self) -> Option> { - Some(Arc::new(session().0)) + Some(self.0.clone()) + } + } + impl Default for Metadata { + fn default() -> Self { + Self(Arc::new(session().0)) } } #[test] fn should_subscribe() { // given - let called = Arc::new(AtomicBool::new(false)); - let called2 = called.clone(); let (subscribe, _) = new_subscription( "test".into(), - move |params, _meta, _subscriber| { + move |params, _meta, subscriber: Subscriber| { assert_eq!(params, core::Params::None); - called2.store(true, Ordering::SeqCst); + let _sink = subscriber.assign_id(SubscriptionId::Number(5)).unwrap(); }, |_id, _meta| async { Ok(core::Value::Bool(true)) }, ); - let meta = Metadata; // when + let meta = Metadata::default(); let result = subscribe.call(core::Params::None, meta); // then - assert_eq!(called.load(Ordering::SeqCst), true); + assert_eq!(futures::executor::block_on(result), Ok(serde_json::json!(5))); + } + + #[test] + fn should_unsubscribe() { + // given + const SUB_ID: u64 = 5; + let (subscribe, unsubscribe) = new_subscription( + "test".into(), + move |params, _meta, subscriber: Subscriber| { + assert_eq!(params, core::Params::None); + let _sink = subscriber.assign_id(SubscriptionId::Number(SUB_ID)).unwrap(); + }, + |_id, _meta| async { Ok(core::Value::Bool(true)) }, + ); + + // when + let meta = Metadata::default(); + futures::executor::block_on(subscribe.call(core::Params::None, meta.clone())).unwrap(); + let result = unsubscribe.call(core::Params::Array(vec![serde_json::json!(SUB_ID)]), meta); + + // then + assert_eq!(futures::executor::block_on(result), Ok(serde_json::json!(true))); + } + + #[test] + fn should_not_unsubscribe_if_invalid() { + // given + const SUB_ID: u64 = 5; + let (subscribe, unsubscribe) = new_subscription( + "test".into(), + move |params, _meta, subscriber: Subscriber| { + assert_eq!(params, core::Params::None); + let _sink = subscriber.assign_id(SubscriptionId::Number(SUB_ID)).unwrap(); + }, + |_id, _meta| async { Ok(core::Value::Bool(true)) }, + ); + + // when + let meta = Metadata::default(); + futures::executor::block_on(subscribe.call(core::Params::None, meta.clone())).unwrap(); + let result = unsubscribe.call(core::Params::Array(vec![serde_json::json!(SUB_ID + 1)]), meta); + + // then assert_eq!( futures::executor::block_on(result), Err(core::Error { - code: core::ErrorCode::ServerError(-32091), - message: "Subscription rejected".into(), + code: core::ErrorCode::InvalidParams, + message: "Invalid subscription id.".into(), data: None, }) ); From 609d7a6cc160742d035510fa89fb424ccf077660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 1 Apr 2021 15:17:21 +0200 Subject: [PATCH 144/149] Bump minor version. (#624) * Bump minor version. * Fix build warning. * Fix ui tests. * Remove windows runner since it's decomissioned. --- .gitlab-ci.yml | 7 ------- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core-client/transports/src/transports/http.rs | 2 +- core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- derive/tests/ui/attr-invalid-meta-list-names.stderr | 3 +-- derive/tests/ui/attr-invalid-meta-words.stderr | 3 +-- derive/tests/ui/attr-invalid-name-values.stderr | 3 +-- derive/tests/ui/attr-missing-rpc-name.stderr | 3 +-- derive/tests/ui/multiple-rpc-attributes.stderr | 3 +-- derive/tests/ui/too-many-params.stderr | 3 +-- http/Cargo.toml | 6 +++--- ipc/Cargo.toml | 6 +++--- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- tcp/Cargo.toml | 6 +++--- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- 21 files changed, 50 insertions(+), 63 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eef0e83ac..6db651b08 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -56,13 +56,6 @@ test-linux-stable: <<: *only <<: *test_and_build -test-windows-stable: - stage: test - <<: *test_and_build - <<: *only - tags: - - rust-windows - test-mac-stable: stage: test <<: *test_and_build diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index ac64b4094..d8414b041 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" categories = [ "asynchronous", @@ -26,7 +26,7 @@ ipc = ["jsonrpc-client-transports/ipc"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "17.0", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "17.1", path = "./transports", default-features = false } futures = { version = "0.3", features = [ "compat" ] } [badges] diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index f92978f51..af4b3a62a 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" categories = [ "asynchronous", @@ -37,8 +37,8 @@ arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary [dependencies] derive_more = "0.99" futures = "0.3" -jsonrpc-core = { version = "17.0", path = "../../core" } -jsonrpc-pubsub = { version = "17.0", path = "../../pubsub" } +jsonrpc-core = { version = "17.1", path = "../../core" } +jsonrpc-pubsub = { version = "17.1", path = "../../pubsub" } log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -46,15 +46,15 @@ url = "1.7" hyper = { version = "0.13", optional = true } hyper-tls = { version = "0.4", optional = true } -jsonrpc-server-utils = { version = "17.0", path = "../../server-utils", optional = true } +jsonrpc-server-utils = { version = "17.1", path = "../../server-utils", optional = true } parity-tokio-ipc = { version = "0.8", optional = true } tokio = { version = "0.2", optional = true } websocket = { version = "0.24", optional = true } [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "17.0", path = "../../http" } -jsonrpc-ipc-server = { version = "17.0", path = "../../ipc" } +jsonrpc-http-server = { version = "17.1", path = "../../http" } +jsonrpc-ipc-server = { version = "17.1", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index df518340a..248b41890 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -273,7 +273,7 @@ mod tests { if let Err(RpcError::Other(err)) = res { if let Some(err) = err.downcast_ref::() { - assert!(err.is_connect(), format!("Expected Connection Error, got {:?}", err)) + assert!(err.is_connect(), "Expected Connection Error, got {:?}", err) } else { panic!("Expected a hyper::Error") } diff --git a/core/Cargo.toml b/core/Cargo.toml index 08536ae8c..39e2de93d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index c07ee212c..9fd65141e 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [lib] proc-macro = true @@ -20,10 +20,10 @@ proc-macro-crate = "0.1.4" [dev-dependencies] assert_matches = "1.3" -jsonrpc-core = { version = "17.0", path = "../core" } -jsonrpc-core-client = { version = "17.0", path = "../core-client" } -jsonrpc-pubsub = { version = "17.0", path = "../pubsub" } -jsonrpc-tcp-server = { version = "17.0", path = "../tcp" } +jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-core-client = { version = "17.1", path = "../core-client" } +jsonrpc-pubsub = { version = "17.1", path = "../pubsub" } +jsonrpc-tcp-server = { version = "17.1", path = "../tcp" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" trybuild = "1.0" diff --git a/derive/tests/ui/attr-invalid-meta-list-names.stderr b/derive/tests/ui/attr-invalid-meta-list-names.stderr index 443966e27..b1beb38c4 100644 --- a/derive/tests/ui/attr-invalid-meta-list-names.stderr +++ b/derive/tests/ui/attr-invalid-meta-list-names.stderr @@ -1,8 +1,7 @@ error: Invalid attribute parameter(s): 'Xalias'. Expected 'alias' --> $DIR/attr-invalid-meta-list-names.rs:5:2 | -5 | /// Returns a protocol version - | _____^ +5 | / /// Returns a protocol version 6 | | #[rpc(name = "protocolVersion", Xalias("alias"))] 7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index 7b5d63d17..b76def12b 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,8 +1,7 @@ error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params' --> $DIR/attr-invalid-meta-words.rs:5:2 | -5 | /// Returns a protocol version - | _____^ +5 | / /// Returns a protocol version 6 | | #[rpc(name = "protocolVersion", Xmeta)] 7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index 6811f0c3e..f308254c1 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,8 +1,7 @@ error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns, params' --> $DIR/attr-invalid-name-values.rs:5:2 | -5 | /// Returns a protocol version - | _____^ +5 | / /// Returns a protocol version 6 | | #[rpc(Xname = "protocolVersion")] 7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/attr-missing-rpc-name.stderr b/derive/tests/ui/attr-missing-rpc-name.stderr index fca07fc75..d919e31de 100644 --- a/derive/tests/ui/attr-missing-rpc-name.stderr +++ b/derive/tests/ui/attr-missing-rpc-name.stderr @@ -1,8 +1,7 @@ error: rpc attribute should have a name e.g. `name = "method_name"` --> $DIR/attr-missing-rpc-name.rs:5:2 | -5 | /// Returns a protocol version - | _____^ +5 | / /// Returns a protocol version 6 | | #[rpc] 7 | | fn protocol_version(&self) -> Result; | |_________________________________________________^ diff --git a/derive/tests/ui/multiple-rpc-attributes.stderr b/derive/tests/ui/multiple-rpc-attributes.stderr index 05099f7e3..dddb37788 100644 --- a/derive/tests/ui/multiple-rpc-attributes.stderr +++ b/derive/tests/ui/multiple-rpc-attributes.stderr @@ -1,8 +1,7 @@ error: Expected only a single rpc attribute per method --> $DIR/multiple-rpc-attributes.rs:5:2 | -5 | /// Returns a protocol version - | _____^ +5 | / /// Returns a protocol version 6 | | #[rpc(name = "protocolVersion")] 7 | | #[rpc(name = "protocolVersionAgain")] 8 | | fn protocol_version(&self) -> Result; diff --git a/derive/tests/ui/too-many-params.stderr b/derive/tests/ui/too-many-params.stderr index b83347661..3c23fdf72 100644 --- a/derive/tests/ui/too-many-params.stderr +++ b/derive/tests/ui/too-many-params.stderr @@ -1,8 +1,7 @@ error: Maximum supported number of params is 16 --> $DIR/too-many-params.rs:5:2 | -5 | /// Has too many params - | _____^ +5 | / /// Has too many params 6 | | #[rpc(name = "tooManyParams")] 7 | | fn to_many_params( 8 | | &self, diff --git a/http/Cargo.toml b/http/Cargo.toml index b837fc444..1e6172abc 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [dependencies] futures = "0.3" hyper = "0.13" -jsonrpc-core = { version = "17.0", path = "../core" } -jsonrpc-server-utils = { version = "17.0", path = "../server-utils" } +jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } log = "0.4" net2 = "0.2" parking_lot = "0.11.0" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index cf2b9bad4..0eef09da3 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.1" +version = "17.1.0" [dependencies] futures = "0.3" log = "0.4" tower-service = "0.3" -jsonrpc-core = { version = "17.0", path = "../core" } -jsonrpc-server-utils = { version = "17.0", path = "../server-utils", default-features = false } +jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-server-utils = { version = "17.1", path = "../server-utils", default-features = false } parity-tokio-ipc = "0.8" parking_lot = "0.11.0" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index f4d0f2e26..7ecd921c6 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,11 +8,11 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [dependencies] futures = { version = "0.3", features = ["thread-pool"] } -jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-core = { version = "17.1", path = "../core" } lazy_static = "1.4" log = "0.4" parking_lot = "0.11.0" @@ -20,7 +20,7 @@ rand = "0.7" serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "17.0", path = "../tcp" } +jsonrpc-tcp-server = { version = "17.1", path = "../tcp" } serde_json = "1.0" [badges] diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index b222ba108..8a222242e 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "17.0", path = "../../core" } -jsonrpc-pubsub = { version = "17.0", path = "../" } -jsonrpc-ws-server = { version = "17.0", path = "../../ws" } -jsonrpc-ipc-server = { version = "17.0", path = "../../ipc" } +jsonrpc-core = { version = "17.1", path = "../../core" } +jsonrpc-pubsub = { version = "17.1", path = "../" } +jsonrpc-ws-server = { version = "17.1", path = "../../ws" } +jsonrpc-ipc-server = { version = "17.1", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 4adbda3c6..aacbce4f0 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [dependencies] bytes = "0.5" futures = "0.3" globset = "0.4" -jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-core = { version = "17.1", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "0.2", features = ["rt-threaded", "io-driver", "io-util", "time", "tcp"] } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index ba8725c71..8c907431f 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [dependencies] futures = "0.3" -jsonrpc-core = { version = "17.0", path = "../core" } +jsonrpc-core = { version = "17.1", path = "../core" } log = "0.4" tokio = { version = "0.2", features = ["io-std", "io-driver", "io-util"] } tokio-util = { version = "0.3", features = ["codec"] } diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index 990ef7816..a25777300 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [dependencies] -jsonrpc-core = { version = "17.0", path = "../core" } -jsonrpc-server-utils = { version = "17.0", path = "../server-utils" } +jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" tower-service = "0.3" diff --git a/test/Cargo.toml b/test/Cargo.toml index 2dc6cfedd..bfe2c061f 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "17.0.0" +version = "17.1.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "17.0", path = "../core" } -jsonrpc-core-client = { version = "17.0", path = "../core-client" } -jsonrpc-pubsub = { version = "17.0", path = "../pubsub" } +jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-core-client = { version = "17.1", path = "../core-client" } +jsonrpc-pubsub = { version = "17.1", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "17.0", path = "../derive" } +jsonrpc-derive = { version = "17.1", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index bfbf74237..a03be3f8c 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,12 +7,12 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.0.0" +version = "17.1.0" [dependencies] futures = "0.3" -jsonrpc-core = { version = "17.0", path = "../core" } -jsonrpc-server-utils = { version = "17.0", path = "../server-utils" } +jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" slab = "0.4" From ac72c857b4a02822df289544832774ad9abc5ad9 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 7 Jul 2021 15:19:53 +0000 Subject: [PATCH 145/149] Update to Tokio 1.0 (#628) * WIP: Update to Tokio 1.0 * ipc: Migrate remaining Unix test code to Tokio 1.0 * core-client: Don't depend on unused hyper/server feature * http: Fix used feature set by hyper * WIP: Bump to the transferred parity-tokio-ipc repo * Use newly released version of parity-tokio-ipc * Remove extra newline in Cargo.toml * Refactor suspension slightly in SuspendableStream --- core-client/transports/Cargo.toml | 8 ++++---- http/Cargo.toml | 2 +- ipc/Cargo.toml | 4 ++-- ipc/src/server.rs | 7 ++++--- server-utils/Cargo.toml | 7 ++++--- server-utils/src/lib.rs | 1 + server-utils/src/reactor.rs | 5 ++--- server-utils/src/suspendable_stream.rs | 23 ++++++++++++++--------- stdio/Cargo.toml | 6 +++--- tcp/src/server.rs | 2 ++ tcp/src/tests.rs | 14 +++++++------- 11 files changed, 44 insertions(+), 35 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index af4b3a62a..f5617df2c 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -44,11 +44,11 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" url = "1.7" -hyper = { version = "0.13", optional = true } -hyper-tls = { version = "0.4", optional = true } +hyper = { version = "0.14", features = ["client", "http1"], optional = true } +hyper-tls = { version = "0.5", optional = true } jsonrpc-server-utils = { version = "17.1", path = "../../server-utils", optional = true } -parity-tokio-ipc = { version = "0.8", optional = true } -tokio = { version = "0.2", optional = true } +parity-tokio-ipc = { version = "0.9", optional = true } +tokio = { version = "1", optional = true } websocket = { version = "0.24", optional = true } [dev-dependencies] diff --git a/http/Cargo.toml b/http/Cargo.toml index 1e6172abc..8208268f2 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -12,7 +12,7 @@ version = "17.1.0" [dependencies] futures = "0.3" -hyper = "0.13" +hyper = { version = "0.14", features = ["http1", "tcp", "server", "stream"] } jsonrpc-core = { version = "17.1", path = "../core" } jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } log = "0.4" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 0eef09da3..6f3d9adaf 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -15,7 +15,7 @@ log = "0.4" tower-service = "0.3" jsonrpc-core = { version = "17.1", path = "../core" } jsonrpc-server-utils = { version = "17.1", path = "../server-utils", default-features = false } -parity-tokio-ipc = "0.8" +parity-tokio-ipc = "0.9" parking_lot = "0.11.0" [dev-dependencies] @@ -23,7 +23,7 @@ env_logger = "0.7" lazy_static = "1.0" [target.'cfg(not(windows))'.dev-dependencies] -tokio = { version = "0.2", default-features = false, features = ["uds", "time", "rt-threaded", "io-driver"] } +tokio = { version = "1", default-features = false, features = ["net", "time", "rt-multi-thread"] } [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 107150304..1b8fd0969 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -360,7 +360,7 @@ mod tests { reply.expect("there should be one reply") }; - let mut rt = tokio::runtime::Runtime::new().unwrap(); + let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(reply).expect("wait for reply") } @@ -609,9 +609,10 @@ mod tests { tx.send(true).expect("failed to report that the server has stopped"); }); - let mut rt = tokio::runtime::Runtime::new().unwrap(); + let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { - let timeout = tokio::time::delay_for(Duration::from_millis(500)); + let timeout = tokio::time::sleep(Duration::from_millis(500)); + futures::pin_mut!(timeout); match futures::future::select(rx, timeout).await { futures::future::Either::Left((result, _)) => { diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index aacbce4f0..5a6e27f18 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -11,14 +11,15 @@ repository = "https://github.com/paritytech/jsonrpc" version = "17.1.0" [dependencies] -bytes = "0.5" +bytes = "1.0" futures = "0.3" globset = "0.4" jsonrpc-core = { version = "17.1", path = "../core" } lazy_static = "1.1.0" log = "0.4" -tokio = { version = "0.2", features = ["rt-threaded", "io-driver", "io-util", "time", "tcp"] } -tokio-util = { version = "0.3", features = ["codec"] } +tokio = { version = "1", features = ["rt-multi-thread", "io-util", "time", "net"] } +tokio-util = { version = "0.6", features = ["codec"] } +tokio-stream = { version = "0.1", features = ["net"] } unicase = "2.0" diff --git a/server-utils/src/lib.rs b/server-utils/src/lib.rs index 5c9f52d1a..3d6cd5cc3 100644 --- a/server-utils/src/lib.rs +++ b/server-utils/src/lib.rs @@ -9,6 +9,7 @@ extern crate log; extern crate lazy_static; pub use tokio; +pub use tokio_stream; pub use tokio_util; pub mod cors; diff --git a/server-utils/src/reactor.rs b/server-utils/src/reactor.rs index df8afd408..041328528 100644 --- a/server-utils/src/reactor.rs +++ b/server-utils/src/reactor.rs @@ -96,9 +96,8 @@ impl RpcEventLoop { pub fn with_name(name: Option) -> io::Result { let (stop, stopped) = futures::channel::oneshot::channel(); - let mut tb = runtime::Builder::new(); - tb.core_threads(1); - tb.threaded_scheduler(); + let mut tb = runtime::Builder::new_multi_thread(); + tb.worker_threads(1); tb.enable_all(); if let Some(name) = name { diff --git a/server-utils/src/suspendable_stream.rs b/server-utils/src/suspendable_stream.rs index 8d3179da9..96af9c91a 100644 --- a/server-utils/src/suspendable_stream.rs +++ b/server-utils/src/suspendable_stream.rs @@ -2,9 +2,7 @@ use std::future::Future; use std::io; use std::pin::Pin; use std::task::Poll; -use std::time::Duration; - -use tokio::time::Delay; +use std::time::{Duration, Instant}; /// `Incoming` is a stream of incoming sockets /// Polling the stream may return a temporary io::Error (for instance if we can't open the connection because of "too many open files" limit) @@ -19,7 +17,7 @@ pub struct SuspendableStream { next_delay: Duration, initial_delay: Duration, max_delay: Duration, - timeout: Option, + suspended_until: Option, } impl SuspendableStream { @@ -31,7 +29,7 @@ impl SuspendableStream { next_delay: Duration::from_millis(20), initial_delay: Duration::from_millis(10), max_delay: Duration::from_secs(5), - timeout: None, + suspended_until: None, } } } @@ -44,10 +42,17 @@ where fn poll_next(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { loop { - if let Some(timeout) = self.timeout.as_mut() { - match Pin::new(timeout).poll(cx) { + // If we encountered a connection error before then we suspend + // polling from the underlying stream for a bit + if let Some(deadline) = &mut self.suspended_until { + let deadline = tokio::time::Instant::from_std(*deadline); + let sleep = tokio::time::sleep_until(deadline); + futures::pin_mut!(sleep); + match sleep.poll(cx) { Poll::Pending => return Poll::Pending, - Poll::Ready(()) => {} + Poll::Ready(()) => { + self.suspended_until = None; + } } } @@ -78,7 +83,7 @@ where }; debug!("Error accepting connection: {}", err); debug!("The server will stop accepting connections for {:?}", self.next_delay); - self.timeout = Some(tokio::time::delay_for(self.next_delay)); + self.suspended_until = Some(Instant::now() + self.next_delay); } } } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 8c907431f..2e5c91ba9 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -13,11 +13,11 @@ version = "17.1.0" futures = "0.3" jsonrpc-core = { version = "17.1", path = "../core" } log = "0.4" -tokio = { version = "0.2", features = ["io-std", "io-driver", "io-util"] } -tokio-util = { version = "0.3", features = ["codec"] } +tokio = { version = "1", features = ["io-std", "io-util"] } +tokio-util = { version = "0.6", features = ["codec"] } [dev-dependencies] -tokio = { version = "0.2", features = ["rt-core", "macros"] } +tokio = { version = "1", features = ["rt", "macros"] } lazy_static = "1.0" env_logger = "0.7" diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 55a2b8d4a..9e6aad222 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -7,6 +7,7 @@ use tower_service::Service as _; use crate::futures::{self, future}; use crate::jsonrpc::{middleware, MetaIoHandler, Metadata, Middleware}; +use crate::server_utils::tokio_stream::wrappers::TcpListenerStream; use crate::server_utils::{codecs, reactor, tokio, tokio_util::codec::Framed, SuspendableStream}; use crate::dispatch::{Dispatcher, PeerMessageQueue, SenderChannels}; @@ -94,6 +95,7 @@ where executor.executor().spawn(async move { let start = async { let listener = tokio::net::TcpListener::bind(&address).await?; + let listener = TcpListenerStream::new(listener); let connections = SuspendableStream::new(listener); let server = connections.map(|socket| { diff --git a/tcp/src/tests.rs b/tcp/src/tests.rs index b95e1b288..36d7c45c8 100644 --- a/tcp/src/tests.rs +++ b/tcp/src/tests.rs @@ -1,4 +1,4 @@ -use std::net::{Shutdown, SocketAddr}; +use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -23,7 +23,7 @@ fn casual_server() -> ServerBuilder { } fn run_future(fut: impl std::future::Future + Send) -> O { - let mut rt = tokio::runtime::Runtime::new().unwrap(); + let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(fut) } @@ -60,9 +60,9 @@ fn disconnect() { let _server = server.start(&addr).expect("Server must run with no issues"); run_future(async move { - let stream = TcpStream::connect(&addr).await.unwrap(); + let mut stream = TcpStream::connect(&addr).await.unwrap(); assert_eq!(stream.peer_addr().unwrap(), addr); - stream.shutdown(::std::net::Shutdown::Both).unwrap(); + stream.shutdown().await.unwrap(); }); ::std::thread::sleep(::std::time::Duration::from_millis(50)); @@ -76,7 +76,7 @@ fn dummy_request(addr: &SocketAddr, data: Vec) -> Vec { let stream = async move { let mut stream = TcpStream::connect(addr).await?; stream.write_all(&data).await?; - stream.shutdown(Shutdown::Write)?; + stream.shutdown().await?; let mut read_buf = vec![]; let _ = stream.read_to_end(&mut read_buf).await; @@ -243,7 +243,7 @@ fn message() { let client = async move { let stream = TcpStream::connect(&addr); - let delay = tokio::time::delay_for(Duration::from_millis(500)); + let delay = tokio::time::sleep(Duration::from_millis(500)); let (stream, _) = futures::join!(stream, delay); let mut stream = stream?; @@ -272,7 +272,7 @@ fn message() { let data = b"{\"jsonrpc\": \"2.0\", \"method\": \"say_hello\", \"params\": [42, 23], \"id\": 1}\n"; stream.write_all(&data[..]).await?; - stream.shutdown(Shutdown::Write).unwrap(); + stream.shutdown().await.unwrap(); let mut read_buf = vec![]; let _ = stream.read_to_end(&mut read_buf).await?; From 68511bc10ae1e0abb2d6a3f8978cc2e9d962a57c Mon Sep 17 00:00:00 2001 From: Chevdor Date: Thu, 8 Jul 2021 10:37:53 +0200 Subject: [PATCH 146/149] Add clippy to CI and fix several clippy warnings (#625) * Remove ambiguity when using deps * Fix doc and some linting hints * Make tests more readable * Run clippy during the CI checks * Fix formatting * A few more linting fixes * Remove old comment --- .gitlab-ci.yml | 3 +- core-client/transports/src/lib.rs | 2 +- .../transports/src/transports/duplex.rs | 2 +- core-client/transports/src/transports/mod.rs | 2 +- core/src/io.rs | 16 +- core/src/lib.rs | 20 +- core/src/types/params.rs | 1 - core/src/types/response.rs | 4 +- derive/src/lib.rs | 211 +++++++++--------- derive/src/rpc_attr.rs | 17 +- derive/src/rpc_trait.rs | 5 +- derive/src/to_client.rs | 16 +- derive/src/to_delegate.rs | 13 +- derive/tests/client.rs | 6 +- http/src/lib.rs | 20 +- http/src/utils.rs | 7 +- ipc/src/server.rs | 2 +- pubsub/src/oneshot.rs | 2 +- pubsub/src/typed.rs | 1 - server-utils/src/cors.rs | 4 +- server-utils/src/stream_codec.rs | 5 +- stdio/src/lib.rs | 12 +- tcp/src/lib.rs | 16 +- tcp/src/server.rs | 4 +- tcp/src/service.rs | 2 +- test/src/lib.rs | 14 +- 26 files changed, 193 insertions(+), 214 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6db651b08..7cc7c9725 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -45,8 +45,9 @@ checkstyle-linux-stable: <<: *only <<: *docker-env script: - - rustup component add rustfmt + - rustup component add rustfmt clippy - cargo fmt --all -- --check + - cargo clippy allow_failure: true # test rust stable diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index 001177a33..8b1112945 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -178,7 +178,7 @@ impl Stream for TypedSubscriptionStream None, - Some(Err(err)) => Some(Err(err.into())), + Some(Err(err)) => Some(Err(err)), } .into() } diff --git a/core-client/transports/src/transports/duplex.rs b/core-client/transports/src/transports/duplex.rs index c92cd1fe8..28751f64d 100644 --- a/core-client/transports/src/transports/duplex.rs +++ b/core-client/transports/src/transports/duplex.rs @@ -281,7 +281,7 @@ where } match self.outgoing.pop_front() { Some(request) => { - if let Err(_) = self.sink.as_mut().start_send(request) { + if self.sink.as_mut().start_send(request).is_err() { // the channel is disconnected. return err().into(); } diff --git a/core-client/transports/src/transports/mod.rs b/core-client/transports/src/transports/mod.rs index 00b6f0600..9df534a0d 100644 --- a/core-client/transports/src/transports/mod.rs +++ b/core-client/transports/src/transports/mod.rs @@ -151,7 +151,7 @@ impl From for Result { (Some(_), _, Some(error)) => { let error = serde_json::from_value::(error.to_owned()) .ok() - .unwrap_or_else(|| Error::parse_error()); + .unwrap_or_else(Error::parse_error); Err(error) } _ => Ok(n.params.into()), diff --git a/core/src/io.rs b/core/src/io.rs index 713b47f97..c67ff2620 100644 --- a/core/src/io.rs +++ b/core/src/io.rs @@ -8,7 +8,6 @@ use std::pin::Pin; use std::sync::Arc; use futures_util::{self, future, FutureExt}; -use serde_json; use crate::calls::{ Metadata, RemoteProcedure, RpcMethod, RpcMethodSimple, RpcMethodSync, RpcNotification, RpcNotificationSimple, @@ -60,10 +59,10 @@ impl Default for Compatibility { impl Compatibility { fn is_version_valid(self, version: Option) -> bool { - match (self, version) { - (Compatibility::V1, None) | (Compatibility::V2, Some(Version::V2)) | (Compatibility::Both, _) => true, - _ => false, - } + matches!( + (self, version), + (Compatibility::V1, None) | (Compatibility::V2, Some(Version::V2)) | (Compatibility::Both, _) + ) } fn default_version(self) -> Option { @@ -208,10 +207,7 @@ impl> MetaIoHandler { use self::future::Either::{Left, Right}; fn as_string(response: Option) -> Option { let res = response.map(write_response); - debug!(target: "rpc", "Response: {}.", match res { - Some(ref res) => res, - None => "None", - }); + debug!(target: "rpc", "Response: {}.", res.as_ref().unwrap_or(&"None".to_string())); res } @@ -237,7 +233,7 @@ impl> MetaIoHandler { } fn outputs_as_batch(outs: Vec>) -> Option { - let outs: Vec<_> = outs.into_iter().filter_map(|v| v).collect(); + let outs: Vec<_> = outs.into_iter().flatten().collect(); if outs.is_empty() { None } else { diff --git a/core/src/lib.rs b/core/src/lib.rs index 736363c3d..1828b806a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,19 +3,17 @@ //! Right now it supports only server side handling requests. //! //! ```rust -//! use jsonrpc_core::*; +//! use jsonrpc_core::IoHandler; +//! use jsonrpc_core::Value; +//! let mut io = IoHandler::new(); +//! io.add_sync_method("say_hello", |_| { +//! Ok(Value::String("Hello World!".into())) +//! }); //! -//! fn main() { -//! let mut io = IoHandler::new(); -//! io.add_sync_method("say_hello", |_| { -//! Ok(Value::String("Hello World!".into())) -//! }); +//! let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; +//! let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#; //! -//! let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#; -//! let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#; -//! -//! assert_eq!(io.handle_request_sync(request), Some(response.to_string())); -//! } +//! assert_eq!(io.handle_request_sync(request), Some(response.to_string())); //! ``` #![deny(missing_docs)] diff --git a/core/src/types/params.rs b/core/src/types/params.rs index de32fd04e..2eac38606 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -1,7 +1,6 @@ //! jsonrpc params field use serde::de::DeserializeOwned; -use serde_json; use serde_json::value::from_value; use super::{Error, Value}; diff --git a/core/src/types/response.rs b/core/src/types/response.rs index f7d54b07b..5c45e5c04 100644 --- a/core/src/types/response.rs +++ b/core/src/types/response.rs @@ -43,8 +43,8 @@ impl Output { /// Creates new output given `Result`, `Id` and `Version`. pub fn from(result: CoreResult, id: Id, jsonrpc: Option) -> Self { match result { - Ok(result) => Output::Success(Success { id, jsonrpc, result }), - Err(error) => Output::Failure(Failure { id, jsonrpc, error }), + Ok(result) => Output::Success(Success { jsonrpc, result, id }), + Err(error) => Output::Failure(Failure { jsonrpc, error, id }), } } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 9f0f50657..2a8109a69 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -5,42 +5,42 @@ //! Example //! //! ``` -//! use jsonrpc_derive::rpc; //! use jsonrpc_core::{IoHandler, Result, BoxFuture}; //! use jsonrpc_core::futures::future; +//! use jsonrpc_derive::rpc; //! //! #[rpc(server)] //! pub trait Rpc { -//! #[rpc(name = "protocolVersion")] -//! fn protocol_version(&self) -> Result; +//! #[rpc(name = "protocolVersion")] +//! fn protocol_version(&self) -> Result; //! -//! #[rpc(name = "add")] -//! fn add(&self, a: u64, b: u64) -> Result; +//! #[rpc(name = "add")] +//! fn add(&self, a: u64, b: u64) -> Result; //! -//! #[rpc(name = "callAsync")] -//! fn call(&self, a: u64) -> BoxFuture>; +//! #[rpc(name = "callAsync")] +//! fn call(&self, a: u64) -> BoxFuture>; //! } //! //! struct RpcImpl; //! impl Rpc for RpcImpl { -//! fn protocol_version(&self) -> Result { -//! Ok("version1".into()) -//! } +//! fn protocol_version(&self) -> Result { +//! Ok("version1".into()) +//! } //! -//! fn add(&self, a: u64, b: u64) -> Result { -//! Ok(a + b) -//! } +//! fn add(&self, a: u64, b: u64) -> Result { +//! Ok(a + b) +//! } //! -//! fn call(&self, _: u64) -> BoxFuture> { -//! Box::pin(future::ready(Ok("OK".to_owned()).into())) -//! } +//! fn call(&self, _: u64) -> BoxFuture> { +//! Box::pin(future::ready(Ok("OK".to_owned()).into())) +//! } //! } //! //! fn main() { -//! let mut io = IoHandler::new(); -//! let rpc = RpcImpl; +//! let mut io = IoHandler::new(); +//! let rpc = RpcImpl; //! -//! io.extend_with(rpc.to_delegate()); +//! io.extend_with(rpc.to_delegate()); //! } //! ``` //! @@ -51,7 +51,6 @@ //! have a matching unique subscription name. //! //! ``` -//! use std::thread; //! use std::sync::{atomic, Arc, RwLock}; //! use std::collections::HashMap; //! @@ -61,80 +60,80 @@ //! //! #[rpc] //! pub trait Rpc { -//! type Metadata; -//! -//! /// Hello subscription -//! #[pubsub( -//! subscription = "hello", -//! subscribe, -//! name = "hello_subscribe", -//! alias("hello_sub") -//! )] -//! fn subscribe(&self, _: Self::Metadata, _: Subscriber, param: u64); -//! -//! /// Unsubscribe from hello subscription. -//! #[pubsub( -//! subscription = "hello", -//! unsubscribe, -//! name = "hello_unsubscribe" -//! )] -//! fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +//! type Metadata; +//! +//! /// Hello subscription +//! #[pubsub( +//! subscription = "hello", +//! subscribe, +//! name = "hello_subscribe", +//! alias("hello_sub") +//! )] +//! fn subscribe(&self, _: Self::Metadata, _: Subscriber, param: u64); +//! +//! /// Unsubscribe from hello subscription. +//! #[pubsub( +//! subscription = "hello", +//! unsubscribe, +//! name = "hello_unsubscribe" +//! )] +//! fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; //! } //! //! //! #[derive(Default)] //! struct RpcImpl { -//! uid: atomic::AtomicUsize, -//! active: Arc>>>, +//! uid: atomic::AtomicUsize, +//! active: Arc>>>, //! } //! impl Rpc for RpcImpl { -//! type Metadata = Arc; -//! -//! fn subscribe(&self, _meta: Self::Metadata, subscriber: Subscriber, param: u64) { -//! if param != 10 { -//! subscriber.reject(Error { -//! code: ErrorCode::InvalidParams, -//! message: "Rejecting subscription - invalid parameters provided.".into(), -//! data: None, -//! }).unwrap(); -//! return; -//! } -//! -//! let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst); -//! let sub_id = SubscriptionId::Number(id as u64); -//! let sink = subscriber.assign_id(sub_id.clone()).unwrap(); -//! self.active.write().unwrap().insert(sub_id, sink); -//! } -//! -//! fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { -//! let removed = self.active.write().unwrap().remove(&id); -//! if removed.is_some() { -//! Ok(true) -//! } else { -//! Err(Error { -//! code: ErrorCode::InvalidParams, -//! message: "Invalid subscription.".into(), -//! data: None, -//! }) -//! } -//! } +//! type Metadata = Arc; +//! +//! fn subscribe(&self, _meta: Self::Metadata, subscriber: Subscriber, param: u64) { +//! if param != 10 { +//! subscriber.reject(Error { +//! code: ErrorCode::InvalidParams, +//! message: "Rejecting subscription - invalid parameters provided.".into(), +//! data: None, +//! }).unwrap(); +//! return; +//! } +//! +//! let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst); +//! let sub_id = SubscriptionId::Number(id as u64); +//! let sink = subscriber.assign_id(sub_id.clone()).unwrap(); +//! self.active.write().unwrap().insert(sub_id, sink); +//! } +//! +//! fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { +//! let removed = self.active.write().unwrap().remove(&id); +//! if removed.is_some() { +//! Ok(true) +//! } else { +//! Err(Error { +//! code: ErrorCode::InvalidParams, +//! message: "Invalid subscription.".into(), +//! data: None, +//! }) +//! } +//! } //! } //! //! fn main() { -//! let mut io = jsonrpc_core::MetaIoHandler::default(); -//! io.extend_with(RpcImpl::default().to_delegate()); +//! let mut io = jsonrpc_core::MetaIoHandler::default(); +//! io.extend_with(RpcImpl::default().to_delegate()); //! -//! let server_builder = jsonrpc_tcp_server::ServerBuilder::with_meta_extractor( -//! io, -//! |request: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(request.sender.clone())) -//! ); -//! let server = server_builder -//! .start(&"127.0.0.1:3030".parse().unwrap()) -//! .expect("Unable to start TCP server"); +//! let server_builder = jsonrpc_tcp_server::ServerBuilder::with_meta_extractor( +//! io, +//! |request: &jsonrpc_tcp_server::RequestContext| Arc::new(Session::new(request.sender.clone())) +//! ); +//! let server = server_builder +//! .start(&"127.0.0.1:3030".parse().unwrap()) +//! .expect("Unable to start TCP server"); //! //! // The server spawns a separate thread. Dropping the `server` handle causes it to close. //! // Uncomment the line below to keep the server running in your example. -//! // server.wait(); +//! // server.wait(); //! } //! ``` //! @@ -149,47 +148,47 @@ //! /// Rpc trait //! #[rpc] //! pub trait Rpc { -//! /// Returns a protocol version -//! #[rpc(name = "protocolVersion")] -//! fn protocol_version(&self) -> Result; +//! /// Returns a protocol version +//! #[rpc(name = "protocolVersion")] +//! fn protocol_version(&self) -> Result; //! -//! /// Adds two numbers and returns a result -//! #[rpc(name = "add", alias("callAsyncMetaAlias"))] -//! fn add(&self, a: u64, b: u64) -> Result; +//! /// Adds two numbers and returns a result +//! #[rpc(name = "add", alias("callAsyncMetaAlias"))] +//! fn add(&self, a: u64, b: u64) -> Result; //! -//! /// Performs asynchronous operation -//! #[rpc(name = "callAsync")] -//! fn call(&self, a: u64) -> BoxFuture>; +//! /// Performs asynchronous operation +//! #[rpc(name = "callAsync")] +//! fn call(&self, a: u64) -> BoxFuture>; //! } //! //! struct RpcImpl; //! //! impl Rpc for RpcImpl { -//! fn protocol_version(&self) -> Result { -//! Ok("version1".into()) -//! } +//! fn protocol_version(&self) -> Result { +//! Ok("version1".into()) +//! } //! -//! fn add(&self, a: u64, b: u64) -> Result { -//! Ok(a + b) -//! } +//! fn add(&self, a: u64, b: u64) -> Result { +//! Ok(a + b) +//! } //! -//! fn call(&self, _: u64) -> BoxFuture> { -//! Box::pin(future::ready(Ok("OK".to_owned()))) -//! } +//! fn call(&self, _: u64) -> BoxFuture> { +//! Box::pin(future::ready(Ok("OK".to_owned()))) +//! } //! } //! //! fn main() { -//! let exec = futures::executor::ThreadPool::new().unwrap(); -//! exec.spawn_ok(run()) +//! let exec = futures::executor::ThreadPool::new().unwrap(); +//! exec.spawn_ok(run()) //! } //! async fn run() { -//! let mut io = IoHandler::new(); -//! io.extend_with(RpcImpl.to_delegate()); +//! let mut io = IoHandler::new(); +//! io.extend_with(RpcImpl.to_delegate()); //! -//! let (client, server) = local::connect::(io); -//! let res = client.add(5, 6).await.unwrap(); -//! println!("5 + 6 = {}", res); -//! server.await.unwrap() +//! let (client, server) = local::connect::(io); +//! let res = client.add(5, 6).await.unwrap(); +//! println!("5 + 6 = {}", res); +//! server.await.unwrap() //! } //! //! ``` diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 215979d19..b66967efb 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -71,7 +71,7 @@ impl RpcMethodAttribute { fn parse_meta(attr: &syn::Attribute, output: &syn::ReturnType) -> Option> { match attr.parse_meta().and_then(validate_attribute_meta) { Ok(ref meta) => { - let attr_kind = match path_to_str(meta.path()).as_ref().map(String::as_str) { + let attr_kind = match path_to_str(meta.path()).as_deref() { Some(RPC_ATTR_NAME) => Some(Self::parse_rpc(meta, output)), Some(PUB_SUB_ATTR_NAME) => Some(Self::parse_pubsub(meta)), _ => None, @@ -89,9 +89,7 @@ impl RpcMethodAttribute { // "`raw_params` will be deprecated in a future release. Use `params = \"raw\" instead`" Ok(Some(ParamStyle::Raw)) } - false => { - get_meta_list(meta).map_or(Ok(None), |ml| get_params_style(ml).map(|s| Some(s))) - } + false => get_meta_list(meta).map_or(Ok(None), |ml| get_params_style(ml).map(Some)), }?; Ok(RpcMethodAttribute { attr: attr.clone(), @@ -110,13 +108,12 @@ impl RpcMethodAttribute { fn parse_rpc(meta: &syn::Meta, output: &syn::ReturnType) -> Result { let has_metadata = get_meta_list(meta).map_or(false, |ml| has_meta_word(METADATA_META_WORD, ml)); - let returns = get_meta_list(meta).map_or(None, |ml| get_name_value(RETURNS_META_WORD, ml)); + let returns = get_meta_list(meta).and_then(|ml| get_name_value(RETURNS_META_WORD, ml)); let is_notification = match output { syn::ReturnType::Default => true, - syn::ReturnType::Type(_, ret) => match **ret { - syn::Type::Tuple(ref tup) if tup.elems.empty_or_trailing() => true, - _ => false, - }, + syn::ReturnType::Type(_, ret) => { + matches!(**ret, syn::Type::Tuple(ref tup) if tup.elems.empty_or_trailing()) + } }; if is_notification && returns.is_some() { @@ -188,7 +185,7 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { visit::visit_meta(&mut visitor, &meta); let ident = path_to_str(meta.path()); - match ident.as_ref().map(String::as_str) { + match ident.as_deref() { Some(RPC_ATTR_NAME) => { validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD])?; validate_idents( diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index a95be556b..25bc004b6 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -45,10 +45,9 @@ impl<'a> Fold for RpcTrait { fold::fold_trait_item_method(self, foldable_method) } - fn fold_trait_item_type(&mut self, ty: syn::TraitItemType) -> syn::TraitItemType { + fn fold_trait_item_type(&mut self, mut ty: syn::TraitItemType) -> syn::TraitItemType { if ty.ident == METADATA_TYPE { self.has_metadata = true; - let mut ty = ty.clone(); if self.has_pubsub_methods { ty.bounds.push(parse_quote!(_jsonrpc_pubsub::PubSubMetadata)) } else { @@ -146,7 +145,7 @@ fn compute_method_registrations(item_trait: &syn::ItemTrait) -> Result<(Vec { quote! { // use object style serialization with field names taken from the function param names serde_json::json!({ @@ -188,7 +193,7 @@ fn get_doc_comments(attrs: &[syn::Attribute]) -> Vec { .. } => match &segments[0] { syn::PathSegment { ident, .. } => { - if ident.to_string() == "doc" { + if *ident == "doc" { doc_comments.push(attr.to_owned()); } } @@ -212,10 +217,9 @@ fn compute_args(method: &syn::TraitItemMethod) -> Punctuated segments, _ => continue, }; - let ident = match &segments[0] { - syn::PathSegment { ident, .. } => ident, - }; - if ident.to_string() == "Self" { + let syn::PathSegment { ident, .. } = &segments[0]; + let ident = ident; + if *ident == "Self" { continue; } args.push(arg.to_owned()); diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 384e92d39..7e9148a82 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -154,7 +154,7 @@ pub fn generate_trait_item_method( }; let predicates = generate_where_clause_serialization_predicates(&trait_item, false); - let mut method = method.clone(); + let mut method = method; method.sig.generics.make_where_clause().predicates.extend(predicates); Ok(method) } @@ -220,7 +220,7 @@ impl RpcMethod { // special args are those which are not passed directly via rpc params: metadata, subscriber let special_args = Self::special_args(¶m_types); - param_types.retain(|ty| special_args.iter().find(|(_, sty)| sty == ty).is_none()); + param_types.retain(|ty| !special_args.iter().any(|(_, sty)| sty == ty)); if param_types.len() > TUPLE_FIELD_NAMES.len() { return Err(syn::Error::new_spanned( &self.trait_item, @@ -236,6 +236,7 @@ impl RpcMethod { let parse_params = { // last arguments that are `Option`-s are optional 'trailing' arguments let trailing_args_num = param_types.iter().rev().take_while(|t| is_option_type(t)).count(); + if trailing_args_num != 0 { self.params_with_trailing(trailing_args_num, param_types, tuple_fields) } else if param_types.is_empty() { @@ -244,9 +245,7 @@ impl RpcMethod { quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } } else if self.attr.params_style == Some(ParamStyle::Positional) { quote! { let params = params.parse::<(#(#param_types, )*)>(); } - } else - /* if self.attr.params_style == Some(ParamStyle::Named) */ - { + } else { unimplemented!("Server side named parameters are not implemented"); } }; @@ -324,10 +323,10 @@ impl RpcMethod { let mut special_args = Vec::new(); if let Some(meta) = meta_arg { - special_args.push((ident(METADATA_CLOSURE_ARG), meta.clone())); + special_args.push((ident(METADATA_CLOSURE_ARG), meta)); } if let Some(subscriber) = subscriber_arg { - special_args.push((ident(SUBSCRIBER_CLOSURE_ARG), subscriber.clone())); + special_args.push((ident(SUBSCRIBER_CLOSURE_ARG), subscriber)); } special_args } diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 317c3f995..8ac820ab0 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -38,7 +38,7 @@ mod client_server { .add(3, 4) .map_ok(move |res| client.notify(res).map(move |_| res)) .map(|res| { - assert_matches!(res, Ok(Ok(7))); + self::assert_matches!(res, Ok(Ok(7))); }); let exec = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); exec.spawn_ok(async move { @@ -82,7 +82,7 @@ mod named_params { .call_with_named(3, String::from("test string"), json!({"key": ["value"]})) .map_ok(move |res| client.notify(res.clone()).map(move |_| res)) .map(move |res| { - assert_matches!(res, Ok(Ok(x)) if x == expected); + self::assert_matches!(res, Ok(Ok(x)) if x == expected); }); let exec = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); exec.spawn_ok(async move { futures::join!(fut, rpc_client).1.unwrap() }); @@ -120,7 +120,7 @@ mod raw_params { .call_raw_single_param(expected.clone()) .map_ok(move |res| client.notify(res.clone()).map(move |_| res)) .map(move |res| { - assert_matches!(res, Ok(Ok(x)) if x == expected); + self::assert_matches!(res, Ok(Ok(x)) if x == expected); }); let exec = futures::executor::ThreadPool::builder().pool_size(1).create().unwrap(); exec.spawn_ok(async move { futures::join!(fut, rpc_client).1.unwrap() }); diff --git a/http/src/lib.rs b/http/src/lib.rs index a229ae509..976c95791 100644 --- a/http/src/lib.rs +++ b/http/src/lib.rs @@ -5,23 +5,22 @@ //! use jsonrpc_http_server::*; //! //! fn main() { -//! let mut io = IoHandler::new(); -//! io.add_sync_method("say_hello", |_: Params| { -//! Ok(Value::String("hello".to_string())) -//! }); +//! let mut io = IoHandler::new(); +//! io.add_sync_method("say_hello", |_: Params| { +//! Ok(Value::String("hello".to_string())) +//! }); //! -//! let _server = ServerBuilder::new(io) -//! .start_http(&"127.0.0.1:3030".parse().unwrap()) -//! .expect("Unable to start RPC server"); +//! let _server = ServerBuilder::new(io) +//! .start_http(&"127.0.0.1:3030".parse().unwrap()) +//! .expect("Unable to start RPC server"); //! -//! _server.wait(); +//! _server.wait(); //! } //! ``` #![deny(missing_docs)] use jsonrpc_server_utils as server_utils; -use net2; pub use hyper; pub use jsonrpc_core; @@ -678,10 +677,11 @@ impl CloseHandle { } } +type Executors = Arc)>>>>; /// jsonrpc http server instance pub struct Server { address: SocketAddr, - executors: Arc)>>>>, + executors: Executors, done: Option>>, } diff --git a/http/src/utils.rs b/http/src/utils.rs index 22775e24f..6140b508b 100644 --- a/http/src/utils.rs +++ b/http/src/utils.rs @@ -59,12 +59,7 @@ pub fn cors_allow_headers( /// `false` indicates `Connection: close`. pub fn keep_alive(request: &hyper::Request, keep_alive: bool) -> bool { read_header(request, "connection") - .map(|val| match (keep_alive, val) { - // indicate that connection should be closed - (false, _) | (_, "close") => false, - // don't include any headers otherwise - _ => true, - }) + .map(|val| !matches!((keep_alive, val), (false, _) | (_, "close"))) // if the client header is not present, close connection if we don't keep_alive .unwrap_or(keep_alive) } diff --git a/ipc/src/server.rs b/ipc/src/server.rs index 1b8fd0969..1f1411a47 100644 --- a/ipc/src/server.rs +++ b/ipc/src/server.rs @@ -210,7 +210,7 @@ where }) .try_buffer_unordered(client_buffer_size) // Filter out previously ignored service errors as `None`s - .try_filter_map(|x| futures::future::ok(x)) + .try_filter_map(futures::future::ok) // we use `select_with_weak` here, instead of `select`, to close the stream // as soon as the ipc pipe is closed .select_with_weak(receiver.map(Ok)); diff --git a/pubsub/src/oneshot.rs b/pubsub/src/oneshot.rs index 2ab4208d3..6e6f3cdf7 100644 --- a/pubsub/src/oneshot.rs +++ b/pubsub/src/oneshot.rs @@ -52,7 +52,7 @@ impl Sender { pub fn send_and_wait(self, t: T) -> impl Future> { let Self { sender, receipt } = self; - if let Err(_) = sender.send(t) { + if sender.send(t).is_err() { return future::Either::Left(future::ready(Err(()))); } diff --git a/pubsub/src/typed.rs b/pubsub/src/typed.rs index e64b6f81e..4830d5342 100644 --- a/pubsub/src/typed.rs +++ b/pubsub/src/typed.rs @@ -5,7 +5,6 @@ use std::pin::Pin; use crate::subscription; use crate::types::{SinkResult, SubscriptionId, TransportError}; -use serde; use crate::core::futures::task::{Context, Poll}; use crate::core::futures::{self, channel}; diff --git a/server-utils/src/cors.rs b/server-utils/src/cors.rs index 3f1a5ef82..78eab604c 100644 --- a/server-utils/src/cors.rs +++ b/server-utils/src/cors.rs @@ -1,11 +1,9 @@ //! CORS handling utility functions -use unicase; - -pub use self::unicase::Ascii; use crate::hosts::{Host, Port}; use crate::matcher::{Matcher, Pattern}; use std::collections::HashSet; use std::{fmt, ops}; +pub use unicase::Ascii; /// Origin Protocol #[derive(Clone, Hash, Debug, PartialEq, Eq)] diff --git a/server-utils/src/stream_codec.rs b/server-utils/src/stream_codec.rs index 4edef5add..92033891e 100644 --- a/server-utils/src/stream_codec.rs +++ b/server-utils/src/stream_codec.rs @@ -41,10 +41,7 @@ impl StreamCodec { } fn is_whitespace(byte: u8) -> bool { - match byte { - 0x0D | 0x0A | 0x20 | 0x09 => true, - _ => false, - } + matches!(byte, 0x0D | 0x0A | 0x20 | 0x09) } impl tokio_util::codec::Decoder for StreamCodec { diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index 79918c44a..6183aceb2 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -7,13 +7,13 @@ //! //! #[tokio::main] //! async fn main() { -//! let mut io = IoHandler::default(); -//! io.add_sync_method("say_hello", |_params| { -//! Ok(Value::String("hello".to_owned())) -//! }); +//! let mut io = IoHandler::default(); +//! io.add_sync_method("say_hello", |_params| { +//! Ok(Value::String("hello".to_owned())) +//! }); //! -//! let server = ServerBuilder::new(io).build(); -//! server.await; +//! let server = ServerBuilder::new(io).build(); +//! server.await; //! } //! ``` diff --git a/tcp/src/lib.rs b/tcp/src/lib.rs index b78e4be54..69c7c4f6d 100644 --- a/tcp/src/lib.rs +++ b/tcp/src/lib.rs @@ -5,15 +5,15 @@ //! use jsonrpc_tcp_server::ServerBuilder; //! //! fn main() { -//! let mut io = IoHandler::default(); -//! io.add_sync_method("say_hello", |_params| { -//! Ok(Value::String("hello".to_string())) -//! }); -//! let server = ServerBuilder::new(io) -//! .start(&"0.0.0.0:0".parse().unwrap()) -//! .expect("Server must start with no issues."); +//! let mut io = IoHandler::default(); +//! io.add_sync_method("say_hello", |_params| { +//! Ok(Value::String("hello".to_string())) +//! }); +//! let server = ServerBuilder::new(io) +//! .start(&"0.0.0.0:0".parse().unwrap()) +//! .expect("Server must start with no issues."); //! -//! server.wait(); +//! server.wait(); //! } //! ``` diff --git a/tcp/src/server.rs b/tcp/src/server.rs index 9e6aad222..40df9ec5f 100644 --- a/tcp/src/server.rs +++ b/tcp/src/server.rs @@ -143,7 +143,7 @@ where let mut peer_message_queue = { let mut channels = channels.lock(); - channels.insert(peer_addr, sender.clone()); + channels.insert(peer_addr, sender); PeerMessageQueue::new(responses, receiver, peer_addr) }; @@ -166,7 +166,7 @@ where match start.await { Ok(server) => { tx.send(Ok(())).expect("Rx is blocking parent thread."); - let server = server.buffer_unordered(1024).for_each(|_| async { () }); + let server = server.buffer_unordered(1024).for_each(|_| async {}); future::select(Box::pin(server), stop_rx).await; } diff --git a/tcp/src/service.rs b/tcp/src/service.rs index 558b2feba..a085fe9aa 100644 --- a/tcp/src/service.rs +++ b/tcp/src/service.rs @@ -16,8 +16,8 @@ pub struct Service = middleware::Noop> { impl> Service { pub fn new(peer_addr: SocketAddr, handler: Arc>, meta: M) -> Self { Service { - peer_addr, handler, + peer_addr, meta, } } diff --git a/test/src/lib.rs b/test/src/lib.rs index 5cc963ab9..3143cc2c5 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -8,16 +8,16 @@ //! //! #[rpc] //! pub trait Test { -//! #[rpc(name = "rpc_some_method")] -//! fn some_method(&self, a: u64) -> Result; +//! #[rpc(name = "rpc_some_method")] +//! fn some_method(&self, a: u64) -> Result; //! } //! //! //! struct Dummy; //! impl Test for Dummy { -//! fn some_method(&self, x: u64) -> Result { -//! Ok(x * 2) -//! } +//! fn some_method(&self, x: u64) -> Result { +//! Ok(x * 2) +//! } //! } //! //! fn main() { @@ -31,7 +31,7 @@ //! let rpc = { //! let mut io = IoHandler::new(); //! io.add_sync_method("rpc_test_method", |_| { -//! Err(Error::internal_error()) +//! Err(Error::internal_error()) //! }); //! test::Rpc::from(io) //! }; @@ -46,8 +46,6 @@ #![deny(missing_docs)] extern crate jsonrpc_core as rpc; -use serde; -use serde_json; /// Test RPC options. #[derive(Default, Debug)] From ab301704adf9cc10db1e67073817bcc0d629a0ef Mon Sep 17 00:00:00 2001 From: Koute Date: Tue, 20 Jul 2021 23:42:51 +0900 Subject: [PATCH 147/149] Update to `parity-ws` 0.11 (#630) --- ws/Cargo.toml | 2 +- ws/src/server.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ws/Cargo.toml b/ws/Cargo.toml index a03be3f8c..d5842480a 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -16,7 +16,7 @@ jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" slab = "0.4" -parity-ws = "0.10" +parity-ws = "0.11" [badges] travis-ci = { repository = "paritytech/jsonrpc", branch = "master"} diff --git a/ws/src/server.rs b/ws/src/server.rs index 55717dc7e..e5d795f2b 100644 --- a/ws/src/server.rs +++ b/ws/src/server.rs @@ -70,8 +70,8 @@ impl Server { config.max_connections = max_connections; // don't accept super large requests config.max_fragment_size = max_payload_bytes; - config.max_in_buffer_capacity = max_in_buffer_capacity; - config.max_out_buffer_capacity = max_out_buffer_capacity; + config.in_buffer_capacity_hard_limit = max_in_buffer_capacity; + config.out_buffer_capacity_hard_limit = max_out_buffer_capacity; // don't grow non-final fragments (to prevent DOS) config.fragments_grow = false; config.fragments_capacity = cmp::max(1, max_payload_bytes / config.fragment_size); From dc6b8ede1e86feb32a2bbe4770628ee3788b3f4d Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 20 Jul 2021 14:43:23 +0000 Subject: [PATCH 148/149] Bump versions to v18 (#629) --- core-client/Cargo.toml | 4 ++-- core-client/transports/Cargo.toml | 12 ++++++------ core/Cargo.toml | 2 +- derive/Cargo.toml | 10 +++++----- http/Cargo.toml | 6 +++--- ipc/Cargo.toml | 6 +++--- pubsub/Cargo.toml | 6 +++--- pubsub/more-examples/Cargo.toml | 10 +++++----- server-utils/Cargo.toml | 4 ++-- stdio/Cargo.toml | 4 ++-- tcp/Cargo.toml | 6 +++--- test/Cargo.toml | 10 +++++----- ws/Cargo.toml | 6 +++--- 13 files changed, 43 insertions(+), 43 deletions(-) diff --git a/core-client/Cargo.toml b/core-client/Cargo.toml index d8414b041..e4340540a 100644 --- a/core-client/Cargo.toml +++ b/core-client/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core-client" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" categories = [ "asynchronous", @@ -26,7 +26,7 @@ ipc = ["jsonrpc-client-transports/ipc"] arbitrary_precision = ["jsonrpc-client-transports/arbitrary_precision"] [dependencies] -jsonrpc-client-transports = { version = "17.1", path = "./transports", default-features = false } +jsonrpc-client-transports = { version = "18.0.0", path = "./transports", default-features = false } futures = { version = "0.3", features = [ "compat" ] } [badges] diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index f5617df2c..c397d1314 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-client-transports" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" categories = [ "asynchronous", @@ -37,8 +37,8 @@ arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary [dependencies] derive_more = "0.99" futures = "0.3" -jsonrpc-core = { version = "17.1", path = "../../core" } -jsonrpc-pubsub = { version = "17.1", path = "../../pubsub" } +jsonrpc-core = { version = "18.0.0", path = "../../core" } +jsonrpc-pubsub = { version = "18.0.0", path = "../../pubsub" } log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -46,15 +46,15 @@ url = "1.7" hyper = { version = "0.14", features = ["client", "http1"], optional = true } hyper-tls = { version = "0.5", optional = true } -jsonrpc-server-utils = { version = "17.1", path = "../../server-utils", optional = true } +jsonrpc-server-utils = { version = "18.0.0", path = "../../server-utils", optional = true } parity-tokio-ipc = { version = "0.9", optional = true } tokio = { version = "1", optional = true } websocket = { version = "0.24", optional = true } [dev-dependencies] assert_matches = "1.1" -jsonrpc-http-server = { version = "17.1", path = "../../http" } -jsonrpc-ipc-server = { version = "17.1", path = "../../ipc" } +jsonrpc-http-server = { version = "18.0.0", path = "../../http" } +jsonrpc-ipc-server = { version = "18.0.0", path = "../../ipc" } lazy_static = "1.0" env_logger = "0.7" diff --git a/core/Cargo.toml b/core/Cargo.toml index 39e2de93d..b1ba659e9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-core" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" categories = [ "asynchronous", diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 9fd65141e..41ae32c10 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-derive" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [lib] proc-macro = true @@ -20,10 +20,10 @@ proc-macro-crate = "0.1.4" [dev-dependencies] assert_matches = "1.3" -jsonrpc-core = { version = "17.1", path = "../core" } -jsonrpc-core-client = { version = "17.1", path = "../core-client" } -jsonrpc-pubsub = { version = "17.1", path = "../pubsub" } -jsonrpc-tcp-server = { version = "17.1", path = "../tcp" } +jsonrpc-core = { version = "18.0.0", path = "../core" } +jsonrpc-core-client = { version = "18.0.0", path = "../core-client" } +jsonrpc-pubsub = { version = "18.0.0", path = "../pubsub" } +jsonrpc-tcp-server = { version = "18.0.0", path = "../tcp" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" trybuild = "1.0" diff --git a/http/Cargo.toml b/http/Cargo.toml index 8208268f2..276c5f78d 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "server"] license = "MIT" name = "jsonrpc-http-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] futures = "0.3" hyper = { version = "0.14", features = ["http1", "tcp", "server", "stream"] } -jsonrpc-core = { version = "17.1", path = "../core" } -jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } +jsonrpc-core = { version = "18.0.0", path = "../core" } +jsonrpc-server-utils = { version = "18.0.0", path = "../server-utils" } log = "0.4" net2 = "0.2" parking_lot = "0.11.0" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 6f3d9adaf..1bb5d66ba 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -7,14 +7,14 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ipc-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] futures = "0.3" log = "0.4" tower-service = "0.3" -jsonrpc-core = { version = "17.1", path = "../core" } -jsonrpc-server-utils = { version = "17.1", path = "../server-utils", default-features = false } +jsonrpc-core = { version = "18.0.0", path = "../core" } +jsonrpc-server-utils = { version = "18.0.0", path = "../server-utils", default-features = false } parity-tokio-ipc = "0.9" parking_lot = "0.11.0" diff --git a/pubsub/Cargo.toml b/pubsub/Cargo.toml index 7ecd921c6..89c462655 100644 --- a/pubsub/Cargo.toml +++ b/pubsub/Cargo.toml @@ -8,11 +8,11 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "macros"] license = "MIT" name = "jsonrpc-pubsub" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] futures = { version = "0.3", features = ["thread-pool"] } -jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-core = { version = "18.0.0", path = "../core" } lazy_static = "1.4" log = "0.4" parking_lot = "0.11.0" @@ -20,7 +20,7 @@ rand = "0.7" serde = "1.0" [dev-dependencies] -jsonrpc-tcp-server = { version = "17.1", path = "../tcp" } +jsonrpc-tcp-server = { version = "18.0.0", path = "../tcp" } serde_json = "1.0" [badges] diff --git a/pubsub/more-examples/Cargo.toml b/pubsub/more-examples/Cargo.toml index 8a222242e..f999583ae 100644 --- a/pubsub/more-examples/Cargo.toml +++ b/pubsub/more-examples/Cargo.toml @@ -3,12 +3,12 @@ name = "jsonrpc-pubsub-examples" description = "Examples of Publish-Subscribe extension for jsonrpc." homepage = "https://github.com/paritytech/jsonrpc" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" authors = ["tomusdrw "] license = "MIT" [dependencies] -jsonrpc-core = { version = "17.1", path = "../../core" } -jsonrpc-pubsub = { version = "17.1", path = "../" } -jsonrpc-ws-server = { version = "17.1", path = "../../ws" } -jsonrpc-ipc-server = { version = "17.1", path = "../../ipc" } +jsonrpc-core = { version = "18.0.0", path = "../../core" } +jsonrpc-pubsub = { version = "18.0.0", path = "../" } +jsonrpc-ws-server = { version = "18.0.0", path = "../../ws" } +jsonrpc-ipc-server = { version = "18.0.0", path = "../../ipc" } diff --git a/server-utils/Cargo.toml b/server-utils/Cargo.toml index 5a6e27f18..c03b74f06 100644 --- a/server-utils/Cargo.toml +++ b/server-utils/Cargo.toml @@ -8,13 +8,13 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"] license = "MIT" name = "jsonrpc-server-utils" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] bytes = "1.0" futures = "0.3" globset = "0.4" -jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-core = { version = "18.0.0", path = "../core" } lazy_static = "1.1.0" log = "0.4" tokio = { version = "1", features = ["rt-multi-thread", "io-util", "time", "net"] } diff --git a/stdio/Cargo.toml b/stdio/Cargo.toml index 2e5c91ba9..fe39581ea 100644 --- a/stdio/Cargo.toml +++ b/stdio/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-stdio-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] futures = "0.3" -jsonrpc-core = { version = "17.1", path = "../core" } +jsonrpc-core = { version = "18.0.0", path = "../core" } log = "0.4" tokio = { version = "1", features = ["io-std", "io-util"] } tokio-util = { version = "0.6", features = ["codec"] } diff --git a/tcp/Cargo.toml b/tcp/Cargo.toml index a25777300..3b5eefe33 100644 --- a/tcp/Cargo.toml +++ b/tcp/Cargo.toml @@ -7,11 +7,11 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-tcp-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] -jsonrpc-core = { version = "17.1", path = "../core" } -jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } +jsonrpc-core = { version = "18.0.0", path = "../core" } +jsonrpc-server-utils = { version = "18.0.0", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" tower-service = "0.3" diff --git a/test/Cargo.toml b/test/Cargo.toml index bfe2c061f..e3f150450 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonrpc-test" description = "Simple test framework for JSON-RPC." -version = "17.1.0" +version = "18.0.0" authors = ["Tomasz Drwięga "] license = "MIT" homepage = "https://github.com/paritytech/jsonrpc" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/jsonrpc-test/" edition = "2018" [dependencies] -jsonrpc-core = { version = "17.1", path = "../core" } -jsonrpc-core-client = { version = "17.1", path = "../core-client" } -jsonrpc-pubsub = { version = "17.1", path = "../pubsub" } +jsonrpc-core = { version = "18.0.0", path = "../core" } +jsonrpc-core-client = { version = "18.0.0", path = "../core-client" } +jsonrpc-pubsub = { version = "18.0.0", path = "../pubsub" } log = "0.4" serde = "1.0" serde_json = "1.0" @@ -21,5 +21,5 @@ serde_json = "1.0" arbitrary_precision = ["jsonrpc-core-client/arbitrary_precision", "serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"] [dev-dependencies] -jsonrpc-derive = { version = "17.1", path = "../derive" } +jsonrpc-derive = { version = "18.0.0", path = "../derive" } diff --git a/ws/Cargo.toml b/ws/Cargo.toml index d5842480a..6c9008fd3 100644 --- a/ws/Cargo.toml +++ b/ws/Cargo.toml @@ -7,12 +7,12 @@ homepage = "https://github.com/paritytech/jsonrpc" license = "MIT" name = "jsonrpc-ws-server" repository = "https://github.com/paritytech/jsonrpc" -version = "17.1.0" +version = "18.0.0" [dependencies] futures = "0.3" -jsonrpc-core = { version = "17.1", path = "../core" } -jsonrpc-server-utils = { version = "17.1", path = "../server-utils" } +jsonrpc-core = { version = "18.0.0", path = "../core" } +jsonrpc-server-utils = { version = "18.0.0", path = "../server-utils" } log = "0.4" parking_lot = "0.11.0" slab = "0.4" From 4e119017031b78a9c9ff6d39de0fdf37bfae04e9 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 21 Oct 2021 09:53:23 +0200 Subject: [PATCH 149/149] wip: http transport: Optionally allow gzip responses --- core-client/transports/Cargo.toml | 1 + core-client/transports/src/transports/http.rs | 32 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/core-client/transports/Cargo.toml b/core-client/transports/Cargo.toml index c397d1314..3ca892697 100644 --- a/core-client/transports/Cargo.toml +++ b/core-client/transports/Cargo.toml @@ -50,6 +50,7 @@ jsonrpc-server-utils = { version = "18.0.0", path = "../../server-utils", option parity-tokio-ipc = { version = "0.9", optional = true } tokio = { version = "1", optional = true } websocket = { version = "0.24", optional = true } +flate2 = "0.2" [dev-dependencies] assert_matches = "1.1" diff --git a/core-client/transports/src/transports/http.rs b/core-client/transports/src/transports/http.rs index 248b41890..99def1f39 100644 --- a/core-client/transports/src/transports/http.rs +++ b/core-client/transports/src/transports/http.rs @@ -6,21 +6,31 @@ use super::RequestBuilder; use crate::{RpcChannel, RpcError, RpcMessage, RpcResult}; use futures::{future, Future, FutureExt, StreamExt, TryFutureExt}; use hyper::{http, Client, Request, Uri}; +use flate2::read::GzDecoder; +use std::io::Read; /// Create a HTTP Client -pub async fn connect(url: &str) -> RpcResult +pub async fn connect_with_options(url: &str, allow_gzip: bool) -> RpcResult where TClient: From, { let url: Uri = url.parse().map_err(|e| RpcError::Other(Box::new(e)))?; - let (client_api, client_worker) = do_connect(url).await; + let (client_api, client_worker) = do_connect(url, allow_gzip).await; tokio::spawn(client_worker); Ok(TClient::from(client_api)) } -async fn do_connect(url: Uri) -> (RpcChannel, impl Future) { +/// Create a HTTP Client +pub async fn connect(url: &str) -> RpcResult +where + TClient: From, +{ + connect_with_options(url, false).await +} + +async fn do_connect(url: Uri, allow_gzip: bool) -> (RpcChannel, impl Future) { let max_parallel = 8; #[cfg(feature = "tls")] @@ -59,6 +69,10 @@ async fn do_connect(url: Uri) -> (RpcChannel, impl Future) { http::header::ACCEPT, http::header::HeaderValue::from_static("application/json"), ) + .header( + http::header::ACCEPT_ENCODING, + http::header::HeaderValue::from_static(if allow_gzip { "gzip" } else { "identity" }), + ) .body(request.into()) .expect("Uri and request headers are valid; qed"); @@ -78,9 +92,21 @@ async fn do_connect(url: Uri) -> (RpcChannel, impl Future) { } Err(err) => Err(RpcError::Other(Box::new(err))), Ok(res) => { + let is_gzip_response = res.headers().get(http::header::CONTENT_ENCODING).unwrap_or(&http::header::HeaderValue::from_static("identity")) == "gzip"; hyper::body::to_bytes(res.into_body()) .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e))) .await + .and_then(|bytes| { + if is_gzip_response { + let mut decoder = GzDecoder::new(bytes.as_ref()) + .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?; + let mut buf = String::new(); + let _ = decoder.read_to_string(&mut buf); + Ok(buf.into()) + } else { + Ok(bytes) + } + }) } };