1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 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 * @bug 8221481 27 * @library /test/lib 28 * @build jdk.test.lib.Utils 29 * @run testng/timeout=180 Timeouts 30 * @summary Test Socket timeouts 31 */ 32 33 import java.io.Closeable; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 import java.net.ConnectException; 38 import java.net.InetAddress; 39 import java.net.InetSocketAddress; 40 import java.net.ServerSocket; 41 import java.net.Socket; 42 import java.net.SocketAddress; 43 import java.net.SocketException; 44 import java.net.SocketTimeoutException; 45 import java.util.concurrent.Executors; 46 import java.util.concurrent.ExecutionException; 47 import java.util.concurrent.ExecutorService; 48 import java.util.concurrent.Future; 49 import java.util.concurrent.ScheduledExecutorService; 50 import java.util.concurrent.TimeUnit; 51 52 import org.testng.annotations.Test; 53 import static org.testng.Assert.*; 54 import jdk.test.lib.Utils; 55 56 @Test 57 public class Timeouts { 58 59 /** 60 * Test timed connect where connection is established 61 */ 62 public void testTimedConnect1() throws IOException { 63 try (ServerSocket ss = boundServerSocket()) { 64 try (Socket s = new Socket()) { 65 s.connect(ss.getLocalSocketAddress(), 2000); 66 } 67 } 68 } 69 70 /** 71 * Test timed connect where connection is refused 72 */ 73 public void testTimedConnect2() throws IOException { 74 try (Socket s = new Socket()) { 75 SocketAddress remote = Utils.refusingEndpoint(); 76 try { 77 s.connect(remote, 2000); 78 } catch (ConnectException expected) { } 79 } 80 } 81 82 /** 83 * Test connect with a timeout of Integer.MAX_VALUE 84 */ 85 public void testTimedConnect3() throws IOException { 86 try (ServerSocket ss = boundServerSocket()) { 87 try (Socket s = new Socket()) { 88 s.connect(ss.getLocalSocketAddress(), Integer.MAX_VALUE); 89 } 90 } 91 } 92 93 /** 94 * Test connect with a negative timeout. 95 */ 96 public void testTimedConnect4() throws IOException { 97 try (ServerSocket ss = boundServerSocket()) { 98 try (Socket s = new Socket()) { 99 expectThrows(IllegalArgumentException.class, 100 () -> s.connect(ss.getLocalSocketAddress(), -1)); 101 } 102 } 103 } 104 105 /** 106 * Test timed read where the read succeeds immediately 107 */ 108 public void testTimedRead1() throws IOException { 109 withConnection((s1, s2) -> { 110 s1.getOutputStream().write(99); 111 s2.setSoTimeout(30*1000); 112 int b = s2.getInputStream().read(); 113 assertTrue(b == 99); 114 }); 115 } 116 117 /** 118 * Test timed read where the read succeeds after a delay 119 */ 120 public void testTimedRead2() throws IOException { 121 withConnection((s1, s2) -> { 122 scheduleWrite(s1.getOutputStream(), 99, 2000); 123 s2.setSoTimeout(30*1000); 124 int b = s2.getInputStream().read(); 125 assertTrue(b == 99); 126 }); 127 } 128 129 /** 130 * Test timed read where the read times out 131 */ 132 public void testTimedRead3() throws IOException { 133 withConnection((s1, s2) -> { 134 s2.setSoTimeout(2000); 135 long startMillis = millisTime(); 136 expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read()); 137 int timeout = s2.getSoTimeout(); 138 checkDuration(startMillis, timeout-100, timeout+2000); 139 }); 140 } 141 142 /** 143 * Test timed read that succeeds after a previous read has timed out 144 */ 145 public void testTimedRead4() throws IOException { 146 withConnection((s1, s2) -> { 147 s2.setSoTimeout(2000); 148 expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read()); 149 s1.getOutputStream().write(99); 150 int b = s2.getInputStream().read(); 151 assertTrue(b == 99); 152 }); 153 } 154 155 /** 156 * Test timed read that succeeds after a previous read has timed out and 157 * after a short delay 158 */ 159 public void testTimedRead5() throws IOException { 160 withConnection((s1, s2) -> { 161 s2.setSoTimeout(2000); 162 expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read()); 163 s2.setSoTimeout(30*3000); 164 scheduleWrite(s1.getOutputStream(), 99, 2000); 165 int b = s2.getInputStream().read(); 166 assertTrue(b == 99); 167 }); 168 } 169 170 /** 171 * Test untimed read that succeeds after a previous read has timed out 172 */ 173 public void testTimedRead6() throws IOException { 174 withConnection((s1, s2) -> { 175 s2.setSoTimeout(2000); 176 expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read()); 177 s1.getOutputStream().write(99); 178 s2.setSoTimeout(0); 179 int b = s2.getInputStream().read(); 180 assertTrue(b == 99); 181 }); 182 } 183 184 /** 185 * Test untimed read that succeeds after a previous read has timed out and 186 * after a short delay 187 */ 188 public void testTimedRead7() throws IOException { 189 withConnection((s1, s2) -> { 190 s2.setSoTimeout(2000); 191 expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read()); 192 scheduleWrite(s1.getOutputStream(), 99, 2000); 193 s2.setSoTimeout(0); 194 int b = s2.getInputStream().read(); 195 assertTrue(b == 99); 196 }); 197 } 198 199 /** 200 * Test async close of timed read 201 */ 202 public void testTimedRead8() throws IOException { 203 withConnection((s1, s2) -> { 204 s2.setSoTimeout(30*1000); 205 scheduleClose(s2, 2000); 206 expectThrows(SocketException.class, () -> s2.getInputStream().read()); 207 }); 208 } 209 210 /** 211 * Test read with a timeout of Integer.MAX_VALUE 212 */ 213 public void testTimedRead9() throws IOException { 214 withConnection((s1, s2) -> { 215 scheduleWrite(s1.getOutputStream(), 99, 2000); 216 s2.setSoTimeout(Integer.MAX_VALUE); 217 int b = s2.getInputStream().read(); 218 assertTrue(b == 99); 219 }); 220 } 221 222 /** 223 * Test writing after a timed read. 224 */ 225 public void testTimedWrite1() throws IOException { 226 withConnection((s1, s2) -> { 227 s1.getOutputStream().write(99); 228 s2.setSoTimeout(3000); 229 int b = s2.getInputStream().read(); 230 assertTrue(b == 99); 231 232 // schedule thread to read s1 to EOF 233 scheduleReadToEOF(s1.getInputStream(), 3000); 234 235 // write a lot so that write blocks 236 byte[] data = new byte[128*1024]; 237 for (int i = 0; i < 100; i++) { 238 s2.getOutputStream().write(data); 239 } 240 }); 241 } 242 243 /** 244 * Test async close of writer (after a timed read). 245 */ 246 public void testTimedWrite2() throws IOException { 247 withConnection((s1, s2) -> { 248 s1.getOutputStream().write(99); 249 s2.setSoTimeout(3000); 250 int b = s2.getInputStream().read(); 251 assertTrue(b == 99); 252 253 // schedule s2 to be be closed 254 scheduleClose(s2, 3000); 255 256 // write a lot so that write blocks 257 byte[] data = new byte[128*1024]; 258 try { 259 while (true) { 260 s2.getOutputStream().write(data); 261 } 262 } catch (SocketException expected) { } 263 }); 264 } 265 266 /** 267 * Test timed accept where a connection is established immediately 268 */ 269 public void testTimedAccept1() throws IOException { 270 Socket s1 = null; 271 Socket s2 = null; 272 try (ServerSocket ss = boundServerSocket()) { 273 s1 = new Socket(); 274 s1.connect(ss.getLocalSocketAddress()); 275 ss.setSoTimeout(30*1000); 276 s2 = ss.accept(); 277 } finally { 278 if (s1 != null) s1.close(); 279 if (s2 != null) s2.close(); 280 } 281 } 282 283 /** 284 * Test timed accept where a connection is established after a short delay 285 */ 286 public void testTimedAccept2() throws IOException { 287 try (ServerSocket ss = boundServerSocket()) { 288 ss.setSoTimeout(30*1000); 289 scheduleConnect(ss.getLocalSocketAddress(), 2000); 290 Socket s = ss.accept(); 291 s.close(); 292 } 293 } 294 295 /** 296 * Test timed accept where the accept times out 297 */ 298 public void testTimedAccept3() throws IOException { 299 try (ServerSocket ss = boundServerSocket()) { 300 ss.setSoTimeout(2000); 301 long startMillis = millisTime(); 302 try { 303 Socket s = ss.accept(); 304 s.close(); 305 fail(); 306 } catch (SocketTimeoutException expected) { 307 int timeout = ss.getSoTimeout(); 308 checkDuration(startMillis, timeout-100, timeout+2000); 309 } 310 } 311 } 312 313 /** 314 * Test timed accept where a connection is established immediately after a 315 * previous accept timed out. 316 */ 317 public void testTimedAccept4() throws IOException { 318 try (ServerSocket ss = boundServerSocket()) { 319 ss.setSoTimeout(2000); 320 try { 321 Socket s = ss.accept(); 322 s.close(); 323 fail(); 324 } catch (SocketTimeoutException expected) { } 325 try (Socket s1 = new Socket()) { 326 s1.connect(ss.getLocalSocketAddress()); 327 Socket s2 = ss.accept(); 328 s2.close(); 329 } 330 } 331 } 332 333 /** 334 * Test untimed accept where a connection is established after a previous 335 * accept timed out 336 */ 337 public void testTimedAccept5() throws IOException { 338 try (ServerSocket ss = boundServerSocket()) { 339 ss.setSoTimeout(2000); 340 try { 341 Socket s = ss.accept(); 342 s.close(); 343 fail(); 344 } catch (SocketTimeoutException expected) { } 345 ss.setSoTimeout(0); 346 try (Socket s1 = new Socket()) { 347 s1.connect(ss.getLocalSocketAddress()); 348 Socket s2 = ss.accept(); 349 s2.close(); 350 } 351 } 352 } 353 354 /** 355 * Test untimed accept where a connection is established after a previous 356 * accept timed out and after a short delay 357 */ 358 public void testTimedAccept6() throws IOException { 359 try (ServerSocket ss = boundServerSocket()) { 360 ss.setSoTimeout(2000); 361 try { 362 Socket s = ss.accept(); 363 s.close(); 364 fail(); 365 } catch (SocketTimeoutException expected) { } 366 ss.setSoTimeout(0); 367 scheduleConnect(ss.getLocalSocketAddress(), 2000); 368 Socket s = ss.accept(); 369 s.close(); 370 } 371 } 372 373 /** 374 * Test async close of a timed accept 375 */ 376 public void testTimedAccept7() throws IOException { 377 try (ServerSocket ss = boundServerSocket()) { 378 ss.setSoTimeout(30*1000); 379 long delay = 2000; 380 scheduleClose(ss, delay); 381 long startMillis = millisTime(); 382 try { 383 ss.accept().close(); 384 fail(); 385 } catch (SocketException expected) { 386 checkDuration(startMillis, delay-100, delay+2000); 387 } 388 } 389 } 390 391 /** 392 * Test timed accept with the thread interrupt status set. 393 */ 394 public void testTimedAccept8() throws IOException { 395 try (ServerSocket ss = boundServerSocket()) { 396 ss.setSoTimeout(2000); 397 Thread.currentThread().interrupt(); 398 long startMillis = millisTime(); 399 try { 400 Socket s = ss.accept(); 401 s.close(); 402 fail(); 403 } catch (SocketTimeoutException expected) { 404 // accept should have blocked for 2 seconds 405 int timeout = ss.getSoTimeout(); 406 checkDuration(startMillis, timeout-100, timeout+2000); 407 assertTrue(Thread.currentThread().isInterrupted()); 408 } finally { 409 Thread.interrupted(); // clear interrupt status 410 } 411 } 412 } 413 414 /** 415 * Test interrupt of thread blocked in timed accept. 416 */ 417 public void testTimedAccept9() throws IOException { 418 try (ServerSocket ss = boundServerSocket()) { 419 ss.setSoTimeout(4000); 420 // interrupt thread after 1 second 421 Future<?> interrupter = scheduleInterrupt(Thread.currentThread(), 1000); 422 long startMillis = millisTime(); 423 try { 424 Socket s = ss.accept(); // should block for 4 seconds 425 s.close(); 426 fail(); 427 } catch (SocketTimeoutException expected) { 428 // accept should have blocked for 4 seconds 429 int timeout = ss.getSoTimeout(); 430 checkDuration(startMillis, timeout-100, timeout+2000); 431 assertTrue(Thread.currentThread().isInterrupted()); 432 } finally { 433 interrupter.cancel(true); 434 Thread.interrupted(); // clear interrupt status 435 } 436 } 437 } 438 439 /** 440 * Test two threads blocked in timed accept where no connection is established. 441 */ 442 public void testTimedAccept10() throws Exception { 443 ExecutorService pool = Executors.newFixedThreadPool(2); 444 try (ServerSocket ss = boundServerSocket()) { 445 ss.setSoTimeout(4000); 446 447 long startMillis = millisTime(); 448 449 Future<Socket> result1 = pool.submit(ss::accept); 450 Future<Socket> result2 = pool.submit(ss::accept); 451 452 // both tasks should complete with SocketTimeoutException 453 Throwable e = expectThrows(ExecutionException.class, result1::get); 454 assertTrue(e.getCause() instanceof SocketTimeoutException); 455 e = expectThrows(ExecutionException.class, result2::get); 456 assertTrue(e.getCause() instanceof SocketTimeoutException); 457 458 // should get here in 4 seconds, not 8 seconds 459 int timeout = ss.getSoTimeout(); 460 checkDuration(startMillis, timeout-100, timeout+2000); 461 } finally { 462 pool.shutdown(); 463 } 464 } 465 466 /** 467 * Test two threads blocked in timed accept where one connection is established. 468 */ 469 public void testTimedAccept11() throws Exception { 470 ExecutorService pool = Executors.newFixedThreadPool(2); 471 try (ServerSocket ss = boundServerSocket()) { 472 ss.setSoTimeout(4000); 473 474 long startMillis = millisTime(); 475 476 Future<Socket> result1 = pool.submit(ss::accept); 477 Future<Socket> result2 = pool.submit(ss::accept); 478 479 // establish connection after 2 seconds 480 scheduleConnect(ss.getLocalSocketAddress(), 2000); 481 482 // one task should have accepted the connection, the other should 483 // have completed with SocketTimeoutException 484 Socket s1 = null; 485 try { 486 s1 = result1.get(); 487 s1.close(); 488 } catch (ExecutionException e) { 489 assertTrue(e.getCause() instanceof SocketTimeoutException); 490 } 491 Socket s2 = null; 492 try { 493 s2 = result2.get(); 494 s2.close(); 495 } catch (ExecutionException e) { 496 assertTrue(e.getCause() instanceof SocketTimeoutException); 497 } 498 assertTrue((s1 != null) ^ (s2 != null)); 499 500 // should get here in 4 seconds, not 8 seconds 501 int timeout = ss.getSoTimeout(); 502 checkDuration(startMillis, timeout-100, timeout+2000); 503 } finally { 504 pool.shutdown(); 505 } 506 } 507 508 /** 509 * Test Socket setSoTimeout with a negative timeout. 510 */ 511 @Test(expectedExceptions = { IllegalArgumentException.class }) 512 public void testBadTimeout1() throws IOException { 513 try (Socket s = new Socket()) { 514 s.setSoTimeout(-1); 515 } 516 } 517 518 /** 519 * Test ServerSocket setSoTimeout with a negative timeout. 520 */ 521 @Test(expectedExceptions = { IllegalArgumentException.class }) 522 public void testBadTimeout2() throws IOException { 523 try (ServerSocket ss = new ServerSocket()) { 524 ss.setSoTimeout(-1); 525 } 526 } 527 528 /** 529 * Returns a ServerSocket bound to a port on the loopback address 530 */ 531 static ServerSocket boundServerSocket() throws IOException { 532 var loopback = InetAddress.getLoopbackAddress(); 533 ServerSocket ss = new ServerSocket(); 534 ss.bind(new InetSocketAddress(loopback, 0)); 535 return ss; 536 } 537 538 /** 539 * An operation that accepts two arguments and may throw IOException 540 */ 541 interface ThrowingBiConsumer<T, U> { 542 void accept(T t, U u) throws IOException; 543 } 544 545 /** 546 * Invokes the consumer with a connected pair of sockets 547 */ 548 static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer) 549 throws IOException 550 { 551 Socket s1 = null; 552 Socket s2 = null; 553 try (ServerSocket ss = boundServerSocket()) { 554 s1 = new Socket(); 555 s1.connect(ss.getLocalSocketAddress()); 556 s2 = ss.accept(); 557 consumer.accept(s1, s2); 558 } finally { 559 if (s1 != null) s1.close(); 560 if (s2 != null) s2.close(); 561 } 562 } 563 564 /** 565 * Schedule c to be closed after a delay 566 */ 567 static void scheduleClose(Closeable c, long delay) { 568 schedule(() -> { 569 try { 570 c.close(); 571 } catch (IOException ioe) { } 572 }, delay); 573 } 574 575 /** 576 * Schedule thread to be interrupted after a delay 577 */ 578 static Future<?> scheduleInterrupt(Thread thread, long delay) { 579 return schedule(() -> thread.interrupt(), delay); 580 } 581 582 /** 583 * Schedule a thread to connect to the given end point after a delay 584 */ 585 static void scheduleConnect(SocketAddress remote, long delay) { 586 schedule(() -> { 587 try (Socket s = new Socket()) { 588 s.connect(remote); 589 } catch (IOException ioe) { } 590 }, delay); 591 } 592 593 /** 594 * Schedule a thread to read to EOF after a delay 595 */ 596 static void scheduleReadToEOF(InputStream in, long delay) { 597 schedule(() -> { 598 byte[] bytes = new byte[8192]; 599 try { 600 while (in.read(bytes) != -1) { } 601 } catch (IOException ioe) { } 602 }, delay); 603 } 604 605 /** 606 * Schedule a thread to write after a delay 607 */ 608 static void scheduleWrite(OutputStream out, byte[] data, long delay) { 609 schedule(() -> { 610 try { 611 out.write(data); 612 } catch (IOException ioe) { } 613 }, delay); 614 } 615 static void scheduleWrite(OutputStream out, int b, long delay) { 616 scheduleWrite(out, new byte[] { (byte)b }, delay); 617 } 618 619 static Future<?> schedule(Runnable task, long delay) { 620 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 621 try { 622 return executor.schedule(task, delay, TimeUnit.MILLISECONDS); 623 } finally { 624 executor.shutdown(); 625 } 626 } 627 628 /** 629 * Returns the current time in milliseconds. 630 */ 631 private static long millisTime() { 632 long now = System.nanoTime(); 633 return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS); 634 } 635 636 /** 637 * Check the duration of a task 638 * @param start start time, in milliseconds 639 * @param min minimum expected duration, in milliseconds 640 * @param max maximum expected duration, in milliseconds 641 * @return the duration (now - start), in milliseconds 642 */ 643 private static long checkDuration(long start, long min, long max) { 644 long duration = millisTime() - start; 645 assertTrue(duration >= min, 646 "Duration " + duration + "ms, expected >= " + min + "ms"); 647 assertTrue(duration <= max, 648 "Duration " + duration + "ms, expected <= " + max + "ms"); 649 return duration; 650 } 651 }