Skip to content

Commit d4af94b

Browse files
committed
Allow for custom HTTPHrnResolvers
This allows for giving a custom reqwest client when creating a HTTPHrnResolver. This is useful for adding custom headers, proxying connections, etc. This also has the added benefit of using the same reqwest client across every call so it'll be better and reusing tls connections.
1 parent 0e9af05 commit d4af94b

File tree

2 files changed

+53
-22
lines changed

2 files changed

+53
-22
lines changed

src/http_resolver.rs

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,28 @@ const DOH_ENDPOINT: &'static str = "https://dns.google/dns-query?dns=";
2929
///
3030
/// Note that using this may reveal our IP address to the recipient and information about who we're
3131
/// paying to Google (via `dns.google`).
32-
pub struct HTTPHrnResolver;
32+
#[derive(Debug, Clone)]
33+
pub struct HTTPHrnResolver {
34+
client: reqwest::Client,
35+
}
36+
37+
impl HTTPHrnResolver {
38+
/// Create a new `HTTPHrnResolver` with a default `reqwest::Client`.
39+
pub fn new() -> Self {
40+
HTTPHrnResolver::default()
41+
}
42+
43+
/// Create a new `HTTPHrnResolver` with a custom `reqwest::Client`.
44+
pub fn with_client(client: reqwest::Client) -> Self {
45+
HTTPHrnResolver { client }
46+
}
47+
}
48+
49+
impl Default for HTTPHrnResolver {
50+
fn default() -> Self {
51+
HTTPHrnResolver { client: reqwest::Client::new() }
52+
}
53+
}
3354

3455
const B64_CHAR: [u8; 64] = [
3556
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P',
@@ -107,11 +128,10 @@ impl HTTPHrnResolver {
107128
let mut pending_queries = vec![initial_query];
108129

109130
while let Some(query) = pending_queries.pop() {
110-
let client = reqwest::Client::new();
111-
112131
let request_url = query_to_url(query);
113-
let req = client.get(request_url).header("accept", "application/dns-message").build();
114-
let resp = client.execute(req.map_err(|_| DNS_ERR)?).await.map_err(|_| DNS_ERR)?;
132+
let req =
133+
self.client.get(request_url).header("accept", "application/dns-message").build();
134+
let resp = self.client.execute(req.map_err(|_| DNS_ERR)?).await.map_err(|_| DNS_ERR)?;
115135
let body = resp.bytes().await.map_err(|_| DNS_ERR)?;
116136

117137
let mut answer = QueryBuf::new_zeroed(0);
@@ -136,8 +156,15 @@ impl HTTPHrnResolver {
136156

137157
async fn resolve_lnurl_impl(&self, lnurl_url: &str) -> Result<HrnResolution, &'static str> {
138158
let err = "Failed to fetch LN-Address initial well-known endpoint";
139-
let init: LNURLInitResponse =
140-
reqwest::get(lnurl_url).await.map_err(|_| err)?.json().await.map_err(|_| err)?;
159+
let init: LNURLInitResponse = self
160+
.client
161+
.get(lnurl_url)
162+
.send()
163+
.await
164+
.map_err(|_| err)?
165+
.json()
166+
.await
167+
.map_err(|_| err)?;
141168

142169
if init.tag != "payRequest" {
143170
return Err("LNURL initial init_response had an incorrect tag value");
@@ -198,8 +225,15 @@ impl HrnResolver for HTTPHrnResolver {
198225
} else {
199226
write!(&mut callback, "?amount={}", amt.milli_sats()).expect("Write to String");
200227
}
201-
let callback_response: LNURLCallbackResponse =
202-
reqwest::get(callback).await.map_err(|_| err)?.json().await.map_err(|_| err)?;
228+
let callback_response: LNURLCallbackResponse = self
229+
.client
230+
.get(callback)
231+
.send()
232+
.await
233+
.map_err(|_| err)?
234+
.json()
235+
.await
236+
.map_err(|_| err)?;
203237

204238
if !callback_response.routes.is_empty() {
205239
return Err("LNURL callback response contained a non-empty routes array");
@@ -257,7 +291,7 @@ mod tests {
257291

258292
#[tokio::test]
259293
async fn test_dns_via_http_hrn_resolver() {
260-
let resolver = HTTPHrnResolver;
294+
let resolver = HTTPHrnResolver::default();
261295
let instructions = PaymentInstructions::parse(
262296
"send.some@satsto.me",
263297
bitcoin::Network::Bitcoin,
@@ -303,10 +337,11 @@ mod tests {
303337

304338
#[tokio::test]
305339
async fn test_http_hrn_resolver() {
340+
let resolver = HTTPHrnResolver::default();
306341
let instructions = PaymentInstructions::parse(
307342
"lnurltest@bitcoin.ninja",
308343
bitcoin::Network::Bitcoin,
309-
&HTTPHrnResolver,
344+
&resolver,
310345
true,
311346
)
312347
.await
@@ -323,7 +358,7 @@ mod tests {
323358
assert_eq!(hrn.user(), "lnurltest");
324359
assert_eq!(hrn.domain(), "bitcoin.ninja");
325360

326-
instr.set_amount(Amount::from_sats(100_000).unwrap(), &HTTPHrnResolver).await.unwrap()
361+
instr.set_amount(Amount::from_sats(100_000).unwrap(), &resolver).await.unwrap()
327362
} else {
328363
panic!();
329364
};
@@ -348,11 +383,12 @@ mod tests {
348383

349384
#[tokio::test]
350385
async fn test_http_lnurl_resolver() {
386+
let resolver = HTTPHrnResolver::default();
351387
let instructions = PaymentInstructions::parse(
352388
// lnurl encoding for lnurltest@bitcoin.ninja
353389
"lnurl1dp68gurn8ghj7cnfw33k76tw9ehxjmn2vyhjuam9d3kz66mwdamkutmvde6hymrs9akxuatjd36x2um5ahcq39",
354390
Network::Bitcoin,
355-
&HTTPHrnResolver,
391+
&resolver,
356392
true,
357393
)
358394
.await
@@ -365,7 +401,7 @@ mod tests {
365401
assert_eq!(instr.pop_callback(), None);
366402
assert!(instr.bip_353_dnssec_proof().is_none());
367403

368-
instr.set_amount(Amount::from_sats(100_000).unwrap(), &HTTPHrnResolver).await.unwrap()
404+
instr.set_amount(Amount::from_sats(100_000).unwrap(), &resolver).await.unwrap()
369405
} else {
370406
panic!();
371407
};

src/lib.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,14 +1351,9 @@ mod tests {
13511351

13521352
#[cfg(feature = "http")]
13531353
async fn test_lnurl(str: &str) {
1354-
let parsed = PaymentInstructions::parse(
1355-
str,
1356-
Network::Signet,
1357-
&http_resolver::HTTPHrnResolver,
1358-
false,
1359-
)
1360-
.await
1361-
.unwrap();
1354+
let resolver = http_resolver::HTTPHrnResolver::default();
1355+
let parsed =
1356+
PaymentInstructions::parse(str, Network::Signet, &resolver, false).await.unwrap();
13621357

13631358
let parsed = match parsed {
13641359
PaymentInstructions::ConfigurableAmount(parsed) => parsed,

0 commit comments

Comments
 (0)