diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index 6668a4dc7..32f880dcf 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -369,7 +369,10 @@ public class RouterContext extends I2PAppContext { */ public SegmentedNetworkDatabaseFacade netDbSegmentor() { return _netDb; } - public NetworkDatabaseFacade netDb() { return _netDb.mainNetDB(); } + /** + * @return may be null if called very early or in some unit tests + */ + public NetworkDatabaseFacade netDb() { return _netDb != null ? _netDb.mainNetDB() : null; } /** * Get the client netDb for the given id. diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 69808eee7..2891674fe 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -675,7 +675,8 @@ class ClientConnectionRunner { } } } - if (isPrimary && _floodfillNetworkDatabaseFacade == null) { + // router() null for some unit tests + if (isPrimary && _floodfillNetworkDatabaseFacade == null && _context.router() != null) { if (_log.shouldDebug()) _log.debug("Initializing subDb for client" + destHash); _floodfillNetworkDatabaseFacade = new FloodfillNetworkDatabaseFacade(_context, destHash); diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java index ffe29e8dc..1e74b8361 100644 --- a/router/java/src/net/i2p/router/client/ClientManager.java +++ b/router/java/src/net/i2p/router/client/ClientManager.java @@ -40,6 +40,7 @@ import net.i2p.data.i2cp.SetDateMessage; import net.i2p.internal.I2CPMessageQueue; import net.i2p.router.ClientManagerFacade; import net.i2p.router.ClientMessage; +import net.i2p.router.CommSystemFacade; import net.i2p.router.Job; import net.i2p.router.JobImpl; import net.i2p.router.RouterContext; @@ -433,7 +434,8 @@ class ClientManager { * @since 0.9.12 */ private SessionId locked_getNextSessionId() { - if (_ctx.commSystem().isDummy()) + CommSystemFacade csf = _ctx.commSystem(); + if (csf != null && csf.isDummy()) // unit tests if null return null; int max = Math.max(1, Math.min(2048, _ctx.getProperty(PROP_MAX_SESSIONS, DEFAULT_MAX_SESSIONS))); if (_runnerSessionIds.size() >= max) { 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 c55f0095f..b9ee5fb3b 100644 --- a/router/java/test/junit/net/i2p/router/client/LocalClientManager.java +++ b/router/java/test/junit/net/i2p/router/client/LocalClientManager.java @@ -8,6 +8,8 @@ package net.i2p.router.client; * */ +import java.util.HashMap; +import java.util.Map; import java.util.Properties; import gnu.getopt.Getopt; @@ -19,6 +21,7 @@ import net.i2p.data.i2cp.MessageStatusMessage; import net.i2p.router.RouterContext; import net.i2p.util.I2PThread; import net.i2p.util.SimpleTimer2; +import net.i2p.util.SyntheticREDQueue; /** * For testing clients without a full router. @@ -30,7 +33,9 @@ import net.i2p.util.SimpleTimer2; * @since 0.9.8 */ class LocalClientManager extends ClientManager { - private static int dropX1000 = 0, jitter = 0, latency = 0; + private static int dropX1000 = 0, jitter = 0, latency = 0, ratelimit = 0; + + private final Map limiters = new HashMap(); /** * @param context stub, may be constructed with new RouterContext(null), @@ -66,6 +71,23 @@ class LocalClientManager extends ClientManager { System.out.println("Message " + msgId + " DROPPED randomly"); // pretend success sender.updateMessageDeliveryStatus(fromDest, msgId, messageNonce, MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS); + return; + } + } + if (ratelimit > 0) { + SyntheticREDQueue srq; + synchronized(limiters) { + srq = limiters.get(fromDest); + if (srq == null) { + srq = new SyntheticREDQueue(_ctx, ratelimit * 1024); + limiters.put(fromDest, srq); + } + if (!srq.offer(payload.getSize(), 1)) { + System.out.println("Message " + msgId + " length " + payload.getSize() + " DROPPED by " + srq); + // pretend success + sender.updateMessageDeliveryStatus(fromDest, msgId, messageNonce, MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS); + return; + } } } if (latency > 0 || jitter > 0) { @@ -115,8 +137,9 @@ class LocalClientManager extends ClientManager { public static void main(String args[]) { int dropX1000 = 0, jitter = 0, latency = 0; int port = ClientManagerFacadeImpl.DEFAULT_PORT; + int rate = 0; boolean error = false; - Getopt g = new Getopt("LocalClientManager", args, "d:j:l:p:"); + Getopt g = new Getopt("LocalClientManager", args, "d:j:l:p:r:"); try { int c; while ((c = g.getopt()) != -1) { @@ -146,6 +169,12 @@ class LocalClientManager extends ClientManager { error = true; break; + case 'r': + rate = Integer.parseInt(g.getOptarg()); + if (rate < 0) + error = true; + break; + default: error = true; } @@ -167,6 +196,7 @@ class LocalClientManager extends ClientManager { mgr.dropX1000 = dropX1000; mgr.jitter = jitter; mgr.latency = latency; + mgr.ratelimit = rate; mgr.start(); System.out.println("Listening on port " + port); try { Thread.sleep(60*60*1000); } catch (InterruptedException ie) {} @@ -178,6 +208,7 @@ class LocalClientManager extends ClientManager { " [-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)\n" + - " [-p port] // (I2CP port, default 7654)"); + " [-p port] // (I2CP port, default 7654)\n" + + " [-r ratelimit] // (rate limit in KBps, default none)"); } }