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 }