Skip to content

Commit 559e8e3

Browse files
committed
Add async support to jsonrpc
Update the lockfiles because of the new dev dependency on futures.
1 parent 7302ebd commit 559e8e3

7 files changed

Lines changed: 276 additions & 30 deletions

File tree

Cargo-minimal.lock

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,94 @@ dependencies = [
361361
"percent-encoding",
362362
]
363363

364+
[[package]]
365+
name = "futures"
366+
version = "0.3.32"
367+
source = "registry+https://github.com/rust-lang/crates.io-index"
368+
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
369+
dependencies = [
370+
"futures-channel",
371+
"futures-core",
372+
"futures-executor",
373+
"futures-io",
374+
"futures-sink",
375+
"futures-task",
376+
"futures-util",
377+
]
378+
379+
[[package]]
380+
name = "futures-channel"
381+
version = "0.3.32"
382+
source = "registry+https://github.com/rust-lang/crates.io-index"
383+
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
384+
dependencies = [
385+
"futures-core",
386+
"futures-sink",
387+
]
388+
389+
[[package]]
390+
name = "futures-core"
391+
version = "0.3.32"
392+
source = "registry+https://github.com/rust-lang/crates.io-index"
393+
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
394+
395+
[[package]]
396+
name = "futures-executor"
397+
version = "0.3.32"
398+
source = "registry+https://github.com/rust-lang/crates.io-index"
399+
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
400+
dependencies = [
401+
"futures-core",
402+
"futures-task",
403+
"futures-util",
404+
]
405+
406+
[[package]]
407+
name = "futures-io"
408+
version = "0.3.32"
409+
source = "registry+https://github.com/rust-lang/crates.io-index"
410+
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
411+
412+
[[package]]
413+
name = "futures-macro"
414+
version = "0.3.32"
415+
source = "registry+https://github.com/rust-lang/crates.io-index"
416+
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
417+
dependencies = [
418+
"proc-macro2",
419+
"quote",
420+
"syn",
421+
]
422+
423+
[[package]]
424+
name = "futures-sink"
425+
version = "0.3.32"
426+
source = "registry+https://github.com/rust-lang/crates.io-index"
427+
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
428+
429+
[[package]]
430+
name = "futures-task"
431+
version = "0.3.32"
432+
source = "registry+https://github.com/rust-lang/crates.io-index"
433+
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
434+
435+
[[package]]
436+
name = "futures-util"
437+
version = "0.3.32"
438+
source = "registry+https://github.com/rust-lang/crates.io-index"
439+
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
440+
dependencies = [
441+
"futures-channel",
442+
"futures-core",
443+
"futures-io",
444+
"futures-macro",
445+
"futures-sink",
446+
"futures-task",
447+
"memchr",
448+
"pin-project-lite",
449+
"slab",
450+
]
451+
364452
[[package]]
365453
name = "getrandom"
366454
version = "0.2.15"
@@ -472,6 +560,7 @@ version = "0.19.0"
472560
dependencies = [
473561
"base64 0.22.1",
474562
"bitreq",
563+
"futures",
475564
"serde",
476565
"serde_json",
477566
"socks",
@@ -959,6 +1048,12 @@ version = "1.3.0"
9591048
source = "registry+https://github.com/rust-lang/crates.io-index"
9601049
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
9611050

1051+
[[package]]
1052+
name = "slab"
1053+
version = "0.4.12"
1054+
source = "registry+https://github.com/rust-lang/crates.io-index"
1055+
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
1056+
9621057
[[package]]
9631058
name = "smallvec"
9641059
version = "1.15.1"

Cargo-recent.lock

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,94 @@ dependencies = [
361361
"percent-encoding",
362362
]
363363

364+
[[package]]
365+
name = "futures"
366+
version = "0.3.32"
367+
source = "registry+https://github.com/rust-lang/crates.io-index"
368+
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
369+
dependencies = [
370+
"futures-channel",
371+
"futures-core",
372+
"futures-executor",
373+
"futures-io",
374+
"futures-sink",
375+
"futures-task",
376+
"futures-util",
377+
]
378+
379+
[[package]]
380+
name = "futures-channel"
381+
version = "0.3.32"
382+
source = "registry+https://github.com/rust-lang/crates.io-index"
383+
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
384+
dependencies = [
385+
"futures-core",
386+
"futures-sink",
387+
]
388+
389+
[[package]]
390+
name = "futures-core"
391+
version = "0.3.32"
392+
source = "registry+https://github.com/rust-lang/crates.io-index"
393+
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
394+
395+
[[package]]
396+
name = "futures-executor"
397+
version = "0.3.32"
398+
source = "registry+https://github.com/rust-lang/crates.io-index"
399+
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
400+
dependencies = [
401+
"futures-core",
402+
"futures-task",
403+
"futures-util",
404+
]
405+
406+
[[package]]
407+
name = "futures-io"
408+
version = "0.3.32"
409+
source = "registry+https://github.com/rust-lang/crates.io-index"
410+
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
411+
412+
[[package]]
413+
name = "futures-macro"
414+
version = "0.3.32"
415+
source = "registry+https://github.com/rust-lang/crates.io-index"
416+
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
417+
dependencies = [
418+
"proc-macro2",
419+
"quote",
420+
"syn",
421+
]
422+
423+
[[package]]
424+
name = "futures-sink"
425+
version = "0.3.32"
426+
source = "registry+https://github.com/rust-lang/crates.io-index"
427+
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
428+
429+
[[package]]
430+
name = "futures-task"
431+
version = "0.3.32"
432+
source = "registry+https://github.com/rust-lang/crates.io-index"
433+
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
434+
435+
[[package]]
436+
name = "futures-util"
437+
version = "0.3.32"
438+
source = "registry+https://github.com/rust-lang/crates.io-index"
439+
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
440+
dependencies = [
441+
"futures-channel",
442+
"futures-core",
443+
"futures-io",
444+
"futures-macro",
445+
"futures-sink",
446+
"futures-task",
447+
"memchr",
448+
"pin-project-lite",
449+
"slab",
450+
]
451+
364452
[[package]]
365453
name = "getrandom"
366454
version = "0.2.15"
@@ -472,6 +560,7 @@ version = "0.19.0"
472560
dependencies = [
473561
"base64 0.22.1",
474562
"bitreq",
563+
"futures",
475564
"serde",
476565
"serde_json",
477566
"socks",
@@ -959,6 +1048,12 @@ version = "1.3.0"
9591048
source = "registry+https://github.com/rust-lang/crates.io-index"
9601049
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
9611050

1051+
[[package]]
1052+
name = "slab"
1053+
version = "0.4.12"
1054+
source = "registry+https://github.com/rust-lang/crates.io-index"
1055+
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
1056+
9621057
[[package]]
9631058
name = "smallvec"
9641059
version = "1.15.1"

jsonrpc/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ default = [ "simple_http", "simple_tcp" ]
2222
simple_http = [ "base64" ]
2323
# A transport that uses `bitreq` as the HTTP client.
2424
bitreq_http = [ "base64", "bitreq" ]
25+
# A transport that uses `bitreq` as the async HTTP client.
26+
bitreq_http_async = [ "base64", "bitreq", "bitreq/async", "client_async" ]
27+
# An async JSON-RPC client implementation.
28+
client_async = []
2529
# Basic transport over a raw TcpListener
2630
simple_tcp = []
2731
# Basic transport over a raw UnixStream
@@ -37,5 +41,8 @@ base64 = { version = "0.22.1", optional = true }
3741
bitreq = { version = "0.3.0", path = "../bitreq", features = ["json-using-serde"], optional = true }
3842
socks = { version = "0.3.4", optional = true}
3943

44+
[dev-dependencies]
45+
futures = "0.3"
46+
4047
[lints.rust]
4148
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(jsonrpc_fuzz)'] }

jsonrpc/src/client_async.rs

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
//! # Client support
3+
//! # Async client support
44
//!
55
//! Support for connecting to JSONRPC servers over HTTP, sending requests,
66
//! and parsing responses.
77
88
use std::borrow::Cow;
99
use std::collections::HashMap;
1010
use std::fmt;
11+
use std::future::Future;
1112
use std::hash::{Hash, Hasher};
13+
use std::pin::Pin;
1214
use std::sync::atomic;
1315

1416
use serde_json::value::RawValue;
@@ -17,20 +19,27 @@ use serde_json::Value;
1719
use crate::error::Error;
1820
use crate::{Request, Response};
1921

20-
/// An interface for a transport over which to use the JSONRPC protocol.
22+
const JSONRPC_VERSION: &str = "2.0";
23+
24+
/// Boxed future type used by async transports.
25+
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
26+
27+
/// An interface for an async transport over which to use the JSONRPC protocol.
2128
pub trait Transport: Send + Sync + 'static {
2229
/// Sends an RPC request over the transport.
23-
fn send_request(&self, _: Request) -> Result<Response, Error>;
30+
fn send_request<'a>(&'a self, req: Request<'a>) -> BoxFuture<'a, Result<Response, Error>>;
2431
/// Sends a batch of RPC requests over the transport.
25-
fn send_batch(&self, _: &[Request]) -> Result<Vec<Response>, Error>;
32+
fn send_batch<'a>(
33+
&'a self,
34+
reqs: &'a [Request<'a>],
35+
) -> BoxFuture<'a, Result<Vec<Response>, Error>>;
2636
/// Formats the target of this transport. I.e. the URL/socket/...
2737
fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result;
2838
}
2939

30-
/// A JSON-RPC client.
40+
/// An async JSON-RPC client.
3141
///
32-
/// Creates a new Client using one of the transport-specific constructors e.g.,
33-
/// [`Client::simple_http`] for a bare-minimum HTTP transport.
42+
/// Creates a new Client using one of the transport-specific constructors.
3443
pub struct Client {
3544
pub(crate) transport: Box<dyn Transport>,
3645
nonce: atomic::AtomicUsize,
@@ -48,11 +57,16 @@ impl Client {
4857
/// [`crate::arg`] or [`crate::try_arg`].
4958
pub fn build_request<'a>(&self, method: &'a str, params: Option<&'a RawValue>) -> Request<'a> {
5059
let nonce = self.nonce.fetch_add(1, atomic::Ordering::Relaxed);
51-
Request { method, params, id: serde_json::Value::from(nonce), jsonrpc: Some("2.0") }
60+
Request {
61+
method,
62+
params,
63+
id: serde_json::Value::from(nonce),
64+
jsonrpc: Some(JSONRPC_VERSION),
65+
}
5266
}
5367

5468
/// Sends a request to a client.
55-
pub fn send_request(&self, request: Request) -> Result<Response, Error> {
69+
pub fn send_request<'a>(&'a self, request: Request<'a>) -> BoxFuture<'a, Result<Response, Error>> {
5670
self.transport.send_request(request)
5771
}
5872

@@ -65,14 +79,17 @@ impl Client {
6579
///
6680
/// The return vector holds the response for the request at the corresponding index. If no
6781
/// response was provided, it's [`None`].
68-
pub fn send_batch(&self, requests: &[Request]) -> Result<Vec<Option<Response>>, Error> {
82+
pub async fn send_batch(
83+
&self,
84+
requests: &[Request<'_>],
85+
) -> Result<Vec<Option<Response>>, Error> {
6986
if requests.is_empty() {
7087
return Err(Error::EmptyBatch);
7188
}
7289

7390
// If the request body is invalid JSON, the response is a single response object.
7491
// We ignore this case since we are confident we are producing valid JSON.
75-
let responses = self.transport.send_batch(requests)?;
92+
let responses = self.transport.send_batch(requests).await?;
7693
if responses.len() > requests.len() {
7794
return Err(Error::WrongBatchResponseSize);
7895
}
@@ -104,16 +121,16 @@ impl Client {
104121
///
105122
/// To construct the arguments, one can use one of the shorthand methods
106123
/// [`crate::arg`] or [`crate::try_arg`].
107-
pub fn call<R: for<'a> serde::de::Deserialize<'a>>(
124+
pub async fn call<R: for<'a> serde::de::Deserialize<'a>>(
108125
&self,
109126
method: &str,
110127
args: Option<&RawValue>,
111128
) -> Result<R, Error> {
112129
let request = self.build_request(method, args);
113130
let id = request.id.clone();
114131

115-
let response = self.send_request(request)?;
116-
if response.jsonrpc.is_some() && response.jsonrpc != Some(From::from("2.0")) {
132+
let response = self.send_request(request).await?;
133+
if response.jsonrpc.is_some() && response.jsonrpc.as_deref() != Some(JSONRPC_VERSION) {
117134
return Err(Error::VersionMismatch);
118135
}
119136
if response.id != id {
@@ -124,7 +141,7 @@ impl Client {
124141
}
125142
}
126143

127-
impl fmt::Debug for crate::Client {
144+
impl fmt::Debug for Client {
128145
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129146
write!(f, "jsonrpc::Client(")?;
130147
self.transport.fmt_target(f)?;
@@ -195,12 +212,23 @@ mod tests {
195212
use std::str::FromStr;
196213
use std::sync;
197214

215+
use futures::future::{err, ok};
216+
198217
use super::*;
199218

200219
struct DummyTransport;
201220
impl Transport for DummyTransport {
202-
fn send_request(&self, _: Request) -> Result<Response, Error> { Err(Error::NonceMismatch) }
203-
fn send_batch(&self, _: &[Request]) -> Result<Vec<Response>, Error> { Ok(vec![]) }
221+
fn send_request<'a>(&'a self, _: Request<'a>) -> BoxFuture<'a, Result<Response, Error>> {
222+
Box::pin(err(Error::NonceMismatch))
223+
}
224+
225+
fn send_batch<'a>(
226+
&'a self,
227+
_: &'a [Request<'a>],
228+
) -> BoxFuture<'a, Result<Vec<Response>, Error>> {
229+
Box::pin(ok(vec![]))
230+
}
231+
204232
fn fmt_target(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
205233
}
206234

0 commit comments

Comments
 (0)