package com.limegroup.gnutella.connection; import java.io.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.ReadableByteChannel; import java.util.*; import java.net.*; import junit.framework.Test; import com.limegroup.gnutella.*; import com.limegroup.gnutella.io.InterestReadChannel; import com.limegroup.gnutella.messages.*; import com.limegroup.gnutella.util.*; /** * Tests that MessageReader extracts messages from a source channel correctly. */ public final class MessageReaderTest extends BaseTestCase { private StubMessageReceiver STUB = new StubMessageReceiver(); private MessageReader READER = new MessageReader(STUB); private static final byte[] IP = new byte[] { (byte)127, 0, 0, 1 }; public MessageReaderTest(String name) { super(name); } public static Test suite() { return buildTestSuite(MessageReaderTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public void setUp() throws Exception { STUB.clear(); RouterService.getAcceptor().setAddress(InetAddress.getLocalHost()); } public void testSingleMessageRead() throws Exception { Message out = new PingRequest((byte)1); READER.setReadChannel(channel(buffer(out))); assertEquals(0, STUB.size()); READER.handleRead(); assertEquals(1, STUB.size()); Message in = STUB.getMessage(); assertEquals(buffer(out), buffer(in)); assertFalse(STUB.isClosed()); } public void testReadMultipleMessages() throws Exception { Message out1 = new PingRequest((byte)1); Message out2 = QueryRequest.createQuery("test"); Message out3 = new QueryReply(GUID.makeGuid(), (byte) 4, 6346, IP, 0, new Response[0], GUID.makeGuid(), new byte[0], false, false, true, true, true, false, null); Message out4 = new PushRequest(GUID.makeGuid(), (byte)0, GUID.makeGuid(), 0, IP, 6346); Message out5 = PingReply.create(GUID.makeGuid(),(byte)1, new Endpoint("1.2.3.4", 5)); Message[] allOut = new Message[] { out1, out2, out3, out4, out5 }; READER.setReadChannel(channel(buffer(allOut))); assertEquals(0, STUB.size()); READER.handleRead(); assertEquals(5, STUB.size()); Message in1 = STUB.getMessage(); Message in2 = STUB.getMessage(); Message in3 = STUB.getMessage(); Message in4 = STUB.getMessage(); Message in5 = STUB.getMessage(); Message[] allIn = new Message[] { in1, in2, in3, in4, in5 }; assertEquals("out: " + out1 + ", in: " + in1, buffer(out1), buffer(in1)); assertEquals("out: " + out2 + ", in: " + in2, buffer(out2), buffer(in2)); assertEquals("out: " + out3 + ", in: " + in3, buffer(out3), buffer(in3)); assertEquals("out: " + out4 + ", in: " + in4, buffer(out4), buffer(in4)); assertEquals("out: " + out5 + ", in: " + in5, buffer(out5), buffer(in5)); assertEquals(buffer(allOut), buffer(allIn)); assertFalse(STUB.isClosed()); } public void testBadPacketIgnored() throws Exception { Message out1 = new PingRequest((byte)1); Message out2 = QueryRequest.createQuery("test"); Message out3 = new QueryReply(GUID.makeGuid(), (byte) 4, 6346, IP, 0, new Response[0], GUID.makeGuid(), new byte[0], false, false, true, true, true, false, null); Message out4 = new PushRequest(GUID.makeGuid(), (byte)0, GUID.makeGuid(), 0, IP, 6346); Message out5 = PingReply.create(GUID.makeGuid(),(byte)1, new Endpoint("1.2.3.4", 5)); ByteBuffer b1 = buffer(out1); ByteBuffer b2 = buffer(out2); ByteBuffer b3 = buffer(out3); ByteBuffer b4 = buffer(out4); ByteBuffer b5 = buffer(out5); b2.put(18, (byte)100); // change the hops of the query request. to be absurdly high READER.setReadChannel(channel(buffer(new ByteBuffer[] { b1, b2, b3, b4, b5 }))); assertEquals(0, STUB.size()); READER.handleRead(); assertEquals(4, STUB.size()); Message in1 = STUB.getMessage(); Message in3 = STUB.getMessage(); Message in4 = STUB.getMessage(); Message in5 = STUB.getMessage(); assertEquals("out: " + out1 + ", in: " + in1, buffer(out1), buffer(in1)); assertEquals("out: " + out3 + ", in: " + in3, buffer(out3), buffer(in3)); assertEquals("out: " + out4 + ", in: " + in4, buffer(out4), buffer(in4)); assertEquals("out: " + out5 + ", in: " + in5, buffer(out5), buffer(in5)); assertFalse(STUB.isClosed()); } public void testLargeLengthThrows() throws Exception { Message out1 = new PingRequest((byte)1); Message out2 = QueryRequest.createQuery("test"); Message out3 = new QueryReply(GUID.makeGuid(), (byte) 4, 6346, IP, 0, new Response[0], GUID.makeGuid(), new byte[0], false, false, true, true, true, false, null); Message out4 = new PushRequest(GUID.makeGuid(), (byte)0, GUID.makeGuid(), 0, IP, 6346); Message out5 = PingReply.create(GUID.makeGuid(),(byte)1, new Endpoint("1.2.3.4", 5)); ByteBuffer b1 = buffer(out1); ByteBuffer b2 = buffer(out2); ByteBuffer b3 = buffer(out3); ByteBuffer b4 = buffer(out4); ByteBuffer b5 = buffer(out5); b4.order(ByteOrder.LITTLE_ENDIAN); b4.putInt(19, 64 * 1024 + 1); READER.setReadChannel(channel(buffer(new ByteBuffer[] { b1, b2, b3, b4, b5 }))); assertEquals(0, STUB.size()); try { READER.handleRead(); fail("didn't throw IOX"); } catch(IOException expected) {} assertEquals(3, STUB.size()); Message in1 = STUB.getMessage(); Message in2 = STUB.getMessage(); Message in3 = STUB.getMessage(); assertEquals("out: " + out1 + ", in: " + in1, buffer(out1), buffer(in1)); assertEquals("out: " + out2 + ", in: " + in2, buffer(out2), buffer(in2)); assertEquals("out: " + out3 + ", in: " + in3, buffer(out3), buffer(in3)); // the close in real life is actually handled by NIODispatcher catching the IOX // and shutting down the NIOSocket, which shuts down this MessageReader, which // forwards the close to the MessageReceiver. since we don't have that framework // here, the close won't actually happen. //assertTrue(STUB.isClosed()); } public void testSmallLengthThrows() throws Exception { Message out1 = new PingRequest((byte)1); Message out2 = QueryRequest.createQuery("test"); Message out3 = new QueryReply(GUID.makeGuid(), (byte) 4, 6346, IP, 0, new Response[0], GUID.makeGuid(), new byte[0], false, false, true, true, true, false, null); Message out4 = new PushRequest(GUID.makeGuid(), (byte)0, GUID.makeGuid(), 0, IP, 6346); Message out5 = PingReply.create(GUID.makeGuid(),(byte)1, new Endpoint("1.2.3.4", 5)); ByteBuffer b1 = buffer(out1); ByteBuffer b2 = buffer(out2); ByteBuffer b3 = buffer(out3); ByteBuffer b4 = buffer(out4); ByteBuffer b5 = buffer(out5); b4.order(ByteOrder.LITTLE_ENDIAN); b4.putInt(19, -1); READER.setReadChannel(channel(buffer(new ByteBuffer[] { b1, b2, b3, b4, b5 }))); assertEquals(0, STUB.size()); try { READER.handleRead(); fail("didn't throw IOX"); } catch(IOException expected) {} assertEquals(3, STUB.size()); Message in1 = STUB.getMessage(); Message in2 = STUB.getMessage(); Message in3 = STUB.getMessage(); assertEquals("out: " + out1 + ", in: " + in1, buffer(out1), buffer(in1)); assertEquals("out: " + out2 + ", in: " + in2, buffer(out2), buffer(in2)); assertEquals("out: " + out3 + ", in: " + in3, buffer(out3), buffer(in3)); // the close in real life is actually handled by NIODispatcher catching the IOX // and shutting down the NIOSocket, which shuts down this MessageReader, which // forwards the close to the MessageReceiver. since we don't have that framework // here, the close won't actually happen. //assertTrue(STUB.isClosed()); } public void testReadInPasses() throws Exception { Message out = QueryRequest.createQuery("this is a really long query"); ByteBuffer b1 = buffer(out); ByteBuffer b2 = b1.duplicate(); ByteBuffer b3 = b1.duplicate(); ByteBuffer b4 = b1.duplicate(); b1.limit(8); b2.position(8); b2.limit(23); b3.position(23); b3.limit(30); b4.position(30); READER.setReadChannel(channel(b1)); assertEquals(0, STUB.size()); assertTrue(b1.hasRemaining()); READER.handleRead(); assertEquals(0, STUB.size()); assertFalse(b1.hasRemaining()); READER.setReadChannel(channel(b2)); assertEquals(0, STUB.size()); assertTrue(b2.hasRemaining()); READER.handleRead(); assertEquals(0, STUB.size()); assertFalse(b2.hasRemaining()); READER.setReadChannel(channel(b3)); assertEquals(0, STUB.size()); assertTrue(b3.hasRemaining()); READER.handleRead(); assertEquals(0, STUB.size()); assertFalse(b3.hasRemaining()); READER.setReadChannel(channel(b4)); assertEquals(0, STUB.size()); assertTrue(b4.hasRemaining()); READER.handleRead(); assertEquals(1, STUB.size()); assertFalse(b4.hasRemaining()); Message in = STUB.getMessage(); assertEquals(buffer(out), buffer(in)); } public void testEOFInHeaderThrows() throws Exception { Message out = QueryRequest.createQuery("test"); ByteBuffer b = buffer(out); b.limit(20); READER.setReadChannel(eof(b)); assertTrue(b.hasRemaining()); assertEquals(0, STUB.size()); try { READER.handleRead(); fail("expected IOX"); } catch(IOException expected) {} assertEquals(0, STUB.size()); assertFalse(b.hasRemaining()); } public void testEOFAfterHeaderThrows() throws Exception { Message out = QueryRequest.createQuery("test"); ByteBuffer b = buffer(out); b.limit(23); READER.setReadChannel(eof(b)); assertTrue(b.hasRemaining()); assertEquals(0, STUB.size()); try { READER.handleRead(); fail("expected IOX"); } catch(IOException expected) {} assertEquals(0, STUB.size()); assertFalse(b.hasRemaining()); } public void testEOFInPayloadThrows() throws Exception { Message out = QueryRequest.createQuery("test"); ByteBuffer b = buffer(out); b.limit(30); READER.setReadChannel(eof(b)); assertTrue(b.hasRemaining()); assertEquals(0, STUB.size()); try { READER.handleRead(); fail("expected IOX"); } catch(IOException expected) {} assertEquals(0, STUB.size()); assertFalse(b.hasRemaining()); } public void testEOFAfterPayloadThrowsButMessageIsRead() throws Exception { Message out = QueryRequest.createQuery("test"); ByteBuffer b = buffer(out); READER.setReadChannel(eof(b)); assertTrue(b.hasRemaining()); assertEquals(0, STUB.size()); try { READER.handleRead(); fail("expected IOX"); } catch(IOException expected) {} assertEquals(1, STUB.size()); assertFalse(b.hasRemaining()); Message in = STUB.getMessage(); assertEquals(buffer(out), buffer(in)); } public void testShutdown() throws Exception { assertFalse(STUB.isClosed()); READER.shutdown(); assertTrue(STUB.isClosed()); } public void testChannelMethods() throws Exception { try { READER.setReadChannel(null); fail("expected NPE"); } catch(NullPointerException expected) {} InterestReadChannel channel = new ReadBufferChannel(); READER.setReadChannel(channel); assertSame(channel, READER.getReadChannel()); try { new MessageReader(null); fail("expected NPE"); } catch(NullPointerException expected) {} READER = new MessageReader(channel, STUB); assertSame(channel, READER.getReadChannel()); READER = new MessageReader(null, STUB); assertNull(READER.getReadChannel()); } private InterestReadChannel channel(ByteBuffer buffer) throws Exception { return new ReadBufferChannel(buffer); } private InterestReadChannel eof(ByteBuffer buffer) throws Exception { return new ReadBufferChannel(buffer, true); } private ByteBuffer buffer(Message m) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); m.write(out); out.flush(); return ByteBuffer.wrap(out.toByteArray()); } private ByteBuffer buffer(Message m[]) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); for(int i = 0; i < m.length; i++) m[i].write(out); out.flush(); return ByteBuffer.wrap(out.toByteArray()); } private ByteBuffer buffer(List ms) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); for(Iterator i = ms.iterator(); i.hasNext(); ) ((Message)i.next()).write(out); out.flush(); return ByteBuffer.wrap(out.toByteArray()); } private ByteBuffer buffer(ByteBuffer[] bufs) throws Exception { int length = 0; for(int i = 0; i < bufs.length; i++) length += bufs[i].limit(); ByteBuffer combined = ByteBuffer.allocate(length); for(int i = 0; i < bufs.length; i++) combined.put(bufs[i]); combined.flip(); return combined; } }