2121 * questions.
2222 */
2323
24- /**
24+ /*
2525 * @test
26- * @run main/othervm DeadSSLLdapTimeoutTest
2726 * @bug 8141370
2827 * @key intermittent
28+ * @library /lib/testlibrary
29+ * @build DeadSSLSocketFactory
30+ * @run main/othervm DeadSSLLdapTimeoutTest
2931 */
3032
31- import java .net .Socket ;
33+ import java .io .EOFException ;
34+ import java .io .IOException ;
35+ import java .net .InetAddress ;
36+ import java .net .InetSocketAddress ;
3237import java .net .ServerSocket ;
38+ import java .net .Socket ;
39+ import java .net .SocketAddress ;
3340import java .net .SocketTimeoutException ;
34- import java .io .*;
35- import javax .naming .*;
36- import javax .naming .directory .*;
37- import java .util .List ;
41+ import javax .naming .Context ;
42+ import javax .naming .InitialContext ;
43+ import javax .naming .NamingException ;
3844import java .util .Hashtable ;
39- import java .util .ArrayList ;
4045import java .util .concurrent .Callable ;
41- import java .util .concurrent .ExecutionException ;
42- import java .util .concurrent .Executors ;
43- import java .util .concurrent .ExecutorService ;
44- import java .util .concurrent .Future ;
45- import java .util .concurrent .ScheduledExecutorService ;
46- import java .util .concurrent .ScheduledFuture ;
47- import java .util .concurrent .TimeoutException ;
46+ import java .util .concurrent .CountDownLatch ;
4847import java .util .concurrent .TimeUnit ;
48+ import javax .naming .directory .InitialDirContext ;
4949import javax .net .ssl .SSLHandshakeException ;
5050
51- import static java .util .concurrent .TimeUnit .MILLISECONDS ;
51+ import jdk .testlibrary .net .URIBuilder ;
52+
5253import static java .util .concurrent .TimeUnit .NANOSECONDS ;
5354
5455
@@ -57,26 +58,26 @@ class DeadServerTimeoutSSLTest implements Callable {
5758 Hashtable env ;
5859 DeadSSLServer server ;
5960 boolean passed = false ;
60- private int HANGING_TEST_TIMEOUT = 20_000 ;
6161
6262 public DeadServerTimeoutSSLTest (Hashtable env ) throws IOException {
63- this .server = new DeadSSLServer ();
63+ SocketAddress sockAddr = new InetSocketAddress (
64+ InetAddress .getLoopbackAddress (), 0 );
65+ this .server = new DeadSSLServer (sockAddr );
6466 this .env = env ;
6567 }
6668
67- public void performOp (InitialContext ctx ) throws NamingException {}
68-
69- public void handleNamingException (NamingException e , long start , long end ) {
69+ public void handleNamingException (NamingException e ) {
7070 if (e .getCause () instanceof SocketTimeoutException
7171 || e .getCause ().getCause () instanceof SocketTimeoutException ) {
7272 // SSL connect will timeout via readReply using
7373 // SocketTimeoutException
74- e . printStackTrace ( );
74+ System . out . println ( "PASS: Observed expected SocketTimeoutException" );
7575 pass ();
7676 } else if (e .getCause () instanceof SSLHandshakeException
7777 && e .getCause ().getCause () instanceof EOFException ) {
7878 // test seems to be failing intermittently on some
7979 // platforms.
80+ System .out .println ("PASS: Observed expected SSLHandshakeException/EOFException" );
8081 pass ();
8182 } else {
8283 fail (e );
@@ -92,6 +93,7 @@ public void fail() {
9293 }
9394
9495 public void fail (Exception e ) {
96+ System .err .println ("FAIL: Unexpected exception was observed:" + e .getMessage ());
9597 throw new RuntimeException ("Test failed" , e );
9698 }
9799
@@ -106,55 +108,106 @@ boolean shutItDown(InitialContext ctx) {
106108
107109 public Boolean call () {
108110 InitialContext ctx = null ;
109- ScheduledFuture killer = null ;
110- long start = System .nanoTime ();
111111
112112 try {
113- while (!server .accepting ())
114- Thread .sleep (200 ); // allow the server to start up
113+ server .serverStarted .await (); // Wait for the server to start-up
115114 Thread .sleep (200 ); // to be sure
116115
117- env .put (Context .PROVIDER_URL , "ldap://localhost:" +
118- server .getLocalPort ());
116+ env .put (Context .PROVIDER_URL ,
117+ URIBuilder .newBuilder ()
118+ .scheme ("ldap" )
119+ .loopback ()
120+ .port (server .getLocalPort ())
121+ .buildUnchecked ().toString ()
122+ );
119123
124+ long start = System .nanoTime ();
120125 try {
121126 ctx = new InitialDirContext (env );
122- performOp (ctx );
123127 fail ();
124128 } catch (NamingException e ) {
125129 long end = System .nanoTime ();
126130 System .out .println (this .getClass ().toString () + " - elapsed: "
127131 + NANOSECONDS .toMillis (end - start ));
128- handleNamingException (e , start , end );
132+ handleNamingException (e );
129133 } finally {
130- if ( killer != null && ! killer . isDone ())
131- killer . cancel ( true );
134+ // Stop the server side thread
135+ server . testDone . countDown ( );
132136 shutItDown (ctx );
133137 server .close ();
134138 }
135139 return passed ;
136- } catch (IOException | InterruptedException e ) {
140+ } catch (IOException | InterruptedException e ) {
137141 throw new RuntimeException (e );
138142 }
139143 }
140144}
141145
142146class DeadSSLServer extends Thread {
143147 ServerSocket serverSock ;
144- boolean accepting = false ;
145-
146- public DeadSSLServer () throws IOException {
147- this .serverSock = new ServerSocket (0 );
148+ // Latch to be used by client to wait for server to start
149+ CountDownLatch serverStarted = new CountDownLatch (1 );
150+
151+ // Latch to be used by server thread to wait for client to finish testing
152+ CountDownLatch testDone = new CountDownLatch (1 );
153+
154+ public DeadSSLServer (SocketAddress socketAddress ) throws IOException {
155+ // create unbound server socket
156+ ServerSocket srvSock = new ServerSocket ();
157+ // bind it to the address provided
158+ srvSock .bind (socketAddress );
159+ this .serverSock = srvSock ;
148160 start ();
149161 }
150162
151163 public void run () {
152- while (true ) {
153- try {
154- accepting = true ;
155- Socket socket = serverSock .accept ();
164+ // Signal client to proceed with the test
165+ serverStarted .countDown ();
166+ while (true ) {
167+ try (Socket acceptedSocket = serverSock .accept ()) {
168+ System .err .println ("Accepted connection:" + acceptedSocket );
169+ int iteration = 0 ;
170+ // Wait for socket to get opened by DeadSSLSocketFactory and connected to the test server
171+ while (iteration ++ < 20 ) {
172+ if (DeadSSLSocketFactory .firstCreatedSocket .get () != null &&
173+ DeadSSLSocketFactory .firstCreatedSocket .get ().isConnected ()) {
174+ break ;
175+ }
176+ try {
177+ TimeUnit .MILLISECONDS .sleep (50 );
178+ } catch (InterruptedException ie ) {
179+ }
180+ }
181+ Socket clientSideSocket = DeadSSLSocketFactory .firstCreatedSocket .get ();
182+ System .err .printf ("Got SSLSocketFactory connection after %d iterations: %s%n" ,
183+ iteration , clientSideSocket );
184+
185+ if (clientSideSocket == null || !clientSideSocket .isConnected ()) {
186+ // If after 1000 ms client side connection is not opened - probably other local process
187+ // tried to connect to the test server socket. Close current connection and retry accept.
188+ continue ;
189+ } else {
190+ // Check if accepted socket is connected to the LDAP client
191+ if (acceptedSocket .getLocalPort () == clientSideSocket .getPort () &&
192+ acceptedSocket .getPort () == clientSideSocket .getLocalPort () &&
193+ acceptedSocket .getInetAddress ().equals (clientSideSocket .getLocalAddress ())) {
194+ System .err .println ("Accepted connection is originated from LDAP client:" + acceptedSocket );
195+ try {
196+ // Give LDAP client time to fully establish the connection.
197+ // When client is done - the accepted socket will be closed
198+ testDone .await ();
199+ } catch (InterruptedException e ) {
200+ }
201+ break ;
202+ } else {
203+ // If accepted socket is not from the LDAP client - the accepted connection will be closed and new
204+ // one will be accepted
205+ System .err .println ("SSLSocketFactory connection has been established, but originated not from" +
206+ " the test's LDAP client:" + acceptedSocket );
207+ }
208+ }
156209 } catch (Exception e ) {
157- break ;
210+ System . err . println ( "Server socket. Failure to accept connection:" + e . getMessage ()) ;
158211 }
159212 }
160213 }
@@ -163,28 +216,26 @@ public int getLocalPort() {
163216 return serverSock .getLocalPort ();
164217 }
165218
166- public boolean accepting () {
167- return accepting ;
168- }
169-
170219 public void close () throws IOException {
171220 serverSock .close ();
172221 }
173222}
174223
175224public class DeadSSLLdapTimeoutTest {
225+ // com.sun.jndi.ldap.connect.timeout value to set
226+ static final String CONNECT_TIMEOUT_MS = "10" ;
227+
228+ // com.sun.jndi.ldap.read.timeout value to set
229+ static final String READ_TIMEOUT_MS = "3000" ;
176230
177231 static Hashtable createEnv () {
178232 Hashtable env = new Hashtable (11 );
179233 env .put (Context .INITIAL_CONTEXT_FACTORY ,
180- "com.sun.jndi.ldap.LdapCtxFactory" );
234+ "com.sun.jndi.ldap.LdapCtxFactory" );
181235 return env ;
182236 }
183237
184238 public static void main (String [] args ) throws Exception {
185-
186- InitialContext ctx = null ;
187-
188239 //
189240 // Running this test serially as it seems to tickle a problem
190241 // on older kernels
@@ -193,19 +244,24 @@ public static void main(String[] args) throws Exception {
193244 // and ssl enabled
194245 // this should exit with a SocketTimeoutException as the root cause
195246 // it should also use the connect timeout instead of the read timeout
196- System .out .println ("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL" );
247+ System .out .printf ("Running connect timeout test with %sms connect timeout," +
248+ " %sms read timeout & SSL%n" ,
249+ CONNECT_TIMEOUT_MS , READ_TIMEOUT_MS );
250+
197251 Hashtable sslenv = createEnv ();
198- sslenv .put ("com.sun.jndi.ldap.connect.timeout" , "10" );
199- sslenv .put ("com.sun.jndi.ldap.read.timeout" , "3000" );
252+ // Setup connect timeout environment property
253+ sslenv .put ("com.sun.jndi.ldap.connect.timeout" , CONNECT_TIMEOUT_MS );
254+ // Setup read timeout environment property
255+ sslenv .put ("com.sun.jndi.ldap.read.timeout" , READ_TIMEOUT_MS );
256+ // Setup DeadSSLSocketFactory to track the client's first LDAP connection
257+ sslenv .put ("java.naming.ldap.factory.socket" , "DeadSSLSocketFactory" );
258+ // Use SSL protocol
200259 sslenv .put (Context .SECURITY_PROTOCOL , "ssl" );
201- boolean testFailed =
202- (new DeadServerTimeoutSSLTest (sslenv ).call ()) ? false : true ;
203260
261+ boolean testFailed = !new DeadServerTimeoutSSLTest (sslenv ).call ();
204262 if (testFailed ) {
205263 throw new AssertionError ("some tests failed" );
206264 }
207-
208265 }
209-
210266}
211267
0 commit comments