package org.limewire.rudp;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.concurrent.Executor;
import org.limewire.listener.AsynchronousMulticasterImpl;
import org.limewire.nio.AbstractNBSocket;
import org.limewire.nio.NIODispatcher;
import org.limewire.nio.channel.ChannelReadObserver;
import org.limewire.nio.channel.ChannelReader;
import org.limewire.nio.channel.InterestReadableByteChannel;
import org.limewire.rudp.messages.RUDPMessageFactory;
import org.limewire.rudp.messages.impl.DefaultMessageFactory;
import org.limewire.util.BaseTestCase;
import org.limewire.util.BufferUtils;
import org.limewire.concurrent.ExecutorsHelper;
import junit.framework.Test;
/**
* Tests that NIOSocket delegates events correctly.
*/
public final class UDPConnectNIOTest extends BaseTestCase {
private UDPServiceStub stubService;
private UDPMultiplexor udpMultiplexor;
private UDPSelectorProvider udpSelectorProvider;
private final int PORT_1 = 6346;
private final int PORT_2 = 6348;
public UDPConnectNIOTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(UDPConnectNIOTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
public void setUp() throws Exception {
RUDPMessageFactory factory = new DefaultMessageFactory();
stubService = new UDPServiceStub(factory);
Executor executor = ExecutorsHelper.newProcessingQueue("TestEventThread");
udpSelectorProvider = new UDPSelectorProvider(new DefaultRUDPContext(
factory, NIODispatcher.instance().getTransportListener(),
stubService, new DefaultRUDPSettings()), new AsynchronousMulticasterImpl<UDPSocketChannelConnectionEvent>(executor));
udpMultiplexor = udpSelectorProvider.openSelector();
stubService.setUDPMultiplexor(udpMultiplexor);
NIODispatcher.instance().registerSelector(udpMultiplexor, udpSelectorProvider.getUDPSocketChannelClass());
// Add some simulated connections to the UDPServiceStub
stubService.addReceiver(PORT_1, PORT_2, 10, 0);
stubService.addReceiver(PORT_2, PORT_1, 10, 0);
}
@Override
public void tearDown() throws Exception {
// Clear out the receiver parameters for the UDPServiceStub
stubService.clearReceivers();
NIODispatcher.instance().removeSelector(udpMultiplexor);
}
private StubConnectObserver setupConnection() throws Exception {
AbstractNBSocket conn = udpSelectorProvider.openAcceptorSocketChannel().socket();
StubConnectObserver stub = new StubConnectObserver();
conn.connect(new InetSocketAddress("127.0.0.1", PORT_2), 5000, stub);
return stub;
}
public void testDelayedGetInputStream() throws Exception {
StubConnectObserver stub = setupConnection();
InetSocketAddress addr = new InetSocketAddress("127.0.0.1", PORT_1);
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
socket.setSoTimeout(1000);
socket.connect(addr);
stub.waitForResponse(5000);
Socket accepted = stub.getSocket();
assertNotNull(accepted);
byte[] rnd = new byte[100];
new Random().nextBytes(rnd);
accepted.getOutputStream().write(rnd); // this'll go immediately into the buffer
ICROAdapter icro = new ICROAdapter();
ByteBuffer read = icro.getReadBuffer();
assertEquals(0, read.position());
socket.setReadObserver(icro);
Thread.sleep(500); // let NIODispatcher to its thang.
assertEquals(100, read.position()); // data was transferred to the reader.
for(int i = 0; i < 100; i++)
assertEquals(rnd[i], read.get(i));
InputStream stream = socket.getInputStream();
byte[] readData = new byte[100];
assertEquals(100, stream.read(readData));
assertEquals(rnd, readData);
assertEquals(0, read.position()); // moved to the InputStream
new Random().nextBytes(rnd);
accepted.getOutputStream().write(rnd); // write some more, make sure it goes to stream
Thread.sleep(500);
assertEquals(0, read.position());
assertEquals(100, stream.read(readData));
assertEquals(rnd, readData);
socket.close();
}
public void testSetReadObserverGoesThroughChains() throws Exception {
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
RCROAdapter entry = new RCROAdapter();
socket.setReadObserver(entry);
Thread.sleep(1000);
assertInstanceof(UDPSocketChannel.class, entry.getReadChannel());
RCRAdapter chain1 = new RCRAdapter();
entry.setReadChannel(chain1);
socket.setReadObserver(entry);
Thread.sleep(1000);
assertInstanceof(UDPSocketChannel.class, chain1.getReadChannel());
assertSame(chain1, entry.getReadChannel());
RCRAdapter chain2 = new RCRAdapter();
chain1.setReadChannel(chain2);
socket.setReadObserver(entry);
Thread.sleep(1000);
assertInstanceof(UDPSocketChannel.class, chain2.getReadChannel());
assertSame(chain2, chain1.getReadChannel());
assertSame(chain1, entry.getReadChannel());
}
public void testBlockingConnect() throws Exception {
setupConnection();
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
socket.connect(new InetSocketAddress("127.0.0.1", PORT_1));
assertTrue(socket.isConnected());
socket.close();
Thread.sleep(500);
assertFalse(socket.isConnected());
}
public void testBlockingConnectFailing() throws Exception {
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
try {
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
fail("shouldn't have connected");
} catch(ConnectException iox) {
assertFalse(socket.isConnected());
}
}
public void testBlockingConnectTimesOut() throws Exception {
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
try {
socket.connect(new InetSocketAddress("127.0.0.1", 9999), 1000);
fail("shouldn't have connected");
} catch(SocketTimeoutException iox) {
assertEquals("operation timed out (1000)", iox.getMessage());
}
}
public void testNonBlockingConnect() throws Exception {
setupConnection();
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
StubConnectObserver observer = new StubConnectObserver();
socket.connect(new InetSocketAddress("127.0.0.1", PORT_1), 5000, observer);
observer.waitForResponse(5500);
assertTrue(socket.isConnected());
assertSame(socket, observer.getSocket());
assertFalse(observer.isShutdown());
assertNull(observer.getIoException());
socket.close();
Thread.sleep(500);
assertFalse(observer.isShutdown()); // doesn't get both connect & shutdown
assertFalse(socket.isConnected());
}
public void testNonBlockingConnectFailing() throws Exception {
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
StubConnectObserver observer = new StubConnectObserver();
// UDPConnectionProcessor has a default connect timeout of 20 seconds,
// so we set our timeout at 40 seconds to ensure it doesn't fail because
// of the timeout.
socket.connect(new InetSocketAddress("127.0.0.1", PORT_1), 40000, observer);
observer.waitForResponse(30000);
assertTrue(observer.isShutdown());
assertNull(observer.getSocket());
assertNull(observer.getIoException()); // NIOSocket swallows the IOX.
assertFalse(socket.isConnected());
}
public void testNonBlockingConnectTimesOut() throws Exception {
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
StubConnectObserver observer = new StubConnectObserver();
socket.connect(new InetSocketAddress("127.0.0.1", PORT_1), 1000, observer);
observer.waitForResponse(1500);
assertTrue(observer.isShutdown());
assertNull(observer.getSocket());
assertNull(observer.getIoException()); // NIOSocket swallows the IOX.
assertFalse(socket.isConnected());
}
public void testSoTimeoutUsedForNonBlockingRead() throws Exception {
StubConnectObserver stub = setupConnection();
InetSocketAddress addr = new InetSocketAddress("127.0.0.1", PORT_1);
AbstractNBSocket socket = udpSelectorProvider.openSocketChannel().socket();
socket.setSoTimeout(1000);
socket.connect(addr);
stub.waitForResponse(1000);
Socket accepted = stub.getSocket();
assertNotNull(accepted);
accepted.getOutputStream().write(new byte[100]); // this'll go immediately into the buffer
socket.getInputStream().read(new byte[100]);
Thread.sleep(2000);
assertTrue(!socket.isClosed()); // didn't close 'cause we're using stream reading
accepted.getOutputStream().write(new byte[1]); // give it some data just to make sure it has
socket.setReadObserver(new ReadTester());
Thread.sleep(2000);
assertTrue(socket.isClosed()); // closed because we switched to NB reading w/ timeout set
}
private static class ReadTester implements ChannelReadObserver {
private InterestReadableByteChannel source;
private ByteBuffer readData = ByteBuffer.allocate(128 * 1024);
// ChannelReader methods.
public InterestReadableByteChannel getReadChannel() { return source; }
public void setReadChannel(InterestReadableByteChannel channel) { source = channel; }
// IOErrorObserver methods.
public void handleIOException(IOException x) { fail(x); }
// ReadObserver methods.
public void handleRead() throws IOException {
source.read(readData);
assertEquals(0, source.read(readData)); // must have finish on first read.
}
// Shutdownable methods.
public void shutdown() {}
public ByteBuffer getRead() { return (ByteBuffer)readData.flip(); }
}
private static class ICROAdapter implements ChannelReadObserver, InterestReadableByteChannel {
private InterestReadableByteChannel source;
private ByteBuffer buffer = ByteBuffer.allocate(1024);
public ByteBuffer getReadBuffer() {
return buffer;
}
public InterestReadableByteChannel getReadChannel() {
return source;
}
public void setReadChannel(InterestReadableByteChannel channel) {
source = channel;
}
public int read(ByteBuffer b) {
return BufferUtils.transfer(buffer, b);
}
public void close() throws IOException {
source.close();
}
public boolean isOpen() {
return source.isOpen();
}
public void interestRead(boolean status) {
source.interestRead(status);
}
public void handleRead() throws IOException {
while (buffer.hasRemaining() && source.read(buffer) != 0);
}
public void handleIOException(IOException iox) {
}
public void shutdown() {
}
}
private static class RCRAdapter implements ChannelReader, InterestReadableByteChannel {
protected InterestReadableByteChannel source;
public InterestReadableByteChannel getReadChannel() { return source; }
public void setReadChannel(InterestReadableByteChannel channel) { source = channel; }
public int read(ByteBuffer b) throws IOException { return source.read(b); }
public void close() throws IOException { source.close(); }
public boolean isOpen() { return source.isOpen(); }
public void interestRead(boolean status) { source.interestRead(status); }
}
private static class RCROAdapter extends RCRAdapter implements ChannelReadObserver {
public void handleRead() throws IOException { source.read(ByteBuffer.allocate(1)); }
public void handleIOException(IOException iox) {}
public void shutdown() {}
}
}