@@ -4,7 +4,7 @@ use std::collections::HashMap;
44use std:: fmt;
55use std:: fmt:: { Display , Formatter } ;
66use std:: net:: SocketAddr ;
7- # [ cfg ( feature = "tls" ) ] use std:: sync:: Arc ;
7+ use std:: sync:: { Arc , Mutex } ;
88use std:: time:: Duration ;
99
1010#[ allow( unused_imports) ] use anyhow:: anyhow;
@@ -72,7 +72,8 @@ pub(crate) async fn create_and_bind(
7272 server_id : String ,
7373 pact : V4Pact ,
7474 addr : SocketAddr ,
75- config : MockServerConfig
75+ config : MockServerConfig ,
76+ matches : Arc < Mutex < Vec < MatchResult > > >
7677) -> anyhow:: Result < ( SocketAddr , oneshot:: Sender < ( ) > , mpsc:: Receiver < MockServerEvent > , JoinHandle < ( ) > ) > {
7778 let listener = TcpListener :: bind ( addr) . await ?;
7879 let local_addr = listener. local_addr ( ) ?;
@@ -88,6 +89,7 @@ pub(crate) async fn create_and_bind(
8889 let server_id = server_id. clone ( ) ;
8990 let pact = pact. clone ( ) ;
9091 let config = config. clone ( ) ;
92+ let matches = matches. clone ( ) ;
9193
9294 select ! {
9395 connection = listener. accept( ) => {
@@ -106,9 +108,10 @@ pub(crate) async fn create_and_bind(
106108 let event_send = ev. clone( ) ;
107109 let config = config. clone( ) ;
108110 let server_id = sid. clone( ) ;
111+ let matches = matches. clone( ) ;
109112 LOG_ID . scope( server_id, async move {
110113 handle_mock_request_error(
111- handle_request( req, pact. clone( ) , event_send. clone( ) , & local_addr, & config) . await
114+ handle_request( req, pact. clone( ) , event_send. clone( ) , matches . clone ( ) , & local_addr, & config) . await
112115 )
113116 } )
114117 } )
@@ -162,7 +165,8 @@ pub(crate) async fn create_and_bind_https(
162165 server_id : String ,
163166 pact : V4Pact ,
164167 addr : SocketAddr ,
165- config : MockServerConfig
168+ config : MockServerConfig ,
169+ matches : Arc < Mutex < Vec < MatchResult > > >
166170) -> anyhow:: Result < ( SocketAddr , oneshot:: Sender < ( ) > , mpsc:: Receiver < MockServerEvent > , JoinHandle < ( ) > ) > {
167171 if CryptoProvider :: get_default ( ) . is_none ( ) {
168172 warn ! ( "No TLS cryptographic provider has been configured, defaulting to the standard FIPS provider" ) ;
@@ -197,6 +201,7 @@ pub(crate) async fn create_and_bind_https(
197201 let server_id = server_id. clone ( ) ;
198202 let pact = pact. clone ( ) ;
199203 let config = config. clone ( ) ;
204+ let matches = matches. clone ( ) ;
200205
201206 select ! {
202207 connection = listener. accept( ) => {
@@ -219,9 +224,10 @@ pub(crate) async fn create_and_bind_https(
219224 let event_send = ev. clone( ) ;
220225 let config = config. clone( ) ;
221226 let server_id = sid. clone( ) ;
227+ let matches = matches. clone( ) ;
222228 LOG_ID . scope( server_id, async move {
223229 handle_mock_request_error(
224- handle_request( req, pact. clone( ) , event_send. clone( ) , & local_addr, & config) . await
230+ handle_request( req, pact. clone( ) , event_send. clone( ) , matches . clone ( ) , & local_addr, & config) . await
225231 )
226232 } )
227233 } )
@@ -278,6 +284,7 @@ async fn handle_request(
278284 req : Request < Incoming > ,
279285 pact : V4Pact ,
280286 event_send : Sender < MockServerEvent > ,
287+ matches : Arc < Mutex < Vec < MatchResult > > > ,
281288 local_addr : & SocketAddr ,
282289 config : & MockServerConfig
283290) -> Result < Response < Full < Bytes > > , InteractionError > {
@@ -322,8 +329,13 @@ async fn handle_request(
322329
323330 let match_result = match_request ( & pact_request, & pact) . await ;
324331
325- if let Err ( _) = event_send. send ( MockServerEvent :: RequestMatch ( match_result. clone ( ) ) ) . await {
326- error ! ( "Failed to send RequestMatch event" ) ;
332+ // Record the match result synchronously before returning the response. This ensures that
333+ // pactffi_mock_server_matched() / pactffi_mock_server_mismatches() always sees the correct
334+ // state immediately after the client receives the HTTP response, with no race against the
335+ // async event loop.
336+ {
337+ let mut guard = matches. lock ( ) . unwrap ( ) ;
338+ guard. push ( match_result. clone ( ) ) ;
327339 }
328340
329341 match_result_to_hyper_response ( & pact_request, & match_result, local_addr, config) . await
@@ -569,11 +581,13 @@ mod tests {
569581
570582 #[ tokio:: test]
571583 async fn can_fetch_results_on_current_thread ( ) {
584+ let matches = Arc :: new ( Mutex :: new ( vec ! [ ] ) ) ;
572585 let ( _addr, shutdown, mut events, handle) = create_and_bind (
573586 "can_fetch_results_on_current_thread" . to_string ( ) ,
574587 RequestResponsePact :: default ( ) . as_v4_pact ( ) . unwrap ( ) ,
575588 ( [ 0 , 0 , 0 , 0 ] , 0u16 ) . into ( ) ,
576- MockServerConfig :: default ( )
589+ MockServerConfig :: default ( ) ,
590+ matches
577591 ) . await . unwrap ( ) ;
578592
579593 shutdown. send ( ( ) ) . unwrap ( ) ;
@@ -605,11 +619,13 @@ mod tests {
605619 interactions : vec ! [ RequestResponseInteraction :: default ( ) ] ,
606620 .. RequestResponsePact :: default ( )
607621 } ;
622+ let matches = Arc :: new ( Mutex :: new ( vec ! [ ] ) ) ;
608623 let ( addr, shutdown, mut events, handle) = create_and_bind (
609624 "can_fetch_results_on_current_thread" . to_string ( ) ,
610625 pact. as_v4_pact ( ) . unwrap ( ) ,
611626 ( [ 127 , 0 , 0 , 1 ] , 0u16 ) . into ( ) ,
612- MockServerConfig :: default ( )
627+ MockServerConfig :: default ( ) ,
628+ matches. clone ( )
613629 ) . await . unwrap ( ) ;
614630
615631 let client = reqwest:: ClientBuilder :: new ( )
@@ -625,17 +641,18 @@ mod tests {
625641 shutdown. send ( ( ) ) . unwrap ( ) ;
626642 let _ = handle. await ;
627643
628- // Should be at least 3 events
629- expect ! ( events. len( ) ) . to ( be_greater_or_equal_to ( 3 ) ) ;
644+ // Match results are now recorded directly in the mutex, not via the event channel.
645+ // Events should contain RequestReceived plus the shutdown event (and possibly a
646+ // ConnectionFailed on Linux once the server shuts down).
647+ expect ! ( events. len( ) ) . to ( be_greater_or_equal_to ( 2 ) ) ;
630648 assert_eq ! ( events. recv( ) . await . unwrap( ) , MockServerEvent :: RequestReceived ( "/" . to_string( ) ) ) ;
631- if let MockServerEvent :: RequestMatch ( _) = events. recv ( ) . await . unwrap ( ) {
632- // expected
633- } else {
634- panic ! ( "Was expected a request match event" ) ;
635- }
636649 // For some reason, a http2 connection returns an error once the server is shutdown on Linux
637650 let mut events_list = vec ! [ ] ;
638651 events. recv_many ( & mut events_list, 2 ) . await ;
639652 assert_eq ! ( events_list. last( ) . unwrap( ) , & MockServerEvent :: ServerShutdown ) ;
653+
654+ // Verify the match result was recorded synchronously in the mutex
655+ let guard = matches. lock ( ) . unwrap ( ) ;
656+ assert_eq ! ( guard. len( ) , 1 ) ;
640657 }
641658}
0 commit comments