@@ -1912,19 +1912,7 @@ impl<'a, N: 'a + Notifier> ServerWithNotifier<'a, N> {
1912
1912
modify_sent : false ,
1913
1913
} => {
1914
1914
if self . are_channels_reset ( matches ! ( next_action, ConnectionAction :: Reset ) ) {
1915
- self . inner . state = ConnectionState :: Disconnecting {
1916
- next_action,
1917
- modify_sent : true ,
1918
- } ;
1919
-
1920
- // Reset server state and disconnect the relay if there is one.
1921
- self . notifier
1922
- . modify_connection ( ModifyConnectionRequest {
1923
- monitor_page : Update :: Reset ,
1924
- interrupt_page : Update :: Reset ,
1925
- ..Default :: default ( )
1926
- } )
1927
- . expect ( "resetting state should not fail" ) ;
1915
+ self . notify_disconnect ( next_action) ;
1928
1916
}
1929
1917
}
1930
1918
ConnectionState :: Disconnecting {
@@ -1936,6 +1924,25 @@ impl<'a, N: 'a + Notifier> ServerWithNotifier<'a, N> {
1936
1924
}
1937
1925
}
1938
1926
1927
+ /// Informs the notifier to reset the connection state when disconnecting.
1928
+ fn notify_disconnect ( & mut self , next_action : ConnectionAction ) {
1929
+ // Assert this on debug only because it is an expensive check if there are many channels.
1930
+ debug_assert ! ( self . are_channels_reset( matches!( next_action, ConnectionAction :: Reset ) ) ) ;
1931
+ self . inner . state = ConnectionState :: Disconnecting {
1932
+ next_action,
1933
+ modify_sent : true ,
1934
+ } ;
1935
+
1936
+ // Reset server state and disconnect the relay if there is one.
1937
+ self . notifier
1938
+ . modify_connection ( ModifyConnectionRequest {
1939
+ monitor_page : Update :: Reset ,
1940
+ interrupt_page : Update :: Reset ,
1941
+ ..Default :: default ( )
1942
+ } )
1943
+ . expect ( "resetting state should not fail" ) ;
1944
+ }
1945
+
1939
1946
/// If true, the server is mid-reset and cannot take certain actions such
1940
1947
/// as handling synic messages or saving state.
1941
1948
fn is_resetting ( & self ) -> bool {
@@ -2413,7 +2420,7 @@ impl<'a, N: 'a + Notifier> ServerWithNotifier<'a, N> {
2413
2420
2414
2421
ConnectionState :: Connected { .. } => {
2415
2422
if self . are_channels_reset ( vm_reset) {
2416
- self . inner . state = ConnectionState :: Disconnected ;
2423
+ self . notify_disconnect ( new_action ) ;
2417
2424
} else {
2418
2425
self . inner . state = ConnectionState :: Disconnecting {
2419
2426
next_action : new_action,
@@ -3897,32 +3904,34 @@ mod tests {
3897
3904
3898
3905
#[ test]
3899
3906
fn test_version_negotiation_feature_flags ( ) {
3900
- let ( mut notifier, _recv) = TestNotifier :: new ( ) ;
3901
- let mut server = Server :: new ( Vtl :: Vtl0 , MESSAGE_CONNECTION_ID , 0 ) ;
3907
+ let mut env = TestEnv :: new ( ) ;
3902
3908
3903
3909
// Test with no feature flags.
3904
3910
let mut target_info = TargetInfo :: new ( )
3905
3911
. with_sint ( SINT )
3906
3912
. with_vtl ( 0 )
3907
3913
. with_feature_flags ( FeatureFlags :: new ( ) . into ( ) ) ;
3908
3914
test_initiate_contact (
3909
- & mut server,
3910
- & mut notifier,
3915
+ & mut env . server ,
3916
+ & mut env . notifier ,
3911
3917
Version :: Copper as u32 ,
3912
3918
target_info. into ( ) ,
3913
3919
true ,
3914
3920
0 ,
3915
3921
) ;
3916
3922
3923
+ env. c ( ) . handle_unload ( ) ;
3924
+ env. complete_reset ( ) ;
3925
+ env. notifier . messages . clear ( ) ;
3917
3926
// Request supported feature flags.
3918
3927
target_info. set_feature_flags (
3919
3928
FeatureFlags :: new ( )
3920
3929
. with_guest_specified_signal_parameters ( true )
3921
3930
. into ( ) ,
3922
3931
) ;
3923
3932
test_initiate_contact (
3924
- & mut server,
3925
- & mut notifier,
3933
+ & mut env . server ,
3934
+ & mut env . notifier ,
3926
3935
Version :: Copper as u32 ,
3927
3936
target_info. into ( ) ,
3928
3937
true ,
@@ -3931,14 +3940,17 @@ mod tests {
3931
3940
. into ( ) ,
3932
3941
) ;
3933
3942
3943
+ env. c ( ) . handle_unload ( ) ;
3944
+ env. complete_reset ( ) ;
3945
+ env. notifier . messages . clear ( ) ;
3934
3946
// Request unsupported feature flags. This will succeed and report back the supported ones.
3935
3947
target_info. set_feature_flags (
3936
3948
u32:: from ( FeatureFlags :: new ( ) . with_guest_specified_signal_parameters ( true ) )
3937
3949
| 0xf0000000 ,
3938
3950
) ;
3939
3951
test_initiate_contact (
3940
- & mut server,
3941
- & mut notifier,
3952
+ & mut env . server ,
3953
+ & mut env . notifier ,
3942
3954
Version :: Copper as u32 ,
3943
3955
target_info. into ( ) ,
3944
3956
true ,
@@ -3947,11 +3959,14 @@ mod tests {
3947
3959
. into ( ) ,
3948
3960
) ;
3949
3961
3962
+ env. c ( ) . handle_unload ( ) ;
3963
+ env. complete_reset ( ) ;
3964
+ env. notifier . messages . clear ( ) ;
3950
3965
// Verify client ID feature flag.
3951
3966
target_info. set_feature_flags ( FeatureFlags :: new ( ) . with_client_id ( true ) . into ( ) ) ;
3952
3967
test_initiate_contact (
3953
- & mut server,
3954
- & mut notifier,
3968
+ & mut env . server ,
3969
+ & mut env . notifier ,
3955
3970
Version :: Copper as u32 ,
3956
3971
target_info. into ( ) ,
3957
3972
true ,
@@ -4589,9 +4604,7 @@ mod tests {
4589
4604
self . server . with_notifier ( & mut self . notifier )
4590
4605
}
4591
4606
4592
- // Completes a reset operation if the server send a modify request as part of it. This
4593
- // shouldn't be called if the server was not connected or had no open channels or gpadls
4594
- // during the reset.
4607
+ // Completes a reset operation if the server sends a modify request as part of it.
4595
4608
fn complete_reset ( & mut self ) {
4596
4609
let _ = self . next_action ( ) ;
4597
4610
self . c ( )
@@ -5045,6 +5058,7 @@ mod tests {
5045
5058
env. c ( ) . reset ( ) ;
5046
5059
// We have to "complete" the connection to let the reset go through.
5047
5060
env. complete_connect ( ) ;
5061
+ env. complete_reset ( ) ;
5048
5062
env. notifier . check_reset ( ) ;
5049
5063
5050
5064
env. c ( ) . restore ( state) . unwrap ( ) ;
@@ -5107,6 +5121,7 @@ mod tests {
5107
5121
5108
5122
let state = env. server . save ( ) ;
5109
5123
env. c ( ) . reset ( ) ;
5124
+ env. complete_reset ( ) ;
5110
5125
env. notifier . check_reset ( ) ;
5111
5126
5112
5127
env. c ( ) . restore ( state) . unwrap ( ) ;
@@ -5359,6 +5374,7 @@ mod tests {
5359
5374
5360
5375
// Reserved channels and gpadls should stay open across unloads
5361
5376
env. c ( ) . handle_unload ( ) ;
5377
+ env. complete_reset ( ) ;
5362
5378
5363
5379
// Closing while disconnected should work
5364
5380
env. close_reserved ( 2 , 2 , SINT . into ( ) ) ;
@@ -5417,6 +5433,7 @@ mod tests {
5417
5433
env. c ( ) . open_complete ( offer_id1, 0 ) ;
5418
5434
5419
5435
env. c ( ) . handle_unload ( ) ;
5436
+ env. complete_reset ( ) ;
5420
5437
5421
5438
// Reset while disconnected should cleanup reserved channels
5422
5439
// and complete disconnect automatically
@@ -5439,6 +5456,7 @@ mod tests {
5439
5456
env. c ( ) . open_complete ( offer_id2, 0 ) ;
5440
5457
5441
5458
env. c ( ) . handle_unload ( ) ;
5459
+ env. complete_reset ( ) ;
5442
5460
5443
5461
env. close_reserved ( 2 , 2 , SINT . into ( ) ) ;
5444
5462
env. c ( ) . close_complete ( offer_id2) ;
@@ -5776,4 +5794,138 @@ mod tests {
5776
5794
}
5777
5795
) ;
5778
5796
}
5797
+
5798
+ #[ test]
5799
+ fn test_disconnect ( ) {
5800
+ let mut env = TestEnv :: new ( ) ;
5801
+ let _offer_id1 = env. offer ( 1 ) ;
5802
+ let _offer_id2 = env. offer ( 2 ) ;
5803
+ let _offer_id3 = env. offer ( 3 ) ;
5804
+
5805
+ env. connect ( Version :: Win10 , FeatureFlags :: new ( ) ) ;
5806
+ env. c ( ) . handle_request_offers ( ) . unwrap ( ) ;
5807
+
5808
+ // Send unload message with all channels already closed.
5809
+ env. c ( ) . handle_unload ( ) ;
5810
+
5811
+ // Check that modify_connection was invoked on the notifier.
5812
+ let req = env. notifier . next_action ( ) ;
5813
+ assert_eq ! (
5814
+ req,
5815
+ ModifyConnectionRequest {
5816
+ monitor_page: Update :: Reset ,
5817
+ interrupt_page: Update :: Reset ,
5818
+ ..Default :: default ( )
5819
+ }
5820
+ ) ;
5821
+
5822
+ env. notifier . messages . clear ( ) ;
5823
+ env. c ( ) . complete_disconnect ( ) ;
5824
+ env. notifier
5825
+ . check_message ( OutgoingMessage :: new ( & protocol:: UnloadComplete { } ) ) ;
5826
+ }
5827
+
5828
+ #[ test]
5829
+ fn test_disconnect_open_channels ( ) {
5830
+ let mut env = TestEnv :: new ( ) ;
5831
+ let offer_id1 = env. offer ( 1 ) ;
5832
+ let offer_id2 = env. offer ( 2 ) ;
5833
+ let _offer_id3 = env. offer ( 3 ) ;
5834
+
5835
+ env. connect ( Version :: Win10 , FeatureFlags :: new ( ) ) ;
5836
+ env. c ( ) . handle_request_offers ( ) . unwrap ( ) ;
5837
+
5838
+ // Open two channels.
5839
+ env. open ( 1 ) ;
5840
+ env. open ( 2 ) ;
5841
+
5842
+ env. c ( ) . open_complete ( offer_id1, 0 ) ;
5843
+ env. c ( ) . open_complete ( offer_id2, 0 ) ;
5844
+
5845
+ // Send unload message with channels still open.
5846
+ env. c ( ) . handle_unload ( ) ;
5847
+
5848
+ assert ! ( env. notifier. modify_requests. is_empty( ) ) ;
5849
+
5850
+ // Unload will close the channels, so complete that operation.
5851
+ env. c ( ) . close_complete ( offer_id1) ;
5852
+ env. c ( ) . close_complete ( offer_id2) ;
5853
+
5854
+ // Modify connection will be invoked once all channels are closed.
5855
+ let req = env. notifier . next_action ( ) ;
5856
+ assert_eq ! (
5857
+ req,
5858
+ ModifyConnectionRequest {
5859
+ monitor_page: Update :: Reset ,
5860
+ interrupt_page: Update :: Reset ,
5861
+ ..Default :: default ( )
5862
+ }
5863
+ ) ;
5864
+
5865
+ env. notifier . messages . clear ( ) ;
5866
+ env. c ( ) . complete_disconnect ( ) ;
5867
+ env. notifier
5868
+ . check_message ( OutgoingMessage :: new ( & protocol:: UnloadComplete { } ) ) ;
5869
+ }
5870
+
5871
+ #[ test]
5872
+ fn test_reinitiate_contact ( ) {
5873
+ let mut env = TestEnv :: new ( ) ;
5874
+ let _offer_id1 = env. offer ( 1 ) ;
5875
+ let _offer_id2 = env. offer ( 2 ) ;
5876
+ let _offer_id3 = env. offer ( 3 ) ;
5877
+
5878
+ env. connect ( Version :: Win10 , FeatureFlags :: new ( ) ) ;
5879
+ env. c ( ) . handle_request_offers ( ) . unwrap ( ) ;
5880
+ env. notifier . messages . clear ( ) ;
5881
+
5882
+ // Send a new InitiateContact message to force a disconnect without using reload.
5883
+ let result = env. c ( ) . handle_synic_message ( in_msg_ex (
5884
+ protocol:: MessageType :: INITIATE_CONTACT ,
5885
+ protocol:: InitiateContact {
5886
+ version_requested : Version :: Win10 as u32 ,
5887
+ interrupt_page_or_target_info : TargetInfo :: new ( ) . with_sint ( SINT ) . with_vtl ( 0 ) . into ( ) ,
5888
+ child_to_parent_monitor_page_gpa : 0x123f000 ,
5889
+ parent_to_child_monitor_page_gpa : 0x321f000 ,
5890
+ ..FromZeros :: new_zeroed ( )
5891
+ } ,
5892
+ false ,
5893
+ false ,
5894
+ ) ) ;
5895
+ assert ! ( result. is_ok( ) ) ;
5896
+
5897
+ // We will first receive a request indicating the forced disconnect.
5898
+ let req = env. notifier . next_action ( ) ;
5899
+ assert_eq ! (
5900
+ req,
5901
+ ModifyConnectionRequest {
5902
+ monitor_page: Update :: Reset ,
5903
+ interrupt_page: Update :: Reset ,
5904
+ ..Default :: default ( )
5905
+ }
5906
+ ) ;
5907
+
5908
+ env. c ( ) . complete_disconnect ( ) ;
5909
+
5910
+ // No UnloadComplete is sent in this case since Unload was not sent.
5911
+ assert ! ( env. notifier. messages. is_empty( ) ) ;
5912
+
5913
+ // Now we receive the request for the new connection.
5914
+ let req = env. notifier . next_action ( ) ;
5915
+ assert_eq ! (
5916
+ req,
5917
+ ModifyConnectionRequest {
5918
+ version: Some ( Version :: Win10 as u32 ) ,
5919
+ monitor_page: Update :: Set ( MonitorPageGpas {
5920
+ child_to_parent: 0x123f000 ,
5921
+ parent_to_child: 0x321f000 ,
5922
+ } ) ,
5923
+ interrupt_page: Update :: Reset ,
5924
+ target_message_vp: Some ( 0 ) ,
5925
+ ..Default :: default ( )
5926
+ }
5927
+ ) ;
5928
+
5929
+ env. complete_connect ( ) ;
5930
+ }
5779
5931
}
0 commit comments