@@ -29,7 +29,28 @@ const DOH_ENDPOINT: &'static str = "https://dns.google/dns-query?dns=";
29
29
///
30
30
/// Note that using this may reveal our IP address to the recipient and information about who we're
31
31
/// 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
+ }
33
54
34
55
const B64_CHAR : [ u8 ; 64 ] = [
35
56
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 {
107
128
let mut pending_queries = vec ! [ initial_query] ;
108
129
109
130
while let Some ( query) = pending_queries. pop ( ) {
110
- let client = reqwest:: Client :: new ( ) ;
111
-
112
131
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 ) ?;
115
135
let body = resp. bytes ( ) . await . map_err ( |_| DNS_ERR ) ?;
116
136
117
137
let mut answer = QueryBuf :: new_zeroed ( 0 ) ;
@@ -136,8 +156,15 @@ impl HTTPHrnResolver {
136
156
137
157
async fn resolve_lnurl_impl ( & self , lnurl_url : & str ) -> Result < HrnResolution , & ' static str > {
138
158
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) ?;
141
168
142
169
if init. tag != "payRequest" {
143
170
return Err ( "LNURL initial init_response had an incorrect tag value" ) ;
@@ -198,8 +225,15 @@ impl HrnResolver for HTTPHrnResolver {
198
225
} else {
199
226
write ! ( & mut callback, "?amount={}" , amt. milli_sats( ) ) . expect ( "Write to String" ) ;
200
227
}
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) ?;
203
237
204
238
if !callback_response. routes . is_empty ( ) {
205
239
return Err ( "LNURL callback response contained a non-empty routes array" ) ;
@@ -257,7 +291,7 @@ mod tests {
257
291
258
292
#[ tokio:: test]
259
293
async fn test_dns_via_http_hrn_resolver ( ) {
260
- let resolver = HTTPHrnResolver ;
294
+ let resolver = HTTPHrnResolver :: default ( ) ;
261
295
let instructions = PaymentInstructions :: parse (
262
296
"send.some@satsto.me" ,
263
297
bitcoin:: Network :: Bitcoin ,
@@ -303,10 +337,11 @@ mod tests {
303
337
304
338
#[ tokio:: test]
305
339
async fn test_http_hrn_resolver ( ) {
340
+ let resolver = HTTPHrnResolver :: default ( ) ;
306
341
let instructions = PaymentInstructions :: parse (
307
342
"lnurltest@bitcoin.ninja" ,
308
343
bitcoin:: Network :: Bitcoin ,
309
- & HTTPHrnResolver ,
344
+ & resolver ,
310
345
true ,
311
346
)
312
347
. await
@@ -323,7 +358,7 @@ mod tests {
323
358
assert_eq ! ( hrn. user( ) , "lnurltest" ) ;
324
359
assert_eq ! ( hrn. domain( ) , "bitcoin.ninja" ) ;
325
360
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 ( )
327
362
} else {
328
363
panic ! ( ) ;
329
364
} ;
@@ -348,11 +383,12 @@ mod tests {
348
383
349
384
#[ tokio:: test]
350
385
async fn test_http_lnurl_resolver ( ) {
386
+ let resolver = HTTPHrnResolver :: default ( ) ;
351
387
let instructions = PaymentInstructions :: parse (
352
388
// lnurl encoding for lnurltest@bitcoin.ninja
353
389
"lnurl1dp68gurn8ghj7cnfw33k76tw9ehxjmn2vyhjuam9d3kz66mwdamkutmvde6hymrs9akxuatjd36x2um5ahcq39" ,
354
390
Network :: Bitcoin ,
355
- & HTTPHrnResolver ,
391
+ & resolver ,
356
392
true ,
357
393
)
358
394
. await
@@ -365,7 +401,7 @@ mod tests {
365
401
assert_eq ! ( instr. pop_callback( ) , None ) ;
366
402
assert ! ( instr. bip_353_dnssec_proof( ) . is_none( ) ) ;
367
403
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 ( )
369
405
} else {
370
406
panic ! ( ) ;
371
407
} ;
0 commit comments