1
0
mirror of https://github.com/romanz/electrs.git synced 2024-11-19 01:43:29 +01:00

Abstract cookie handling to a trait object

This would allow re-loading JSONRPC cookie after bitcoind restart
This commit is contained in:
Roman Zeyde 2018-07-28 11:29:19 +03:00
parent 91e4dd8b6f
commit 1d59449677
No known key found for this signature in database
GPG Key ID: 87CAE5FA46917CBB
5 changed files with 61 additions and 25 deletions

View File

@ -19,7 +19,7 @@ fn run() -> Result<()> {
let daemon = Daemon::new(
&config.daemon_dir,
config.daemon_rpc_addr,
&config.cookie,
config.cookie_getter(),
config.network_type,
&metrics,
)?;

View File

@ -21,7 +21,7 @@ fn run(config: Config) -> Result<()> {
let daemon = Daemon::new(
&config.daemon_dir,
config.daemon_rpc_addr,
&config.cookie,
config.cookie_getter(),
config.network_type,
&metrics,
)?;

View File

@ -20,7 +20,7 @@ fn run_server(config: &Config) -> Result<()> {
let daemon = Daemon::new(
&config.daemon_dir,
config.daemon_rpc_addr,
&config.cookie,
config.cookie_getter(),
config.network_type,
&metrics,
)?;

View File

@ -3,21 +3,13 @@ use std::env::home_dir;
use std::fs;
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use stderrlog;
use daemon::Network;
use daemon::{CookieGetter, Network};
use errors::*;
fn read_cookie(daemon_dir: &Path) -> Result<String> {
let mut path = daemon_dir.to_path_buf();
path.push(".cookie");
let contents = String::from_utf8(
fs::read(&path).chain_err(|| format!("failed to read cookie from {:?}", path))?
).chain_err(|| "invalid cookie string")?;
Ok(contents.trim().to_owned())
}
#[derive(Debug)]
pub struct Config {
pub log: stderrlog::StdErrLog,
@ -25,7 +17,7 @@ pub struct Config {
pub db_path: PathBuf, // RocksDB directory path
pub daemon_dir: PathBuf, // Bitcoind data directory
pub daemon_rpc_addr: SocketAddr, // for connecting Bitcoind JSONRPC
pub cookie: String, // for bitcoind JSONRPC authentication ("USER:PASSWORD")
pub cookie: Option<String>, // for bitcoind JSONRPC authentication ("USER:PASSWORD")
pub electrum_rpc_addr: SocketAddr, // for serving Electrum clients
pub monitoring_addr: SocketAddr, // for Prometheus monitoring
pub skip_bulk_import: bool, // slower initial indexing, for low-memory systems
@ -141,9 +133,7 @@ impl Config {
Network::Testnet => daemon_dir.push("testnet3"),
Network::Regtest => daemon_dir.push("regtest"),
}
let cookie = m.value_of("cookie")
.map(|s| s.to_owned())
.unwrap_or_else(|| read_cookie(&daemon_dir).unwrap());
let cookie = m.value_of("cookie").map(|s| s.to_owned());
let mut log = stderrlog::new();
log.verbosity(m.occurrences_of("verbosity") as usize);
@ -167,4 +157,45 @@ impl Config {
eprintln!("{:?}", config);
config
}
pub fn cookie_getter(&self) -> Arc<CookieGetter> {
if let Some(ref value) = self.cookie {
Arc::new(StaticCookie {
value: value.clone(),
})
} else {
Arc::new(CookieFile {
daemon_dir: self.daemon_dir.clone(),
})
}
}
}
struct StaticCookie {
value: String,
}
impl CookieGetter for StaticCookie {
fn get(&self) -> String {
self.value.clone()
}
}
struct CookieFile {
daemon_dir: PathBuf,
}
impl CookieGetter for CookieFile {
fn get(&self) -> String {
read_cookie(&self.daemon_dir).unwrap()
}
}
fn read_cookie(daemon_dir: &Path) -> Result<String> {
let mut path = daemon_dir.to_path_buf();
path.push(".cookie");
let contents = String::from_utf8(
fs::read(&path).chain_err(|| format!("failed to read cookie from {:?}", path))?
).chain_err(|| "invalid cookie string")?;
Ok(contents.trim().to_owned())
}

View File

@ -11,7 +11,7 @@ use std::collections::HashSet;
use std::io::{BufRead, BufReader, Lines, Write};
use std::net::{SocketAddr, TcpStream};
use std::path::PathBuf;
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use metrics::{HistogramOpts, HistogramVec, Metrics};
use util::HeaderList;
@ -125,10 +125,14 @@ impl MempoolEntry {
}
}
pub trait CookieGetter: Send + Sync {
fn get(&self) -> String;
}
struct Connection {
tx: TcpStream,
rx: Lines<BufReader<TcpStream>>,
cookie_b64: String,
cookie_getter: Arc<CookieGetter>,
addr: SocketAddr,
}
@ -138,14 +142,14 @@ fn tcp_connect(addr: SocketAddr) -> Result<TcpStream> {
}
impl Connection {
fn new(addr: SocketAddr, cookie_b64: String) -> Result<Connection> {
fn new(addr: SocketAddr, cookie_getter: Arc<CookieGetter>) -> Result<Connection> {
let conn = tcp_connect(addr)?;
let reader = BufReader::new(conn.try_clone()
.chain_err(|| format!("failed to clone {:?}", conn))?);
Ok(Connection {
tx: conn,
rx: reader.lines(),
cookie_b64,
cookie_getter,
addr,
})
}
@ -157,15 +161,16 @@ impl Connection {
Ok(Connection {
tx: conn,
rx: reader.lines(),
cookie_b64: self.cookie_b64.clone(),
cookie_getter: self.cookie_getter.clone(),
addr: self.addr,
})
}
fn send(&mut self, request: &str) -> Result<()> {
let cookie_b64 = base64::encode(&self.cookie_getter.get());
let msg = format!(
"POST / HTTP/1.1\nAuthorization: Basic {}\nContent-Length: {}\n\n{}",
self.cookie_b64,
cookie_b64,
request.len(),
request,
);
@ -231,14 +236,14 @@ impl Daemon {
pub fn new(
daemon_dir: &PathBuf,
daemon_rpc_addr: SocketAddr,
cookie: &str,
cookie_getter: Arc<CookieGetter>,
network: Network,
metrics: &Metrics,
) -> Result<Daemon> {
let daemon = Daemon {
daemon_dir: daemon_dir.clone(),
network,
conn: Mutex::new(Connection::new(daemon_rpc_addr, base64::encode(cookie))?),
conn: Mutex::new(Connection::new(daemon_rpc_addr, cookie_getter)?),
message_id: Counter::new(),
latency: metrics.histogram_vec(
HistogramOpts::new("daemon_rpc", "Bitcoind RPC latency (in seconds)"),