/* * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* @test * @bug 8222774 4430139 * @run testng AdaptorStreams * @summary Exercise socket adaptor input/output streams */ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.SocketChannel; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.testng.annotations.Test; import static org.testng.Assert.*; @Test public class AdaptorStreams { /** * Test read when bytes are available */ public void testRead1() throws Exception { withConnection((sc, peer) -> { peer.getOutputStream().write(99); int n = sc.socket().getInputStream().read(); assertEquals(n, 99); }); } /** * Test read blocking before bytes are available */ public void testRead2() throws Exception { withConnection((sc, peer) -> { scheduleWrite(peer.getOutputStream(), 99, 1000); int n = sc.socket().getInputStream().read(); assertEquals(n, 99); }); } /** * Test read when peer has closed connection */ public void testRead3() throws Exception { withConnection((sc, peer) -> { peer.close(); int n = sc.socket().getInputStream().read(); assertEquals(n, -1); }); } /** * Test read blocking before peer closes connection */ public void testRead4() throws Exception { withConnection((sc, peer) -> { scheduleClose(peer, 1000); int n = sc.socket().getInputStream().read(); assertEquals(n, -1); }); } /** * Test async close of socket when thread blocked in read */ public void testRead5() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); InputStream in = sc.socket().getInputStream(); expectThrows(IOException.class, () -> in.read()); }); } /** * Test interrupt status set before read */ public void testRead6() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); Thread.currentThread().interrupt(); try { InputStream in = s.getInputStream(); expectThrows(IOException.class, () -> in.read()); } finally { Thread.interrupted(); // clear interrupt } assertTrue(s.isClosed()); }); } /** * Test interrupt of thread blocked in read */ public void testRead7() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); Socket s = sc.socket(); try { InputStream in = s.getInputStream(); expectThrows(IOException.class, () -> in.read()); } finally { interrupter.cancel(true); Thread.interrupted(); // clear interrupt } assertTrue(s.isClosed()); }); } /** * Test read when channel is configured non-blocking */ public void testRead8() throws Exception { withConnection((sc, peer) -> { sc.configureBlocking(false); InputStream in = sc.socket().getInputStream(); expectThrows(IllegalBlockingModeException.class, () -> in.read()); }); } /** * Test timed read when bytes are available */ public void testTimedRead1() throws Exception { withConnection((sc, peer) -> { peer.getOutputStream().write(99); Socket s = sc.socket(); s.setSoTimeout(60_000); int n = s.getInputStream().read(); assertEquals(n, 99); }); } /** * Test timed read blocking before bytes are available */ public void testTimedRead2() throws Exception { withConnection((sc, peer) -> { scheduleWrite(peer.getOutputStream(), 99, 1000); Socket s = sc.socket(); s.setSoTimeout(60_000); int n = s.getInputStream().read(); assertEquals(n, 99); }); } /** * Test timed read when the read times out */ public void testTimedRead3() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); s.setSoTimeout(500); InputStream in = s.getInputStream(); expectThrows(SocketTimeoutException.class, () -> in.read()); }); } /** * Test async close of socket when thread blocked in timed read */ public void testTimedRead4() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); Socket s = sc.socket(); s.setSoTimeout(60_000); InputStream in = s.getInputStream(); expectThrows(IOException.class, () -> in.read()); }); } /** * Test interrupt status set before timed read */ public void testTimedRead5() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); Thread.currentThread().interrupt(); try { s.setSoTimeout(60_000); InputStream in = s.getInputStream(); expectThrows(IOException.class, () -> in.read()); } finally { Thread.interrupted(); // clear interrupt } assertTrue(s.isClosed()); }); } /** * Test interrupt of thread blocked in timed read */ public void testTimedRead6() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); Socket s = sc.socket(); try { s.setSoTimeout(60_000); InputStream in = s.getInputStream(); expectThrows(IOException.class, () -> in.read()); assertTrue(s.isClosed()); } finally { interrupter.cancel(true); Thread.interrupted(); // clear interrupt } assertTrue(s.isClosed()); }); } /** * Test async close of socket when thread blocked in write */ public void testWrite1() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); expectThrows(IOException.class, () -> { OutputStream out = sc.socket().getOutputStream(); byte[] data = new byte[64*1000]; while (true) { out.write(data); } }); }); } /** * Test interrupt status set before write */ public void testWrite2() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); Thread.currentThread().interrupt(); try { OutputStream out = s.getOutputStream(); expectThrows(IOException.class, () -> out.write(99)); } finally { Thread.interrupted(); // clear interrupt } assertTrue(s.isClosed()); }); } /** * Test interrupt of thread blocked in write */ public void testWrite3() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); Socket s = sc.socket(); try { expectThrows(IOException.class, () -> { OutputStream out = sc.socket().getOutputStream(); byte[] data = new byte[64*1000]; while (true) { out.write(data); } }); } finally { interrupter.cancel(true); Thread.interrupted(); // clear interrupt } assertTrue(s.isClosed()); }); } /** * Test write when channel is configured non-blocking */ public void testWrite4() throws Exception { withConnection((sc, peer) -> { sc.configureBlocking(false); OutputStream out = sc.socket().getOutputStream(); expectThrows(IllegalBlockingModeException.class, () -> out.write(99)); }); } /** * Test read when there are bytes available and another thread is blocked * in write */ public void testConcurrentReadWrite1() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); // block thread in write execute(() -> { var data = new byte[64*1024]; OutputStream out = s.getOutputStream(); for (;;) { out.write(data); } }); Thread.sleep(1000); // give writer time to block // test read when bytes are available peer.getOutputStream().write(99); int n = s.getInputStream().read(); assertEquals(n, 99); }); } /** * Test read blocking when another thread is blocked in write */ public void testConcurrentReadWrite2() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); // block thread in write execute(() -> { var data = new byte[64*1024]; OutputStream out = s.getOutputStream(); for (;;) { out.write(data); } }); Thread.sleep(1000); // give writer time to block // test read blocking until bytes are available scheduleWrite(peer.getOutputStream(), 99, 500); int n = s.getInputStream().read(); assertEquals(n, 99); }); } /** * Test writing when another thread is blocked in read */ public void testConcurrentReadWrite3() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); // block thread in read execute(() -> { s.getInputStream().read(); }); Thread.sleep(100); // give reader time to block // test write s.getOutputStream().write(99); int n = peer.getInputStream().read(); assertEquals(n, 99); }); } /** * Test timed read when there are bytes available and another thread is * blocked in write */ public void testConcurrentTimedReadWrite1() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); // block thread in write execute(() -> { var data = new byte[64*1024]; OutputStream out = s.getOutputStream(); for (;;) { out.write(data); } }); Thread.sleep(1000); // give writer time to block // test read when bytes are available peer.getOutputStream().write(99); s.setSoTimeout(60_000); int n = s.getInputStream().read(); assertEquals(n, 99); }); } /** * Test timed read blocking when another thread is blocked in write */ public void testConcurrentTimedReadWrite2() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); // block thread in write execute(() -> { var data = new byte[64*1024]; OutputStream out = s.getOutputStream(); for (;;) { out.write(data); } }); Thread.sleep(1000); // give writer time to block // test read blocking until bytes are available scheduleWrite(peer.getOutputStream(), 99, 500); s.setSoTimeout(60_000); int n = s.getInputStream().read(); assertEquals(n, 99); }); } /** * Test writing when another thread is blocked in read */ public void testConcurrentTimedReadWrite3() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); // block thread in read execute(() -> { s.setSoTimeout(60_000); s.getInputStream().read(); }); Thread.sleep(100); // give reader time to block // test write s.getOutputStream().write(99); int n = peer.getInputStream().read(); assertEquals(n, 99); }); } // -- test infrastructure -- interface ThrowingTask { void run() throws Exception; } interface ThrowingBiConsumer { void accept(T t, U u) throws Exception; } /** * Invokes the consumer with a connected pair of socket channel and socket */ static void withConnection(ThrowingBiConsumer consumer) throws Exception { var loopback = InetAddress.getLoopbackAddress(); try (ServerSocket ss = new ServerSocket()) { ss.bind(new InetSocketAddress(loopback, 0)); try (SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress())) { try (Socket peer = ss.accept()) { consumer.accept(sc, peer); } } } } static Future scheduleWrite(OutputStream out, byte[] data, long delay) { return schedule(() -> { try { out.write(data); } catch (IOException ioe) { } }, delay); } static Future scheduleWrite(OutputStream out, int b, long delay) { return scheduleWrite(out, new byte[] { (byte)b }, delay); } static Future scheduleClose(Closeable c, long delay) { return schedule(() -> { try { c.close(); } catch (IOException ioe) { } }, delay); } static Future scheduleInterrupt(Thread t, long delay) { return schedule(() -> t.interrupt(), delay); } static Future schedule(Runnable task, long delay) { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); try { return executor.schedule(task, delay, TimeUnit.MILLISECONDS); } finally { executor.shutdown(); } } static Future execute(ThrowingTask task) { ExecutorService pool = Executors.newFixedThreadPool(1); try { return pool.submit(() -> { task.run(); return null; }); } finally { pool.shutdown(); } } }