blob: 42b7250b811617137149d4a7dc80db178f983d18 [file] [log] [blame]
/*
* Copyright (C) 2009 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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketImpl;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SocketTest extends junit.framework.TestCase {
// See http://b/2980559.
public void test_close() throws Exception {
Socket s = new Socket();
s.close();
// Closing a closed socket does nothing.
s.close();
}
/**
* Our getLocalAddress and getLocalPort currently use getsockname(3).
* This means they give incorrect results on closed sockets (as well
* as requiring an unnecessary call into native code).
*/
public void test_getLocalAddress_after_close() throws Exception {
Socket s = new Socket();
try {
// Bind to an ephemeral local port.
s.bind(new InetSocketAddress("localhost", 0));
assertTrue(s.getLocalAddress().toString(), s.getLocalAddress().isLoopbackAddress());
// What local port did we get?
int localPort = s.getLocalPort();
assertTrue(localPort > 0);
// Now close the socket...
s.close();
// The RI returns the ANY address but the original local port after close.
assertTrue(s.getLocalAddress().isAnyLocalAddress());
assertEquals(localPort, s.getLocalPort());
} finally {
s.close();
}
}
// http://code.google.com/p/android/issues/detail?id=7935
public void test_newSocket_connection_refused() throws Exception {
try {
new Socket("localhost", 80);
fail("connection should have been refused");
} catch (ConnectException expected) {
}
}
// http://code.google.com/p/android/issues/detail?id=3123
// http://code.google.com/p/android/issues/detail?id=1933
public void test_socketLocalAndRemoteAddresses() throws Exception {
checkSocketLocalAndRemoteAddresses(false);
checkSocketLocalAndRemoteAddresses(true);
}
public void checkSocketLocalAndRemoteAddresses(boolean setOptions) throws Exception {
InetAddress host = InetAddress.getLocalHost();
// Open a local server port.
ServerSocketChannel ssc = ServerSocketChannel.open();
InetSocketAddress listenAddr = new InetSocketAddress(host, 0);
ssc.socket().bind(listenAddr, 0);
ServerSocket ss = ssc.socket();
// Open a socket to the local port.
SocketChannel out = SocketChannel.open();
out.configureBlocking(false);
if (setOptions) {
out.socket().setTcpNoDelay(false);
}
InetSocketAddress addr = new InetSocketAddress(host, ssc.socket().getLocalPort());
out.connect(addr);
while (!out.finishConnect()) {
Thread.sleep(1);
}
SocketChannel in = ssc.accept();
if (setOptions) {
in.socket().setTcpNoDelay(false);
}
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("inLocalAddress: " + inLocalAddress);
System.err.println("inRemoteAddress: " + inRemoteAddress);
System.err.println("outLocalAddress: " + outLocalAddress);
System.err.println("outRemoteAddress: " + outRemoteAddress);
assertEquals(outRemoteAddress.getPort(), ss.getLocalPort());
assertEquals(inLocalAddress.getPort(), ss.getLocalPort());
assertEquals(inRemoteAddress.getPort(), outLocalAddress.getPort());
assertEquals(inLocalAddress.getAddress(), ss.getInetAddress());
assertEquals(inRemoteAddress.getAddress(), ss.getInetAddress());
assertEquals(outLocalAddress.getAddress(), ss.getInetAddress());
assertEquals(outRemoteAddress.getAddress(), ss.getInetAddress());
in.close();
out.close();
ssc.close();
assertNull(in.socket().getRemoteSocketAddress());
assertNull(out.socket().getRemoteSocketAddress());
assertEquals(in.socket().getLocalSocketAddress(), ss.getLocalSocketAddress());
}
// SocketOptions.setOption has weird behavior for setSoLinger/SO_LINGER.
// This test ensures we do what the RI does.
public void test_SocketOptions_setOption() throws Exception {
class MySocketImpl extends SocketImpl {
public int option;
public Object value;
public boolean createCalled;
public boolean createStream;
public MySocketImpl() { super(); }
@Override protected void accept(SocketImpl arg0) throws IOException { }
@Override protected int available() throws IOException { return 0; }
@Override protected void bind(InetAddress arg0, int arg1) throws IOException { }
@Override protected void close() throws IOException { }
@Override protected void connect(String arg0, int arg1) throws IOException { }
@Override protected void connect(InetAddress arg0, int arg1) throws IOException { }
@Override protected void connect(SocketAddress arg0, int arg1) throws IOException { }
@Override protected InputStream getInputStream() throws IOException { return null; }
@Override protected OutputStream getOutputStream() throws IOException { return null; }
@Override protected void listen(int arg0) throws IOException { }
@Override protected void sendUrgentData(int arg0) throws IOException { }
public Object getOption(int arg0) throws SocketException { return null; }
@Override protected void create(boolean isStream) throws IOException {
this.createCalled = true;
this.createStream = isStream;
}
public void setOption(int option, Object value) throws SocketException {
this.option = option;
this.value = value;
}
}
class MySocket extends Socket {
public MySocket(SocketImpl impl) throws SocketException {
super(impl);
}
}
MySocketImpl impl = new MySocketImpl();
Socket s = new MySocket(impl);
// Check that, as per the SocketOptions.setOption documentation, we pass false rather
// than -1 to the SocketImpl when setSoLinger is called with the first argument false.
s.setSoLinger(false, -1);
assertEquals(Boolean.FALSE, (Boolean) impl.value);
// We also check that SocketImpl.create was called. SocketChannelImpl.SocketAdapter
// subclasses Socket, and whether or not to call SocketImpl.create is the main behavioral
// difference.
assertEquals(true, impl.createCalled);
s.setSoLinger(false, 0);
assertEquals(Boolean.FALSE, (Boolean) impl.value);
s.setSoLinger(false, 1);
assertEquals(Boolean.FALSE, (Boolean) impl.value);
// Check that otherwise, we pass down an Integer.
s.setSoLinger(true, 0);
assertEquals(Integer.valueOf(0), (Integer) impl.value);
s.setSoLinger(true, 1);
assertEquals(Integer.valueOf(1), (Integer) impl.value);
}
public void test_setTrafficClass() throws Exception {
Socket s = new Socket();
s.setTrafficClass(123);
assertEquals(123, s.getTrafficClass());
}
public void testReadAfterClose() throws Exception {
MockServer server = new MockServer();
server.enqueue(new byte[]{5, 3}, 0);
Socket socket = new Socket("localhost", server.port);
InputStream in = socket.getInputStream();
assertEquals(5, in.read());
assertEquals(3, in.read());
assertEquals(-1, in.read());
assertEquals(-1, in.read());
socket.close();
in.close();
/*
* Rather astonishingly, read() doesn't throw even though the stream is
* closed. This is consistent with the RI's behavior.
*/
assertEquals(-1, in.read());
assertEquals(-1, in.read());
server.shutdown();
}
public void testWriteAfterClose() throws Exception {
MockServer server = new MockServer();
server.enqueue(new byte[0], 3);
Socket socket = new Socket("localhost", server.port);
OutputStream out = socket.getOutputStream();
out.write(5);
out.write(3);
socket.close();
out.close();
try {
out.write(9);
fail();
} catch (IOException expected) {
}
server.shutdown();
}
// http://b/5534202
public void testAvailable() throws Exception {
for (int i = 0; i < 100; i++) {
assertAvailableReturnsZeroAfterSocketReadsAllData();
System.out.println("Success on rep " + i);
}
}
private void assertAvailableReturnsZeroAfterSocketReadsAllData() throws Exception {
final byte[] data = "foo".getBytes();
final ServerSocket serverSocket = new ServerSocket(0);
new Thread() {
@Override public void run() {
try {
Socket socket = serverSocket.accept();
socket.getOutputStream().write(data);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
Socket socket = new Socket("localhost", serverSocket.getLocalPort());
byte[] readBuffer = new byte[128];
InputStream in = socket.getInputStream();
int total = 0;
// to prevent available() from cheating after EOF, stop reading before -1 is returned
while (total < data.length) {
total += in.read(readBuffer);
}
assertEquals(0, in.available());
socket.close();
serverSocket.close();
}
static class MockServer {
private ExecutorService executor;
private ServerSocket serverSocket;
private int port = -1;
MockServer() throws IOException {
executor = Executors.newCachedThreadPool();
serverSocket = new ServerSocket(0);
serverSocket.setReuseAddress(true);
port = serverSocket.getLocalPort();
}
public Future<byte[]> enqueue(final byte[] sendBytes, final int receiveByteCount)
throws IOException {
return executor.submit(new Callable<byte[]>() {
@Override public byte[] call() throws Exception {
Socket socket = serverSocket.accept();
OutputStream out = socket.getOutputStream();
out.write(sendBytes);
InputStream in = socket.getInputStream();
byte[] result = new byte[receiveByteCount];
int total = 0;
while (total < receiveByteCount) {
total += in.read(result, total, result.length - total);
}
socket.close();
return result;
}
});
}
public void shutdown() throws IOException {
serverSocket.close();
executor.shutdown();
}
}
}