package net.i2p.router.tunnel;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.ArrayList;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static junit.framework.TestCase.*;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.TunnelId;
import net.i2p.data.i2np.DataMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.router.RouterContext;
/**
* Simple test to see if the fragmentation is working, testing the preprocessor,
* FragmentHandler, and FragmentedMessage operation.
*
*/
public class FragmentTest {
protected static RouterContext _context;
@BeforeClass
public static void globalSetUp() {
_context = new RouterContext(null);
}
@Before
public void set() {
_context.random().nextBoolean();
FragmentHandler.MAX_DEFRAGMENT_TIME = 10*1000;
}
protected TunnelGateway.QueuePreprocessor createPreprocessor(RouterContext ctx) {
return new TrivialPreprocessor(ctx);
}
/**
* Send a message that fits inside a single fragment through
*
*/
@Test
public void testSingle() {
PendingGatewayMessage pending = createPending(949, false, false);
ArrayList<PendingGatewayMessage> messages = new ArrayList<PendingGatewayMessage>();
messages.add(pending);
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
SenderImpl sender = new SenderImpl();
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending.getData());
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
byte msg[] = pending.getData();
try {
pre.preprocessQueue(messages, new SenderImpl(), receiver);
fail("should have thrown UOE");
} catch (UnsupportedOperationException expected){}
}
/**
* Send a message with two fragments through with no delay
*
*/
@Test
public void testMultiple() throws Exception {
PendingGatewayMessage pending = createPending(2048, false, false);
ArrayList<PendingGatewayMessage> messages = new ArrayList<PendingGatewayMessage>();
messages.add(pending);
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
SenderImpl sender = new SenderImpl();
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending.getData());
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
byte msg[] = pending.getData();
try {
pre.preprocessQueue(messages, new SenderImpl(), receiver);
fail("should have thrown UOE");
} catch (UnsupportedOperationException expected){}
}
/**
* Send a fragmented message, except wait a while between each fragment, causing
* the defragmentation to fail (since the fragments will expire)
*
*/
public void runDelayed() {
PendingGatewayMessage pending = createPending(2048, false, false);
ArrayList<PendingGatewayMessage> messages = new ArrayList<PendingGatewayMessage>();
messages.add(pending);
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
SenderImpl sender = new SenderImpl();
FragmentHandler handler = new FragmentHandler(_context, new DefragmentedReceiverImpl(pending.getData()));
ReceiverImpl receiver = new ReceiverImpl(handler, 11*1000);
byte msg[] = pending.getData();
boolean keepGoing = true;
while (keepGoing) {
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
if (keepGoing)
try { Thread.sleep(100); } catch (InterruptedException ie) {}
}
}
public void runVaried() {
for (int i = 0; i <= 4096; i++) {
assertTrue(runVaried(i, false, false));
assertTrue(runVaried(i, true, false));
assertTrue(runVaried(i, true, true));
}
}
protected boolean runVaried(int size, boolean includeRouter, boolean includeTunnel) {
PendingGatewayMessage pending = createPending(size, includeRouter, includeTunnel);
ArrayList<PendingGatewayMessage> messages = new ArrayList<PendingGatewayMessage>();
messages.add(pending);
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending.getData());
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
SenderImpl sender = new SenderImpl();
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
byte msg[] = pending.getData();
boolean keepGoing = true;
while (keepGoing) {
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
if (keepGoing)
try { Thread.sleep(100); } catch (InterruptedException ie) {}
}
return handleReceiver.receivedOk();
}
protected PendingGatewayMessage createPending(int size, boolean includeRouter, boolean includeTunnel) {
DataMessage m = new DataMessage(_context);
byte data[] = new byte[size];
_context.random().nextBytes(data);
m.setData(data);
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
m.setMessageExpiration(_context.clock().now() + 60*1000);
Hash toRouter = null;
TunnelId toTunnel = null;
if (includeRouter) {
toRouter = new Hash(new byte[Hash.HASH_LENGTH]);
_context.random().nextBytes(toRouter.getData());
}
if (includeTunnel)
toTunnel = new TunnelId(_context.random().nextLong(TunnelId.MAX_ID_VALUE));
return new PendingGatewayMessage(m, toRouter, toTunnel);
}
protected class SenderImpl implements TunnelGateway.Sender {
public long sendPreprocessed(byte[] preprocessed, TunnelGateway.Receiver receiver) {
return receiver.receiveEncrypted(preprocessed);
}
}
protected class ReceiverImpl implements TunnelGateway.Receiver {
private FragmentHandler _handler;
private int _delay;
public ReceiverImpl(FragmentHandler handler, int delay) {
_handler = handler;
_delay = delay;
}
public long receiveEncrypted(byte[] encrypted) {
_handler.receiveTunnelMessage(encrypted, 0, encrypted.length);
try { Thread.sleep(_delay); } catch (Exception e) {}
return -1; // or do we need to return the real message ID?
}
@Override
public Hash getSendTo() {
// TODO Auto-generated method stub
return null;
}
}
protected class DefragmentedReceiverImpl implements FragmentHandler.DefragmentedReceiver {
private volatile byte _expected[];
private volatile byte _expected2[];
private volatile byte _expected3[];
private volatile int _received;
public DefragmentedReceiverImpl(byte expected[]) {
this(expected, null);
}
public DefragmentedReceiverImpl(byte expected[], byte expected2[]) {
this(expected, expected2, null);
}
public DefragmentedReceiverImpl(byte expected[], byte expected2[], byte expected3[]) {
_expected = expected;
_expected2 = expected2;
_expected3 = expected3;
_received = 0;
}
public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
boolean ok = false;
byte m[] = msg.toByteArray();
if ( (_expected != null) && (DataHelper.eq(_expected, m)) )
ok = true;
if (!ok && (_expected2 != null) && (DataHelper.eq(_expected2, m)) )
ok = true;
if (!ok && (_expected3 != null) && (DataHelper.eq(_expected3, m)) )
ok = true;
if (ok)
_received++;
//_log.info("** equal? " + ok);
}
public boolean receivedOk() {
if ( (_expected != null) && (_expected2 != null) && (_expected3 != null) )
return _received == 3;
else if ( (_expected != null) && (_expected2 != null) )
return _received == 2;
else if ( (_expected != null) || (_expected2 != null) )
return _received == 1;
else
return _received == 0;
}
}
}