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); }