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