Skip to content
This repository was archived by the owner on Sep 4, 2024. It is now read-only.

Commit

Permalink
Add minreq_http module
Browse files Browse the repository at this point in the history
Add a module that implements `client::Transport` by way of the `minreq`
HTTP client crate (https://github.com/neonmoe/minreq/).

Includes fuzzing types in the `simple_minreq` module (with the old
"fuzzing" cfg), but fuzz tests will be added next commit (including
change to use jsonrpc_fuzzing).

Add integration testing copied and modified from `bitcoincore-rpc`.
  • Loading branch information
tcharding committed May 25, 2023
1 parent 51e87f7 commit 3c82e8f
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 10 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ rustdoc-args = ["--cfg", "docsrs"]
default = [ "simple_http", "simple_tcp" ]
# A bare-minimum HTTP transport.
simple_http = [ "base64" ]
# A transport that uses `minreq` as the HTTP client.
minreq_http = [ "base64", "minreq" ]
# Basic transport over a raw TcpListener
simple_tcp = []
# Basic transport over a raw UnixStream
Expand All @@ -32,7 +34,8 @@ serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = [ "raw_value" ] }

base64 = { version = "0.13.0", optional = true }
minreq = { version = "2.7.0", features = ["json-using-serde"], optional = true }
socks = { version = "0.3.4", optional = true}

[workspace]
members = ["fuzz"]
members = ["fuzz", "integration_test"]
12 changes: 12 additions & 0 deletions integration_test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "integration_test"
version = "0.1.0"
authors = ["Steven Roose <steven@stevenroose.org>, Tobin C. Harding <me@tobin.cc>"]
edition = "2018"

[dependencies]
jsonrpc = { path = "..", features = ["minreq_http"] }
lazy_static = "1.4.0"
log = "0.4"
backtrace = "0.3.50"
serde_json = { version = "1.0", features = ["raw_value"] }
52 changes: 52 additions & 0 deletions integration_test/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/sh

BITCOIND_PATH="${BITCOIND_PATH:-bitcoind}"
TESTDIR=/tmp/rust_bitcoincore_rpc_test

rm -rf ${TESTDIR}
mkdir -p ${TESTDIR}/1 ${TESTDIR}/2

# To kill any remaining open bitcoind.
killall -9 bitcoind

${BITCOIND_PATH} -regtest \
-datadir=${TESTDIR}/1 \
-port=12348 \
-server=0 \
-printtoconsole=0 &
PID1=$!

# Make sure it's listening on its p2p port.
sleep 3

BLOCKFILTERARG=""
if ${BITCOIND_PATH} -version | grep -q "v\(2\|0\.19\|0.2\)"; then
BLOCKFILTERARG="-blockfilterindex=1"
fi

FALLBACKFEEARG=""
if ${BITCOIND_PATH} -version | grep -q "v\(2\|0\.2\)"; then
FALLBACKFEEARG="-fallbackfee=0.00001000"
fi

${BITCOIND_PATH} -regtest $BLOCKFILTERARG $FALLBACKFEEARG \
-datadir=${TESTDIR}/2 \
-connect=127.0.0.1:12348 \
-rpcport=12349 \
-server=1 \
-txindex=1 \
-printtoconsole=0 &
PID2=$!

# Let it connect to the other node.
sleep 5

RPC_URL=http://localhost:12349 \
RPC_COOKIE=${TESTDIR}/2/regtest/.cookie \
cargo run

RESULT=$?

kill -9 $PID1 $PID2

exit $RESULT
149 changes: 149 additions & 0 deletions integration_test/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//! # rust-bitcoincore-rpc integration test
//!
//! The test methods are named to mention the methods tested.
//! Individual test methods don't use any methods not tested before or
//! mentioned in the test method name.
//!
//! The goal of this test is not to test the correctness of the server, but
//! to test the serialization of arguments and deserialization of responses.
//!
#![deny(unused)]
#![allow(deprecated)]

#[macro_use]
extern crate lazy_static;

use std::cell::RefCell;
use std::time::Duration;
use std::{fs, mem, panic};

use backtrace::Backtrace;

use jsonrpc::http::minreq_http;
use jsonrpc::{Client, Request};

struct StdLogger;

impl log::Log for StdLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.target().contains("jsonrpc") || metadata.target().contains("bitcoincore_rpc")
}

fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
println!("[{}][{}]: {}", record.level(), record.metadata().target(), record.args());
}
}

fn flush(&self) {}
}

static LOGGER: StdLogger = StdLogger;

fn get_rpc_url() -> String {
return std::env::var("RPC_URL").expect("RPC_URL must be set");
}

fn get_auth() -> (String, Option<String>) {
if let Ok(cookie) = std::env::var("RPC_COOKIE") {
let contents =
fs::read_to_string(&cookie).expect(&format!("failed to read cookie file: {}", cookie));
let mut split = contents.split(':');
let user = split.next().expect("failed to get username from cookie file");
let pass = split.next().map_or("".to_string(), |s| s.to_string());
return (user.to_string(), Some(pass));
} else if let Ok(user) = std::env::var("RPC_USER") {
return (user, std::env::var("RPC_PASS").ok());
} else {
panic!("Either RPC_COOKIE or RPC_USER + RPC_PASS must be set.");
};
}

fn make_client() -> Client {
let (user, pass) = get_auth();
let tp = minreq_http::Builder::new()
.timeout(Duration::from_secs(1))
.url(&get_rpc_url())
.unwrap()
.basic_auth(user, pass)
.build();
Client::with_transport(tp)
}

lazy_static! {
static ref CLIENT: Client = make_client();
}

thread_local! {
static LAST_PANIC: RefCell<Option<(String, Backtrace)>> = RefCell::new(None);
}

/// Here we will collect all the results of the individual tests, preserving ordering.
/// Ideally this would be preset with capacity, but static prevents this.
static mut RESULTS: Vec<(&'static str, bool)> = Vec::new();

macro_rules! run_test {
($method:ident) => {
println!("Running {}...", stringify!($method));
let result = panic::catch_unwind(|| {
$method(&*CLIENT);
});
if result.is_err() {
let (msg, bt) = LAST_PANIC.with(|b| b.borrow_mut().take()).unwrap();
println!("{}", msg);
println!("{:?}", bt);
println!("--");
}

unsafe {
RESULTS.push((stringify!($method), result.is_ok()));
}
};
}

fn main() {
log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::max())).unwrap();

// let default_hook = std::panic::take_hook()
std::panic::set_hook(Box::new(|panic_info| {
let bt = Backtrace::new();
LAST_PANIC.with(move |b| b.borrow_mut().replace((panic_info.to_string(), bt)));
}));

run_test!(test_get_network_info);

// Print results
println!("");
println!("");
println!("Summary:");
let mut error_count = 0;
for (name, success) in mem::replace(unsafe { &mut RESULTS }, Vec::new()).into_iter() {
if !success {
println!(" - {}: FAILED", name);
error_count += 1;
} else {
println!(" - {}: PASSED", name);
}
}

println!("");

if error_count == 0 {
println!("All tests succesful!");
} else {
println!("{} tests failed", error_count);
std::process::exit(1);
}
}

fn test_get_network_info(cl: &Client) {
let request = Request {
method: "getnetworkinfo".into(),
params: &[],
id: serde_json::json!(1),
jsonrpc: Some("2.0"),
};

let _ = cl.send_request(request).unwrap();
}
Loading

0 comments on commit 3c82e8f

Please sign in to comment.