1717package com .mongodb .internal .connection ;
1818
1919import com .mongodb .ClusterFixture ;
20+ import com .mongodb .MongoSocketException ;
2021import com .mongodb .MongoSocketOpenException ;
2122import com .mongodb .ServerAddress ;
23+ import com .mongodb .connection .AsyncCompletionHandler ;
2224import com .mongodb .connection .SocketSettings ;
2325import com .mongodb .connection .SslSettings ;
2426import com .mongodb .internal .TimeoutContext ;
2527import com .mongodb .internal .TimeoutSettings ;
28+ import com .mongodb .spi .dns .InetAddressResolver ;
2629import org .bson .ByteBuf ;
2730import org .bson .ByteBufNIO ;
2831import org .junit .jupiter .api .DisplayName ;
2932import org .junit .jupiter .api .Test ;
3033import org .junit .jupiter .params .ParameterizedTest ;
3134import org .junit .jupiter .params .provider .ValueSource ;
35+ import org .mockito .ArgumentCaptor ;
3236import org .mockito .MockedStatic ;
3337import org .mockito .Mockito ;
3438import org .mockito .invocation .InvocationOnMock ;
3741import javax .net .ssl .SSLContext ;
3842import javax .net .ssl .SSLEngine ;
3943import java .io .IOException ;
44+ import java .net .InetAddress ;
4045import java .net .ServerSocket ;
4146import java .nio .ByteBuffer ;
4247import java .nio .channels .InterruptedByTimeoutException ;
4348import java .nio .channels .SocketChannel ;
4449import java .util .Collections ;
50+ import java .util .List ;
4551import java .util .concurrent .TimeUnit ;
4652
4753import static com .mongodb .ClusterFixture .getPrimaryServerDescription ;
5258import static org .junit .jupiter .api .Assertions .assertFalse ;
5359import static org .junit .jupiter .api .Assertions .assertInstanceOf ;
5460import static org .junit .jupiter .api .Assertions .assertNotNull ;
61+ import static org .junit .jupiter .api .Assertions .assertSame ;
5562import static org .junit .jupiter .api .Assertions .assertThrows ;
5663import static org .junit .jupiter .api .Assertions .assertTrue ;
5764import static org .junit .jupiter .api .Assertions .fail ;
5865import static org .junit .jupiter .api .Assumptions .assumeTrue ;
66+ import static org .mockito .ArgumentMatchers .any ;
5967import static org .mockito .ArgumentMatchers .anyInt ;
6068import static org .mockito .ArgumentMatchers .anyString ;
6169import static org .mockito .Mockito .atLeast ;
@@ -68,6 +76,69 @@ class TlsChannelStreamFunctionalTest {
6876 private static final String UNREACHABLE_PRIVATE_IP_ADDRESS = "10.255.255.1" ;
6977 private static final int UNREACHABLE_PORT = 65333 ;
7078
79+ @ Test
80+ void shouldFailAsyncCompletionHandlerWithoutOpeningSocketChannelIfNameResolutionFails () {
81+ //given
82+ ServerAddress serverAddress = new ServerAddress ();
83+ MongoSocketException exception = new MongoSocketException ("Temporary failure in name resolution" , serverAddress );
84+ InetAddressResolver inetAddressResolver = new InetAddressResolver () {
85+ @ Override
86+ public List <InetAddress > lookupByName (final String host ) {
87+ throw exception ;
88+ }
89+ };
90+
91+ try (StreamFactoryFactory streamFactoryFactory = new TlsChannelStreamFactoryFactory (inetAddressResolver );
92+ MockedStatic <SocketChannel > socketChannelMockedStatic = Mockito .mockStatic (SocketChannel .class )) {
93+ StreamFactory streamFactory = streamFactoryFactory .create (SocketSettings .builder ()
94+ .connectTimeout (100 , TimeUnit .MILLISECONDS )
95+ .build (), SSL_SETTINGS );
96+ Stream stream = streamFactory .create (serverAddress );
97+ @ SuppressWarnings ("unchecked" )
98+ AsyncCompletionHandler <Void > handler = Mockito .mock (AsyncCompletionHandler .class );
99+
100+ //when
101+ stream .openAsync (createOperationContext (100 ), handler );
102+
103+ //then
104+ verify (handler ).failed (exception );
105+ verify (handler , times (0 )).completed (null );
106+ socketChannelMockedStatic .verify (SocketChannel ::open , times (0 ));
107+ }
108+ }
109+
110+ @ Test
111+ void shouldCloseSocketChannelIfConnectFailsBeforeRegistration () throws IOException {
112+ //given
113+ ServerAddress serverAddress = new ServerAddress ();
114+ IOException exception = new IOException ("connect failed" );
115+ InetAddressResolver inetAddressResolver = host -> Collections .singletonList (InetAddress .getLoopbackAddress ());
116+
117+ try (SocketChannel socketChannel = Mockito .spy (SocketChannel .open ());
118+ StreamFactoryFactory streamFactoryFactory = new TlsChannelStreamFactoryFactory (inetAddressResolver );
119+ MockedStatic <SocketChannel > socketChannelMockedStatic = Mockito .mockStatic (SocketChannel .class )) {
120+ socketChannelMockedStatic .when (SocketChannel ::open ).thenReturn (socketChannel );
121+ Mockito .doThrow (exception ).when (socketChannel ).connect (any ());
122+ StreamFactory streamFactory = streamFactoryFactory .create (SocketSettings .builder ()
123+ .connectTimeout (100 , TimeUnit .MILLISECONDS )
124+ .build (), SSL_SETTINGS );
125+ Stream stream = streamFactory .create (serverAddress );
126+ @ SuppressWarnings ("unchecked" )
127+ AsyncCompletionHandler <Void > handler = Mockito .mock (AsyncCompletionHandler .class );
128+ ArgumentCaptor <Throwable > failureCaptor = ArgumentCaptor .forClass (Throwable .class );
129+
130+ //when
131+ stream .openAsync (createOperationContext (100 ), handler );
132+
133+ //then
134+ verify (handler ).failed (failureCaptor .capture ());
135+ MongoSocketOpenException actualException = assertInstanceOf (MongoSocketOpenException .class , failureCaptor .getValue ());
136+ assertSame (exception , actualException .getCause ());
137+ verify (handler , times (0 )).completed (null );
138+ verify (socketChannel ).close ();
139+ }
140+ }
141+
71142 @ ParameterizedTest
72143 @ ValueSource (ints = {500 , 1000 , 2000 })
73144 void shouldInterruptConnectionEstablishmentWhenConnectionTimeoutExpires (final int connectTimeoutMs ) throws IOException {
0 commit comments