Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ before_script:
script:
- cargo build --all
- cargo test --all
- cargo test --manifest-path=core-client/Cargo.toml --features "http"
- cargo test --manifest-path=core-client/Cargo.toml --features "http, tls"
- cargo test --manifest-path=core-client/Cargo.toml --features "ws"
- |
([ $TRAVIS_RUST_VERSION = stable ] && cargo fmt --all -- --check) || true

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"core",
"core-client",
"core-client/transports",
"http",
"ipc",
"derive",
Expand Down
11 changes: 10 additions & 1 deletion _automate/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

set -exu

ORDER=(core core-client server-utils tcp ws ws/client http ipc stdio pubsub derive test)
VERSION=$(grep "^version" ./core/Cargo.toml | sed -e 's/.*"\(.*\)"/\1/')
ORDER=(core core-client/transports core-client server-utils tcp ws ws/client http ipc stdio pubsub derive test)

echo "Publishing version $VERSION"
cargo clean

for crate in ${ORDER[@]}; do
cd $crate
echo "Publishing $crate@$VERSION"
sleep 5
cargo publish $@
cd -
done

echo "Tagging version $VERSION"
git tag -a v$VERSION -m "Version $VERSION"
git push --tags
29 changes: 5 additions & 24 deletions core-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"]
license = "MIT"
name = "jsonrpc-core-client"
repository = "https://github.com/paritytech/jsonrpc"
version = "11.0.0"
version = "12.0.0"

categories = [
"asynchronous",
Expand All @@ -19,31 +19,12 @@ categories = [
]

[features]
tls = ["hyper-tls"]
http = ["hyper"]
ws = [
"websocket",
"tokio",
]
tls = ["jsonrpc-client-transports/tls"]
http = ["jsonrpc-client-transports/http"]
ws = ["jsonrpc-client-transports/ws"]

[dependencies]
failure = "0.1"
futures = "0.1.26"
hyper = { version = "0.12", optional = true }
hyper-tls = { version = "0.3.2", optional = true }
jsonrpc-core = { version = "11.0", path = "../core" }
log = "0.4"
serde = "1.0"
serde_json = "1.0"
tokio = { version = "0.1", optional = true }
websocket = { version = "0.22", optional = true }

[dev-dependencies]
assert_matches = "1.1"
jsonrpc-http-server = { version = "11.0", path = "../http" }
lazy_static = "1.0"
env_logger = "0.6"
tokio = "0.1"
jsonrpc-client-transports = { version = "12.0", path = "./transports", default-features = false }

[badges]
travis-ci = { repository = "paritytech/jsonrpc", branch = "master"}
225 changes: 8 additions & 217 deletions core-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,217 +1,8 @@
//! JSON-RPC client implementation.
#![deny(missing_docs)]

use failure::{format_err, Fail};
use futures::sync::{mpsc, oneshot};
use futures::{future, prelude::*};
use jsonrpc_core::{Error, Params};
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::Value;

pub mod transports;

#[cfg(test)]
mod logger;

/// The errors returned by the client.
#[derive(Debug, Fail)]
pub enum RpcError {
/// An error returned by the server.
#[fail(display = "Server returned rpc error {}", _0)]
JsonRpcError(Error),
/// Failure to parse server response.
#[fail(display = "Failed to parse server response as {}: {}", _0, _1)]
ParseError(String, failure::Error),
/// Request timed out.
#[fail(display = "Request timed out")]
Timeout,
/// The server returned a response with an unknown id.
#[fail(display = "Server returned a response with an unknown id")]
UnknownId,
/// Not rpc specific errors.
#[fail(display = "{}", _0)]
Other(failure::Error),
}

impl From<Error> for RpcError {
fn from(error: Error) -> Self {
RpcError::JsonRpcError(error)
}
}

/// A message sent to the `RpcClient`. This is public so that
/// the derive crate can generate a client.
struct RpcMessage {
/// The rpc method name.
method: String,
/// The rpc method parameters.
params: Params,
/// The oneshot channel to send the result of the rpc
/// call to.
sender: oneshot::Sender<Result<Value, RpcError>>,
}

/// A channel to a `RpcClient`.
#[derive(Clone)]
pub struct RpcChannel(mpsc::Sender<RpcMessage>);

impl RpcChannel {
fn send(
&self,
msg: RpcMessage,
) -> impl Future<Item = mpsc::Sender<RpcMessage>, Error = mpsc::SendError<RpcMessage>> {
self.0.to_owned().send(msg)
}
}

impl From<mpsc::Sender<RpcMessage>> for RpcChannel {
fn from(sender: mpsc::Sender<RpcMessage>) -> Self {
RpcChannel(sender)
}
}

/// The future returned by the rpc call.
pub struct RpcFuture {
recv: oneshot::Receiver<Result<Value, RpcError>>,
}

impl RpcFuture {
/// Creates a new `RpcFuture`.
pub fn new(recv: oneshot::Receiver<Result<Value, RpcError>>) -> Self {
RpcFuture { recv }
}
}

impl Future for RpcFuture {
type Item = Value;
type Error = RpcError;

fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
// TODO should timeout (#410)
match self.recv.poll() {
Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)),
Ok(Async::Ready(Err(error))) => Err(error),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(error) => Err(RpcError::Other(error.into())),
}
}
}

/// Client for raw JSON RPC requests
#[derive(Clone)]
pub struct RawClient(RpcChannel);

impl From<RpcChannel> for RawClient {
fn from(channel: RpcChannel) -> Self {
RawClient(channel)
}
}

impl RawClient {
/// Call RPC with raw JSON
pub fn call_method(&self, method: &str, params: Params) -> impl Future<Item = Value, Error = RpcError> {
let (sender, receiver) = oneshot::channel();
let msg = RpcMessage {
method: method.into(),
params,
sender,
};
self.0
.send(msg)
.map_err(|error| RpcError::Other(error.into()))
.and_then(|_| RpcFuture::new(receiver))
}
}

/// Client for typed JSON RPC requests
#[derive(Clone)]
pub struct TypedClient(RawClient);

impl From<RpcChannel> for TypedClient {
fn from(channel: RpcChannel) -> Self {
TypedClient(channel.into())
}
}

impl TypedClient {
/// Create new TypedClient
pub fn new(raw_cli: RawClient) -> Self {
TypedClient(raw_cli)
}

/// Call RPC with serialization of request and deserialization of response
pub fn call_method<T: Serialize, R: DeserializeOwned + 'static>(
&self,
method: &str,
returns: &'static str,
args: T,
) -> impl Future<Item = R, Error = RpcError> {
let args =
serde_json::to_value(args).expect("Only types with infallible serialisation can be used for JSON-RPC");
let params = match args {
Value::Array(vec) => Params::Array(vec),
Value::Null => Params::None,
_ => {
return future::Either::A(future::err(RpcError::Other(format_err!(
"RPC params should serialize to a JSON array, or null"
))))
}
};

future::Either::B(self.0.call_method(method, params).and_then(move |value: Value| {
log::debug!("response: {:?}", value);
let result =
serde_json::from_value::<R>(value).map_err(|error| RpcError::ParseError(returns.into(), error.into()));
future::done(result)
}))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::transports::local;
use crate::{RpcChannel, RpcError, TypedClient};
use jsonrpc_core::{self, IoHandler};

#[derive(Clone)]
struct AddClient(TypedClient);

impl From<RpcChannel> for AddClient {
fn from(channel: RpcChannel) -> Self {
AddClient(channel.into())
}
}

impl AddClient {
fn add(&self, a: u64, b: u64) -> impl Future<Item = u64, Error = RpcError> {
self.0.call_method("add", "u64", (a, b))
}
}

#[test]
fn test_client_terminates() {
let mut handler = IoHandler::new();
handler.add_method("add", |params: Params| {
let (a, b) = params.parse::<(u64, u64)>()?;
let res = a + b;
Ok(jsonrpc_core::to_value(res).unwrap())
});

let (client, rpc_client) = local::connect::<AddClient, _, _>(handler);
let fut = client
.clone()
.add(3, 4)
.and_then(move |res| client.add(res, 5))
.join(rpc_client)
.map(|(res, ())| {
assert_eq!(res, 12);
})
.map_err(|err| {
eprintln!("{:?}", err);
assert!(false);
});
tokio::run(fut);
}
}
//! JSON-RPC client implementation primitives.
//!
//! By default this crate does not implement any transports,
//! use corresponding features (`tls`, `http` or `ws`) to opt-in for them.
//!
//! See documentation of [`jsonrpc-client-transports`](../jsonrpc_client_transports/) for more details.

pub use jsonrpc_client_transports::*;
50 changes: 50 additions & 0 deletions core-client/transports/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[package]
authors = ["Parity Technologies <admin@parity.io>"]
description = "Transport agnostic JSON-RPC 2.0 client implementation."
documentation = "https://docs.rs/jsonrpc-client-transports/"
edition = "2018"
homepage = "https://github.com/paritytech/jsonrpc"
keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"]
license = "MIT"
name = "jsonrpc-client-transports"
repository = "https://github.com/paritytech/jsonrpc"
version = "12.0.0"

categories = [
"asynchronous",
"network-programming",
"web-programming::http-client",
"web-programming::http-server",
"web-programming::websocket",
]

[features]
default = ["http", "tls", "ws"]
tls = ["hyper-tls", "http"]
http = ["hyper"]
ws = [
"websocket",
"tokio",
]

[dependencies]
failure = "0.1"
futures = "0.1.26"
hyper = { version = "0.12", optional = true }
hyper-tls = { version = "0.3.2", optional = true }
jsonrpc-core = { version = "12.0", path = "../../core" }
log = "0.4"
serde = "1.0"
serde_json = "1.0"
tokio = { version = "0.1", optional = true }
websocket = { version = "0.22", optional = true }

[dev-dependencies]
assert_matches = "1.1"
jsonrpc-http-server = { version = "12.0", path = "../../http" }
lazy_static = "1.0"
env_logger = "0.6"
tokio = "0.1"

[badges]
travis-ci = { repository = "paritytech/jsonrpc", branch = "master"}
Loading