@ -1196,8 +1196,8 @@ class MockMultiServerEventThreadTest
: public MockEventThreadOptsTest ,
public : : testing : : WithParamInterface < std : : tuple < ares_evsys_t , int , bool > > {
public :
MockMultiServerEventThreadTest ( bool rotate )
: MockEventThreadOptsTest ( 3 , std : : get < 0 > ( GetParam ( ) ) , std : : get < 1 > ( GetParam ( ) ) , std : : get < 2 > ( GetParam ( ) ) , nullptr , rotate ? ARES_OPT_ROTATE : ARES_OPT_NOROTATE ) { }
MockMultiServerEventThreadTest ( ares_options * opts , int optmask )
: MockEventThreadOptsTest ( 3 , std : : get < 0 > ( GetParam ( ) ) , std : : get < 1 > ( GetParam ( ) ) , std : : get < 2 > ( GetParam ( ) ) , opts , optmask ) { }
void CheckExample ( ) {
HostResult result ;
ares_gethostbyname ( channel_ , " www.example.com. " , AF_INET , HostCallback , & result ) ;
@ -1211,7 +1211,22 @@ class MockMultiServerEventThreadTest
class NoRotateMultiMockEventThreadTest : public MockMultiServerEventThreadTest {
public :
NoRotateMultiMockEventThreadTest ( ) : MockMultiServerEventThreadTest ( false ) { }
NoRotateMultiMockEventThreadTest ( ) : MockMultiServerEventThreadTest ( nullptr , ARES_OPT_NOROTATE ) { }
} ;
class ServerFailoverOptsMockEventThreadTest : public MockMultiServerEventThreadTest {
public :
ServerFailoverOptsMockEventThreadTest ( )
: MockMultiServerEventThreadTest ( FillOptions ( & opts_ ) ,
ARES_OPT_SERVER_FAILOVER | ARES_OPT_NOROTATE ) { }
static struct ares_options * FillOptions ( struct ares_options * opts ) {
memset ( opts , 0 , sizeof ( struct ares_options ) ) ;
opts - > server_failover_opts . retry_chance = 1 ;
opts - > server_failover_opts . retry_delay = 100 ;
return opts ;
}
private :
struct ares_options opts_ ;
} ;
TEST_P ( NoRotateMultiMockEventThreadTest , ThirdServer ) {
@ -1333,6 +1348,87 @@ TEST_P(NoRotateMultiMockEventThreadTest, ServerNoResponseFailover) {
EXPECT_EQ ( " {'www.example.com' aliases=[] addrs=[2.3.4.5]} " , ss4 . str ( ) ) ;
}
// Test case to trigger server failover behavior. We use a retry chance of
// 100% and a retry delay of 100ms so that we can test behavior reliably.
TEST_P ( ServerFailoverOptsMockEventThreadTest , ServerFailoverOpts ) {
DNSPacket servfailrsp ;
servfailrsp . set_response ( ) . set_aa ( ) . set_rcode ( SERVFAIL )
. add_question ( new DNSQuestion ( " www.example.com " , T_A ) ) ;
DNSPacket okrsp ;
okrsp . set_response ( ) . set_aa ( )
. add_question ( new DNSQuestion ( " www.example.com " , T_A ) )
. add_answer ( new DNSARR ( " www.example.com " , 100 , { 2 , 3 , 4 , 5 } ) ) ;
// 1. If all servers are healthy, then the first server should be selected.
EXPECT_CALL ( * servers_ [ 0 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & okrsp ) ) ;
CheckExample ( ) ;
// 2. Failed servers should be retried after the retry delay.
//
// Fail server #0 but leave server #1 as healthy.
EXPECT_CALL ( * servers_ [ 0 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & servfailrsp ) ) ;
EXPECT_CALL ( * servers_ [ 1 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 1 ] . get ( ) , & okrsp ) ) ;
CheckExample ( ) ;
// Sleep for the retry delay and send in another query. Server #0 should be
// retried.
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ;
EXPECT_CALL ( * servers_ [ 0 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & okrsp ) ) ;
CheckExample ( ) ;
// 3. If there are multiple failed servers, then the servers should be
// retried in sorted order.
//
// Fail all servers for the first round of tries. On the second round server
// #1 responds successfully.
EXPECT_CALL ( * servers_ [ 0 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & servfailrsp ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & servfailrsp ) ) ;
EXPECT_CALL ( * servers_ [ 1 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 1 ] . get ( ) , & servfailrsp ) )
. WillOnce ( SetReply ( servers_ [ 1 ] . get ( ) , & okrsp ) ) ;
EXPECT_CALL ( * servers_ [ 2 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 2 ] . get ( ) , & servfailrsp ) ) ;
CheckExample ( ) ;
// At this point the sorted servers look like [1] (f0) [2] (f1) [0] (f2).
// Sleep for the retry delay and send in another query. Server #2 should be
// retried first, and then server #0.
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ;
EXPECT_CALL ( * servers_ [ 2 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 2 ] . get ( ) , & servfailrsp ) ) ;
EXPECT_CALL ( * servers_ [ 0 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & okrsp ) ) ;
CheckExample ( ) ;
// 4. If there are multiple failed servers, then servers which have not yet
// met the retry delay should be skipped.
//
// The sorted servers currently look like [0] (f0) [1] (f0) [2] (f2) and
// server #2 has just been retried.
// Sleep for half the retry delay and trigger a failure on server #0.
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 50 ) ) ;
EXPECT_CALL ( * servers_ [ 0 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 0 ] . get ( ) , & servfailrsp ) ) ;
EXPECT_CALL ( * servers_ [ 1 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 1 ] . get ( ) , & okrsp ) ) ;
CheckExample ( ) ;
// The sorted servers now look like [1] (f0) [0] (f1) [2] (f2). Server #0
// has just failed whilst server #2 is halfway through the retry delay.
// Sleep for another half the retry delay and check that server #2 is retried
// whilst server #0 is not.
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 50 ) ) ;
EXPECT_CALL ( * servers_ [ 2 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 2 ] . get ( ) , & servfailrsp ) ) ;
EXPECT_CALL ( * servers_ [ 1 ] , OnRequest ( " www.example.com " , T_A ) )
. WillOnce ( SetReply ( servers_ [ 1 ] . get ( ) , & okrsp ) ) ;
CheckExample ( ) ;
}
static const char * evsys_tostr ( ares_evsys_t evsys )
{
@ -1396,6 +1492,8 @@ INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockEDNSEventThreadTest, ::testing::Va
INSTANTIATE_TEST_SUITE_P ( TransportModes , NoRotateMultiMockEventThreadTest , : : testing : : ValuesIn ( ares : : test : : evsys_families_modes ) , ares : : test : : PrintEvsysFamilyMode ) ;
INSTANTIATE_TEST_SUITE_P ( TransportModes , ServerFailoverOptsMockEventThreadTest , : : testing : : ValuesIn ( ares : : test : : evsys_families_modes ) , ares : : test : : PrintEvsysFamilyMode ) ;
} // namespace test
} // namespace ares