test/com/sun/jndi/ldap/LdapTimeoutTest.java

Print this page




  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @run main/othervm LdapTimeoutTest
  27  * @bug 7094377 8000487 6176036 7056489
  28  * @summary Timeout tests for ldap
  29  */
  30 
  31 import com.sun.jndi.ldap.Connection;
  32 
  33 import java.net.Socket;
  34 import java.net.ServerSocket;
  35 import java.net.SocketTimeoutException;
  36 import java.io.*;
  37 import javax.naming.*;
  38 import javax.naming.directory.*;

  39 import java.util.Hashtable;

  40 import java.util.concurrent.Callable;

  41 import java.util.concurrent.Executors;


  42 import java.util.concurrent.ScheduledExecutorService;
  43 import java.util.concurrent.ScheduledFuture;


  44 
  45 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  46 import static java.util.concurrent.TimeUnit.NANOSECONDS;
  47 
  48 public class LdapTimeoutTest {
  49 
  50     static volatile int passed = 0, failed = 0;
  51     static void pass() {passed++;}
  52     static void fail() {failed++; Thread.dumpStack();}
  53 
  54     public static void main(String[] args) throws Exception {
  55         ServerSocket serverSock = new ServerSocket(0);
  56         Server s = new Server(serverSock);
  57         s.start();
  58         Thread.sleep(200);
  59 
  60         Hashtable env = new Hashtable(11);
  61         env.put(Context.INITIAL_CONTEXT_FACTORY,
  62             "com.sun.jndi.ldap.LdapCtxFactory");
  63         env.put(Context.PROVIDER_URL, "ldap://localhost:" +
  64             serverSock.getLocalPort());
  65 
  66         env.put(Context.SECURITY_AUTHENTICATION,"simple");





  67 
  68         env.put(Context.SECURITY_PRINCIPAL, "user");
  69         env.put(Context.SECURITY_CREDENTIALS, "password");

  70 
  71         InitialContext ctx = null;
  72         try {
  73             new LdapTimeoutTest().deadServerNoTimeout(env);
  74 
  75             env.put("com.sun.jndi.ldap.connect.timeout", "10");
  76             env.put("com.sun.jndi.ldap.read.timeout", "3000");
  77             new LdapTimeoutTest().ldapReadTimeoutTest(env, false);
  78             new LdapTimeoutTest().ldapReadTimeoutTest(env, true);
  79             new LdapTimeoutTest().simpleAuthConnectTest(env);
  80         } finally {
  81             s.interrupt();
  82         }
  83 
  84         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
  85         if (failed > 0) throw new AssertionError("Some tests failed");




  86     }

  87 
  88     void ldapReadTimeoutTest(Hashtable env, boolean ssl) {
  89         InitialContext ctx = null;
  90         if (ssl) env.put(Context.SECURITY_PROTOCOL, "ssl");
  91         long start = System.nanoTime();

  92         try {






















  93             ctx = new InitialDirContext(env);

































  94             SearchControls scl = new SearchControls();
  95             scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
  96             NamingEnumeration<SearchResult> answer = ((InitialDirContext)ctx)
  97                 .search("ou=People,o=JNDITutorial", "(objectClass=*)", scl);
  98             // shouldn't reach here
  99             fail();
 100         } catch (NamingException e) {
 101             if (ssl) {
 102                 if (e.getCause() instanceof SocketTimeoutException) {
 103                     pass();
 104                 } else if (e.getCause() instanceof InterruptedIOException) {
 105                     Thread.interrupted();
 106                     fail();
 107                 }


































 108             } else {
 109                 pass();
 110             }
 111         } finally {
 112             if (!shutItDown(ctx)) fail();
 113         }






 114     }
 115 
 116     void simpleAuthConnectTest(Hashtable env) {
 117         InitialContext ctx = null;
 118         long start = System.nanoTime();
 119         try {
 120             ctx = new InitialDirContext(env);
 121             // shouldn't reach here
 122             System.err.println("Fail: InitialDirContext succeeded");
 123             fail();
 124         } catch (NamingException e) {
 125             long end = System.nanoTime();
 126             if (e.getCause() instanceof SocketTimeoutException) {
 127                 if (NANOSECONDS.toMillis(end - start) < 2_900) {
 128                     pass();
 129                 } else {
 130                     System.err.println("Fail: Waited too long");
 131                     fail();
 132                 }
 133             } else if (e.getCause() instanceof InterruptedIOException) {
 134                 Thread.interrupted();
 135                 fail();











 136             } else {
 137                 fail();
 138             }
 139         } finally {
 140             if (!shutItDown(ctx)) fail();
 141         }









 142     }
 143 
 144     void deadServerNoTimeout(Hashtable env) {
 145         InitialContext ctx = null;
 146         long start = System.currentTimeMillis();
 147         try {
 148             ctx = new InitialDirContext(env);
 149             SearchControls scl = new SearchControls();
 150             scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
 151             NamingEnumeration<SearchResult> answer = ((InitialDirContext)ctx)
 152                 .search("ou=People,o=JNDITutorial", "(objectClass=*)", scl);
 153             // shouldn't reach here
 154             fail();
 155         } catch (NamingException e) {
 156             long elapsed = System.currentTimeMillis() - start;
 157             if (elapsed < Connection.DEFAULT_READ_TIMEOUT_MILLIS) {
 158                 System.err.printf("fail: timeout should be at least %s ms, " +
 159                                 "actual time is %s ms%n",
 160                         Connection.DEFAULT_READ_TIMEOUT_MILLIS, elapsed);
 161                 e.printStackTrace();
 162                 fail();
 163             } else {
 164                 pass();
 165             }
 166         } finally {
 167             if (!shutItDown(ctx)) fail();
 168         }






 169     }
 170 
 171     boolean shutItDown(InitialContext ctx) {
 172         try {
 173             if (ctx != null) ctx.close();
 174             return true;
 175         } catch (NamingException ex) {
 176             return false;
 177         }
 178     }

 179 
 180     static class Server extends Thread {
 181         final ServerSocket serverSock;
 182 
 183         Server(ServerSocket serverSock) {
 184             this.serverSock = serverSock;
 185         }

 186 







 187         public void run() {
 188             try {

 189                 Socket socket = serverSock.accept();
 190             } catch (IOException e) {}






 191         }






 192     }

 193 }
 194 

























































































































  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @run main/othervm LdapTimeoutTest
  27  * @bug 7094377 8000487 6176036 7056489
  28  * @summary Timeout tests for ldap
  29  */
  30 
  31 import com.sun.jndi.ldap.Connection;
  32 
  33 import java.net.Socket;
  34 import java.net.ServerSocket;
  35 import java.net.SocketTimeoutException;
  36 import java.io.*;
  37 import javax.naming.*;
  38 import javax.naming.directory.*;
  39 import java.util.List;
  40 import java.util.Hashtable;
  41 import java.util.ArrayList;
  42 import java.util.concurrent.Callable;
  43 import java.util.concurrent.ExecutionException;
  44 import java.util.concurrent.Executors;
  45 import java.util.concurrent.ExecutorService;
  46 import java.util.concurrent.Future;
  47 import java.util.concurrent.ScheduledExecutorService;
  48 import java.util.concurrent.ScheduledFuture;
  49 import java.util.concurrent.TimeoutException;
  50 import java.util.concurrent.TimeUnit;
  51 
  52 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  53 import static java.util.concurrent.TimeUnit.NANOSECONDS;
  54 

  55 
  56 abstract class LdapTest implements Callable {


  57 
  58     Hashtable env;
  59     TestServer server;
  60     ScheduledExecutorService killSwitchPool;
  61     boolean passed = false;
  62     private int HANGING_TEST_TIMEOUT = 20_000;
  63 
  64     public LdapTest (TestServer server, Hashtable env) {
  65         this.server = server;
  66         this.env = env;
  67     }

  68 
  69     public LdapTest(TestServer server, Hashtable env, 
  70             ScheduledExecutorService killSwitchPool) 
  71     {
  72         this(server, env);
  73         this.killSwitchPool = killSwitchPool;
  74     }
  75 
  76     public abstract void performOp(InitialContext ctx) throws NamingException;
  77     public abstract void handleNamingException(
  78         NamingException e, long start, long end);
  79 
  80     public void pass() {
  81         this.passed = true;
  82     }
  83 
  84     public void fail() {
  85         throw new RuntimeException("Test failed");





  86     }
  87 
  88     boolean shutItDown(InitialContext ctx) {
  89         try {
  90             if (ctx != null) ctx.close();
  91             return true;
  92         } catch (NamingException ex) {
  93             return false;
  94         }
  95     }
  96 
  97     public Boolean call() {
  98         InitialContext ctx = null;
  99         ScheduledFuture killer = null;
 100         long start = System.nanoTime();
 101 
 102         try {
 103             ServerSocket serverSock = new ServerSocket(0);
 104             server.setServerSock(serverSock);
 105             server.start();
 106             Thread.sleep(200);
 107 
 108             // if this is a hanging test, scheduled a thread to
 109             // interrupt after a certain time
 110             if (killSwitchPool != null) {
 111                 final Thread current = Thread.currentThread();
 112                 killer = killSwitchPool.schedule(
 113                     new Callable<Void>() {
 114                         public Void call() throws Exception {
 115                             current.interrupt();
 116                             return null;
 117                         }
 118                     }, HANGING_TEST_TIMEOUT, MILLISECONDS);
 119             }
 120 
 121             env.put(Context.PROVIDER_URL, "ldap://localhost:" +
 122                     serverSock.getLocalPort());
 123             
 124             try {
 125                 ctx = new InitialDirContext(env);
 126                 performOp(ctx);
 127                 fail();
 128             } catch (NamingException e) {
 129                 long end = System.nanoTime();
 130                 System.out.println(this.getClass().toString() + " - elapsed: "
 131                         + NANOSECONDS.toMillis(end - start));
 132                 handleNamingException(e, start, end);
 133             } finally {
 134                 if (killer != null && !killer.isDone())
 135                     killer.cancel(true);
 136                 shutItDown(ctx);
 137                 serverSock.close();
 138             }
 139             return passed;
 140         } catch (IOException|InterruptedException e) {
 141             throw new RuntimeException(e);
 142         }
 143     }
 144 }
 145 
 146 abstract class ReadServerTest extends LdapTest {
 147     
 148     public ReadServerTest(Hashtable env) {
 149         super(new BindableServer(), env);
 150     }
 151 
 152     public ReadServerTest(
 153             Hashtable env, ScheduledExecutorService killSwitchPool) 
 154     {
 155         super(new BindableServer(), env, killSwitchPool);
 156     }
 157 
 158     public void performOp(InitialContext ctx) throws NamingException {
 159         SearchControls scl = new SearchControls();
 160         scl.setSearchScope(SearchControls.SUBTREE_SCOPE);
 161         NamingEnumeration<SearchResult> answer = ((InitialDirContext)ctx)
 162             .search("ou=People,o=JNDITutorial", "(objectClass=*)", scl);









 163     }
 164 }
 165 
 166 abstract class DeadServerTest extends LdapTest {
 167     
 168     public DeadServerTest(Hashtable env) {
 169         super(new DeadServer(), env);
 170     }
 171 
 172     public DeadServerTest(
 173             Hashtable env, ScheduledExecutorService killSwitchPool) 
 174     {
 175         super(new DeadServer(), env, killSwitchPool);
 176     }
 177 
 178     public void performOp(InitialContext ctx) throws NamingException {}
 179 }
 180 
 181 class DeadServerNoTimeoutTest extends DeadServerTest {
 182 
 183     public DeadServerNoTimeoutTest(
 184             Hashtable env, ScheduledExecutorService killSwitchPool) 
 185     {
 186         super(env, killSwitchPool);
 187     }
 188 
 189     public void handleNamingException(NamingException e, long start, long end) {
 190         if (e instanceof InterruptedNamingException) Thread.interrupted();
 191 
 192         if (NANOSECONDS.toMillis(end - start) < LdapTimeoutTest.MIN_TIMEOUT) {
 193             System.err.printf("DeadServerNoTimeoutTest fail: timeout should be " + 
 194                               "at least %s ms, actual time is %s ms%n",
 195                               LdapTimeoutTest.MIN_TIMEOUT, 
 196                               NANOSECONDS.toMillis(end - start));
 197             fail();
 198         } else {
 199             pass();
 200         }


 201     }
 202 }
 203 
 204 class DeadServerTimeoutTest extends DeadServerTest {
 205 
 206     public DeadServerTimeoutTest(Hashtable env) {
 207         super(env);
 208     }
 209 
 210     public void handleNamingException(NamingException e, long start, long end) {
 211         // non SSL connect will timeout via readReply using connectTimeout









 212         if (NANOSECONDS.toMillis(end - start) < 2_900) {
 213             pass();
 214         } else {
 215             System.err.println("Fail: Waited too long");
 216             fail();
 217         }
 218     }
 219 }
 220 
 221 class DeadServerTimeoutSSLTest extends DeadServerTest {
 222 
 223     public DeadServerTimeoutSSLTest(Hashtable env) {
 224         super(env);
 225     }
 226 
 227     public void handleNamingException(NamingException e, long start, long end) {
 228         if (e.getCause() instanceof SocketTimeoutException) {
 229             // SSL connect will timeout via readReply using 
 230             // SocketTimeoutException
 231             pass();
 232         } else {
 233             fail();
 234         }


 235     }
 236 }
 237 
 238 
 239 class ReadServerNoTimeoutTest extends ReadServerTest {
 240 
 241     public ReadServerNoTimeoutTest(
 242             Hashtable env, ScheduledExecutorService killSwitchPool) 
 243     {
 244         super(env, killSwitchPool);
 245     }
 246 
 247     public void handleNamingException(NamingException e, long start, long end) {
 248         if (e instanceof InterruptedNamingException) Thread.interrupted();
 249 
 250         if (NANOSECONDS.toMillis(end - start) < LdapTimeoutTest.MIN_TIMEOUT) {
 251             System.err.printf("ReadServerNoTimeoutTest fail: timeout should be " + 
 252                               "at least %s ms, actual time is %s ms%n",
 253                               LdapTimeoutTest.MIN_TIMEOUT, 
 254                               NANOSECONDS.toMillis(end - start));


 255             fail();








 256         } else {
 257             pass();
 258         }


 259     }
 260 }
 261 
 262 class ReadServerTimeoutTest extends ReadServerTest {
 263 
 264     public ReadServerTimeoutTest(Hashtable env) {
 265         super(env);
 266     }
 267 
 268     public void handleNamingException(NamingException e, long start, long end) {
 269         if (NANOSECONDS.toMillis(end - start) < 2_900) {
 270             fail();
 271         } else {
 272             pass();

 273         }
 274     }
 275 }
 276 
 277 class TestServer extends Thread {
 278     ServerSocket serverSock;
 279 
 280     public void setServerSock(ServerSocket s) {
 281         this.serverSock = s;
 282     }
 283 }
 284 
 285 class BindableServer extends TestServer {
 286 
 287     private byte[] bindResponse = {
 288         0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A,
 289         0x01, 0x00, 0x04, 0x00, 0x04, 0x00
 290     };
 291 
 292     public void run() {
 293         try {
 294             int serverPort = serverSock.getLocalPort();
 295             Socket socket = serverSock.accept();
 296             InputStream in = socket.getInputStream();
 297             OutputStream out = socket.getOutputStream();
 298 
 299             // Read the LDAP BindRequest
 300             while (in.read() != -1) {
 301                 in.skip(in.available());
 302                 break;
 303             }
 304 
 305             // Write an LDAP BindResponse
 306             out.write(bindResponse);
 307             out.flush();
 308         } catch (IOException e) {
 309             // ignore
 310         }
 311     }
 312 }
 313 
 314 class DeadServer extends TestServer {
 315     public void run() {
 316         while(true) {
 317             try {
 318                 Socket socket = serverSock.accept();
 319             } catch (Exception e) {
 320                 break;
 321             }
 322         }
 323     }
 324 }
 325 
 326 public class LdapTimeoutTest {
 327 
 328     private static final ExecutorService testPool =
 329         Executors.newFixedThreadPool(3);
 330     private static final ScheduledExecutorService killSwitchPool =
 331         Executors.newScheduledThreadPool(3);
 332     public static int MIN_TIMEOUT = 18_000;
 333 
 334     static Hashtable createEnv() {
 335         Hashtable env = new Hashtable(11);
 336         env.put(Context.INITIAL_CONTEXT_FACTORY,
 337             "com.sun.jndi.ldap.LdapCtxFactory");
 338         return env;
 339     }
 340     
 341     public static void main(String[] args) throws Exception {
 342         
 343         InitialContext ctx = null;
 344         List<Future> results = new ArrayList<>();
 345 
 346         try {
 347             // run the DeadServerTest with no timeouts set
 348             // this should get stuck indefinitely, so we need to kill
 349             // it after a timeout
 350             System.out.println("Running connect timeout test with 20s kill switch");
 351             Hashtable env = createEnv();
 352             results.add(
 353                     testPool.submit(new DeadServerNoTimeoutTest(env, killSwitchPool)));
 354 
 355             // run the DeadServerTest with connect timeout set
 356             // this should exit after the connect timeout expires
 357             System.out.println("Running connect timeout test with 10ms connect timeout");
 358             Hashtable env1 = createEnv();
 359             env1.put("com.sun.jndi.ldap.connect.timeout", "10");
 360             results.add(testPool.submit(new DeadServerTimeoutTest(env1)));
 361             
 362             // run the DeadServerTest with connect / read timeouts set
 363             // this should exit after the connect timeout expires
 364             System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout");
 365             Hashtable env2 = createEnv();
 366             env2.put("com.sun.jndi.ldap.connect.timeout", "10");
 367             env2.put("com.sun.jndi.ldap.read.timeout", "3000");
 368             results.add(testPool.submit(new DeadServerTimeoutTest(env2)));
 369             
 370             // run the DeadServerTest with connect / read timeouts set
 371             // and ssl enabled
 372             // this should exit with a SocketTimeoutException as the root cause
 373             // it should also use the connect timeout instead of the read timeout
 374             System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL");
 375             Hashtable env3 = createEnv();
 376             env3.put("com.sun.jndi.ldap.connect.timeout", "10");
 377             env3.put("com.sun.jndi.ldap.read.timeout", "3000");
 378             env3.put(Context.SECURITY_PROTOCOL, "ssl");
 379             results.add(testPool.submit(new DeadServerTimeoutSSLTest(env3)));
 380 
 381 
 382             // run the ReadServerTest with no timeouts set
 383             // this should get stuck indefinitely, so we need to kill
 384             // it after a timeout
 385             System.out.println("Running read timeout test with 20s kill switch");
 386             results.add(testPool.submit(
 387                     new ReadServerNoTimeoutTest(env, killSwitchPool)));
 388 
 389             // run the ReadServerTest with connect timeout set
 390             // this should get stuck indefinitely so we need to kill
 391             // it after a timeout
 392             System.out.println("Running read timeout test with 10ms connect timeout & 20s kill switch");
 393             results.add(testPool.submit(
 394                     new ReadServerNoTimeoutTest(env1, killSwitchPool)));
 395             
 396             // run the ReadServerTest with connect / read timeouts set
 397             // this should exit after the connect timeout expires
 398             System.out.println("Running read timeout test with 10ms connect timeout, 3000ms read timeout");
 399             results.add(testPool.submit(new ReadServerTimeoutTest(env2)));
 400             
 401             // 8000487: Java JNDI connection library on ldap conn is 
 402             // not honoring configured timeout 
 403             System.out.println("Running simple auth connection test");
 404             Hashtable env4 = createEnv();
 405             env4.put("com.sun.jndi.ldap.connect.timeout", "10");
 406             env4.put("com.sun.jndi.ldap.read.timeout", "3000");
 407             env4.put(Context.SECURITY_AUTHENTICATION, "simple");
 408             env4.put(Context.SECURITY_PRINCIPAL, "user");
 409             env4.put(Context.SECURITY_CREDENTIALS, "password");
 410             results.add(testPool.submit(new DeadServerTimeoutTest(env4)));
 411 
 412             boolean testFailed = false;
 413             for (Future test : results) {
 414                 while (!test.isDone()) {
 415                     if ((Boolean) test.get() == false)
 416                         testFailed = true;
 417                 }
 418             }
 419 
 420             if (testFailed) {
 421                 throw new AssertionError("some tests failed");
 422             }
 423 
 424 
 425         } finally {
 426             LdapTimeoutTest.killSwitchPool.shutdown();
 427             LdapTimeoutTest.testPool.shutdown();
 428         }
 429     }
 430 
 431 }
 432