@@ -8,12 +8,11 @@ use bitreq::RequestExt;
88
99use std:: convert:: TryFrom ;
1010use std:: fmt;
11- use std:: time:: Duration ;
1211
13- /// Timeout for requests. This is set to a high value as it is not uncommon for Bitcoin Core to be
14- /// blocked waiting on UTXO cache flushes for upwards of 10 minutes on slow devices (e.g. RPis with
15- /// SSDs over USB).
16- const TCP_STREAM_RESPONSE_TIMEOUT : Duration = Duration :: from_secs ( 300 ) ;
12+ /// Timeout for requests in seconds . This is set to a high value as it is not uncommon for Bitcoin
13+ /// Core to be blocked waiting on UTXO cache flushes for upwards of 10 minutes on slow devices
14+ /// (e.g. RPis with SSDs over USB).
15+ const TCP_STREAM_RESPONSE_TIMEOUT : u64 = 300 ;
1716
1817/// Maximum HTTP message body size in bytes. Enough for a hex-encoded block in JSON format and any
1918/// overhead for HTTP chunked transfer encoding.
@@ -68,79 +67,25 @@ impl From<HttpError> for HttpClientError {
6867 }
6968}
7069
71- /// Endpoint for interacting with an HTTP-based API.
72- #[ derive( Debug ) ]
73- pub struct HttpEndpoint {
74- host : String ,
75- port : Option < u16 > ,
76- path : String ,
77- }
78-
79- impl HttpEndpoint {
80- /// Creates an endpoint for the given host and default HTTP port.
81- pub fn for_host ( host : String ) -> Self {
82- Self { host, port : None , path : String :: from ( "/" ) }
83- }
84-
85- /// Specifies a port to use with the endpoint.
86- pub fn with_port ( mut self , port : u16 ) -> Self {
87- self . port = Some ( port) ;
88- self
89- }
90-
91- /// Specifies a path to use with the endpoint.
92- pub fn with_path ( mut self , path : String ) -> Self {
93- self . path = path;
94- self
95- }
96-
97- /// Returns the endpoint host.
98- pub fn host ( & self ) -> & str {
99- & self . host
100- }
101-
102- /// Returns the endpoint port.
103- pub fn port ( & self ) -> u16 {
104- match self . port {
105- None => 80 ,
106- Some ( port) => port,
107- }
108- }
109-
110- /// Returns the endpoint path.
111- pub fn path ( & self ) -> & str {
112- & self . path
113- }
114- }
115-
116- impl < ' a > std:: net:: ToSocketAddrs for & ' a HttpEndpoint {
117- type Iter = <( & ' a str , u16 ) as std:: net:: ToSocketAddrs >:: Iter ;
118-
119- fn to_socket_addrs ( & self ) -> std:: io:: Result < Self :: Iter > {
120- ( self . host ( ) , self . port ( ) ) . to_socket_addrs ( )
121- }
122- }
123-
12470/// Maximum number of cached connections in the connection pool.
12571#[ cfg( feature = "tokio" ) ]
12672const MAX_CONNECTIONS : usize = 10 ;
12773
12874/// Client for making HTTP requests.
12975pub ( crate ) struct HttpClient {
130- host : String ,
131- port : u16 ,
76+ base_url : String ,
13277 #[ cfg( feature = "tokio" ) ]
13378 client : bitreq:: Client ,
13479}
13580
13681impl HttpClient {
137- /// Creates a new HTTP client for the given endpoint .
82+ /// Creates a new HTTP client for the given base URL .
13883 ///
84+ /// The base URL should include the scheme, host, and port (e.g., "http://127.0.0.1:8332").
13985 /// DNS resolution is deferred until the first request is made.
140- pub fn new ( endpoint : & HttpEndpoint ) -> Self {
86+ pub fn new ( base_url : & str ) -> Self {
14187 Self {
142- host : endpoint. host ( ) . to_string ( ) ,
143- port : endpoint. port ( ) ,
88+ base_url : base_url. to_string ( ) ,
14489 #[ cfg( feature = "tokio" ) ]
14590 client : bitreq:: Client :: new ( MAX_CONNECTIONS ) ,
14691 }
@@ -154,9 +99,9 @@ impl HttpClient {
15499 where
155100 F : TryFrom < Vec < u8 > , Error = std:: io:: Error > ,
156101 {
157- let url = format ! ( "http://{}:{}{} " , self . host , self . port , uri) ;
102+ let url = format ! ( "{}{} " , self . base_url , uri) ;
158103 let request = bitreq:: get ( url)
159- . with_timeout ( TCP_STREAM_RESPONSE_TIMEOUT . as_secs ( ) )
104+ . with_timeout ( TCP_STREAM_RESPONSE_TIMEOUT )
160105 . with_max_body_size ( Some ( MAX_HTTP_MESSAGE_BODY_SIZE ) ) ;
161106 let response_body = self . send_request ( request) . await ?;
162107 F :: try_from ( response_body) . map_err ( HttpClientError :: Io )
@@ -174,11 +119,11 @@ impl HttpClient {
174119 where
175120 F : TryFrom < Vec < u8 > , Error = std:: io:: Error > ,
176121 {
177- let url = format ! ( "http://{}:{}{} " , self . host , self . port , uri) ;
122+ let url = format ! ( "{}{} " , self . base_url , uri) ;
178123 let request = bitreq:: post ( url)
179124 . with_header ( "Authorization" , auth)
180125 . with_header ( "Content-Type" , "application/json" )
181- . with_timeout ( TCP_STREAM_RESPONSE_TIMEOUT . as_secs ( ) )
126+ . with_timeout ( TCP_STREAM_RESPONSE_TIMEOUT )
182127 . with_max_body_size ( Some ( MAX_HTTP_MESSAGE_BODY_SIZE ) )
183128 . with_body ( content. to_string ( ) ) ;
184129 let response_body = self . send_request ( request) . await ?;
@@ -249,6 +194,7 @@ impl TryFrom<Vec<u8>> for JsonResponse {
249194pub ( crate ) mod client_tests {
250195 use super :: * ;
251196 use std:: io:: { BufRead , Read , Write } ;
197+ use std:: time:: Duration ;
252198
253199 /// Server for handling HTTP client requests with a stock response.
254200 pub struct HttpServer {
@@ -392,15 +338,14 @@ pub(crate) mod client_tests {
392338 Self { address, handler : Some ( handler) , shutdown }
393339 }
394340
395- pub fn endpoint ( & self ) -> HttpEndpoint {
396- HttpEndpoint :: for_host ( self . address . ip ( ) . to_string ( ) ) . with_port ( self . address . port ( ) )
341+ pub fn endpoint ( & self ) -> String {
342+ format ! ( "http://{}:{}" , self . address. ip( ) , self . address. port( ) )
397343 }
398344 }
399345
400346 #[ tokio:: test]
401347 async fn connect_with_invalid_host ( ) {
402- let endpoint = HttpEndpoint :: for_host ( "invalid.host.example" . to_string ( ) ) . with_port ( 80 ) ;
403- let client = HttpClient :: new ( & endpoint) ;
348+ let client = HttpClient :: new ( "http://invalid.host.example:80" ) ;
404349 match client. get :: < JsonResponse > ( "/foo" ) . await {
405350 Err ( HttpClientError :: Transport ( _) ) => { } ,
406351 Err ( e) => panic ! ( "Unexpected error type: {:?}" , e) ,
0 commit comments