Skip to content

Commit ea0116f

Browse files
committed
Reproduce Bitcoin Core's handling of cookie files
Bitcoin Core uses a single call to std::getline to read the contents of cookie files, making it ignore newlines in the file, as well as ignore any lines after the first. This reproduces that behavior, so that all cookie files that work with Bitcoin Core should work with this library.
1 parent bde02d7 commit ea0116f

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

client/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ jsonrpc = "0.13.0"
2727
# Used for deserialization of JSON.
2828
serde = "1"
2929
serde_json = "1"
30+
31+
[dev-dependencies]
32+
tempfile = "3.3.0"

client/src/client.rs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use std::collections::HashMap;
1212
use std::fs::File;
13+
use std::io::{BufRead, BufReader};
1314
use std::iter::FromIterator;
1415
use std::path::PathBuf;
1516
use std::{fmt, result};
@@ -197,20 +198,16 @@ pub enum Auth {
197198
impl Auth {
198199
/// Convert into the arguments that jsonrpc::Client needs.
199200
pub fn get_user_pass(self) -> Result<(Option<String>, Option<String>)> {
200-
use std::io::Read;
201201
match self {
202202
Auth::None => Ok((None, None)),
203203
Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
204-
Auth::CookieFile(path) => {
205-
let mut file = File::open(path)?;
206-
let mut contents = String::new();
207-
file.read_to_string(&mut contents)?;
208-
let mut split = contents.splitn(2, ":");
209-
Ok((
210-
Some(split.next().ok_or(Error::InvalidCookieFile)?.into()),
211-
Some(split.next().ok_or(Error::InvalidCookieFile)?.into()),
212-
))
213-
}
204+
Auth::CookieFile(path) => BufReader::new(File::open(path)?)
205+
.lines()
206+
.next()
207+
.ok_or(Error::InvalidCookieFile)??
208+
.split_once(':')
209+
.map(|(user, pass)| (Some(user.into()), Some(pass.into())))
210+
.ok_or(Error::InvalidCookieFile),
214211
}
215212
}
216213
}
@@ -1357,4 +1354,34 @@ mod tests {
13571354
fn test_handle_defaults() {
13581355
test_handle_defaults_inner().unwrap();
13591356
}
1357+
1358+
#[test]
1359+
fn auth_cookie_file_ignores_newline() {
1360+
let tempdir = tempfile::tempdir().unwrap();
1361+
let path = tempdir.path().join("cookie");
1362+
std::fs::write(&path, "foo:bar\n").unwrap();
1363+
assert_eq!(
1364+
Auth::CookieFile(path).get_user_pass().unwrap(),
1365+
(Some("foo".into()), Some("bar".into())),
1366+
);
1367+
}
1368+
1369+
#[test]
1370+
fn auth_cookie_file_ignores_additional_lines() {
1371+
let tempdir = tempfile::tempdir().unwrap();
1372+
let path = tempdir.path().join("cookie");
1373+
std::fs::write(&path, "foo:bar\nbaz").unwrap();
1374+
assert_eq!(
1375+
Auth::CookieFile(path).get_user_pass().unwrap(),
1376+
(Some("foo".into()), Some("bar".into())),
1377+
);
1378+
}
1379+
1380+
#[test]
1381+
fn auth_cookie_file_fails_if_colon_isnt_present() {
1382+
let tempdir = tempfile::tempdir().unwrap();
1383+
let path = tempdir.path().join("cookie");
1384+
std::fs::write(&path, "foobar").unwrap();
1385+
assert!(matches!(Auth::CookieFile(path).get_user_pass(), Err(Error::InvalidCookieFile)));
1386+
}
13601387
}

0 commit comments

Comments
 (0)