1616
1717package com .mongodb .internal .connection ;
1818
19+ import com .mongodb .MongoException ;
20+ import com .mongodb .MongoInterruptedException ;
1921import com .mongodb .MongoSocketException ;
2022import com .mongodb .MongoSocketOpenException ;
2123import com .mongodb .MongoSocketReadException ;
24+ import com .mongodb .MongoSocksProxyException ;
2225import com .mongodb .ServerAddress ;
2326import com .mongodb .connection .AsyncCompletionHandler ;
2427import com .mongodb .connection .ProxySettings ;
2528import com .mongodb .connection .SocketSettings ;
2629import com .mongodb .connection .SslSettings ;
30+ import com .mongodb .lang .Nullable ;
2731import com .mongodb .spi .dns .InetAddressResolver ;
2832import org .bson .ByteBuf ;
2933
3842import java .net .SocketTimeoutException ;
3943import java .util .Iterator ;
4044import java .util .List ;
45+ import java .util .Optional ;
4146
4247import static com .mongodb .assertions .Assertions .assertTrue ;
4348import static com .mongodb .assertions .Assertions .notNull ;
@@ -79,13 +84,26 @@ public void open(final OperationContext operationContext) {
7984 socket = initializeSocket (operationContext );
8085 outputStream = socket .getOutputStream ();
8186 inputStream = socket .getInputStream ();
87+ } catch (MongoSocksProxyException e ) {
88+ close ();
89+ throw translateOr (e .getCause (), e );
8290 } catch (IOException e ) {
8391 close ();
84- throw translateInterruptedException (e , "Interrupted while connecting" )
85- .orElseThrow (() -> new MongoSocketOpenException ("Exception opening socket" , getAddress (), e ));
92+ throw translateOr (e , new MongoSocketOpenException ("Exception opening socket" , getAddress (), e ));
8693 }
8794 }
8895
96+ /**
97+ * If {@code interruptCandidate} represents a thread interruption, returns the corresponding
98+ * {@link MongoInterruptedException}; otherwise returns {@code fallback}.
99+ */
100+ private static MongoException translateOr (@ Nullable final Throwable interruptCandidate ,
101+ final MongoException fallback ) {
102+ Optional <MongoInterruptedException > translated =
103+ translateInterruptedException (interruptCandidate , "Interrupted while connecting" );
104+ return translated .isPresent () ? translated .get () : fallback ;
105+ }
106+
89107 protected Socket initializeSocket (final OperationContext operationContext ) throws IOException {
90108 ProxySettings proxySettings = settings .getProxySettings ();
91109 if (proxySettings .isProxyEnabled ()) {
@@ -119,15 +137,28 @@ private SSLSocket initializeSslSocketOverSocksProxy(final OperationContext opera
119137 final int serverPort = address .getPort ();
120138
121139 SocksSocket socksProxy = new SocksSocket (settings .getProxySettings ());
122- configureSocket (socksProxy , operationContext , settings );
123- InetSocketAddress inetSocketAddress = toSocketAddress (serverHost , serverPort );
124- socksProxy .connect (inetSocketAddress , operationContext .getTimeoutContext ().getConnectTimeoutMs ());
125-
126- SSLSocket sslSocket = (SSLSocket ) sslSocketFactory .createSocket (socksProxy , serverHost , serverPort , true );
127- //Even though Socks proxy connection is already established, TLS handshake has not been performed yet.
128- //So it is possible to set SSL parameters before handshake is done.
129- configureSslSocket (sslSocket , sslSettings , inetSocketAddress );
130- return sslSocket ;
140+ // Track the outermost socket layer to close on failure. Initially this is socksProxy;
141+ // once we wrap it into an SSLSocket, that becomes the outermost layer and closing it
142+ // tears down the underlying socksProxy as well.
143+ Socket toClose = socksProxy ;
144+ try {
145+ configureSocket (socksProxy , operationContext , settings );
146+ InetSocketAddress inetSocketAddress = toSocketAddress (serverHost , serverPort );
147+ socksProxy .connect (inetSocketAddress , operationContext .getTimeoutContext ().getConnectTimeoutMs ());
148+ SSLSocket sslSocket = (SSLSocket ) sslSocketFactory .createSocket (socksProxy , serverHost , serverPort , true );
149+ toClose = sslSocket ;
150+ //Even though Socks proxy connection is already established, TLS handshake has not been performed yet.
151+ //So it is possible to set SSL parameters before handshake is done.
152+ configureSslSocket (sslSocket , sslSettings , inetSocketAddress );
153+ return sslSocket ;
154+ } catch (IOException | RuntimeException e ) {
155+ try {
156+ toClose .close ();
157+ } catch (IOException closeException ) {
158+ e .addSuppressed (closeException );
159+ }
160+ throw e ;
161+ }
131162 }
132163
133164
@@ -141,17 +172,27 @@ private static InetSocketAddress toSocketAddress(final String serverHost, final
141172
142173 private Socket initializeSocketOverSocksProxy (final OperationContext operationContext ) throws IOException {
143174 Socket createdSocket = socketFactory .createSocket ();
144- configureSocket (createdSocket , operationContext , settings );
145- /*
146- Wrap the configured socket with SocksSocket to add extra functionality.
147- Reason for separate steps: We can't directly extend Java 11 methods within 'SocksSocket'
148- to configure itself.
149- */
150- SocksSocket socksProxy = new SocksSocket (createdSocket , settings .getProxySettings ());
151-
152- socksProxy .connect (toSocketAddress (address .getHost (), address .getPort ()),
153- operationContext .getTimeoutContext ().getConnectTimeoutMs ());
154- return socksProxy ;
175+ try {
176+ configureSocket (createdSocket , operationContext , settings );
177+ /*
178+ Wrap the configured socket with SocksSocket to add extra functionality.
179+ Reason for separate steps: We can't directly extend Java 11 methods within 'SocksSocket'
180+ to configure itself.
181+ */
182+ SocksSocket socksProxy = new SocksSocket (createdSocket , settings .getProxySettings ());
183+ socksProxy .connect (toSocketAddress (address .getHost (), address .getPort ()),
184+ operationContext .getTimeoutContext ().getConnectTimeoutMs ());
185+ return socksProxy ;
186+ } catch (IOException | RuntimeException e ) {
187+ // SocksSocket.connect() closes itself on failure, but createdSocket may not yet
188+ // be owned by a SocksSocket (e.g. configureSocket threw). Close defensively;
189+ try {
190+ createdSocket .close ();
191+ } catch (IOException closeException ) {
192+ e .addSuppressed (closeException );
193+ }
194+ throw e ;
195+ }
155196 }
156197
157198 @ Override
0 commit comments