@@ -360,22 +360,27 @@ mod test {
360360
361361 /// Setup both [`BlockingClient`] and [`AsyncClient`].
362362 fn setup_clients ( & self ) -> ( BlockingClient , AsyncClient ) {
363- self . setup_clients_with_headers ( HashMap :: new ( ) )
363+ self . setup_clients_with_headers (
364+ self . electrsd . esplora_url . as_ref ( ) . unwrap ( ) ,
365+ HashMap :: new ( ) ,
366+ )
364367 }
365368
366369 /// Setup both [`BlockingClient`] and [`AsyncClient`] with custom HTTP headers.
367370 fn setup_clients_with_headers (
368371 & self ,
372+ url : & str ,
369373 headers : HashMap < String , String > ,
370374 ) -> ( BlockingClient , AsyncClient ) {
371- let esplora_url = self . electrsd . esplora_url . as_ref ( ) . unwrap ( ) ;
372-
373- let mut builder = Builder :: new ( & format ! ( "http://{esplora_url}" ) ) ;
375+ let mut builder = Builder :: new ( & format ! ( "http://{url}" ) ) ;
374376 for ( k, v) in & headers {
375377 builder = builder. header ( k, v) ;
376378 }
377- let blocking_client = builder. clone ( ) . build_blocking ( ) ;
378- let async_client = builder. build_async ( ) . unwrap ( ) ;
379+ let blocking_client = builder
380+ . clone ( )
381+ . header ( "User-Agent" , "blocking" )
382+ . build_blocking ( ) ;
383+ let async_client = builder. header ( "User-Agent" , "async" ) . build_async ( ) . unwrap ( ) ;
379384
380385 ( blocking_client, async_client)
381386 }
@@ -1094,14 +1099,41 @@ mod test {
10941099
10951100 #[ cfg( all( feature = "blocking" , feature = "async" ) ) ]
10961101 #[ tokio:: test]
1097- async fn test_get_tx_with_http_header ( ) {
1102+ async fn test_get_tx_with_http_headers ( ) {
1103+ use corepc_node:: get_available_port;
1104+ use tokio:: io:: AsyncReadExt ;
1105+ use tokio:: net:: TcpListener ;
1106+
1107+ async fn handle_requests ( listener : TcpListener , count : usize ) -> Vec < [ u8 ; 4096 ] > {
1108+ let mut raw_requests = vec ! [ ] ;
1109+ for _ in 0 ..count {
1110+ let ( mut stream, _) = listener. accept ( ) . await . expect ( "should accept connection!" ) ;
1111+ let mut buf = [ 0u8 ; 4096 ] ;
1112+ AsyncReadExt :: read ( & mut stream, & mut buf)
1113+ . await
1114+ . expect ( "should read from stream" ) ;
1115+ raw_requests. push ( buf) ;
1116+ }
1117+ raw_requests
1118+ }
1119+
1120+ // setup a mocked HTTP server.
1121+ let base_url = format ! (
1122+ "127.0.0.1:{}" ,
1123+ get_available_port( ) . expect( "should get an available port successfully!" )
1124+ ) ;
1125+
1126+ let listener = TcpListener :: bind ( & base_url)
1127+ . await
1128+ . expect ( "should bind the TCP listener successfully" ) ;
1129+
1130+ // setup `TestEnv` and expected HTTP headers.
10981131 let env = TestEnv :: new ( ) ;
1099- let headers = [ (
1100- "Authorization" . to_string ( ) ,
1101- "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" . to_string ( ) ,
1102- ) ]
1103- . into ( ) ;
1104- let ( blocking_client, async_client) = env. setup_clients_with_headers ( headers) ;
1132+ let exp_header_key = "Authorization" ;
1133+ let exp_header_value = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" ;
1134+ let headers = HashMap :: from ( [ ( exp_header_key. to_string ( ) , exp_header_value. to_string ( ) ) ] ) ;
1135+
1136+ let ( blocking_client, async_client) = env. setup_clients_with_headers ( & base_url, headers) ;
11051137
11061138 let address = env. get_legacy_address ( ) ;
11071139 let txid = env
@@ -1112,9 +1144,45 @@ mod test {
11121144 . unwrap ( ) ;
11131145 env. mine_and_wait ( 1 ) ;
11141146
1115- let tx = blocking_client. get_tx ( & txid) . unwrap ( ) ;
1116- let tx_async = async_client. get_tx ( & txid) . await . unwrap ( ) ;
1117- assert_eq ! ( tx, tx_async) ;
1147+ let blocking_task = tokio:: task:: spawn_blocking ( move || blocking_client. get_tx ( & txid) ) ;
1148+ let async_task = tokio:: task:: spawn ( async move { async_client. get_tx ( & txid) . await } ) ;
1149+
1150+ let raw_requests = handle_requests ( listener, 2 ) . await ;
1151+ let requests = raw_requests
1152+ . iter ( )
1153+ . map ( |raw| {
1154+ String :: from_utf8 ( raw. to_vec ( ) ) . expect ( "should parse HTTP requests successfully" )
1155+ } )
1156+ . collect :: < Vec < String > > ( ) ;
1157+
1158+ assert_eq ! (
1159+ requests. len( ) ,
1160+ 2 ,
1161+ "it MUST contain ONLY two requests (i.e a single one from each client)"
1162+ ) ;
1163+
1164+ let assert_request = |user_agent : & str , header_key : & str | {
1165+ let expected_path = format ! ( "GET /tx/{txid}/raw" ) ;
1166+ let expected_auth = format ! ( "{header_key}: {exp_header_value}" ) ;
1167+
1168+ assert ! (
1169+ requests. iter( ) . any( |req| {
1170+ req. contains( & expected_path)
1171+ && req. contains( & expected_auth)
1172+ && req. contains( user_agent)
1173+ } ) ,
1174+ "request MUST call `{expected_path}` with `{user_agent}` and expected authorization header"
1175+ ) ;
1176+ } ;
1177+
1178+ // minreq's blocking client sends title-case headers: "Authorization"
1179+ assert_request ( "User-Agent: blocking" , exp_header_key) ;
1180+ // reqwest's async client sends lowercase headers: "authorization"
1181+ assert_request ( "user-agent: async" , & exp_header_key. to_lowercase ( ) ) ;
1182+
1183+ // cleanup any remaining spawned tasks
1184+ let _ = blocking_task. await . expect ( "blocking task should not panic" ) ;
1185+ let _ = async_task. await . expect ( "async task should not panic" ) ;
11181186 }
11191187
11201188 #[ cfg( all( feature = "blocking" , feature = "async" ) ) ]
0 commit comments