diff options
author | Neil Fuller <nfuller@google.com> | 2014-02-11 15:52:27 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-02-11 15:52:27 +0000 |
commit | 58ac1fd09dddd681504868831909c5185b183582 (patch) | |
tree | 59b8f3014f69cebc2bc811f540a1820cfae0b680 | |
parent | 2b4e2469730e1341980ef79e2f5566c54403bded (diff) | |
parent | 47ae0b5a1d96c8030e0963ccc5b44c3ee66aaec3 (diff) | |
download | libcore-58ac1fd09dddd681504868831909c5185b183582.zip libcore-58ac1fd09dddd681504868831909c5185b183582.tar.gz libcore-58ac1fd09dddd681504868831909c5185b183582.tar.bz2 |
Merge "Implementing bind() and getLocalAddress() for NIO2."
21 files changed, 1129 insertions, 179 deletions
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt index 93c9a5c..3412e1c 100644 --- a/expectations/brokentests.txt +++ b/expectations/brokentests.txt @@ -875,5 +875,13 @@ "org.apache.harmony.tests.java.util.jar.ManifestTest#testRead", "org.apache.harmony.tests.java.util.jar.ManifestTest#testStreamConstructor" ] +}, +{ + description: "Potentially flakey because they rely on a specific local TCP port being free.", + result: EXEC_FAILED, + names: [ + "org.apache.harmony.tests.java.nio.channels.ServerSocketChannelTest#test_bind_explicitPort", + "org.apache.harmony.tests.java.nio.channels.SocketChannelTest#testBind_ExplicitFreePort" + ] } ] diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java index 0b8b26f..731e907 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java @@ -25,6 +25,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; @@ -71,11 +72,11 @@ public class DatagramChannelTest extends TestCase { channel1 = DatagramChannel.open(); channel2 = DatagramChannel.open(); - channel1.socket().bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0)); - channel2.socket().bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0)); + channel1.bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0)); + channel2.bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0)); - channel1Address = (InetSocketAddress) channel1.socket().getLocalSocketAddress(); - channel2Address = (InetSocketAddress) channel2.socket().getLocalSocketAddress(); + channel1Address = (InetSocketAddress) channel1.getLocalAddress(); + channel2Address = (InetSocketAddress) channel2.getLocalAddress(); this.datagramSocket1 = new DatagramSocket(0, Inet6Address.LOOPBACK); this.datagramSocket2 = new DatagramSocket(0, Inet6Address.LOOPBACK); @@ -379,7 +380,8 @@ public class DatagramChannelTest extends TestCase { try { s.connect(datagramSocket2Address); fail(); - } catch (IllegalStateException expected) {} + } catch (IllegalStateException expected) { + } assertTrue(this.channel1.isConnected()); assertTrue(s.isConnected()); @@ -1253,8 +1255,7 @@ public class DatagramChannelTest extends TestCase { Thread.sleep(TIME_UNIT); channel2.send(ByteBuffer.wrap(str.getBytes()), datagramSocket1Address); fail("Should throw SocketException!"); - } catch (SocketException e) { - //expected + } catch (SocketException expected) { } } @@ -1942,7 +1943,7 @@ public class DatagramChannelTest extends TestCase { sourceArray[i] = (byte) i; } - this.channel1.connect(channel1.socket().getLocalSocketAddress()); + this.channel1.connect(channel1.getLocalAddress()); this.channel2.connect(datagramSocket1Address); // the different addr // write @@ -1972,7 +1973,7 @@ public class DatagramChannelTest extends TestCase { assertEquals(CAPACITY_NORMAL, dc.write(sourceBuf)); // Connect channel2 after data has been written. - channel2.connect(dc.socket().getLocalSocketAddress()); + channel2.connect(dc.getLocalAddress()); // read ByteBuffer targetBuf = ByteBuffer.wrap(targetArray); @@ -2164,7 +2165,7 @@ public class DatagramChannelTest extends TestCase { assertEquals(CAPACITY_NORMAL, dc.write(sourceBuf)); // Connect channel2 after data has been written. - channel2.connect(dc.socket().getLocalSocketAddress()); + channel2.connect(dc.getLocalAddress()); // read ByteBuffer targetBuf = ByteBuffer.wrap(targetArray); @@ -2382,8 +2383,8 @@ public class DatagramChannelTest extends TestCase { public void test_bounded_harmony6493() throws IOException { DatagramChannel server = DatagramChannel.open(); InetSocketAddress addr = new InetSocketAddress("localhost", 0); - server.socket().bind(addr); - SocketAddress boundedAddress = server.socket().getLocalSocketAddress(); + server.bind(addr); + SocketAddress boundedAddress = server.getLocalAddress(); DatagramChannel client = DatagramChannel.open(); ByteBuffer sent = ByteBuffer.allocate(1024); @@ -2395,4 +2396,146 @@ public class DatagramChannelTest extends TestCase { server.close(); client.close(); } + + public void test_bind_null() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + try { + assertNull(dc.getLocalAddress()); + + dc.bind(null); + + InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress(); + assertTrue(localAddress.getAddress().isAnyLocalAddress()); + assertTrue(localAddress.getPort() > 0); + } finally { + dc.close(); + } + } + + public void test_bind_failure() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + try { + // Bind to a local address that is in use + dc.bind(channel1Address); + fail(); + } catch (IOException expected) { + } finally { + dc.close(); + } + } + + public void test_bind_closed() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + dc.close(); + + try { + dc.bind(null); + fail(); + } catch (ClosedChannelException expected) { + } finally { + dc.close(); + } + } + + public void test_bind_twice() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + dc.bind(null); + + try { + dc.bind(null); + fail(); + } catch (AlreadyBoundException expected) { + } finally { + dc.close(); + } + } + + public void test_bind_explicitPort() throws Exception { + InetSocketAddress address = (InetSocketAddress) channel1.getLocalAddress(); + assertTrue(address.getPort() > 0); + + DatagramChannel dc = DatagramChannel.open(); + // Allow the socket to bind to a port we know is already in use. + dc.socket().setReuseAddress(true); + InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort()); + dc.bind(bindAddress); + + InetSocketAddress boundAddress = (InetSocketAddress) dc.getLocalAddress(); + assertEquals(bindAddress.getHostName(), boundAddress.getHostName()); + assertEquals(bindAddress.getPort(), boundAddress.getPort()); + + dc.close(); + channel1.close(); + } + + /** Checks that the SocketChannel and associated Socket agree on the socket state. */ + public void test_bind_socketSync() throws IOException { + DatagramChannel dc = DatagramChannel.open(); + assertNull(dc.getLocalAddress()); + + DatagramSocket socket = dc.socket(); + assertNull(socket.getLocalSocketAddress()); + assertFalse(socket.isBound()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + dc.bind(bindAddr); + + InetSocketAddress actualAddr = (InetSocketAddress) dc.getLocalAddress(); + assertEquals(actualAddr, socket.getLocalSocketAddress()); + assertEquals(bindAddr.getHostName(), actualAddr.getHostName()); + assertTrue(socket.isBound()); + assertFalse(socket.isConnected()); + assertFalse(socket.isClosed()); + + dc.close(); + + assertFalse(dc.isOpen()); + assertTrue(socket.isClosed()); + } + + /** + * Checks that the SocketChannel and associated Socket agree on the socket state, even if + * the Socket object is requested/created after bind(). + */ + public void test_bind_socketSyncAfterBind() throws IOException { + DatagramChannel dc = DatagramChannel.open(); + assertNull(dc.getLocalAddress()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + dc.bind(bindAddr); + + // Socket creation after bind(). + DatagramSocket socket = dc.socket(); + InetSocketAddress actualAddr = (InetSocketAddress) dc.getLocalAddress(); + assertEquals(actualAddr, socket.getLocalSocketAddress()); + assertEquals(bindAddr.getHostName(), actualAddr.getHostName()); + assertTrue(socket.isBound()); + assertFalse(socket.isConnected()); + assertFalse(socket.isClosed()); + + dc.close(); + + assertFalse(dc.isOpen()); + assertTrue(socket.isClosed()); + } + + public void test_getLocalSocketAddress_afterClose() throws IOException { + DatagramChannel dc = DatagramChannel.open(); + assertNull(dc.getLocalAddress()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + dc.bind(bindAddr); + + assertNotNull(dc.getLocalAddress()); + + dc.close(); + + assertFalse(dc.isOpen()); + + try { + dc.getLocalAddress(); + fail(); + } catch (ClosedChannelException expected) { + } + } } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java index 828ab30..b417adc 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java @@ -24,6 +24,7 @@ import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.ClosedChannelException; import java.nio.channels.IllegalBlockingModeException; @@ -108,6 +109,157 @@ public class ServerSocketChannelTest extends TestCase { } // ------------------------------------------------------------------- + // Tests for bind() + // ------------------------------------------------------------------- + + public void test_bind_null() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open(); + try { + assertNull(ssc.getLocalAddress()); + + ssc.bind(null); + + InetSocketAddress localAddress = (InetSocketAddress) ssc.getLocalAddress(); + assertTrue(localAddress.getAddress().isAnyLocalAddress()); + assertTrue(localAddress.getPort() > 0); + } finally { + ssc.close(); + } + } + + public void test_bind_failure() throws Exception { + ServerSocketChannel portHog = ServerSocketChannel.open(); + portHog.bind(null); + + ServerSocketChannel ssc = ServerSocketChannel.open(); + try { + // Bind to a local address that is in use + ssc.bind(portHog.getLocalAddress()); + fail(); + } catch (IOException expected) { + } finally { + ssc.close(); + portHog.close(); + } + } + + public void test_bind_closed() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.close(); + + try { + ssc.bind(null); + fail(); + } catch (ClosedChannelException expected) { + } finally { + ssc.close(); + } + } + + public void test_bind_twice() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.bind(null); + + try { + ssc.bind(null); + fail(); + } catch (AlreadyBoundException expected) { + } finally { + ssc.close(); + } + } + + public void test_bind_explicitPort() throws Exception { + ServerSocketChannel portPickingChannel = ServerSocketChannel.open(); + // Have the OS find a free port. + portPickingChannel.bind(null); + + InetSocketAddress address = (InetSocketAddress) portPickingChannel.getLocalAddress(); + assertTrue(address.getPort() > 0); + portPickingChannel.close(); + + // There is a risk of flakiness here if the port is allocated to something else between + // close() and bind(). + ServerSocketChannel ssc = ServerSocketChannel.open(); + InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort()); + ssc.bind(bindAddress); + + InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress(); + assertEquals(bindAddress.getHostName(), boundAddress.getHostName()); + assertEquals(bindAddress.getPort(), boundAddress.getPort()); + + ssc.close(); + } + + public void test_bind_socketSync() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + assertNull(ssc.getLocalAddress()); + + ServerSocket socket = ssc.socket(); + assertNull(socket.getLocalSocketAddress()); + assertFalse(socket.isBound()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + ssc.bind(bindAddr); + + InetSocketAddress actualAddr = (InetSocketAddress) ssc.getLocalAddress(); + assertEquals(actualAddr, socket.getLocalSocketAddress()); + assertEquals(bindAddr.getHostName(), actualAddr.getHostName()); + assertTrue(socket.isBound()); + assertFalse(socket.isClosed()); + + ssc.close(); + + assertFalse(ssc.isOpen()); + assertTrue(socket.isClosed()); + } + + public void test_bind_socketSyncAfterBind() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + assertNull(ssc.getLocalAddress()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + ssc.bind(bindAddr); + + // Socket creation after bind(). + ServerSocket socket = ssc.socket(); + InetSocketAddress actualAddr = (InetSocketAddress) ssc.getLocalAddress(); + assertEquals(actualAddr, socket.getLocalSocketAddress()); + assertEquals(bindAddr.getHostName(), actualAddr.getHostName()); + assertTrue(socket.isBound()); + assertFalse(socket.isClosed()); + + ssc.close(); + + assertFalse(ssc.isOpen()); + assertTrue(socket.isClosed()); + } + + // ------------------------------------------------------------------- + // Test for getLocalSocketAddress() + // ------------------------------------------------------------------- + + public void test_getLocalSocketAddress_afterClose() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + assertNull(ssc.getLocalAddress()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + ssc.bind(bindAddr); + + assertNotNull(ssc.getLocalAddress()); + + ssc.close(); + + assertFalse(ssc.isOpen()); + + try { + ssc.getLocalAddress(); + fail(); + } catch (ClosedChannelException expected) { + } + } + + // ------------------------------------------------------------------- // Test for socket() // ------------------------------------------------------------------- @@ -235,8 +387,7 @@ public class ServerSocketChannelTest extends TestCase { public void testAccept_Block_NoConnect() throws IOException { assertTrue(this.serverChannel.isBlocking()); - ServerSocket gotSocket = this.serverChannel.socket(); - gotSocket.bind(null); + serverChannel.bind(null); // blocking mode , will block and wait for ever... // so must close the server channel with another thread. new Thread() { @@ -259,8 +410,7 @@ public class ServerSocketChannelTest extends TestCase { } public void testAccept_NonBlock_NoConnect() throws IOException { - ServerSocket gotSocket = this.serverChannel.socket(); - gotSocket.bind(null); + this.serverChannel.bind(null); this.serverChannel.configureBlocking(false); // non-blocking mode , will immediately return assertNull(this.serverChannel.accept()); @@ -270,13 +420,13 @@ public class ServerSocketChannelTest extends TestCase { * @tests ServerSocketChannel#accept().socket() */ public void test_read_Blocking_RealData() throws IOException { - serverChannel.socket().bind(null); + serverChannel.bind(null); ByteBuffer buf = ByteBuffer.allocate(CAPACITY_NORMAL); for (int i = 0; i < CAPACITY_NORMAL; i++) { buf.put((byte) i); } - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); Socket serverSocket = serverChannel.accept().socket(); InputStream in = serverSocket.getInputStream(); buf.flip(); @@ -309,13 +459,13 @@ public class ServerSocketChannelTest extends TestCase { */ public void test_read_NonBlocking_RealData() throws Exception { serverChannel.configureBlocking(false); - serverChannel.socket().bind(null); + serverChannel.bind(null); ByteBuffer buf = ByteBuffer.allocate(CAPACITY_NORMAL); for (int i = 0; i < CAPACITY_NORMAL; i++) { buf.put((byte) i); } buf.flip(); - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); Socket serverSocket = serverChannel.accept().socket(); InputStream in = serverSocket.getInputStream(); clientChannel.write(buf); @@ -328,14 +478,13 @@ public class ServerSocketChannelTest extends TestCase { */ public void test_write_Blocking_RealData() throws IOException { assertTrue(serverChannel.isBlocking()); - ServerSocket serverSocket = serverChannel.socket(); - serverSocket.bind(null); + serverChannel.bind(null); byte[] writeContent = new byte[CAPACITY_NORMAL]; for (int i = 0; i < writeContent.length; i++) { writeContent[i] = (byte) i; } - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); Socket socket = serverChannel.accept().socket(); OutputStream out = socket.getOutputStream(); out.write(writeContent); @@ -350,14 +499,13 @@ public class ServerSocketChannelTest extends TestCase { */ public void test_write_NonBlocking_RealData() throws Exception { serverChannel.configureBlocking(false); - ServerSocket serverSocket = serverChannel.socket(); - serverSocket.bind(null); + serverChannel.bind(null); byte[] writeContent = new byte[CAPACITY_NORMAL]; for (int i = 0; i < CAPACITY_NORMAL; i++) { writeContent[i] = (byte) i; } - clientChannel.connect(serverSocket.getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); Socket clientSocket = serverChannel.accept().socket(); OutputStream out = clientSocket.getOutputStream(); out.write(writeContent); @@ -371,13 +519,13 @@ public class ServerSocketChannelTest extends TestCase { */ public void test_read_LByteBuffer_Blocking_ReadWriteRealLargeData() throws IOException, InterruptedException { - serverChannel.socket().bind(null); + serverChannel.bind(null); ByteBuffer buf = ByteBuffer.allocate(CAPACITY_64KB); for (int i = 0; i < CAPACITY_64KB; i++) { buf.put((byte) i); } buf.flip(); - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); WriteChannelThread writeThread = new WriteChannelThread(clientChannel, buf); writeThread.start(); Socket socket = serverChannel.accept().socket(); @@ -416,13 +564,13 @@ public class ServerSocketChannelTest extends TestCase { public void test_read_LByteBuffer_NonBlocking_ReadWriteRealLargeData() throws Exception { serverChannel.configureBlocking(false); - serverChannel.socket().bind(null); + serverChannel.bind(null); ByteBuffer buf = ByteBuffer.allocate(CAPACITY_64KB); for (int i = 0; i < CAPACITY_64KB; i++) { buf.put((byte) i); } buf.flip(); - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); WriteChannelThread writeThread = new WriteChannelThread(clientChannel, buf); writeThread.start(); Socket socket = serverChannel.accept().socket(); @@ -441,12 +589,12 @@ public class ServerSocketChannelTest extends TestCase { public void test_write_LByteBuffer_NonBlocking_ReadWriteRealLargeData() throws Exception { serverChannel.configureBlocking(false); - serverChannel.socket().bind(null); + serverChannel.bind(null); byte[] writeContent = new byte[CAPACITY_64KB]; for (int i = 0; i < writeContent.length; i++) { writeContent[i] = (byte) i; } - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); Socket socket = serverChannel.accept().socket(); WriteSocketThread writeThread = new WriteSocketThread(socket, writeContent); writeThread.start(); @@ -484,12 +632,12 @@ public class ServerSocketChannelTest extends TestCase { */ public void test_write_LByteBuffer_Blocking_ReadWriteRealLargeData() throws Exception { - serverChannel.socket().bind(null); + serverChannel.bind(null); byte[] writeContent = new byte[CAPACITY_64KB]; for (int i = 0; i < writeContent.length; i++) { writeContent[i] = (byte) i; } - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + clientChannel.connect(serverChannel.getLocalAddress()); Socket socket = serverChannel.accept().socket(); WriteSocketThread writeThread = new WriteSocketThread(socket, writeContent); writeThread.start(); @@ -531,9 +679,9 @@ public class ServerSocketChannelTest extends TestCase { final int SO_TIMEOUT = 10; ServerSocketChannel sc = ServerSocketChannel.open(); try { - ServerSocket ss = sc.socket(); - ss.bind(null); + sc.bind(null); sc.configureBlocking(false); + ServerSocket ss = sc.socket(); ss.setSoTimeout(SO_TIMEOUT); SocketChannel client = sc.accept(); // non blocking mode, returns null since there are no pending connections. @@ -556,15 +704,13 @@ public class ServerSocketChannelTest extends TestCase { try { gotSocket.accept(); fail("Should throw an IllegalBlockingModeException"); - } catch (IllegalBlockingModeException e) { - // expected + } catch (IllegalBlockingModeException expected) { } serverChannel.close(); try { gotSocket.accept(); fail("Should throw an IllegalBlockingModeException"); - } catch (IllegalBlockingModeException e) { - // expected + } catch (IllegalBlockingModeException expected) { } } @@ -578,15 +724,13 @@ public class ServerSocketChannelTest extends TestCase { try { gotSocket.accept(); fail("Should throw an IllegalBlockingModeException"); - } catch (IllegalBlockingModeException e) { - // expected + } catch (IllegalBlockingModeException expected) { } serverChannel.close(); try { gotSocket.accept(); fail("Should throw an IllegalBlockingModeException"); - } catch (IllegalBlockingModeException e) { - // expected + } catch (IllegalBlockingModeException expected) { } } @@ -596,20 +740,18 @@ public class ServerSocketChannelTest extends TestCase { public void test_socket_accept_Nonblocking_Bound() throws IOException { // regression test for Harmony-748 serverChannel.configureBlocking(false); + serverChannel.bind(null); ServerSocket gotSocket = serverChannel.socket(); - gotSocket.bind(null); try { gotSocket.accept(); fail("Should throw an IllegalBlockingModeException"); - } catch (IllegalBlockingModeException e) { - // expected + } catch (IllegalBlockingModeException expected) { } serverChannel.close(); try { gotSocket.accept(); fail("Should throw a ClosedChannelException"); - } catch (ClosedChannelException e) { - // expected + } catch (ClosedChannelException expected) { } } @@ -619,22 +761,20 @@ public class ServerSocketChannelTest extends TestCase { public void test_socket_accept_Blocking_Bound() throws IOException { // regression test for Harmony-748 serverChannel.configureBlocking(true); - ServerSocket gotSocket = serverChannel.socket(); - gotSocket.bind(null); + serverChannel.bind(null); serverChannel.close(); try { - gotSocket.accept(); + serverChannel.socket().accept(); fail("Should throw a ClosedChannelException"); - } catch (ClosedChannelException e) { - // expected + } catch (ClosedChannelException expected) { } } /** * Regression test for HARMONY-4961 */ public void test_socket_getLocalPort() throws IOException { - serverChannel.socket().bind(null); - clientChannel.connect(serverChannel.socket().getLocalSocketAddress()); + serverChannel.bind(null); + clientChannel.connect(serverChannel.getLocalAddress()); SocketChannel myChannel = serverChannel.accept(); int port = myChannel.socket().getLocalPort(); assertEquals(serverChannel.socket().getLocalPort(), port); @@ -648,7 +788,7 @@ public class ServerSocketChannelTest extends TestCase { */ public void test_accept_configureBlocking() throws Exception { InetSocketAddress localAddr = new InetSocketAddress("localhost", 0); - serverChannel.socket().bind(localAddr); + serverChannel.bind(localAddr); // configure the channel non-blocking // when it is accepting in main thread @@ -667,8 +807,7 @@ public class ServerSocketChannelTest extends TestCase { try { serverChannel.accept(); fail("should throw AsynchronousCloseException"); - } catch (AsynchronousCloseException e) { - // expected + } catch (AsynchronousCloseException expected) { } serverChannel.close(); } diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java index 2d52bc5..52dff79 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java @@ -30,6 +30,7 @@ import java.net.SocketAddress; import java.net.SocketException; import java.nio.Buffer; import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ConnectionPendingException; @@ -153,6 +154,87 @@ public class SocketChannelTest extends TestCase { } } + public void testBind_Null() throws Exception { + assertNull(channel1.getLocalAddress()); + + channel1.bind(null); + + InetSocketAddress localAddress = (InetSocketAddress) channel1.getLocalAddress(); + assertTrue(localAddress.getAddress().isAnyLocalAddress()); + assertTrue(localAddress.getPort() > 0); + } + + public void testBind_Failure() throws Exception { + assertNull(channel1.getLocalAddress()); + + try { + // Bind to a local address that is in use + channel1.bind(localAddr1); + fail(); + } catch (IOException expected) { + } + } + + public void testBind_Closed() throws Exception { + channel1.close(); + + try { + channel1.bind(null); + fail(); + } catch (ClosedChannelException expected) { + } + } + + public void testBind_Twice() throws Exception { + channel1.bind(null); + + try { + channel1.bind(null); + fail(); + } catch (AlreadyBoundException expected) { + } + } + + public void testBind_explicitPort() throws Exception { + ServerSocketChannel portPickingChannel = ServerSocketChannel.open(); + // Have the OS find a free port. + portPickingChannel.bind(null); + InetSocketAddress address = (InetSocketAddress) portPickingChannel.getLocalAddress(); + assertTrue(address.getPort() > 0); + portPickingChannel.close(); + + // There is a risk of flakiness here if the port is allocated to something else between + // close() and bind(). + InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort()); + // Allow the socket to bind to a port we know is already in use. + channel1.socket().setReuseAddress(true); + channel1.bind(bindAddress); + + InetSocketAddress boundAddress = (InetSocketAddress) channel1.getLocalAddress(); + assertEquals(bindAddress.getHostName(), boundAddress.getHostName()); + assertEquals(bindAddress.getPort(), boundAddress.getPort()); + } + + public void test_getLocalSocketAddress_afterClose() throws IOException { + SocketChannel sc = SocketChannel.open(); + assertNull(sc.getLocalAddress()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + sc.bind(bindAddr); + + assertNotNull(sc.getLocalAddress()); + + sc.close(); + + assertFalse(sc.isOpen()); + + try { + sc.getLocalAddress(); + fail(); + } catch (ClosedChannelException expected) { + } + } + /* * Test method for 'java.nio.channels.SocketChannel.read(ByteBuffer[])' */ @@ -1800,7 +1882,7 @@ public class SocketChannelTest extends TestCase { ServerSocket serversocket = theServerChannel.socket(); serversocket.setReuseAddress(true); // Bind the socket - serversocket.bind(address); + theServerChannel.bind(address); boolean doneNonBlockingConnect = false; // Loop so that we make sure we're definitely testing finishConnect() @@ -2121,8 +2203,7 @@ public class SocketChannelTest extends TestCase { ByteBuffer buffer = ByteBuffer.allocateDirect(128); ServerSocketChannel server = ServerSocketChannel.open(); - server.socket().bind( - new InetSocketAddress(InetAddress.getLocalHost(), 0), 5); + server.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0), 5); Socket client = new Socket(InetAddress.getLocalHost(), server.socket() .getLocalPort()); client.setTcpNoDelay(false); @@ -2740,9 +2821,9 @@ public class SocketChannelTest extends TestCase { */ public void test_writev() throws Exception { ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); - sc.connect(ssc.socket().getLocalSocketAddress()); + sc.connect(ssc.getLocalAddress()); SocketChannel sock = ssc.accept(); ByteBuffer[] buf = { ByteBuffer.allocate(10), ByteBuffer.allocateDirect(20) }; @@ -2767,10 +2848,10 @@ public class SocketChannelTest extends TestCase { public void test_writev2() throws Exception { ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); - boolean connected = sc.connect(ssc.socket().getLocalSocketAddress()); + boolean connected = sc.connect(ssc.getLocalAddress()); SocketChannel sock = ssc.accept(); if (!connected) { sc.finishConnect(); @@ -2805,10 +2886,10 @@ public class SocketChannelTest extends TestCase { public void test_write$NonBlockingException() throws Exception { ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); - boolean connected = sc.connect(ssc.socket().getLocalSocketAddress()); + boolean connected = sc.connect(ssc.getLocalAddress()); SocketChannel sock = ssc.accept(); if (!connected) { sc.finishConnect(); @@ -2841,9 +2922,9 @@ public class SocketChannelTest extends TestCase { public void test_write$LByteBuffer2() throws IOException { // Set-up ServerSocketChannel server = ServerSocketChannel.open(); - server.socket().bind(null); + server.bind(null); SocketChannel client = SocketChannel.open(); - client.connect(server.socket().getLocalSocketAddress()); + client.connect(server.getLocalAddress()); SocketChannel worker = server.accept(); // Test overlapping buffers @@ -2873,9 +2954,9 @@ public class SocketChannelTest extends TestCase { public void test_write$LByteBuffer_buffers() throws IOException { // Set-up ServerSocketChannel server = ServerSocketChannel.open(); - server.socket().bind(null); + server.bind(null); SocketChannel client = SocketChannel.open(); - client.connect(server.socket().getLocalSocketAddress()); + client.connect(server.getLocalAddress()); SocketChannel worker = server.accept(); // A variety of buffer types to write @@ -2915,9 +2996,9 @@ public class SocketChannelTest extends TestCase { public void test_write$LByteBuffer_writes() throws IOException { // Set-up ServerSocketChannel server = ServerSocketChannel.open(); - server.socket().bind(null); + server.bind(null); SocketChannel client = SocketChannel.open(); - client.connect(server.socket().getLocalSocketAddress()); + client.connect(server.getLocalAddress()); SocketChannel worker = server.accept(); // Data to write @@ -2942,7 +3023,7 @@ public class SocketChannelTest extends TestCase { // Read what we wrote and check it ByteBuffer readBuffer = ByteBuffer.allocate(1024); - while (EOF != worker.read(readBuffer)) {}; + while (EOF != worker.read(readBuffer)) {} readBuffer.flip(); assertEquals(ByteBuffer.wrap(data), readBuffer); @@ -2957,10 +3038,10 @@ public class SocketChannelTest extends TestCase { public void test_write$LByteBuffer_invalid() throws IOException { // Set-up ServerSocketChannel server = ServerSocketChannel.open(); - server.socket().bind(null); + server.bind(null); SocketChannel client = SocketChannel.open(); - client.connect(server.socket().getLocalSocketAddress()); + client.connect(server.getLocalAddress()); SocketChannel worker = server.accept(); @@ -2968,32 +3049,27 @@ public class SocketChannelTest extends TestCase { try { client.write((ByteBuffer[]) null); fail("Should throw a NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } try { client.write((ByteBuffer[]) null, 0, 0); fail("Should throw a NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } try { client.write((ByteBuffer[]) null, 1, 0); fail("Should throw a NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } try { client.write((ByteBuffer[]) null, 0, 1); fail("Should throw a NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } try { client.write((ByteBuffer[]) null, 1, 1); fail("Should throw a NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } ByteBuffer[] buffers = new ByteBuffer[1]; @@ -3065,9 +3141,9 @@ public class SocketChannelTest extends TestCase { throws Exception { // regression 1 for HARMONY-549 ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); - sc.connect(ssc.socket().getLocalSocketAddress()); + sc.connect(ssc.getLocalAddress()); ssc.accept().close(); ByteBuffer[] buf = { ByteBuffer.allocate(10) }; assertEquals(-1, sc.read(buf, 0, 1)); @@ -3081,16 +3157,15 @@ public class SocketChannelTest extends TestCase { public void test_socketChannel_write_ByteBufferII() throws Exception { // regression 2 for HARMONY-549 ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); - sc.connect(ssc.socket().getLocalSocketAddress()); + sc.connect(ssc.getLocalAddress()); SocketChannel sock = ssc.accept(); ByteBuffer[] buf = { ByteBuffer.allocate(10), null }; try { sc.write(buf, 0, 2); fail("should throw NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } ssc.close(); sc.close(); @@ -3104,9 +3179,9 @@ public class SocketChannelTest extends TestCase { public void test_socketChannel_read_ByteBufferII_bufNULL() throws Exception { // regression 3 for HARMONY-549 ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); - sc.connect(ssc.socket().getLocalSocketAddress()); + sc.connect(ssc.getLocalAddress()); ssc.accept(); ByteBuffer[] buf = new ByteBuffer[2]; buf[0] = ByteBuffer.allocate(1); @@ -3114,8 +3189,7 @@ public class SocketChannelTest extends TestCase { try { sc.read(buf, 0, 2); fail("should throw NullPointerException"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } ssc.close(); sc.close(); @@ -3127,9 +3201,9 @@ public class SocketChannelTest extends TestCase { public void test_socketChannel_write_close() throws Exception { // regression 4 for HARMONY-549 ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); - sc.connect(ssc.socket().getLocalSocketAddress()); + sc.connect(ssc.getLocalAddress()); SocketChannel sock = ssc.accept(); ByteBuffer buf = null; ssc.close(); @@ -3137,8 +3211,7 @@ public class SocketChannelTest extends TestCase { try { sc.write(buf); fail("should throw NPE"); - } catch (NullPointerException e) { - // expected + } catch (NullPointerException expected) { } sock.close(); } @@ -3153,9 +3226,9 @@ public class SocketChannelTest extends TestCase { ByteBuffer readBuf = ByteBuffer.allocate(11); ByteBuffer buf = ByteBuffer.wrap(testStr.getBytes()); ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); + ssc.bind(null); SocketChannel sc = SocketChannel.open(); - sc.connect(ssc.socket().getLocalSocketAddress()); + sc.connect(ssc.getLocalAddress()); buf.position(2); ssc.accept().write(buf); assertEquals(9, sc.read(readBuf)); diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java index e2acac7..26825d2 100644 --- a/luni/src/main/java/java/net/DatagramSocket.java +++ b/luni/src/main/java/java/net/DatagramSocket.java @@ -176,8 +176,8 @@ public class DatagramSocket implements Closeable { } /** - * Returns the local address to which this socket is bound, - * or {@code null} if this socket is closed. + * Returns the local address to which this socket is bound, a wildcard address if this + * socket is not yet bound, or {@code null} if this socket is closed. */ public InetAddress getLocalAddress() { try { @@ -588,10 +588,11 @@ public class DatagramSocket implements Closeable { } /** - * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket. + * Returns the {@code SocketAddress} this socket is bound to, or {@code null} for an unbound or + * closed socket. */ public SocketAddress getLocalSocketAddress() { - if (!isBound()) { + if (isClosed() || !isBound()) { return null; } return new InetSocketAddress(getLocalAddress(), getLocalPort()); diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java index 8b56ca1..72b197f 100644 --- a/luni/src/main/java/java/net/ServerSocket.java +++ b/luni/src/main/java/java/net/ServerSocket.java @@ -164,8 +164,8 @@ public class ServerSocket implements Closeable { } /** - * Gets the local IP address of this server socket or {@code null} if the - * socket is unbound. This is useful for multihomed hosts. + * Gets the local IP address of this server socket if this socket has ever been bound, + * {@code null} otherwise. This is useful for multihomed hosts. * * @return the local address of this server socket. */ @@ -177,8 +177,9 @@ public class ServerSocket implements Closeable { } /** - * Gets the local port of this server socket or {@code -1} if the socket is - * unbound. + * Gets the local port of this server socket or {@code -1} if the socket is not bound. + * If the socket has ever been bound this method will return the local port it was bound to, + * even after it has been closed. * * @return the local port this server is listening on. */ @@ -342,8 +343,9 @@ public class ServerSocket implements Closeable { } /** - * Gets the local socket address of this server socket or {@code null} if - * the socket is unbound. This is useful on multihomed hosts. + * Gets the local socket address of this server socket or {@code null} if the socket is unbound. + * This is useful on multihomed hosts. If the socket has ever been bound this method will return + * the local address it was bound to, even after it has been closed. * * @return the local socket address and port this socket is bound to. */ diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java index 517571d..5dd350a 100644 --- a/luni/src/main/java/java/net/Socket.java +++ b/luni/src/main/java/java/net/Socket.java @@ -312,6 +312,7 @@ public class Socket implements Closeable { */ public synchronized void close() throws IOException { isClosed = true; + isConnected = false; // RI compatibility: the RI returns the any address (but the original local port) after // close. localAddress = Inet4Address.ANY; @@ -326,6 +327,7 @@ public class Socket implements Closeable { */ public void onClose() { isClosed = true; + isConnected = false; // RI compatibility: the RI returns the any address (but the original local port) after // close. localAddress = Inet4Address.ANY; @@ -371,14 +373,15 @@ public class Socket implements Closeable { /** * Returns the local IP address this socket is bound to, or an address for which - * {@link InetAddress#isAnyLocalAddress()} returns true if the socket is unbound. + * {@link InetAddress#isAnyLocalAddress()} returns true if the socket is closed or unbound. */ public InetAddress getLocalAddress() { return localAddress; } /** - * Returns the local port this socket is bound to, or -1 if the socket is unbound. + * Returns the local port this socket is bound to, or -1 if the socket is unbound. If the socket + * has been closed this method will still return the local port the socket was bound to. */ public int getLocalPort() { if (!isBound()) { @@ -583,6 +586,7 @@ public class Socket implements Closeable { impl.bind(addr, localPort); } isBound = true; + cacheLocalAddress(); impl.connect(dstAddress, dstPort); isConnected = true; cacheLocalAddress(); @@ -691,9 +695,10 @@ public class Socket implements Closeable { } /** - * Returns the local address and port of this socket as a SocketAddress or - * null if the socket is unbound. This is useful on multihomed - * hosts. + * Returns the local address and port of this socket as a SocketAddress or null if the socket + * has never been bound. If the socket is closed but has previously been bound then an address + * for which {@link InetAddress#isAnyLocalAddress()} returns true will be returned with the + * previously-bound port. This is useful on multihomed hosts. */ public SocketAddress getLocalSocketAddress() { if (!isBound()) { @@ -800,6 +805,7 @@ public class Socket implements Closeable { */ public void onBind(InetAddress localAddress, int localPort) { isBound = true; + this.localAddress = localAddress; impl.onBind(localAddress, localPort); } diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java index 5f74bdc..3c4a980 100644 --- a/luni/src/main/java/java/nio/DatagramChannelImpl.java +++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java @@ -24,16 +24,19 @@ import java.net.ConnectException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.DatagramSocketImpl; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.PlainDatagramSocketImpl; import java.net.SocketAddress; import java.net.SocketException; +import java.nio.channels.AlreadyBoundException; import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.NotYetConnectedException; +import java.nio.channels.UnsupportedAddressTypeException; import java.nio.channels.spi.SelectorProvider; import java.util.Arrays; import libcore.io.ErrnoException; @@ -100,6 +103,26 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann return socket; } + /** @hide Until ready for a public API change */ + @Override + synchronized public DatagramChannel bind(SocketAddress local) throws IOException { + checkOpen(); + if (isBound) { + throw new AlreadyBoundException(); + } + + if (local == null) { + local = new InetSocketAddress(Inet4Address.ANY, 0); + } else if (!(local instanceof InetSocketAddress)) { + throw new UnsupportedAddressTypeException(); + } + + InetSocketAddress localAddress = (InetSocketAddress) local; + IoBridge.bind(fd, localAddress.getAddress(), localAddress.getPort()); + onBind(true /* updateSocketState */); + return this; + } + /** * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when * some or all of the bound state has been left to the OS to decide, or when the Socket handled @@ -125,6 +148,13 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann } } + /** @hide Until ready for a public API change */ + @Override + synchronized public SocketAddress getLocalAddress() throws IOException { + checkOpen(); + return isBound ? new InetSocketAddress(localAddress, localPort) : null; + } + @Override synchronized public boolean isConnected() { return connected; @@ -547,40 +577,6 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann } @Override - public boolean isBound() { - return channelImpl.isBound; - } - - @Override - public boolean isConnected() { - return channelImpl.isConnected(); - } - - @Override - public InetAddress getInetAddress() { - if (channelImpl.connectAddress == null) { - return null; - } - return channelImpl.connectAddress.getAddress(); - } - - @Override public InetAddress getLocalAddress() { - try { - return IoBridge.getSocketLocalAddress(channelImpl.fd); - } catch (SocketException ex) { - return null; - } - } - - @Override - public int getPort() { - if (channelImpl.connectAddress == null) { - return -1; - } - return channelImpl.connectAddress.getPort(); - } - - @Override public void bind(SocketAddress localAddr) throws SocketException { if (channelImpl.isConnected()) { throw new AlreadyConnectedException(); diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java index 92bc419..9ae282e 100644 --- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java +++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java @@ -22,12 +22,15 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketAddress; import java.net.SocketTimeoutException; +import java.nio.channels.AlreadyBoundException; import java.nio.channels.ClosedChannelException; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.NotYetBoundException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.nio.channels.UnsupportedAddressTypeException; import java.nio.channels.spi.SelectorProvider; import libcore.io.ErrnoException; import libcore.io.IoUtils; @@ -51,6 +54,32 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD return socket; } + /** @hide Until ready for a public API change */ + @Override + public final ServerSocketChannel bind(SocketAddress localAddr, int backlog) throws IOException { + if (!isOpen()) { + throw new ClosedChannelException(); + } + if (socket.isBound()) { + throw new AlreadyBoundException(); + } + if (localAddr != null && !(localAddr instanceof InetSocketAddress)) { + throw new UnsupportedAddressTypeException(); + } + + socket.bind(localAddr, backlog); + return this; + } + + /** @hide Until ready for a public API change */ + @Override + public SocketAddress getLocalAddress() throws IOException { + if (!isOpen()) { + throw new ClosedChannelException(); + } + return socket.getLocalSocketAddress(); + } + @Override public SocketChannel accept() throws IOException { if (!isOpen()) { @@ -60,7 +89,7 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD throw new NotYetBoundException(); } - // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept. + // Create an empty socket channel. This will be populated by ServerSocketAdapter.implAccept. SocketChannelImpl result = new SocketChannelImpl(provider(), false); try { begin(); diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java index 6af9f9f..977c433 100644 --- a/luni/src/main/java/java/nio/SocketChannelImpl.java +++ b/luni/src/main/java/java/nio/SocketChannelImpl.java @@ -32,6 +32,7 @@ import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketUtils; +import java.nio.channels.AlreadyBoundException; import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ConnectionPendingException; @@ -135,6 +136,28 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { return socket; } + /** @hide Until ready for a public API change */ + @Override + synchronized public final SocketChannel bind(SocketAddress local) throws IOException { + if (!isOpen()) { + throw new ClosedChannelException(); + } + if (isBound) { + throw new AlreadyBoundException(); + } + + if (local == null) { + local = new InetSocketAddress(Inet4Address.ANY, 0); + } else if (!(local instanceof InetSocketAddress)) { + throw new UnsupportedAddressTypeException(); + } + + InetSocketAddress localAddress = (InetSocketAddress) local; + IoBridge.bind(fd, localAddress.getAddress(), localAddress.getPort()); + onBind(true /* updateSocketState */); + return this; + } + /** * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when * some or all of the bound state has been left to the OS to decide, or when the Socket handled @@ -160,6 +183,15 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { } } + /** @hide Until ready for a public API change */ + @Override + synchronized public SocketAddress getLocalAddress() throws IOException { + if (!isOpen()) { + throw new ClosedChannelException(); + } + return isBound ? new InetSocketAddress(localAddress, localPort) : null; + } + @Override synchronized public boolean isConnected() { return status == SOCKET_STATUS_CONNECTED; @@ -526,21 +558,6 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { } @Override - public boolean isBound() { - return channel.isBound; - } - - @Override - public boolean isConnected() { - return channel.isConnected(); - } - - @Override - public InetAddress getLocalAddress() { - return channel.localAddress != null ? channel.localAddress : Inet4Address.ANY; - } - - @Override public void connect(SocketAddress remoteAddr, int timeout) throws IOException { if (!channel.isBlocking()) { throw new IllegalBlockingModeException(); diff --git a/luni/src/main/java/java/nio/channels/AlreadyBoundException.java b/luni/src/main/java/java/nio/channels/AlreadyBoundException.java new file mode 100644 index 0000000..0a35fc3 --- /dev/null +++ b/luni/src/main/java/java/nio/channels/AlreadyBoundException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.channels; + +/** + * An {@code AlreadyBoundException} is thrown when an attempt is made to bind a NetworkChannel that + * is already bound. + * + * @hide Until ready for a public API change + */ +public class AlreadyBoundException extends IllegalStateException { + + private static final long serialVersionUID = 6796072983322737592L; + + public AlreadyBoundException() { + } +} diff --git a/luni/src/main/java/java/nio/channels/DatagramChannel.java b/luni/src/main/java/java/nio/channels/DatagramChannel.java index 486b168..2040b8e 100644 --- a/luni/src/main/java/java/nio/channels/DatagramChannel.java +++ b/luni/src/main/java/java/nio/channels/DatagramChannel.java @@ -40,7 +40,7 @@ import java.nio.channels.spi.SelectorProvider; * same time. */ public abstract class DatagramChannel extends AbstractSelectableChannel - implements ByteChannel, ScatteringByteChannel, GatheringByteChannel { + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel { /** * Constructs a new {@code DatagramChannel}. @@ -88,6 +88,22 @@ public abstract class DatagramChannel extends AbstractSelectableChannel */ public abstract DatagramSocket socket(); + /** @hide Until ready for a public API change */ + @Override + public DatagramChannel bind(SocketAddress local) throws IOException { + // This method was added for interoperability with Java 7, where it is abstract. It is + // concrete here to avoid breaking existing Android applications that extend this class. + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** @hide Until ready for a public API change */ + @Override + public SocketAddress getLocalAddress() throws IOException { + // This method was added for interoperability with Java 7, where it is abstract. It is + // concrete here to avoid breaking existing Android applications that extend this class. + throw new UnsupportedOperationException("Subclasses must override this method"); + } + /** * Returns whether this channel's socket is connected or not. * diff --git a/luni/src/main/java/java/nio/channels/NetworkChannel.java b/luni/src/main/java/java/nio/channels/NetworkChannel.java new file mode 100644 index 0000000..b3505b9 --- /dev/null +++ b/luni/src/main/java/java/nio/channels/NetworkChannel.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.nio.channels; + +import java.io.Closeable; +import java.io.IOException; +import java.net.SocketAddress; + +/** + * A common interface for channels that are backed by network sockets. + * + * @since 1.7 + * @hide Until ready for a public API change + */ +public interface NetworkChannel extends AutoCloseable, Channel, Closeable { + + /** + * Binds this channel to the given local socket address. If the {@code localAddr} is set + * to {@code null} the socket will be bound to an available local address on any free port of + * the system. + * + * @param local + * the local machine address and port to bind on. + * @return this channel. + * @throws UnsupportedAddressTypeException + * if the {@code SocketAddress} is not supported. + * @throws ClosedChannelException + * if the channel is closed. + * @throws AlreadyBoundException + * if the channel is already bound. + * @throws IOException + * if another I/O error occurs. + * @hide Until ready for a public API change + */ + NetworkChannel bind(SocketAddress local) throws IOException; + + /** + * Returns the local socket address the channel is bound to. The socket may be bound explicitly + * via {@link #bind(java.net.SocketAddress)} or similar methods, or as a side-effect when other + * methods are called, depending on the implementation. If the channel is not bound {@code null} + * is returned. + * + * <p>If IP is being used, the returned object will be a subclass of + * {@link java.net.InetSocketAddress} + * + * @return the local socket address, or {@code null} if the socket is not bound + * @throws ClosedChannelException + * if the channel is closed. + * @throws IOException + * if another I/O error occurs. + * @hide Until ready for a public API change + */ + SocketAddress getLocalAddress() throws IOException; +} diff --git a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java index f3c3390..aeb6d8f 100644 --- a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java +++ b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java @@ -19,6 +19,7 @@ package java.nio.channels; import java.io.IOException; import java.net.ServerSocket; +import java.net.SocketAddress; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; @@ -34,7 +35,8 @@ import java.nio.channels.spi.SelectorProvider; * {@link NotYetBoundException}. It can be bound by calling the bind method of a * related {@code ServerSocket} instance. */ -public abstract class ServerSocketChannel extends AbstractSelectableChannel { +public abstract class ServerSocketChannel extends AbstractSelectableChannel + implements NetworkChannel { /** * Constructs a new {@link ServerSocketChannel}. @@ -82,6 +84,52 @@ public abstract class ServerSocketChannel extends AbstractSelectableChannel { public abstract ServerSocket socket(); /** + * {@inheritDoc} + * + * <p>This is equivalent to {@code bind(local, 0)}. + * @hide Until ready for a public API change + */ + @Override + public final ServerSocketChannel bind(SocketAddress local) throws IOException { + return bind(local, 0); + } + + /** + * Binds this server channel to the given local socket address. If the {@code localAddr} is set + * to {@code null} the socket will be bound to an available local address on any free port of + * the system. + * + * @param localAddr + * the local machine address and port to bind on. + * @param backlog the maximum number of unaccepted connections. Passing 0 or + * a negative value yields the default backlog of 50. + * @return this {@code ServerSocketChannel}. + * @throws UnsupportedAddressTypeException + * if the {@code SocketAddress} is not supported. + * @throws ClosedChannelException + * if the channel is closed. + * @throws AlreadyBoundException + * if the channel is already bound. + * @throws IOException + * if another I/O error occurs. + * @since 1.7 + * @hide Until ready for a public API change + */ + public ServerSocketChannel bind(SocketAddress localAddr, int backlog) throws IOException { + // This method was added for interoperability with Java 7, where it is abstract. It is + // concrete here to avoid breaking existing Android applications that extend this class. + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** @hide Until ready for a public API change */ + @Override + public SocketAddress getLocalAddress() throws IOException { + // This method was added for interoperability with Java 7, where it is abstract. It is + // concrete here to avoid breaking existing Android applications that extend this class. + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** * Accepts a connection to this server-socket channel. * <p> * This method returns {@code null} when this channel is non-blocking and no diff --git a/luni/src/main/java/java/nio/channels/SocketChannel.java b/luni/src/main/java/java/nio/channels/SocketChannel.java index da5b7ea..12dfe38 100644 --- a/luni/src/main/java/java/nio/channels/SocketChannel.java +++ b/luni/src/main/java/java/nio/channels/SocketChannel.java @@ -62,7 +62,7 @@ import java.nio.channels.spi.SelectorProvider; * processing, calls to {@link #read} and {@link #write} will block. */ public abstract class SocketChannel extends AbstractSelectableChannel implements - ByteChannel, ScatteringByteChannel, GatheringByteChannel { + ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel { /** * Constructs a new {@code SocketChannel}. @@ -140,6 +140,22 @@ public abstract class SocketChannel extends AbstractSelectableChannel implements */ public abstract Socket socket(); + /** @hide Until ready for a public API change */ + @Override + public SocketChannel bind(SocketAddress local) throws IOException { + // This method was added for interoperability with Java 7, where it is abstract. It is + // concrete here to avoid breaking existing Android applications that extend this class. + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** @hide Until ready for a public API change */ + @Override + public SocketAddress getLocalAddress() throws IOException { + // This method was added for interoperability with Java 7, where it is abstract. It is + // concrete here to avoid breaking existing Android applications that extend this class. + throw new UnsupportedOperationException("Subclasses must override this method"); + } + /** * Indicates whether this channel's socket is connected. * diff --git a/luni/src/test/java/libcore/java/net/DatagramSocketTest.java b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java new file mode 100644 index 0000000..86e47ec --- /dev/null +++ b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.java.net; + +import junit.framework.TestCase; + +import java.net.DatagramSocket; +import java.net.InetSocketAddress; + +public class DatagramSocketTest extends TestCase { + + public void testInitialState() throws Exception { + DatagramSocket ds = new DatagramSocket(); + try { + assertTrue(ds.isBound()); + assertTrue(ds.getBroadcast()); // The RI starts DatagramSocket in broadcast mode. + assertFalse(ds.isClosed()); + assertFalse(ds.isConnected()); + assertTrue(ds.getLocalPort() > 0); + assertTrue(ds.getLocalAddress().isAnyLocalAddress()); + InetSocketAddress socketAddress = (InetSocketAddress) ds.getLocalSocketAddress(); + assertEquals(ds.getLocalPort(), socketAddress.getPort()); + assertEquals(ds.getLocalAddress(), socketAddress.getAddress()); + assertNull(ds.getInetAddress()); + assertEquals(-1, ds.getPort()); + assertNull(ds.getRemoteSocketAddress()); + assertFalse(ds.getReuseAddress()); + assertNull(ds.getChannel()); + } finally { + ds.close(); + } + } + + public void testStateAfterClose() throws Exception { + DatagramSocket ds = new DatagramSocket(); + ds.close(); + assertTrue(ds.isBound()); + assertTrue(ds.isClosed()); + assertFalse(ds.isConnected()); + assertNull(ds.getLocalAddress()); + assertEquals(-1, ds.getLocalPort()); + assertNull(ds.getLocalSocketAddress()); + } +} diff --git a/luni/src/test/java/libcore/java/net/ServerSocketTest.java b/luni/src/test/java/libcore/java/net/ServerSocketTest.java index fe9d423..d82e934 100644 --- a/luni/src/test/java/libcore/java/net/ServerSocketTest.java +++ b/luni/src/test/java/libcore/java/net/ServerSocketTest.java @@ -17,6 +17,8 @@ package libcore.java.net; import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; @@ -43,4 +45,34 @@ public class ServerSocketTest extends junit.framework.TestCase { t.join(); assertEquals(0, result[0].getSoTimeout()); } + + public void testInitialState() throws Exception { + ServerSocket ss = new ServerSocket(); + try { + assertFalse(ss.isBound()); + assertFalse(ss.isClosed()); + assertEquals(-1, ss.getLocalPort()); + assertNull(ss.getLocalSocketAddress()); + assertNull(ss.getInetAddress()); + assertTrue(ss.getReuseAddress()); + assertNull(ss.getChannel()); + } finally { + ss.close(); + } + } + + public void testStateAfterClose() throws Exception { + ServerSocket ss = new ServerSocket(); + ss.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 0)); + InetSocketAddress boundAddress = (InetSocketAddress) ss.getLocalSocketAddress(); + ss.close(); + + assertTrue(ss.isBound()); + assertTrue(ss.isClosed()); + assertEquals(boundAddress.getAddress(), ss.getInetAddress()); + assertEquals(boundAddress.getPort(), ss.getLocalPort()); + + InetSocketAddress localAddressAfterClose = (InetSocketAddress) ss.getLocalSocketAddress(); + assertEquals(boundAddress, localAddressAfterClose); + } } diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java index 42b7250..b9ed99c 100644 --- a/luni/src/test/java/libcore/java/net/SocketTest.java +++ b/luni/src/test/java/libcore/java/net/SocketTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; @@ -89,7 +90,7 @@ public class SocketTest extends junit.framework.TestCase { // Open a local server port. ServerSocketChannel ssc = ServerSocketChannel.open(); InetSocketAddress listenAddr = new InetSocketAddress(host, 0); - ssc.socket().bind(listenAddr, 0); + ssc.bind(listenAddr, 0); ServerSocket ss = ssc.socket(); // Open a socket to the local port. @@ -109,10 +110,12 @@ public class SocketTest extends junit.framework.TestCase { in.socket().setTcpNoDelay(false); } + InetSocketAddress listenAddress = (InetSocketAddress) in.getLocalAddress(); InetSocketAddress outRemoteAddress = (InetSocketAddress) out.socket().getRemoteSocketAddress(); InetSocketAddress outLocalAddress = (InetSocketAddress) out.socket().getLocalSocketAddress(); InetSocketAddress inLocalAddress = (InetSocketAddress) in.socket().getLocalSocketAddress(); InetSocketAddress inRemoteAddress = (InetSocketAddress) in.socket().getRemoteSocketAddress(); + System.err.println("listenAddress: " + listenAddr); System.err.println("inLocalAddress: " + inLocalAddress); System.err.println("inRemoteAddress: " + inRemoteAddress); System.err.println("outLocalAddress: " + outLocalAddress); @@ -127,14 +130,42 @@ public class SocketTest extends junit.framework.TestCase { assertEquals(outLocalAddress.getAddress(), ss.getInetAddress()); assertEquals(outRemoteAddress.getAddress(), ss.getInetAddress()); + assertFalse(ssc.socket().isClosed()); + assertTrue(ssc.socket().isBound()); + assertTrue(in.isConnected()); + assertTrue(in.socket().isConnected()); + assertTrue(out.socket().isConnected()); + assertTrue(out.isConnected()); + in.close(); out.close(); ssc.close(); + assertTrue(ssc.socket().isClosed()); + assertTrue(ssc.socket().isBound()); + assertFalse(in.isConnected()); + assertFalse(in.socket().isConnected()); + assertFalse(out.socket().isConnected()); + assertFalse(out.isConnected()); + assertNull(in.socket().getRemoteSocketAddress()); assertNull(out.socket().getRemoteSocketAddress()); - assertEquals(in.socket().getLocalSocketAddress(), ss.getLocalSocketAddress()); + // As per docs and RI - server socket local address methods continue to return the bind() + // addresses even after close(). + assertEquals(listenAddress, ssc.socket().getLocalSocketAddress()); + + // As per docs and RI - socket local address methods return the wildcard address before + // bind() and after close(), but the port will be the same as it was before close(). + InetSocketAddress inLocalAddressAfterClose = + (InetSocketAddress) in.socket().getLocalSocketAddress(); + assertTrue(inLocalAddressAfterClose.getAddress().isAnyLocalAddress()); + assertEquals(inLocalAddress.getPort(), inLocalAddressAfterClose.getPort()); + + InetSocketAddress outLocalAddressAfterClose = + (InetSocketAddress) out.socket().getLocalSocketAddress(); + assertTrue(outLocalAddressAfterClose.getAddress().isAnyLocalAddress()); + assertEquals(outLocalAddress.getPort(), outLocalAddressAfterClose.getPort()); } // SocketOptions.setOption has weird behavior for setSoLinger/SO_LINGER. @@ -286,6 +317,42 @@ public class SocketTest extends junit.framework.TestCase { serverSocket.close(); } + public void testInitialState() throws Exception { + Socket s = new Socket(); + try { + assertFalse(s.isBound()); + assertFalse(s.isClosed()); + assertFalse(s.isConnected()); + assertEquals(-1, s.getLocalPort()); + assertTrue(s.getLocalAddress().isAnyLocalAddress()); + assertNull(s.getLocalSocketAddress()); + assertNull(s.getInetAddress()); + assertEquals(0, s.getPort()); + assertNull(s.getRemoteSocketAddress()); + assertFalse(s.getReuseAddress()); + assertNull(s.getChannel()); + } finally { + s.close(); + } + } + + public void testStateAfterClose() throws Exception { + Socket s = new Socket(); + s.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 0)); + InetSocketAddress boundAddress = (InetSocketAddress) s.getLocalSocketAddress(); + s.close(); + + assertTrue(s.isBound()); + assertTrue(s.isClosed()); + assertFalse(s.isConnected()); + assertTrue(s.getLocalAddress().isAnyLocalAddress()); + assertEquals(boundAddress.getPort(), s.getLocalPort()); + + InetSocketAddress localAddressAfterClose = (InetSocketAddress) s.getLocalSocketAddress(); + assertTrue(localAddressAfterClose.getAddress().isAnyLocalAddress()); + assertEquals(boundAddress.getPort(), localAddressAfterClose.getPort()); + } + static class MockServer { private ExecutorService executor; private ServerSocket serverSocket; diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java index 13757d2..a0092d0 100644 --- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java @@ -48,11 +48,35 @@ public class DatagramChannelTest extends junit.framework.TestCase { DatagramChannel dc = DatagramChannel.open(); try { dc.configureBlocking(false); - dc.socket().bind(null); + dc.bind(null); // Should return immediately, since we're non-blocking. assertNull(dc.receive(ByteBuffer.allocate(2048))); } finally { dc.close(); } } + + public void testInitialState() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + try { + assertNull(dc.getLocalAddress()); + + DatagramSocket socket = dc.socket(); + assertFalse(socket.isBound()); + assertFalse(socket.getBroadcast()); + assertFalse(socket.isClosed()); + assertFalse(socket.isConnected()); + assertEquals(0, socket.getLocalPort()); + assertTrue(socket.getLocalAddress().isAnyLocalAddress()); + assertNull(socket.getLocalSocketAddress()); + assertNull(socket.getInetAddress()); + assertEquals(-1, socket.getPort()); + assertNull(socket.getRemoteSocketAddress()); + assertFalse(socket.getReuseAddress()); + + assertSame(dc, socket.getChannel()); + } finally { + dc.close(); + } + } } diff --git a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java index e66096c..bceb759 100644 --- a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java @@ -16,7 +16,14 @@ package libcore.java.nio.channels; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Enumeration; public class ServerSocketChannelTest extends junit.framework.TestCase { // http://code.google.com/p/android/issues/detail?id=16579 @@ -24,11 +31,100 @@ public class ServerSocketChannelTest extends junit.framework.TestCase { ServerSocketChannel ssc = ServerSocketChannel.open(); try { ssc.configureBlocking(false); - ssc.socket().bind(null); + ssc.bind(null); // Should return immediately, since we're non-blocking. assertNull(ssc.accept()); } finally { ssc.close(); } } + + /** Checks the state of the ServerSocketChannel and associated ServerSocket after open() */ + public void test_open_initialState() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open(); + try { + assertNull(ssc.getLocalAddress()); + + ServerSocket socket = ssc.socket(); + assertFalse(socket.isBound()); + assertFalse(socket.isClosed()); + assertEquals(-1, socket.getLocalPort()); + assertNull(socket.getLocalSocketAddress()); + assertNull(socket.getInetAddress()); + assertTrue(socket.getReuseAddress()); + + assertSame(ssc, socket.getChannel()); + } finally { + ssc.close(); + } + } + + public void test_bind_nullBindsToAll() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.bind(null); + InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress(); + assertTrue(boundAddress.getAddress().isAnyLocalAddress()); + assertFalse(boundAddress.getAddress().isLinkLocalAddress()); + assertFalse(boundAddress.getAddress().isLoopbackAddress()); + + // Attempt to connect to the "any" address. + assertTrue(canConnect(boundAddress)); + + // Go through all local IPs and try to connect to each in turn - all should succeed. + Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface nic = interfaces.nextElement(); + Enumeration<InetAddress> inetAddresses = nic.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + InetSocketAddress address = + new InetSocketAddress(inetAddresses.nextElement(), boundAddress.getPort()); + assertTrue(canConnect(address)); + } + } + + ssc.close(); + } + + public void test_bind_loopback() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress(); + assertFalse(boundAddress.getAddress().isAnyLocalAddress()); + assertFalse(boundAddress.getAddress().isLinkLocalAddress()); + assertTrue(boundAddress.getAddress().isLoopbackAddress()); + + // Attempt to connect to the "loopback" address. Note: There can be several loopback + // addresses, such as 127.0.0.1 (IPv4) and 0:0:0:0:0:0:0:1 (IPv6) and only one will be + // bound. + InetSocketAddress loopbackAddress = + new InetSocketAddress(InetAddress.getLoopbackAddress(), boundAddress.getPort()); + assertTrue(canConnect(loopbackAddress)); + + // Go through all local IPs and try to connect to each in turn - all should fail except + // for the loopback. + Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface nic = interfaces.nextElement(); + Enumeration<InetAddress> inetAddresses = nic.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + InetSocketAddress address = + new InetSocketAddress(inetAddresses.nextElement(), boundAddress.getPort()); + if (!address.equals(loopbackAddress)) { + assertFalse(canConnect(address)); + } + } + } + + ssc.close(); + } + + private static boolean canConnect(InetSocketAddress address) { + try { + SocketChannel socketChannel = SocketChannel.open(address); + socketChannel.close(); + return true; + } catch (IOException e) { + return false; + } + } } diff --git a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java index fbb2d8d..553e20c 100644 --- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java @@ -16,29 +16,34 @@ package libcore.java.nio.channels; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; +import java.net.Socket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; -import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; +import java.nio.channels.SocketChannel; import java.nio.channels.Selector; import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; import tests.io.MockOs; + import static libcore.io.OsConstants.*; public class SocketChannelTest extends junit.framework.TestCase { + private final MockOs mockOs = new MockOs(); - @Override public void setUp() throws Exception { + @Override + public void setUp() throws Exception { mockOs.install(); } - @Override protected void tearDown() throws Exception { + @Override + protected void tearDown() throws Exception { mockOs.uninstall(); } @@ -129,6 +134,81 @@ public class SocketChannelTest extends junit.framework.TestCase { ss.close(); } + /** Checks the state of the SocketChannel and associated Socket after open() */ + public void test_open_initialState() throws Exception { + SocketChannel sc = SocketChannel.open(); + try { + assertNull(sc.getLocalAddress()); + + Socket socket = sc.socket(); + assertFalse(socket.isBound()); + assertFalse(socket.isClosed()); + assertFalse(socket.isConnected()); + assertEquals(-1, socket.getLocalPort()); + assertTrue(socket.getLocalAddress().isAnyLocalAddress()); + assertNull(socket.getLocalSocketAddress()); + assertNull(socket.getInetAddress()); + assertEquals(0, socket.getPort()); + assertNull(socket.getRemoteSocketAddress()); + assertFalse(socket.getReuseAddress()); + + assertSame(sc, socket.getChannel()); + } finally { + sc.close(); + } + } + + /** Checks that the SocketChannel and associated Socket agree on the socket state. */ + public void test_bind_socketStateSync() throws IOException { + SocketChannel sc = SocketChannel.open(); + assertNull(sc.getLocalAddress()); + + Socket socket = sc.socket(); + assertNull(socket.getLocalSocketAddress()); + assertFalse(socket.isBound()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + sc.bind(bindAddr); + + InetSocketAddress actualAddr = (InetSocketAddress) sc.getLocalAddress(); + assertEquals(actualAddr, socket.getLocalSocketAddress()); + assertEquals(bindAddr.getHostName(), actualAddr.getHostName()); + assertTrue(socket.isBound()); + assertFalse(socket.isConnected()); + assertFalse(socket.isClosed()); + + sc.close(); + + assertFalse(sc.isOpen()); + assertTrue(socket.isClosed()); + } + + /** + * Checks that the SocketChannel and associated Socket agree on the socket state, even if + * the Socket object is requested/created after bind(). + */ + public void test_bind_socketObjectCreationAfterBind() throws IOException { + SocketChannel sc = SocketChannel.open(); + assertNull(sc.getLocalAddress()); + + InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0); + sc.bind(bindAddr); + + // Socket object creation after bind(). + Socket socket = sc.socket(); + InetSocketAddress actualAddr = (InetSocketAddress) sc.getLocalAddress(); + assertEquals(actualAddr, socket.getLocalSocketAddress()); + assertEquals(bindAddr.getHostName(), actualAddr.getHostName()); + assertTrue(socket.isBound()); + assertFalse(socket.isConnected()); + assertFalse(socket.isClosed()); + + sc.close(); + + assertFalse(sc.isOpen()); + assertTrue(socket.isClosed()); + } + /** * Tests connect() and object state for a blocking SocketChannel. Blocking mode is the default. */ |