Skip to content

Commit 0ae498d

Browse files
authored
Merge pull request #5 from chainwayxyz/reqwest-transport
Use custom Reqwest transport
2 parents fb809b0 + 1c21727 commit 0ae498d

File tree

6 files changed

+91
-37
lines changed

6 files changed

+91
-37
lines changed

client/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ bitcoincore-rpc-json = { version = "0.18.0", path = "../json" }
2323

2424
log = "0.4.5"
2525
jsonrpc-async = "2.0.2"
26+
reqwest = { version = "0.12.5", default-features = false, features = ["json", "rustls-tls"] }
2627
async-trait = "0.1.42"
28+
url = "2.5.1"
2729

2830
# Used for deserialization of JSON.
2931
serde = "1"
30-
serde_json = "1"
32+
serde_json = { version = "1" }
3133

3234
[dev-dependencies]
3335
tempfile = "3.3.0"

client/src/client.rs

+22-28
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,28 @@
88
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
99
//
1010

11+
use log::Level::{Debug, Trace, Warn};
1112
use std::collections::HashMap;
1213
use std::fs::File;
1314
use std::io::{BufRead, BufReader};
1415
use std::iter::FromIterator;
1516
use std::path::PathBuf;
1617
use std::{fmt, result};
1718

19+
use crate::transport::ReqwestTransport;
1820
use crate::{bitcoin, deserialize_hex};
1921
use async_trait::async_trait;
2022
use bitcoin::hex::DisplayHex;
21-
use jsonrpc_async;
23+
use jsonrpc_async::Client as JsonRpcClient;
2224
use serde::{self, Serialize};
23-
use serde_json::{self};
25+
use url::Url;
2426

2527
use crate::bitcoin::address::{NetworkChecked, NetworkUnchecked};
2628
use crate::bitcoin::hashes::hex::FromHex;
2729
use crate::bitcoin::secp256k1::ecdsa::Signature;
2830
use crate::bitcoin::{
2931
Address, Amount, Block, OutPoint, PrivateKey, PublicKey, Script, Transaction,
3032
};
31-
use log::Level::{Debug, Trace, Warn};
3233

3334
use crate::error::*;
3435
use crate::json;
@@ -1308,7 +1309,7 @@ pub trait RpcApi: Sized {
13081309

13091310
/// Client implements a JSON-RPC client for the Bitcoin Core daemon or compatible APIs.
13101311
pub struct Client {
1311-
client: jsonrpc_async::client::Client,
1312+
client: JsonRpcClient,
13121313
}
13131314

13141315
impl fmt::Debug for Client {
@@ -1319,28 +1320,24 @@ impl fmt::Debug for Client {
13191320

13201321
impl Client {
13211322
/// Creates a client to a bitcoind JSON-RPC server.
1322-
///
1323-
/// Can only return [Err] when using cookie authentication.
13241323
pub async fn new(url: &str, auth: Auth) -> Result<Self> {
1325-
let (user, pass) = auth.get_user_pass()?;
1326-
jsonrpc_async::client::Client::simple_http(url, user, pass)
1327-
.await
1328-
.map(|client| Client {
1329-
client,
1330-
})
1331-
.map_err(|e| super::error::Error::JsonRpc(e.into()))
1332-
}
1333-
1334-
/// Create a new Client using the given [jsonrpc_async::Client].
1335-
pub fn from_jsonrpc(client: jsonrpc_async::client::Client) -> Client {
1336-
Client {
1337-
client,
1324+
let mut parsed_url = Url::parse(url)?;
1325+
1326+
if let (Some(user), pass) = auth.get_user_pass()? {
1327+
parsed_url
1328+
.set_username(&user)
1329+
.map_err(|_| Error::Auth("Failed to set username".to_string()))?;
1330+
parsed_url
1331+
.set_password(pass.as_deref())
1332+
.map_err(|_| Error::Auth("Failed to set password".to_string()))?;
13381333
}
1339-
}
13401334

1341-
/// Get the underlying JSONRPC client.
1342-
pub fn get_jsonrpc_client(&self) -> &jsonrpc_async::client::Client {
1343-
&self.client
1335+
let transport = ReqwestTransport::new(parsed_url);
1336+
let client = JsonRpcClient::with_transport(transport);
1337+
1338+
Ok(Self {
1339+
client,
1340+
})
13441341
}
13451342
}
13461343

@@ -1385,11 +1382,8 @@ fn log_response(cmd: &str, resp: &Result<jsonrpc_async::Response>) {
13851382
debug!(target: "bitcoincore_rpc", "JSON-RPC error for {}: {:?}", cmd, e);
13861383
}
13871384
} else if log_enabled!(Trace) {
1388-
// we can't use to_raw_value here due to compat with Rust 1.29
1389-
let def = serde_json::value::RawValue::from_string(
1390-
serde_json::Value::Null.to_string(),
1391-
)
1392-
.unwrap();
1385+
let def =
1386+
serde_json::value::to_raw_value(&serde_json::value::Value::Null).unwrap();
13931387
let result = resp.result.as_ref().unwrap_or(&def);
13941388
trace!(target: "bitcoincore_rpc", "JSON-RPC response for {}: {}", cmd, result);
13951389
}

client/src/error.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::{error, fmt, io};
1313
use crate::bitcoin;
1414
use crate::bitcoin::hashes::hex;
1515
use crate::bitcoin::secp256k1;
16-
use jsonrpc_async;
1716
use serde_json;
1817

1918
/// The error type for errors produced in this library.
@@ -31,6 +30,8 @@ pub enum Error {
3130
UnexpectedStructure,
3231
/// The daemon returned an error string.
3332
ReturnedError(String),
33+
Auth(String),
34+
UrlParse(url::ParseError),
3435
}
3536

3637
impl From<jsonrpc_async::error::Error> for Error {
@@ -39,6 +40,12 @@ impl From<jsonrpc_async::error::Error> for Error {
3940
}
4041
}
4142

43+
impl From<url::ParseError> for Error {
44+
fn from(e: url::ParseError) -> Error {
45+
Error::UrlParse(e)
46+
}
47+
}
48+
4249
impl From<hex::HexToBytesError> for Error {
4350
fn from(e: hex::HexToBytesError) -> Error {
4451
Error::Hex(e)
@@ -88,6 +95,8 @@ impl fmt::Display for Error {
8895
Error::InvalidCookieFile => write!(f, "invalid cookie file"),
8996
Error::UnexpectedStructure => write!(f, "the JSON result had an unexpected structure"),
9097
Error::ReturnedError(ref s) => write!(f, "the daemon returned an error string: {}", s),
98+
Error::Auth(ref s) => write!(f, "Auth error: {}", s),
99+
Error::UrlParse(ref s) => write!(f, "Url error: {}", s),
91100
}
92101
}
93102
}

client/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,21 @@ extern crate log;
2222
#[macro_use] // `macro_use` is needed for v1.24.0 compilation.
2323
extern crate serde;
2424

25-
pub extern crate jsonrpc_async;
26-
2725
pub extern crate bitcoincore_rpc_json;
2826
pub use crate::json::bitcoin;
29-
use bitcoincore_rpc_json::bitcoin::hex::FromHex;
3027
pub use bitcoincore_rpc_json as json;
28+
use bitcoincore_rpc_json::bitcoin::hex::FromHex;
3129
use json::bitcoin::consensus::{Decodable, ReadExt};
3230

3331
mod client;
3432
mod error;
3533
mod queryable;
34+
mod transport;
3635

3736
pub use crate::client::*;
3837
pub use crate::error::Error;
3938
pub use crate::queryable::*;
39+
pub use jsonrpc_async::Error as RpcError;
4040

4141
fn deserialize_hex<T: Decodable>(hex: &str) -> Result<T> {
4242
let buf = Vec::<u8>::from_hex(hex)?;

client/src/transport.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use async_trait::async_trait;
2+
use jsonrpc_async::Transport;
3+
use url::Url;
4+
5+
pub struct ReqwestTransport {
6+
client: reqwest::Client,
7+
url: Url,
8+
}
9+
10+
impl ReqwestTransport {
11+
pub fn new(url: Url) -> Self {
12+
Self {
13+
client: reqwest::Client::new(),
14+
url,
15+
}
16+
}
17+
18+
async fn request<R>(&self, req: impl serde::Serialize) -> Result<R, reqwest::Error>
19+
where
20+
R: for<'a> serde::de::Deserialize<'a>,
21+
{
22+
let response = self.client.post(self.url.clone()).json(&req).send().await?;
23+
response.json().await
24+
}
25+
}
26+
27+
#[async_trait]
28+
impl Transport for ReqwestTransport {
29+
async fn send_request(
30+
&self,
31+
r: jsonrpc_async::Request<'_>,
32+
) -> Result<jsonrpc_async::Response, jsonrpc_async::Error> {
33+
Ok(self.request(r).await.map_err(|e| jsonrpc_async::Error::Transport(e.into()))?)
34+
}
35+
36+
async fn send_batch(
37+
&self,
38+
_rs: &[jsonrpc_async::Request<'_>],
39+
) -> Result<Vec<jsonrpc_async::Response>, jsonrpc_async::Error> {
40+
unimplemented!()
41+
}
42+
43+
fn fmt_target(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
44+
if let (Some(host), Some(port)) = (self.url.host(), self.url.port()) {
45+
write!(f, "http://{}:{}{}", host, port, self.url.path())
46+
} else {
47+
write!(f, "http://{:?}", self.url)
48+
}
49+
}
50+
}

integration_test/src/main.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ use std::str::FromStr;
1919
use bitcoin::absolute::LockTime;
2020
use bitcoin::address::{NetworkChecked, NetworkUnchecked};
2121
use bitcoincore_rpc::json;
22-
use bitcoincore_rpc::jsonrpc_async::error::Error as JsonRpcError;
23-
use bitcoincore_rpc::{Auth, Client, Error, RpcApi};
22+
use bitcoincore_rpc::{Auth, Client, Error, RpcApi, RpcError as JsonRpcError};
2423

2524
use crate::json::BlockStatsFields as BsFields;
2625
use bitcoin::consensus::encode::{deserialize, serialize_hex};
@@ -839,12 +838,12 @@ async fn test_test_mempool_accept(cl: &Client) {
839838
.await
840839
.unwrap();
841840
let res = cl.test_mempool_accept(&[&tx]).await.unwrap();
842-
assert!(!res[0].allowed);
841+
assert!(!res[0].allowed.unwrap());
843842
assert!(res[0].reject_reason.is_some());
844843
let signed =
845844
cl.sign_raw_transaction_with_wallet(&tx, None, None).await.unwrap().transaction().unwrap();
846845
let res = cl.test_mempool_accept(&[&signed]).await.unwrap();
847-
assert!(res[0].allowed, "not allowed: {:?}", res[0].reject_reason);
846+
assert!(res[0].allowed.unwrap(), "not allowed: {:?}", res[0].reject_reason);
848847
}
849848

850849
async fn test_wallet_create_funded_psbt(cl: &Client) {

0 commit comments

Comments
 (0)