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 |