jsonrpc_client_core/
lib.rs

1// Copyright 2017 Amagicom AB.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9
10//! A crate for generating transport agnostic, auto serializing, strongly typed JSON-RPC 2.0
11//! clients.
12//!
13//! This crate mainly provides a macro, `jsonrpc_client`. The macro generates structs that can be
14//! used for calling JSON-RPC 2.0 APIs. The macro lets you list methods on the struct with
15//! arguments and a return type. The macro then generates a struct which will automatically
16//! serialize the arguments, send the request and deserialize the response into the target type.
17//!
18//! # Transports
19//!
20//! The `jsonrpc-client-core` crate itself and the structs generated by the `jsonrpc_client` macro
21//! are transport agnostic. They can use any type implementing the `Transport` trait.
22//!
23//! The main (and so far only) transport implementation is the Hyper based HTTP implementation
24//! in the [`jsonrpc-client-http`](../jsonrpc_client_http/index.html) crate.
25//!
26//! # Example
27//!
28//! ```rust,ignore
29//! #[macro_use]
30//! extern crate jsonrpc_client_core;
31//! extern crate jsonrpc_client_http;
32//!
33//! use jsonrpc_client_http::HttpTransport;
34//!
35//! jsonrpc_client!(pub struct FizzBuzzClient {
36//!     /// Returns the fizz-buzz string for the given number.
37//!     pub fn fizz_buzz(&mut self, number: u64) -> RpcRequest<String>;
38//! });
39//!
40//! fn main() {
41//!     let transport = HttpTransport::new().standalone().unwrap();
42//!     let transport_handle = transport
43//!         .handle("http://api.fizzbuzzexample.org/rpc/")
44//!         .unwrap();
45//!     let mut client = FizzBuzzClient::new(transport_handle);
46//!     let result1 = client.fizz_buzz(3).call().unwrap();
47//!     let result2 = client.fizz_buzz(4).call().unwrap();
48//!     let result3 = client.fizz_buzz(5).call().unwrap();
49//!
50//!     // Should print "fizz 4 buzz" if the server implemented the service correctly
51//!     println!("{} {} {}", result1, result2, result3);
52//! }
53//! ```
54//!
55
56#![deny(missing_docs)]
57
58#[macro_use]
59pub extern crate error_chain;
60#[macro_use]
61extern crate futures;
62extern crate jsonrpc_core;
63#[macro_use]
64extern crate log;
65extern crate serde;
66#[cfg_attr(test, macro_use)]
67extern crate serde_json;
68
69use futures::future::Future;
70use futures::Async;
71use jsonrpc_core::types::{Id, MethodCall, Params, Version};
72use serde_json::Value as JsonValue;
73
74/// Contains the main macro of this crate, `jsonrpc_client`.
75#[macro_use]
76mod macros;
77
78/// Module for functions parsing the response to a RPC method call.
79mod response;
80
81/// Module containing an example client. To show in the docs what a generated struct look like.
82pub mod example;
83
84error_chain! {
85    errors {
86        /// Error in the underlying transport layer.
87        TransportError {
88            description("Unable to send the JSON-RPC 2.0 request")
89        }
90        /// Error while serializing method parameters.
91        SerializeError {
92            description("Unable to serialize the method parameters")
93        }
94        /// Error while deserializing or parsing the response data.
95        ResponseError(msg: &'static str) {
96            description("Unable to deserialize the response into the desired type")
97            display("Unable to deserialize the response: {}", msg)
98        }
99        /// The request was replied to, but with a JSON-RPC 2.0 error.
100        JsonRpcError(error: jsonrpc_core::Error) {
101            description("Method call returned JSON-RPC 2.0 error")
102            display("JSON-RPC 2.0 Error: {} ({})", error.code.description(), error.message)
103        }
104    }
105}
106
107
108/// A lazy RPC call `Future`. The actual call has not been sent when an instance of this type
109/// is returned from a client generated by the macro in this crate. This is a `Future` that, when
110/// executed, performs the RPC call.
111pub struct RpcRequest<T, F>(::std::result::Result<InnerRpcRequest<T, F>, Option<Error>>);
112
113impl<T, E, F> RpcRequest<T, F>
114where
115    T: serde::de::DeserializeOwned + Send + 'static,
116    E: ::std::error::Error + Send + 'static,
117    F: Future<Item = Vec<u8>, Error = E> + Send + 'static,
118{
119    /// Consume this RPC request and run it synchronously. This blocks until the RPC call is done,
120    /// then the result of the call is returned.
121    pub fn call(self) -> Result<T> {
122        self.wait()
123    }
124}
125
126impl<T, E, F> Future for RpcRequest<T, F>
127where
128    T: serde::de::DeserializeOwned + Send + 'static,
129    E: ::std::error::Error + Send + 'static,
130    F: Future<Item = Vec<u8>, Error = E> + Send + 'static,
131{
132    type Item = T;
133    type Error = Error;
134
135    fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
136        match self.0 {
137            Ok(ref mut inner) => inner.poll(),
138            Err(ref mut error_option) => Err(error_option
139                .take()
140                .expect("Cannot call RpcRequest poll twice when in error state")),
141        }
142    }
143}
144
145struct InnerRpcRequest<T, F> {
146    transport_future: F,
147    id: Id,
148    _marker: ::std::marker::PhantomData<T>,
149}
150
151impl<T, F> InnerRpcRequest<T, F> {
152    fn new(transport_future: F, id: Id) -> Self {
153        Self {
154            transport_future,
155            id,
156            _marker: ::std::marker::PhantomData,
157        }
158    }
159}
160
161impl<T, E, F> Future for InnerRpcRequest<T, F>
162where
163    T: serde::de::DeserializeOwned + Send + 'static,
164    E: ::std::error::Error + Send + 'static,
165    F: Future<Item = Vec<u8>, Error = E> + Send + 'static,
166{
167    type Item = T;
168    type Error = Error;
169
170    fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
171        let response_raw = try_ready!(
172            self.transport_future
173                .poll()
174                .chain_err(|| ErrorKind::TransportError)
175        );
176        trace!(
177            "Deserializing {} byte response to request with id {:?}",
178            response_raw.len(),
179            self.id
180        );
181        response::parse(&response_raw, &self.id).map(|t| Async::Ready(t))
182    }
183}
184
185
186/// Trait for types acting as a transport layer for the JSON-RPC 2.0 clients generated by the
187/// `jsonrpc_client` macro.
188pub trait Transport {
189    /// The future type this transport returns on send operations.
190    type Future: Future<Item = Vec<u8>, Error = Self::Error> + Send + 'static;
191
192    /// The type of error that this transport emits if it fails.
193    type Error: ::std::error::Error + Send + 'static;
194
195    /// Returns an id that has not yet been used on this transport. Used by the RPC clients
196    /// to fill in the "id" field of a request.
197    fn get_next_id(&mut self) -> u64;
198
199    /// Sends the given data over the transport and returns a future that will complete with the
200    /// response to the request, or the transport specific error if something went wrong.
201    fn send(&self, json_data: Vec<u8>) -> Self::Future;
202}
203
204
205/// Prepares a lazy `RpcRequest` with a given transport, method and parameters.
206/// The call is not sent to the transport until the returned `RpcRequest` is actually executed,
207/// either as a `Future` or by calling `RpcRequest::call()`.
208///
209/// # Not intended for direct use
210/// This is being called from the client structs generated by the `jsonrpc_client` macro. This
211/// function is not intended to be used directly, only the generated structs should call this.
212pub fn call_method<T, P, R>(
213    transport: &mut T,
214    method: String,
215    params: P,
216) -> RpcRequest<R, T::Future>
217where
218    T: Transport,
219    P: serde::Serialize,
220    R: serde::de::DeserializeOwned + Send + 'static,
221{
222    let id = Id::Num(transport.get_next_id());
223    trace!("Serializing call to method \"{}\" with id {:?}", method, id);
224    let request_serialization_result =
225        serialize_request(id.clone(), method, params).chain_err(|| ErrorKind::SerializeError);
226    match request_serialization_result {
227        Err(e) => RpcRequest(Err(Some(e))),
228        Ok(request_raw) => {
229            let transport_future = transport.send(request_raw);
230            RpcRequest(Ok(InnerRpcRequest::new(transport_future, id)))
231        }
232    }
233}
234
235
236/// Creates a JSON-RPC 2.0 request to the given method with the given parameters.
237fn serialize_request<P>(
238    id: Id,
239    method: String,
240    params: P,
241) -> ::std::result::Result<Vec<u8>, serde_json::error::Error>
242where
243    P: serde::Serialize,
244{
245    let serialized_params = match serde_json::to_value(params)? {
246        JsonValue::Null => None,
247        JsonValue::Array(vec) => Some(Params::Array(vec)),
248        JsonValue::Object(obj) => Some(Params::Map(obj)),
249        value => Some(Params::Array(vec![value])),
250    };
251    let method_call = MethodCall {
252        jsonrpc: Some(Version::V2),
253        method,
254        params: serialized_params,
255        id,
256    };
257    serde_json::to_vec(&method_call)
258}
259
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264    use std::io;
265
266    pub type BoxFuture<T, E> = Box<Future<Item = T, Error = E> + Send>;
267
268    /// A test transport that just echoes back a response containing the entire request as the
269    /// result.
270    #[derive(Clone)]
271    struct EchoTransport;
272
273    impl Transport for EchoTransport {
274        type Future = BoxFuture<Vec<u8>, io::Error>;
275        type Error = io::Error;
276
277        fn get_next_id(&mut self) -> u64 {
278            1
279        }
280
281        fn send(&self, json_data: Vec<u8>) -> Self::Future {
282            let json = json!({
283                "jsonrpc": "2.0",
284                "id": 1,
285                "result": serde_json::from_slice::<JsonValue>(&json_data).unwrap(),
286            });
287            Box::new(futures::future::ok(serde_json::to_vec(&json).unwrap()))
288        }
289    }
290
291    /// A transport that always returns an "Invalid request" error
292    #[derive(Clone)]
293    struct InvalidRequestTransport;
294
295    impl Transport for InvalidRequestTransport {
296        type Future = BoxFuture<Vec<u8>, io::Error>;
297        type Error = io::Error;
298
299        fn get_next_id(&mut self) -> u64 {
300            1
301        }
302
303        fn send(&self, _json_data: Vec<u8>) -> Self::Future {
304            let json = json!({
305                "jsonrpc": "2.0",
306                "id": 1,
307                "error": {
308                    "code": -32600,
309                    "message": "This was an invalid request",
310                    "data": [1, 2, 3],
311                }
312            });
313            Box::new(futures::future::ok(serde_json::to_vec(&json).unwrap()))
314        }
315    }
316
317    /// A transport that always returns a future that fails
318    #[derive(Clone)]
319    struct ErrorTransport;
320
321    impl Transport for ErrorTransport {
322        type Future = BoxFuture<Vec<u8>, io::Error>;
323        type Error = io::Error;
324
325        fn get_next_id(&mut self) -> u64 {
326            1
327        }
328
329        fn send(&self, _json_data: Vec<u8>) -> Self::Future {
330            Box::new(futures::future::err(io::Error::new(
331                io::ErrorKind::Other,
332                "Internal transport error",
333            )))
334        }
335    }
336
337    jsonrpc_client!(pub struct TestRpcClient {
338        pub fn ping(&mut self, arg0: &str) -> RpcRequest<JsonValue>;
339    });
340
341    #[test]
342    fn echo() {
343        let mut client = TestRpcClient::new(EchoTransport);
344        let result = client.ping("Hello").call().unwrap();
345        if let JsonValue::Object(map) = result {
346            assert_eq!(Some(&JsonValue::from("2.0")), map.get("jsonrpc"));
347            assert_eq!(Some(&JsonValue::from(1)), map.get("id"));
348            assert_eq!(Some(&JsonValue::from("ping")), map.get("method"));
349            assert_eq!(Some(&JsonValue::from(vec!["Hello"])), map.get("params"));
350            assert_eq!(4, map.len());
351        } else {
352            panic!("Invalid response type: {:?}", result);
353        }
354    }
355
356    #[test]
357    fn invalid_request() {
358        let mut client = TestRpcClient::new(InvalidRequestTransport);
359        let error = client.ping("").call().unwrap_err();
360        if let &ErrorKind::JsonRpcError(ref json_error) = error.kind() {
361            use jsonrpc_core::ErrorCode;
362            assert_eq!(ErrorCode::InvalidRequest, json_error.code);
363            assert_eq!("This was an invalid request", json_error.message);
364            assert_eq!(Some(json!{[1, 2, 3]}), json_error.data);
365        } else {
366            panic!("Wrong error kind");
367        }
368    }
369
370    #[test]
371    fn transport_error() {
372        let mut client = TestRpcClient::new(ErrorTransport);
373        match client.ping("").call().unwrap_err().kind() {
374            &ErrorKind::TransportError => (),
375            _ => panic!("Wrong error kind"),
376        }
377    }
378}