| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.io; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.net.SocketException; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.AsynchronousServerSocketChannel; |
| import java.nio.channels.AsynchronousSocketChannel; |
| import java.nio.channels.FileChannel; |
| import java.nio.channels.ServerSocketChannel; |
| import java.nio.channels.SocketChannel; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.StandardOpenOption; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.eclipse.jetty.toolchain.test.MavenTestingUtils; |
| import org.eclipse.jetty.toolchain.test.OS; |
| import org.eclipse.jetty.util.BufferUtil; |
| import org.eclipse.jetty.util.IO; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| public class IOTest |
| { |
| @Test |
| public void testIO() throws Exception |
| { |
| // Only a little test |
| ByteArrayInputStream in = new ByteArrayInputStream("The quick brown fox jumped over the lazy dog".getBytes()); |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| |
| IO.copy(in, out); |
| |
| assertEquals("copyThread", out.toString(), "The quick brown fox jumped over the lazy dog"); |
| } |
| |
| @Test |
| public void testHalfClose() throws Exception |
| { |
| ServerSocket connector = new ServerSocket(0); |
| |
| Socket client = new Socket("localhost", connector.getLocalPort()); |
| Socket server = connector.accept(); |
| |
| // we can write both ways |
| client.getOutputStream().write(1); |
| assertEquals(1, server.getInputStream().read()); |
| server.getOutputStream().write(1); |
| assertEquals(1, client.getInputStream().read()); |
| |
| // shutdown output results in read -1 |
| client.shutdownOutput(); |
| assertEquals(-1, server.getInputStream().read()); |
| |
| // Even though EOF has been read, the server input is not seen as shutdown |
| assertFalse(server.isInputShutdown()); |
| |
| // and we can read -1 again |
| assertEquals(-1, server.getInputStream().read()); |
| |
| // but cannot write |
| try |
| { |
| client.getOutputStream().write(1); |
| fail("exception expected"); |
| } |
| catch (SocketException e) |
| { |
| } |
| |
| // but can still write in opposite direction. |
| server.getOutputStream().write(1); |
| assertEquals(1, client.getInputStream().read()); |
| |
| // server can shutdown input to match the shutdown out of client |
| server.shutdownInput(); |
| |
| // now we EOF instead of reading -1 |
| try |
| { |
| server.getInputStream().read(); |
| fail("exception expected"); |
| } |
| catch (SocketException e) |
| { |
| } |
| |
| // but can still write in opposite direction. |
| server.getOutputStream().write(1); |
| assertEquals(1, client.getInputStream().read()); |
| |
| // client can shutdown input |
| client.shutdownInput(); |
| |
| // now we EOF instead of reading -1 |
| try |
| { |
| client.getInputStream().read(); |
| fail("exception expected"); |
| } |
| catch (SocketException e) |
| { |
| } |
| |
| // But we can still write at the server (data which will never be read) |
| server.getOutputStream().write(1); |
| |
| // and the server output is not shutdown |
| assertFalse(server.isOutputShutdown()); |
| |
| // until we explictly shut it down |
| server.shutdownOutput(); |
| |
| // and now we can't write |
| try |
| { |
| server.getOutputStream().write(1); |
| fail("exception expected"); |
| } |
| catch (SocketException e) |
| { |
| } |
| |
| // but the sockets are still open |
| assertFalse(client.isClosed()); |
| assertFalse(server.isClosed()); |
| |
| // but if we close one end |
| client.close(); |
| |
| // it is seen as closed. |
| assertTrue(client.isClosed()); |
| |
| // but not the other end |
| assertFalse(server.isClosed()); |
| |
| // which has to be closed explictly |
| server.close(); |
| assertTrue(server.isClosed()); |
| } |
| |
| @Test |
| public void testHalfCloseClientServer() throws Exception |
| { |
| ServerSocketChannel connector = ServerSocketChannel.open(); |
| connector.socket().bind(null); |
| |
| Socket client = SocketChannel.open(connector.socket().getLocalSocketAddress()).socket(); |
| client.setSoTimeout(1000); |
| client.setSoLinger(false, -1); |
| Socket server = connector.accept().socket(); |
| server.setSoTimeout(1000); |
| server.setSoLinger(false, -1); |
| |
| // Write from client to server |
| client.getOutputStream().write(1); |
| |
| // Server reads |
| assertEquals(1, server.getInputStream().read()); |
| |
| // Write from server to client with oshut |
| server.getOutputStream().write(1); |
| // System.err.println("OSHUT "+server); |
| server.shutdownOutput(); |
| |
| // Client reads response |
| assertEquals(1, client.getInputStream().read()); |
| |
| try |
| { |
| // Client reads -1 and does ishut |
| assertEquals(-1, client.getInputStream().read()); |
| assertFalse(client.isInputShutdown()); |
| //System.err.println("ISHUT "+client); |
| client.shutdownInput(); |
| |
| // Client ??? |
| //System.err.println("OSHUT "+client); |
| client.shutdownOutput(); |
| //System.err.println("CLOSE "+client); |
| client.close(); |
| |
| // Server reads -1, does ishut and then close |
| assertEquals(-1, server.getInputStream().read()); |
| assertFalse(server.isInputShutdown()); |
| //System.err.println("ISHUT "+server); |
| |
| try |
| { |
| server.shutdownInput(); |
| } |
| catch (SocketException e) |
| { |
| // System.err.println(e); |
| } |
| //System.err.println("CLOSE "+server); |
| server.close(); |
| |
| } |
| catch (Exception e) |
| { |
| System.err.println(e); |
| assertTrue(OS.IS_OSX); |
| } |
| } |
| |
| @Test |
| public void testHalfCloseBadClient() throws Exception |
| { |
| ServerSocketChannel connector = ServerSocketChannel.open(); |
| connector.socket().bind(null); |
| |
| Socket client = SocketChannel.open(connector.socket().getLocalSocketAddress()).socket(); |
| client.setSoTimeout(1000); |
| client.setSoLinger(false, -1); |
| Socket server = connector.accept().socket(); |
| server.setSoTimeout(1000); |
| server.setSoLinger(false, -1); |
| |
| // Write from client to server |
| client.getOutputStream().write(1); |
| |
| // Server reads |
| assertEquals(1, server.getInputStream().read()); |
| |
| // Write from server to client with oshut |
| server.getOutputStream().write(1); |
| //System.err.println("OSHUT "+server); |
| server.shutdownOutput(); |
| |
| try |
| { |
| // Client reads response |
| assertEquals(1, client.getInputStream().read()); |
| |
| // Client reads -1 |
| assertEquals(-1, client.getInputStream().read()); |
| assertFalse(client.isInputShutdown()); |
| |
| // Client can still write as we are half closed |
| client.getOutputStream().write(1); |
| |
| // Server can still read |
| assertEquals(1, server.getInputStream().read()); |
| |
| // Server now closes |
| server.close(); |
| |
| // Client still reads -1 (not broken pipe !!) |
| assertEquals(-1, client.getInputStream().read()); |
| assertFalse(client.isInputShutdown()); |
| |
| Thread.sleep(100); |
| |
| // Client still reads -1 (not broken pipe !!) |
| assertEquals(-1, client.getInputStream().read()); |
| assertFalse(client.isInputShutdown()); |
| |
| // Client can still write data even though server is closed??? |
| client.getOutputStream().write(1); |
| |
| // Client eventually sees Broken Pipe |
| int i = 0; |
| try |
| { |
| for (i = 0; i < 100000; i++) |
| client.getOutputStream().write(1); |
| |
| Assert.fail(); |
| } |
| catch (IOException e) |
| { |
| } |
| client.close(); |
| |
| } |
| catch (Exception e) |
| { |
| System.err.println("PLEASE INVESTIGATE:"); |
| e.printStackTrace(); |
| } |
| } |
| |
| @Test |
| public void testServerChannelInterrupt() throws Exception |
| { |
| final ServerSocketChannel connector = ServerSocketChannel.open(); |
| connector.configureBlocking(true); |
| connector.socket().bind(null); |
| |
| Socket client = SocketChannel.open(connector.socket().getLocalSocketAddress()).socket(); |
| client.setSoTimeout(2000); |
| client.setSoLinger(false, -1); |
| Socket server = connector.accept().socket(); |
| server.setSoTimeout(2000); |
| server.setSoLinger(false, -1); |
| |
| // Write from client to server |
| client.getOutputStream().write(1); |
| // Server reads |
| assertEquals(1, server.getInputStream().read()); |
| |
| // Write from server to client |
| server.getOutputStream().write(1); |
| // Client reads |
| assertEquals(1, client.getInputStream().read()); |
| |
| |
| // block a thread in accept |
| final CountDownLatch alatch=new CountDownLatch(2); |
| Thread acceptor = new Thread() |
| { |
| @Override |
| public void run() |
| { |
| try |
| { |
| alatch.countDown(); |
| connector.accept(); |
| } |
| catch (Throwable e) |
| { |
| } |
| finally |
| { |
| alatch.countDown(); |
| } |
| } |
| }; |
| acceptor.start(); |
| while (alatch.getCount()==2) |
| Thread.sleep(10); |
| |
| // interrupt the acceptor |
| acceptor.interrupt(); |
| |
| // wait for acceptor to exit |
| assertTrue(alatch.await(10,TimeUnit.SECONDS)); |
| |
| // connector is closed |
| assertFalse(connector.isOpen()); |
| |
| // but connection is still open |
| assertFalse(client.isClosed()); |
| assertFalse(server.isClosed()); |
| |
| // Write from client to server |
| client.getOutputStream().write(42); |
| // Server reads |
| assertEquals(42, server.getInputStream().read()); |
| |
| // Write from server to client |
| server.getOutputStream().write(43); |
| // Client reads |
| assertEquals(43, client.getInputStream().read()); |
| |
| client.close(); |
| |
| } |
| |
| |
| |
| @Test |
| public void testReset() throws Exception |
| { |
| try (ServerSocket connector = new ServerSocket(0); |
| Socket client = new Socket("127.0.0.1", connector.getLocalPort()); |
| Socket server = connector.accept();) |
| { |
| client.setTcpNoDelay(true); |
| client.setSoLinger(true, 0); |
| server.setTcpNoDelay(true); |
| server.setSoLinger(true, 0); |
| |
| client.getOutputStream().write(1); |
| assertEquals(1, server.getInputStream().read()); |
| server.getOutputStream().write(1); |
| assertEquals(1, client.getInputStream().read()); |
| |
| // Server generator shutdowns output after non persistent sending response. |
| server.shutdownOutput(); |
| |
| // client endpoint reads EOF and shutdown input as result |
| assertEquals(-1, client.getInputStream().read()); |
| client.shutdownInput(); |
| |
| // client connection see's EOF and shutsdown output as no more requests to be sent. |
| client.shutdownOutput(); |
| |
| // Since input already shutdown, client also closes socket. |
| client.close(); |
| |
| // Server reads the EOF from client oshut and shut's down it's input |
| assertEquals(-1, server.getInputStream().read()); |
| server.shutdownInput(); |
| |
| // Since output was already shutdown, server closes |
| server.close(); |
| } |
| } |
| |
| @Test |
| public void testAsyncSocketChannel() throws Exception |
| { |
| AsynchronousServerSocketChannel connector = AsynchronousServerSocketChannel.open(); |
| connector.bind(null); |
| InetSocketAddress addr=(InetSocketAddress)connector.getLocalAddress(); |
| Future<AsynchronousSocketChannel> acceptor = connector.accept(); |
| |
| AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); |
| |
| client.connect(new InetSocketAddress("127.0.0.1",addr.getPort())).get(5, TimeUnit.SECONDS); |
| |
| AsynchronousSocketChannel server = acceptor.get(5, TimeUnit.SECONDS); |
| |
| ByteBuffer read = ByteBuffer.allocate(1024); |
| Future<Integer> reading = server.read(read); |
| |
| byte[] data = "Testing 1 2 3".getBytes(StandardCharsets.UTF_8); |
| ByteBuffer write = BufferUtil.toBuffer(data); |
| Future<Integer> writing = client.write(write); |
| |
| writing.get(5, TimeUnit.SECONDS); |
| reading.get(5, TimeUnit.SECONDS); |
| read.flip(); |
| |
| Assert.assertEquals(ByteBuffer.wrap(data), read); |
| } |
| |
| @Test |
| public void testGatherWrite() throws Exception |
| { |
| File dir = MavenTestingUtils.getTargetTestingDir(); |
| if (!dir.exists()) |
| dir.mkdir(); |
| |
| File file = File.createTempFile("test",".txt",dir); |
| file.deleteOnExit(); |
| FileChannel out = FileChannel.open(file.toPath(), |
| StandardOpenOption.CREATE, |
| StandardOpenOption.READ, |
| StandardOpenOption.WRITE, |
| StandardOpenOption.DELETE_ON_CLOSE); |
| |
| ByteBuffer[] buffers = new ByteBuffer[4096]; |
| long expected=0; |
| for (int i=0;i<buffers.length;i++) |
| { |
| buffers[i]=BufferUtil.toBuffer(i); |
| expected+=buffers[i].remaining(); |
| } |
| |
| long wrote = IO.write(out,buffers,0,buffers.length); |
| |
| assertEquals(expected,wrote); |
| |
| for (int i=0;i<buffers.length;i++) |
| assertEquals(0,buffers[i].remaining()); |
| } |
| } |