clightningrpc_common/client.rs
1// Rust JSON-RPC Library
2// Written by
3// Andrew Poelstra <apoelstra@wpsoftware.net>
4// Wladimir J. van der Laan <laanwj@gmail.com>
5//
6// To the extent possible under law, the author(s) have dedicated all
7// copyright and related and neighboring rights to this software to
8// the public domain worldwide. This software is distributed without
9// any warranty.
10//
11// You should have received a copy of the CC0 Public Domain Dedication
12// along with this software.
13// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
14//
15
16//! Client support
17//!
18//! Support for connecting to JSONRPC servers over UNIX socets, sending requests,
19//! and parsing responses
20//!
21
22use std::os::unix::net::UnixStream;
23use std::path::{Path, PathBuf};
24use std::time::Duration;
25
26use serde::de::DeserializeOwned;
27use serde::Serialize;
28use serde_json::{to_writer, Deserializer};
29
30use crate::errors::Error;
31use crate::types::{Request, Response};
32
33/// A handle to a remote JSONRPC server
34#[derive(Debug)]
35pub struct Client {
36 /// field that defines path to the lightning-rpc socket file
37 sockpath: PathBuf,
38 /// timeout for RPC request
39 timeout: Option<Duration>,
40}
41
42impl Client {
43 /// Creates a new client using the path to the socket file and initializing the timeout field to None
44 pub fn new<P: AsRef<Path>>(sockpath: P) -> Client {
45 Client {
46 sockpath: sockpath.as_ref().to_path_buf(),
47 timeout: None,
48 }
49 }
50
51 /// Set an optional timeout for requests
52 pub fn set_timeout(&mut self, timeout: Option<Duration>) {
53 self.timeout = timeout;
54 }
55
56 /// Sends a request to a client
57 pub fn send_request<S: Serialize, D: DeserializeOwned>(
58 &self,
59 method: &str,
60 params: S,
61 ) -> Result<Response<D>, Error> {
62 // Setup connection
63 let mut stream = UnixStream::connect(&self.sockpath)?;
64 stream.set_read_timeout(self.timeout)?;
65 stream.set_write_timeout(self.timeout)?;
66
67 to_writer(
68 &mut stream,
69 &Request {
70 method: method.to_owned(),
71 params,
72 // FIXME: use a custom json to identify the json request!
73 id: Some("0".into()), // we always open a new connection, so we don't have to care about the nonce
74 jsonrpc: "2.0".to_owned(),
75 },
76 )?;
77
78 let response: Response<D> = Deserializer::from_reader(&mut stream)
79 .into_iter()
80 .next()
81 .map_or(Err(Error::NoErrorOrResult), |res| Ok(res?))?;
82 if response
83 .jsonrpc
84 .as_ref()
85 .map_or(false, |version| version != "2.0")
86 {
87 return Err(Error::VersionMismatch);
88 }
89
90 Ok(response)
91 }
92}