Skip to content

Commit bbeaab4

Browse files
committed
Merge #66: add http header support
73cc41c add http header support (John Cantrell) Pull request description: Technically an end-user could do this themselves because the builders support passing in pre-built ureq/reqwest clients but this makes it a bit easier for them. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced the ability to set custom HTTP headers for requests in both asynchronous and synchronous clients. - Added methods in the `Builder` struct for adding HTTP headers to requests. - **Improvements** - Enhanced error handling with new error variants for invalid HTTP header names and values. - **Tests** - Updated test setup to include custom HTTP headers. - Added a new test case for fetching transactions with specific HTTP headers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Top commit has no ACKs. Tree-SHA512: 8043b54b1cb1330492017674f3e028ad0bc80c08a722a2b413c4b39f8503e9228b9a008e379e610943ffdf1b50ad2cefedba55f716fe2e7fa69fc22fa0859146
2 parents 98edbc5 + 73cc41c commit bbeaab4

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

src/async.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use bitcoin::{
2424
#[allow(unused_imports)]
2525
use log::{debug, error, info, trace};
2626

27-
use reqwest::{Client, StatusCode};
27+
use reqwest::{header, Client, StatusCode};
2828

2929
use crate::{BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
3030

@@ -49,6 +49,18 @@ impl AsyncClient {
4949
client_builder = client_builder.timeout(core::time::Duration::from_secs(timeout));
5050
}
5151

52+
if !builder.headers.is_empty() {
53+
let mut headers = header::HeaderMap::new();
54+
for (k, v) in builder.headers {
55+
let header_name = header::HeaderName::from_lowercase(k.to_lowercase().as_bytes())
56+
.map_err(|_| Error::InvalidHttpHeaderName(k))?;
57+
let header_value = header::HeaderValue::from_str(&v)
58+
.map_err(|_| Error::InvalidHttpHeaderValue(v))?;
59+
headers.insert(header_name, header_value);
60+
}
61+
client_builder = client_builder.default_headers(headers);
62+
}
63+
5264
Ok(Self::from_client(builder.base_url, client_builder.build()?))
5365
}
5466

src/blocking.rs

+9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ pub struct BlockingClient {
3636
pub proxy: Option<String>,
3737
/// Socket timeout.
3838
pub timeout: Option<u64>,
39+
/// HTTP headers to set on every request made to Esplora server
40+
pub headers: HashMap<String, String>,
3941
}
4042

4143
impl BlockingClient {
@@ -45,6 +47,7 @@ impl BlockingClient {
4547
url: builder.base_url,
4648
proxy: builder.proxy,
4749
timeout: builder.timeout,
50+
headers: builder.headers,
4851
}
4952
}
5053

@@ -60,6 +63,12 @@ impl BlockingClient {
6063
request = request.with_timeout(*timeout);
6164
}
6265

66+
if !self.headers.is_empty() {
67+
for (key, value) in &self.headers {
68+
request = request.with_header(key, value);
69+
}
70+
}
71+
6372
Ok(request)
6473
}
6574

src/lib.rs

+61-1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ pub struct Builder {
117117
pub proxy: Option<String>,
118118
/// Socket timeout.
119119
pub timeout: Option<u64>,
120+
/// HTTP headers to set on every request made to Esplora server
121+
pub headers: HashMap<String, String>,
120122
}
121123

122124
impl Builder {
@@ -126,6 +128,7 @@ impl Builder {
126128
base_url: base_url.to_string(),
127129
proxy: None,
128130
timeout: None,
131+
headers: HashMap::new(),
129132
}
130133
}
131134

@@ -141,6 +144,12 @@ impl Builder {
141144
self
142145
}
143146

147+
/// Add a header to set on each request
148+
pub fn header(mut self, key: &str, value: &str) -> Self {
149+
self.headers.insert(key.to_string(), value.to_string());
150+
self
151+
}
152+
144153
/// build a blocking client from builder
145154
#[cfg(feature = "blocking")]
146155
pub fn build_blocking(self) -> BlockingClient {
@@ -181,6 +190,10 @@ pub enum Error {
181190
HeaderHeightNotFound(u32),
182191
/// Header hash not found
183192
HeaderHashNotFound(BlockHash),
193+
/// Invalid HTTP Header name specified
194+
InvalidHttpHeaderName(String),
195+
/// Invalid HTTP Header value specified
196+
InvalidHttpHeaderValue(String),
184197
}
185198

186199
impl fmt::Display for Error {
@@ -261,6 +274,13 @@ mod test {
261274

262275
#[cfg(all(feature = "blocking", feature = "async"))]
263276
async fn setup_clients() -> (BlockingClient, AsyncClient) {
277+
setup_clients_with_headers(HashMap::new()).await
278+
}
279+
280+
#[cfg(all(feature = "blocking", feature = "async"))]
281+
async fn setup_clients_with_headers(
282+
headers: HashMap<String, String>,
283+
) -> (BlockingClient, AsyncClient) {
264284
PREMINE
265285
.get_or_init(|| async {
266286
let _miner = MINER.lock().await;
@@ -270,7 +290,11 @@ mod test {
270290

271291
let esplora_url = ELECTRSD.esplora_url.as_ref().unwrap();
272292

273-
let builder = Builder::new(&format!("http://{}", esplora_url));
293+
let mut builder = Builder::new(&format!("http://{}", esplora_url));
294+
if !headers.is_empty() {
295+
builder.headers = headers;
296+
}
297+
274298
let blocking_client = builder.build_blocking();
275299

276300
let builder_async = Builder::new(&format!("http://{}", esplora_url));
@@ -851,4 +875,40 @@ mod test {
851875
let blocks_genesis_async = async_client.get_blocks(Some(0)).await.unwrap();
852876
assert_eq!(blocks_genesis, blocks_genesis_async);
853877
}
878+
879+
#[cfg(all(feature = "blocking", feature = "async"))]
880+
#[tokio::test]
881+
async fn test_get_tx_with_http_header() {
882+
let headers = [(
883+
"Authorization".to_string(),
884+
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_string(),
885+
)]
886+
.into();
887+
let (blocking_client, async_client) = setup_clients_with_headers(headers).await;
888+
889+
let address = BITCOIND
890+
.client
891+
.get_new_address(Some("test"), Some(AddressType::Legacy))
892+
.unwrap()
893+
.assume_checked();
894+
let txid = BITCOIND
895+
.client
896+
.send_to_address(
897+
&address,
898+
Amount::from_sat(1000),
899+
None,
900+
None,
901+
None,
902+
None,
903+
None,
904+
None,
905+
)
906+
.unwrap();
907+
let _miner = MINER.lock().await;
908+
generate_blocks_and_wait(1);
909+
910+
let tx = blocking_client.get_tx(&txid).unwrap();
911+
let tx_async = async_client.get_tx(&txid).await.unwrap();
912+
assert_eq!(tx, tx_async);
913+
}
854914
}

0 commit comments

Comments
 (0)