diff --git a/core/java/build.xml b/core/java/build.xml
index ad7597559..2038fd505 100644
--- a/core/java/build.xml
+++ b/core/java/build.xml
@@ -223,7 +223,11 @@
-
+
+
+
+
+
diff --git a/router/java/test/junit/net/i2p/router/client/LocalClientManager.java b/router/java/test/junit/net/i2p/router/client/LocalClientManager.java
index 0f9ba30ab..2d0003179 100644
--- a/router/java/test/junit/net/i2p/router/client/LocalClientManager.java
+++ b/router/java/test/junit/net/i2p/router/client/LocalClientManager.java
@@ -8,12 +8,15 @@ package net.i2p.router.client;
*
*/
+import gnu.getopt.Getopt;
+
import net.i2p.data.Destination;
import net.i2p.data.Payload;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
+import net.i2p.util.SimpleTimer2;
/**
* For testing clients without a full router.
@@ -25,6 +28,7 @@ import net.i2p.util.I2PThread;
* @since 0.9.8
*/
class LocalClientManager extends ClientManager {
+ private static int dropX1000 = 0, jitter = 0, latency = 0;
/**
* @param context stub, may be constructed with new RouterContext(null),
@@ -45,7 +49,7 @@ class LocalClientManager extends ClientManager {
/**
* Local only
- * TODO: add simulated delay and random drops to test streaming.
+ * TODO: we could have per-destination delay/drop parameters in the client options
*
* @param flags ignored for local
*/
@@ -56,9 +60,31 @@ class LocalClientManager extends ClientManager {
ClientConnectionRunner sender = getRunner(fromDest);
ClientConnectionRunner runner = getRunner(toDest);
if (runner != null) {
- runner.receiveMessage(toDest, fromDest, payload);
- if (sender != null)
- sender.updateMessageDeliveryStatus(fromDest, msgId, messageNonce, MessageStatusMessage.STATUS_SEND_SUCCESS_LOCAL);
+ if (dropX1000 > 0) {
+ if (100 * 1000 * _ctx.random().nextFloat() < dropX1000) {
+ System.out.println("Message " + msgId + " DROPPED randomly");
+ if (sender != null) {
+ // pretend success
+ sender.updateMessageDeliveryStatus(fromDest, msgId, messageNonce, MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
+ }
+ }
+ }
+ if (latency > 0 || jitter > 0) {
+ int delay = latency;
+ if (jitter > 0)
+ delay += (int) (jitter * _ctx.random().nextGaussian());
+ if (delay > 0) {
+ System.out.println("Message " + msgId + " DELAYED " + delay + " ms");
+ DelayedSend ds = new DelayedSend(_ctx, sender, runner, fromDest, toDest, payload, msgId, messageNonce);
+ ds.schedule(delay);
+ return;
+ }
+ }
+ boolean ok = runner.receiveMessage(toDest, fromDest, payload);
+ if (sender != null) {
+ int rc = ok ? MessageStatusMessage.STATUS_SEND_SUCCESS_LOCAL : MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL;
+ sender.updateMessageDeliveryStatus(fromDest, msgId, messageNonce, rc);
+ }
} else {
// remote. ignore.
System.out.println("Message " + msgId + " is targeting a REMOTE destination - DROPPED");
@@ -67,13 +93,87 @@ class LocalClientManager extends ClientManager {
}
}
+ private static class DelayedSend extends SimpleTimer2.TimedEvent {
+ private final ClientConnectionRunner s, r;
+ private final Destination fd, td;
+ private final Payload pl;
+ private final MessageId id;
+ private final long nonce;
+
+ public DelayedSend(RouterContext ctx, ClientConnectionRunner sender, ClientConnectionRunner runner,
+ Destination fromDest, Destination toDest, Payload payload,
+ MessageId msgId, long messageNonce) {
+ super(ctx.simpleTimer2());
+ s = sender; r = runner; fd = fromDest;
+ td = toDest; pl = payload;
+ id = msgId; nonce = messageNonce;
+ }
+
+ public void timeReached() {
+ boolean ok = r.receiveMessage(td, fd, pl);
+ if (s != null) {
+ int rc = ok ? MessageStatusMessage.STATUS_SEND_SUCCESS_LOCAL : MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL;
+ s.updateMessageDeliveryStatus(fd, id, nonce, rc);
+ }
+ }
+ }
+
public static void main(String args[]) {
+ int dropX1000 = 0, jitter = 0, latency = 0;
+ boolean error = false;
+ Getopt g = new Getopt("router", args, "d:j:l:");
+ try {
+ int c;
+ while ((c = g.getopt()) != -1) {
+ switch (c) {
+
+ case 'd':
+ dropX1000 = (int) (1000 * Double.parseDouble(g.getOptarg()));
+ if (dropX1000 < 0 || dropX1000 >= 100 * 1000)
+ error = true;
+ break;
+
+ case 'j':
+ jitter = Integer.parseInt(g.getOptarg());
+ if (jitter < 0)
+ error = true;
+ break;
+
+ case 'l':
+ latency = Integer.parseInt(g.getOptarg());
+ if (latency < 0)
+ error = true;
+ break;
+
+ default:
+ error = true;
+ }
+ }
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ error = true;
+ }
+ if (error || args.length - g.getOptind() > 0) {
+ usage();
+ System.exit(1);
+ }
+
RouterContext ctx = new RouterContext(null);
int port = ClientManagerFacadeImpl.DEFAULT_PORT;
- ClientManager mgr = new LocalClientManager(ctx, port);
+ LocalClientManager mgr = new LocalClientManager(ctx, port);
+ mgr.dropX1000 = dropX1000;
+ mgr.jitter = jitter;
+ mgr.latency = latency;
mgr.start();
System.out.println("Listening on port " + port);
try { Thread.sleep(60*60*1000); } catch (InterruptedException ie) {}
System.out.println("Done listening on port " + port);
}
+
+ private static void usage() {
+ System.err.println("usage: LocalClientManager\n" +
+ " [-d droppercent] // 0.0 - 99.99999 (default 0)\n" +
+ " [-j jitter] // (integer ms for 1 std. deviation, default 0)\n" +
+ " [-l latency] // (integer ms, default 0)");
+ }
}