diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 0c771879fb5f1ad601f6e6aeb6413b03752de852..175af64f3300e061ed4319681de674437faeb528 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -264,7 +264,8 @@ public class SnarkManager implements Snark.CompleteListener { * Grab the torrent given the (canonical) filename */ public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } } - public void addTorrent(String filename) { + public void addTorrent(String filename) { addTorrent(filename, false); } + public void addTorrent(String filename, boolean dontAutoStart) { if (!I2PSnarkUtil.instance().connected()) { addMessage("Connecting to I2P"); boolean ok = I2PSnarkUtil.instance().connect(); @@ -317,7 +318,7 @@ public class SnarkManager implements Snark.CompleteListener { } // ok, snark created, now lets start it up or configure it further File f = new File(filename); - if (shouldAutoStart()) { + if (!dontAutoStart && shouldAutoStart()) { torrent.startTorrent(); addMessage("Torrent added and started: '" + f.getName() + "'"); } else { @@ -459,6 +460,40 @@ public class SnarkManager implements Snark.CompleteListener { } } } + + private static final String DEFAULT_TRACKERS[] = { + "Postman's tracker", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php" + , "Orion's tracker", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt" +// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php" + }; + + /** comma delimited list of name=announceURL for the trackers to be displayed */ + public static final String PROP_TRACKERS = "i2psnark.trackers"; + /** unordered map of announceURL to name */ + public Map getTrackers() { + HashMap rv = new HashMap(); + String trackers = _config.getProperty(PROP_TRACKERS); + if ( (trackers == null) || (trackers.trim().length() <= 0) ) + trackers = _context.getProperty(PROP_TRACKERS); + if ( (trackers == null) || (trackers.trim().length() <= 0) ) { + for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) + rv.put(DEFAULT_TRACKERS[i+1], DEFAULT_TRACKERS[i]); + } else { + StringTokenizer tok = new StringTokenizer(trackers, ","); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int split = pair.indexOf('='); + if (split <= 0) + continue; + String name = pair.substring(0, split).trim(); + String url = pair.substring(split+1).trim(); + if ( (name.length() > 0) && (url.length() > 0) ) + rv.put(url, name); + } + } + + return rv; + } private static class TorrentFilenameFilter implements FilenameFilter { private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter(); diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 56b5da3acaf50a988011fcae82a59e243b53d566..b302bd4b4deab4c6e157e147a30246e73c10cdf1 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -80,7 +80,7 @@ public class I2PSnarkServlet extends HttpServlet { out.write(TABLE_FOOTER); writeAddForm(out, req); - if (false) // seeding needs to register the torrent first (boo, hiss) + if (true) // seeding needs to register the torrent first, so we can't start it automatically (boo, hiss) writeSeedForm(out, req); writeConfigForm(out, req); out.write(FOOTER); @@ -252,8 +252,9 @@ public class I2PSnarkServlet extends HttpServlet { out.write(info.getTorrentData()); out.close(); _manager.addMessage("Torrent created for " + baseFile.getName() + ": " + torrentFile.getAbsolutePath()); - // now fire it up and seed away! - _manager.addTorrent(torrentFile.getCanonicalPath()); + // now fire it up, but don't automatically seed it + _manager.addTorrent(torrentFile.getCanonicalPath(), false); + _manager.addMessage("Many I2P trackers require you to register new torrents before seeding - please do so before starting " + baseFile.getName()); } catch (IOException ioe) { _manager.addMessage("Error creating a torrent for " + baseFile.getAbsolutePath() + ": " + ioe.getMessage()); } @@ -437,11 +438,6 @@ public class I2PSnarkServlet extends HttpServlet { out.write("</form>\n</span>\n"); } - private static final String DEFAULT_TRACKERS[] = { - "Postman's tracker", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php", - "Orion's tracker", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt", - "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php" - }; private void writeSeedForm(PrintWriter out, HttpServletRequest req) throws IOException { String uri = req.getRequestURI(); String baseFile = req.getParameter("baseFile"); @@ -453,19 +449,36 @@ public class I2PSnarkServlet extends HttpServlet { out.write("<form action=\"" + uri + "\" method=\"POST\">\n"); out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" />\n"); //out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br />\n"); - out.write("Data to seed: <input type=\"text\" name=\"baseFile\" size=\"50\" value=\"" + baseFile - + "\" title=\"File within " + _manager.getDataDir().getAbsolutePath() + " to seed\" /><br />\n"); + out.write("Data to seed: " + _manager.getDataDir().getAbsolutePath() + File.separatorChar + + "<input type=\"text\" name=\"baseFile\" size=\"20\" value=\"" + baseFile + + "\" title=\"File to seed (must be within the specified path)\" /><br />\n"); out.write("Tracker: <select name=\"announceURL\"><option value=\"\">Select a tracker</option>\n"); - for (int i = 0; i + 1 < DEFAULT_TRACKERS.length; i += 2) - out.write("\t<option value=\"" + DEFAULT_TRACKERS[i+1] + "\">" + DEFAULT_TRACKERS[i] + "</option>\n"); - out.write("</select><br />\n"); - out.write(" or: "); - out.write("<input type=\"text\" name=\"announceURLOther\" size=\"50\" value=\"http://\" " + - "title=\"Custom tracker URL\" /><br />\n"); + Map trackers = sort(_manager.getTrackers()); + for (Iterator iter = trackers.keySet().iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + String announceURL = (String)trackers.get(name); + // we inject whitespace in sort(...) to guarantee uniqueness, but we can strip it off here + out.write("\t<option value=\"" + announceURL + "\">" + name.trim() + "</option>\n"); + } + out.write("</select>\n"); + out.write("or <input type=\"text\" name=\"announceURLOther\" size=\"50\" value=\"http://\" " + + "title=\"Custom tracker URL\" /> "); out.write("<input type=\"submit\" value=\"Create torrent\" name=\"action\" />\n"); out.write("</form>\n</span>\n"); } + private Map sort(Map trackers) { + TreeMap rv = new TreeMap(); + for (Iterator iter = trackers.keySet().iterator(); iter.hasNext(); ) { + String url = (String)iter.next(); + String name = (String)trackers.get(url); + while (rv.containsKey(name)) + name = name + " "; + rv.put(name, url); + } + return rv; + } + private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException { String uri = req.getRequestURI(); String dataDir = _manager.getDataDir().getAbsolutePath(); @@ -584,7 +597,7 @@ public class I2PSnarkServlet extends HttpServlet { " background-color: #DDDDCC;\n" + "}\n" + ".snarkNewTorrent {\n" + - " font-size: 12pt;\n" + + " font-size: 10pt;\n" + " font-family: monospace;\n" + " background-color: #ADAE9;\n" + "}\n" + diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java index 87a0ef10e09a3ec83e686edda732a8f6ba4cd677..706e7f9b196f3dfd50066919b298e1174a97ad0a 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -768,7 +768,7 @@ public class Connection { long howLong = _options.getInactivityTimeout(); howLong += _context.random().nextInt(30*1000); // randomize it a bit, so both sides don't do it at once if (_log.shouldLog(Log.DEBUG)) - _log.debug("Resetting the inactivity timer to " + howLong, new Exception("Reset by")); + _log.debug("Resetting the inactivity timer to " + howLong, new Exception(toString())); // this will get rescheduled, and rescheduled, and rescheduled... RetransmissionTimer.getInstance().removeEvent(_activityTimer); RetransmissionTimer.getInstance().addEvent(_activityTimer, howLong); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java index 61c01e15d59faf2ad4099c5fdb4d84dc3b7241e9..f008e74b4da6b8e7df772c772451bb97af078082 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java @@ -444,14 +444,20 @@ public class ConnectionPacketHandler { } public void timeReached() { if (_con.getLastSendTime() <= _created) { - if (_con.getResetReceived() || _con.getResetSent() || (_con.getUnackedPacketsReceived() <= 0) ) + if (_con.getResetReceived() || _con.getResetSent()) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Ack dup on " + _con + ", but we have been reset"); return; + } if (_log.shouldLog(Log.DEBUG)) - _log.debug("Last sent was a while ago, and we want to ack a dup"); + _log.debug("Last sent was a while ago, and we want to ack a dup on " + _con); // we haven't done anything since receiving the dup, send an // ack now _con.ackImmediately(); + } else { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Ack dup on " + _con + ", but we have sent (" + (_con.getLastSendTime()-_created) + ")"); } } } diff --git a/history.txt b/history.txt index 9302282f3b5588aec8ba53b9b78d32ba784866f0..edefb44f3a703ce9fb9021b212627a7fa65e4cbd 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,10 @@ -$Id: history.txt,v 1.374 2005/12/30 15:57:53 jrandom Exp $ +$Id: history.txt,v 1.375 2005/12/30 18:33:54 jrandom Exp $ + +2005-12-31 jrandom + * Include a simple torrent creator in the I2PSnark web UI + * Further streaming lib closing improvements + * Refactored the load test components to run off live tunnels (though, + still not safe for normal/anonymous load testing) 2005-12-30 jrandom * Close streams more gracefully diff --git a/router/java/src/net/i2p/router/InNetMessagePool.java b/router/java/src/net/i2p/router/InNetMessagePool.java index 9962ae3a0ec05b87f91d5179455bad9245b53283..7f0e907be04c043bc368c0b4c2e04825828d4761 100644 --- a/router/java/src/net/i2p/router/InNetMessagePool.java +++ b/router/java/src/net/i2p/router/InNetMessagePool.java @@ -178,29 +178,9 @@ public class InNetMessagePool implements Service { } if (allowMatches) { - List origMessages = _context.messageRegistry().getOriginalMessages(messageBody); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Original messages for inbound message: " + origMessages.size()); - if (origMessages.size() > 1) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Orig: " + origMessages + " \nthe above are replies for: " + messageBody, - new Exception("Multiple matches")); - } - - for (int i = 0; i < origMessages.size(); i++) { - OutNetMessage omsg = (OutNetMessage)origMessages.get(i); - ReplyJob job = omsg.getOnReplyJob(); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Original message [" + i + "] " + omsg.getReplySelector() - + " : " + omsg + ": reply job: " + job); + int replies = handleReplies(messageBody); - if (job != null) { - job.setMessage(messageBody); - _context.jobQueue().addJob(job); - } - } - - if (origMessages.size() <= 0) { + if (replies <= 0) { // not handled as a reply if (!jobFound) { // was not handled via HandlerJobBuilder @@ -247,6 +227,31 @@ public class InNetMessagePool implements Service { return 0; // no queue } + public int handleReplies(I2NPMessage messageBody) { + List origMessages = _context.messageRegistry().getOriginalMessages(messageBody); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Original messages for inbound message: " + origMessages.size()); + if (origMessages.size() > 1) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Orig: " + origMessages + " \nthe above are replies for: " + messageBody, + new Exception("Multiple matches")); + } + + for (int i = 0; i < origMessages.size(); i++) { + OutNetMessage omsg = (OutNetMessage)origMessages.get(i); + ReplyJob job = omsg.getOnReplyJob(); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Original message [" + i + "] " + omsg.getReplySelector() + + " : " + omsg + ": reply job: " + job); + + if (job != null) { + job.setMessage(messageBody); + _context.jobQueue().addJob(job); + } + } + return origMessages.size(); + } + // the following short circuits the tunnel dispatching - i'm not sure whether // we'll want to run the dispatching in jobs or whether it shuold go inline with // others and/or on other threads (e.g. transport threads). lets try 'em both. diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java index 8b1c208c339b6c9c63252b87903af119bc59aacd..c2177a6664b0abeeb99a4f39ccab246daa29417b 100644 --- a/router/java/src/net/i2p/router/JobQueue.java +++ b/router/java/src/net/i2p/router/JobQueue.java @@ -226,13 +226,8 @@ public class JobQueue { return false; } - private static final String PROP_LOAD_TEST = "router.loadTest"; public void allowParallelOperation() { _allowParallelOperation = true; - if (Boolean.valueOf(_context.getProperty(PROP_LOAD_TEST, "false")).booleanValue()) { - LoadTestManager t = new LoadTestManager(_context); - addJob(t.getTestJob()); - } } public void restart() { diff --git a/router/java/src/net/i2p/router/LoadTestManager.java b/router/java/src/net/i2p/router/LoadTestManager.java index e31751de04ad6e72bd6ff269d7d140b14c0c1750..42d5fb57386b70c7dcfc9a55d033c45c2b56bca6 100644 --- a/router/java/src/net/i2p/router/LoadTestManager.java +++ b/router/java/src/net/i2p/router/LoadTestManager.java @@ -5,16 +5,19 @@ import java.util.*; import net.i2p.util.*; import net.i2p.data.*; import net.i2p.data.i2np.*; +import net.i2p.router.message.*; import net.i2p.router.tunnel.*; import net.i2p.router.tunnel.pool.*; import net.i2p.router.transport.udp.UDPTransport; /** - * Coordinate some tests of peers to see how much load they can handle. This - * test is not safe for use in anonymous environments, but should help pinpoint - * some performance aspects of the live net. + * Coordinate some tests of peers to see how much load they can handle. If + * TEST_LIVE_TUNNELS is set to false, it builds load test tunnels across various + * peers in ways that are not anonymity sensitive (but may help with testing the net). + * If it is set to true, however, it runs a few tests at a time for actual tunnels that + * are built, to help determine whether our peer selection is insufficient. * - * Each individual load test is conducted by building a single one hop inbound + * Load tests of fake tunnels are conducted by building a single one hop inbound * tunnel with the peer in question acting as the inbound gateway. We then send * messages directly to that gateway, which they batch up and send "down the * tunnel" (aka directly to us), at which point we then send another message, @@ -24,9 +27,15 @@ import net.i2p.router.transport.udp.UDPTransport; * * If "router.loadTestSmall=true", we transmit a tiny DeliveryStatusMessage (~96 bytes * at the SSU level), which is sent back to us as a single TunnelDataMessage (~1KB). - * Otherwise, we transmit a 4KB DataMessage, which is sent back to us as five (1KB) - * TunnelDataMessages. This size is chosen because the streaming lib uses 4KB messages - * by default. + * Otherwise, we transmit a 4KB DataMessage wrapped inside a garlic message, which is + * sent back to us as five (1KB) TunnelDataMessages. This size is chosen because the + * streaming lib uses 4KB messages by default. + * + * Load tests of live tunnels pick a random tunnel from the tested pool's pair (e.g. if + * we are testing an outbound tunnel for a particular destination, it picks an inbound + * tunnel from that destination's inbound pool), with each message going down that one + * randomly paired tunnel for the duration of the load test (varying the paired tunnel + * with each message had poor results) * */ public class LoadTestManager { @@ -34,12 +43,14 @@ public class LoadTestManager { private Log _log; private Writer _out; private List _untestedPeers; - + private List _active; public LoadTestManager(RouterContext ctx) { _context = ctx; _log = ctx.logManager().getLog(LoadTestManager.class); + _active = Collections.synchronizedList(new ArrayList()); try { _out = new BufferedWriter(new FileWriter("loadtest.log", true)); + _out.write("startup at " + ctx.clock().now() + "\n"); } catch (IOException ioe) { _log.log(Log.CRIT, "error creating log", ioe); } @@ -50,6 +61,8 @@ public class LoadTestManager { _context.statManager().createRateStat("test.rttHigh", "How long it takes to get a reply, if it is a slow rtt", "test", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); } + public static final boolean TEST_LIVE_TUNNELS = true; + public Job getTestJob() { return new TestJob(_context); } private class TestJob extends JobImpl { public TestJob(RouterContext ctx) { @@ -59,14 +72,16 @@ public class LoadTestManager { } public String getName() { return "run load tests"; } public void runJob() { - runTest(); - getTiming().setStartAfter(10*60*1000 + getContext().clock().now()); - getContext().jobQueue().addJob(TestJob.this); + if (!TEST_LIVE_TUNNELS) { + runTest(); + getTiming().setStartAfter(10*60*1000 + getContext().clock().now()); + getContext().jobQueue().addJob(TestJob.this); + } } } /** 10 peers at a time */ - private static final int CONCURRENT_PEERS = 10; + private static final int CONCURRENT_PEERS = 0; /** 4 messages per peer at a time */ private static final int CONCURRENT_MESSAGES = 4; @@ -88,8 +103,8 @@ public class LoadTestManager { } catch (NumberFormatException nfe) { rv = CONCURRENT_PEERS; } - if (rv < 1) - rv = 1; + if (rv < 0) + rv = 0; if (rv > 50) rv = 50; return rv; @@ -115,38 +130,119 @@ public class LoadTestManager { private void runTest(LoadTestTunnelConfig tunnel) { log(tunnel, "start"); int peerMessages = getPeerMessages(); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Run test on " + tunnel + " with " + peerMessages + " messages"); for (int i = 0; i < peerMessages; i++) sendTestMessage(tunnel); } + private void pickTunnels(LoadTestTunnelConfig tunnel) { + TunnelInfo inbound = null; + TunnelInfo outbound = null; + if (tunnel.getTunnel().isInbound()) { + inbound = _context.tunnelManager().getTunnelInfo(tunnel.getReceiveTunnelId(0)); + if ( (inbound == null) && (_log.shouldLog(Log.WARN)) ) + _log.warn("where are we? inbound tunnel isn't known: " + tunnel, new Exception("source")); + if (tunnel.getTunnel().getDestination() != null) + outbound = _context.tunnelManager().selectOutboundTunnel(tunnel.getTunnel().getDestination()); + else + outbound = _context.tunnelManager().selectOutboundTunnel(); + } else { + outbound = _context.tunnelManager().getTunnelInfo(tunnel.getSendTunnelId(0)); + if ( (outbound == null) && (_log.shouldLog(Log.WARN)) ) + _log.warn("where are we? outbound tunnel isn't known: " + tunnel, new Exception("source")); + if (tunnel.getTunnel().getDestination() != null) + inbound = _context.tunnelManager().selectInboundTunnel(tunnel.getTunnel().getDestination()); + else + inbound = _context.tunnelManager().selectInboundTunnel(); + } + tunnel.setInbound(inbound); + tunnel.setOutbound(outbound); + } + private void sendTestMessage(LoadTestTunnelConfig tunnel) { - if (_context.clock().now() > tunnel.getExpiration()) - return; - RouterInfo target = _context.netDb().lookupRouterInfoLocally(tunnel.getPeer(0)); - if (target == null) { - log(tunnel, "lookup failed"); + long now = _context.clock().now(); + if (now > tunnel.getExpiration()) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Not sending a test message to " + tunnel + " because it expired"); + tunnel.logComplete(); + _active.remove(tunnel); return; } - I2NPMessage payloadMessage = createPayloadMessage(); - - TunnelGatewayMessage tm = new TunnelGatewayMessage(_context); - tm.setMessage(payloadMessage); - tm.setTunnelId(tunnel.getReceiveTunnelId(0)); - tm.setMessageExpiration(payloadMessage.getMessageExpiration()); - - OutNetMessage om = new OutNetMessage(_context); - om.setMessage(tm); - SendAgain failed = new SendAgain(_context, tunnel, payloadMessage.getUniqueId(), false); - om.setOnFailedReplyJob(failed); - om.setOnReplyJob(new SendAgain(_context, tunnel, payloadMessage.getUniqueId(), true)); - //om.setOnFailedSendJob(failed); - om.setReplySelector(new Selector(tunnel, payloadMessage.getUniqueId())); - om.setTarget(target); - om.setExpiration(tm.getMessageExpiration()); - om.setPriority(40); - _context.outNetMessagePool().add(om); - //log(tunnel, m.getMessageId() + " sent"); + if (TEST_LIVE_TUNNELS) { + TunnelInfo inbound = tunnel.getInbound(); + TunnelInfo outbound = tunnel.getOutbound(); + if ( (inbound == null) || (outbound == null) ) { + pickTunnels(tunnel); + inbound = tunnel.getInbound(); + outbound = tunnel.getOutbound(); + } + + if (inbound == null) { + log(tunnel, "No inbound tunnels found"); + _active.remove(tunnel); + return; + } else if (outbound == null) { + log(tunnel, "No outbound tunnels found"); + tunnel.logComplete(); + _active.remove(tunnel); + return; + } + + if ( (now >= inbound.getExpiration()) || (now >= outbound.getExpiration()) ) { + tunnel.logComplete(); + _active.remove(tunnel); + return; + } + + if (_log.shouldLog(Log.DEBUG)) + _log.debug("inbound and outbound found for " + tunnel); + + I2NPMessage payloadMessage = createPayloadMessage(); + + if (_log.shouldLog(Log.DEBUG)) + _log.debug("testing live tunnels with inbound [" + inbound + "] and outbound [" + outbound + "]"); + + // this should take into consideration both the inbound and outbound tunnels + // ... but it doesn't, yet. + _context.messageRegistry().registerPending(new Selector(tunnel, payloadMessage.getUniqueId()), + new SendAgain(_context, tunnel, payloadMessage.getUniqueId(), true), + new SendAgain(_context, tunnel, payloadMessage.getUniqueId(), false), + 10*1000); + _context.tunnelDispatcher().dispatchOutbound(payloadMessage, outbound.getSendTunnelId(0), + inbound.getReceiveTunnelId(0), + inbound.getPeer(0)); + //log(tunnel, payloadMessage.getUniqueId() + " sent via " + inbound + " / " + outbound); + } else { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("NOT testing live tunnels for [" + tunnel + "]"); + RouterInfo target = _context.netDb().lookupRouterInfoLocally(tunnel.getPeer(0)); + if (target == null) { + log(tunnel, "lookup failed"); + return; + } + + I2NPMessage payloadMessage = createPayloadMessage(); + + TunnelGatewayMessage tm = new TunnelGatewayMessage(_context); + tm.setMessage(payloadMessage); + tm.setTunnelId(tunnel.getReceiveTunnelId(0)); + tm.setMessageExpiration(payloadMessage.getMessageExpiration()); + + OutNetMessage om = new OutNetMessage(_context); + om.setMessage(tm); + SendAgain failed = new SendAgain(_context, tunnel, payloadMessage.getUniqueId(), false); + om.setOnFailedReplyJob(failed); + om.setOnReplyJob(new SendAgain(_context, tunnel, payloadMessage.getUniqueId(), true)); + //om.setOnFailedSendJob(failed); + om.setReplySelector(new Selector(tunnel, payloadMessage.getUniqueId())); + om.setTarget(target); + om.setExpiration(tm.getMessageExpiration()); + om.setPriority(40); + _context.outNetMessagePool().add(om); + //log(tunnel, m.getMessageId() + " sent"); + } } private static final boolean SMALL_PAYLOAD = false; @@ -172,7 +268,40 @@ public class LoadTestManager { m.setData(data); long now = _context.clock().now(); m.setMessageExpiration(now + 10*1000); - return m; + + if (true) { + // garlic wrap the data message to ourselves so the endpoints and gateways + // can't tell its a test, encrypting it with a random key and tag, + // remembering that key+tag so that we can decrypt it later without any ElGamal + DeliveryInstructions instructions = new DeliveryInstructions(); + instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL); + + PayloadGarlicConfig payload = new PayloadGarlicConfig(); + payload.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + payload.setId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE)); + payload.setId(m.getUniqueId()); + payload.setPayload(m); + payload.setRecipient(_context.router().getRouterInfo()); + payload.setDeliveryInstructions(instructions); + payload.setRequestAck(false); + payload.setExpiration(m.getMessageExpiration()); + + SessionKey encryptKey = _context.keyGenerator().generateSessionKey(); + SessionTag encryptTag = new SessionTag(true); + SessionKey sentKey = new SessionKey(); + Set sentTags = null; + GarlicMessage msg = GarlicMessageBuilder.buildMessage(_context, payload, sentKey, sentTags, + _context.keyManager().getPublicKey(), + encryptKey, encryptTag); + + Set encryptTags = new HashSet(1); + encryptTags.add(encryptTag); + _context.sessionKeyManager().tagsReceived(encryptKey, encryptTags); + + return msg; + } else { + return m; + } } } @@ -238,23 +367,66 @@ public class LoadTestManager { } private void log(LoadTestTunnelConfig tunnel, String msg) { + //if (!_log.shouldLog(Log.INFO)) return; StringBuffer buf = new StringBuffer(128); - for (int i = 0; i < tunnel.getLength()-1; i++) { - Hash peer = tunnel.getPeer(i); - if ( (peer != null) && (peer.equals(_context.routerHash())) ) - continue; - else if (peer != null) - buf.append(peer.toBase64()); - else - buf.append("[unknown_peer]"); - buf.append(" "); - TunnelId id = tunnel.getReceiveTunnelId(i); - if (id != null) - buf.append(id.getTunnelId()); - else - buf.append("[unknown_tunnel]"); - buf.append(" "); - buf.append(_context.clock().now()).append(" hop ").append(i).append(" ").append(msg).append("\n"); + if (tunnel.getInbound() == null) { + for (int i = 0; i < tunnel.getLength()-1; i++) { + Hash peer = tunnel.getPeer(i); + if ( (peer != null) && (peer.equals(_context.routerHash())) ) + continue; + else if (peer != null) + buf.append(peer.toBase64()); + else + buf.append("[unknown_peer]"); + buf.append(" "); + TunnelId id = tunnel.getReceiveTunnelId(i); + if (id != null) + buf.append(id.getTunnelId()); + else + buf.append("[unknown_tunnel]"); + buf.append(" "); + buf.append(_context.clock().now()).append(" hop ").append(i).append(" ").append(msg).append("\n"); + } + } else { + int hop = 0; + TunnelInfo info = tunnel.getOutbound(); + for (int i = 0; (info != null) && (i < info.getLength()-1); i++) { + Hash peer = info.getPeer(i); + if ( (peer != null) && (peer.equals(_context.routerHash())) ) + continue; + else if (peer != null) + buf.append(peer.toBase64()); + else + buf.append("[unknown_peer]"); + buf.append(" "); + TunnelId id = tunnel.getReceiveTunnelId(i); + if (id != null) + buf.append(id.getTunnelId()); + else + buf.append("[unknown_tunnel]"); + buf.append(" "); + buf.append(_context.clock().now()).append(" out_hop ").append(hop).append(" ").append(msg).append("\n"); + hop++; + } + info = tunnel.getInbound(); + for (int i = 0; (info != null) && (i < info.getLength()-1); i++) { + Hash peer = info.getPeer(i); + if ( (peer != null) && (peer.equals(_context.routerHash())) ) + continue; + else if (peer != null) + buf.append(peer.toBase64()); + else + buf.append("[unknown_peer]"); + buf.append(" "); + TunnelId id = tunnel.getReceiveTunnelId(i); + if (id != null) + buf.append(id.getTunnelId()); + else + buf.append("[unknown_tunnel]"); + buf.append(" "); + buf.append(_context.clock().now()).append(" in_hop ").append(hop).append(" ").append(msg).append("\n"); + hop++; + } } try { synchronized (_out) { @@ -280,7 +452,7 @@ public class LoadTestManager { private void buildOneHop(Hash peer) { long expiration = _context.clock().now() + 10*60*1000; - LoadTestTunnelConfig cfg = new LoadTestTunnelConfig(_context, 2, true); + PooledTunnelCreatorConfig cfg = new PooledTunnelCreatorConfig(_context, 2, true); // cfg.getPeer() is ordered gateway first cfg.setPeer(0, peer); HopConfig hop = cfg.getConfig(0); @@ -299,8 +471,10 @@ public class LoadTestManager { if (_log.shouldLog(Log.DEBUG)) _log.debug("Config for " + peer.toBase64() + ": " + cfg); - CreatedJob onCreated = new CreatedJob(_context, cfg); - FailedJob fail = new FailedJob(_context, cfg); + LoadTestTunnelConfig ltCfg = new LoadTestTunnelConfig(cfg); + + CreatedJob onCreated = new CreatedJob(_context, ltCfg); + FailedJob fail = new FailedJob(_context, ltCfg); RequestTunnelJob req = new RequestTunnelJob(_context, cfg, onCreated, fail, cfg.getLength()-1, false, true); _context.jobQueue().addJob(req); } @@ -333,7 +507,7 @@ public class LoadTestManager { private void buildLonger(Hash peer) { long expiration = _context.clock().now() + 10*60*1000; - LoadTestTunnelConfig cfg = new LoadTestTunnelConfig(_context, 3, true); + PooledTunnelCreatorConfig cfg = new PooledTunnelCreatorConfig(_context, 3, true); // cfg.getPeer() is ordered gateway first cfg.setPeer(0, peer); HopConfig hop = cfg.getConfig(0); @@ -370,12 +544,61 @@ public class LoadTestManager { if (_log.shouldLog(Log.DEBUG)) _log.debug("Config for " + peer.toBase64() + " with fastPeer: " + fastPeer.toBase64() + ": " + cfg); - CreatedJob onCreated = new CreatedJob(_context, cfg); - FailedJob fail = new FailedJob(_context, cfg); + + LoadTestTunnelConfig ltCfg = new LoadTestTunnelConfig(cfg); + CreatedJob onCreated = new CreatedJob(_context, ltCfg); + FailedJob fail = new FailedJob(_context, ltCfg); RequestTunnelJob req = new RequestTunnelJob(_context, cfg, onCreated, fail, cfg.getLength()-1, false, true); _context.jobQueue().addJob(req); } + /** + * If we are testing live tunnels, see if we want to test the one that was just created + * fully. + */ + public void addTunnelTestCandidate(TunnelCreatorConfig cfg) { + LoadTestTunnelConfig ltCfg = new LoadTestTunnelConfig(cfg); + if (wantToTest(ltCfg)) { + // wait briefly so everyone has their things in order (not really necessary...) + long delay = _context.random().nextInt(30*1000) + 30*1000; + SimpleTimer.getInstance().addEvent(new BeginTest(ltCfg), delay); + if (_log.shouldLog(Log.INFO)) + _log.info("Testing " + cfg + ", with " + _active.size() + " active"); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info("Not testing " + cfg + " because we have " + _active.size() + " active: " + _active); + } + } + public void removeTunnelTestCandidate(TunnelCreatorConfig cfg) { _active.remove(cfg); } + + private class BeginTest implements SimpleTimer.TimedEvent { + private LoadTestTunnelConfig _cfg; + public BeginTest(LoadTestTunnelConfig cfg) { + _cfg = cfg; + } + public void timeReached() { + _context.jobQueue().addJob(new Expire(_context, _cfg, false)); + runTest(_cfg); + } + } + + private boolean wantToTest(LoadTestTunnelConfig cfg) { + // wait 10 minutes before testing anything + if (_context.router().getUptime() <= 10*60*1000) return false; + + if (TEST_LIVE_TUNNELS && _active.size() < getConcurrency()) { + // length == #hops+1 (as it includes the creator) + if (cfg.getLength() < 2) + return false; + // only load test the client tunnels + if (cfg.getTunnel().getDestination() == null) + return false; + _active.add(cfg); + return true; + } else { + return false; + } + } private class CreatedJob extends JobImpl { private LoadTestTunnelConfig _cfg; @@ -387,29 +610,33 @@ public class LoadTestManager { public void runJob() { if (_log.shouldLog(Log.INFO)) _log.info("Tunnel created for testing peer " + _cfg.getPeer(0).toBase64()); - getContext().tunnelDispatcher().joinInbound(_cfg); + getContext().tunnelDispatcher().joinInbound(_cfg.getTunnel()); //log(_cfg, "joined"); - + _active.add(_cfg); Expire j = new Expire(getContext(), _cfg); - _cfg.setExpireJob(j); + //_cfg.setExpireJob(j); getContext().jobQueue().addJob(j); runTest(_cfg); } } private class Expire extends JobImpl { private LoadTestTunnelConfig _cfg; + private boolean _removeFromDispatcher; public Expire(RouterContext ctx, LoadTestTunnelConfig cfg) { + this(ctx, cfg, true); + } + public Expire(RouterContext ctx, LoadTestTunnelConfig cfg, boolean removeFromDispatcher) { super(ctx); _cfg = cfg; + _removeFromDispatcher = removeFromDispatcher; getTiming().setStartAfter(cfg.getExpiration()+60*1000); } public String getName() { return "expire test tunnel"; } public void runJob() { - getContext().tunnelDispatcher().remove(_cfg); - log(_cfg, "expired after sending " + _cfg.getFullMessageCount() + " / " + _cfg.getFailedMessageCount()); - getContext().statManager().addRateData("test.lifetimeSuccessful", _cfg.getFullMessageCount(), _cfg.getFailedMessageCount()); - if (_cfg.getFailedMessageCount() > 0) - getContext().statManager().addRateData("test.lifetimeFailed", _cfg.getFailedMessageCount(), _cfg.getFullMessageCount()); + if (_removeFromDispatcher) + getContext().tunnelDispatcher().remove(_cfg.getTunnel()); + _cfg.logComplete(); + _active.remove(_cfg); } } private class FailedJob extends JobImpl { @@ -426,17 +653,46 @@ public class LoadTestManager { } } - private class LoadTestTunnelConfig extends PooledTunnelCreatorConfig { + private class LoadTestTunnelConfig { + private TunnelCreatorConfig _cfg; private long _failed; private long _fullMessages; - public LoadTestTunnelConfig(RouterContext ctx, int length, boolean isInbound) { - super(ctx, length, isInbound); + private TunnelInfo _testInbound; + private TunnelInfo _testOutbound; + private boolean _completed; + public LoadTestTunnelConfig(TunnelCreatorConfig cfg) { + _cfg = cfg; _failed = 0; _fullMessages = 0; + _completed = false; } + + public long getExpiration() { return _cfg.getExpiration(); } + public Hash getPeer(int peer) { return _cfg.getPeer(peer); } + public TunnelId getReceiveTunnelId(int peer) { return _cfg.getReceiveTunnelId(peer); } + public TunnelId getSendTunnelId(int peer) { return _cfg.getSendTunnelId(peer); } + public int getLength() { return _cfg.getLength(); } + public void incrementFailed() { ++_failed; } public long getFailedMessageCount() { return _failed; } public void incrementFull() { ++_fullMessages; } public long getFullMessageCount() { return _fullMessages; } + public TunnelCreatorConfig getTunnel() { return _cfg; } + public void setInbound(TunnelInfo info) { _testInbound = info; } + public void setOutbound(TunnelInfo info) { _testOutbound = info; } + public TunnelInfo getInbound() { return _testInbound; } + public TunnelInfo getOutbound() { return _testOutbound; } + public String toString() { return _cfg + ": failed=" + _failed + " full=" + _fullMessages; } + + void logComplete() { + if (_completed) return; + _completed = true; + LoadTestTunnelConfig cfg = LoadTestTunnelConfig.this; + log(cfg, "expired after sending " + cfg.getFullMessageCount() + " / " + cfg.getFailedMessageCount() + + " in " + (10*60*1000l - (cfg.getExpiration()-_context.clock().now()))); + _context.statManager().addRateData("test.lifetimeSuccessful", cfg.getFullMessageCount(), cfg.getFailedMessageCount()); + if (cfg.getFailedMessageCount() > 0) + _context.statManager().addRateData("test.lifetimeFailed", cfg.getFailedMessageCount(), cfg.getFullMessageCount()); + } } } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 7a2cae6b03e279a1b552c00a8892c6aa2425b2da..3633c2586ec31406742d8bb5ae15fe668a851e61 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.321 $ $Date: 2005/12/30 15:57:53 $"; + public final static String ID = "$Revision: 1.322 $ $Date: 2005/12/30 18:33:54 $"; public final static String VERSION = "0.6.1.8"; - public final static long BUILD = 5; + public final static long BUILD = 6; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java index ad994ab3434cc4ae08a9c392b1f1f741651faac2..df24a3a057e2a593a69fb3e9d0f0f8d868777308 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java @@ -34,6 +34,7 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec _log = ctx.logManager().getLog(InboundMessageDistributor.class); _receiver = new GarlicMessageReceiver(ctx, this, client); _context.statManager().createRateStat("tunnel.dropDangerousClientTunnelMessage", "How many tunnel messages come down a client tunnel that we shouldn't expect (lifetime is the 'I2NP type')", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); + _context.statManager().createRateStat("tunnel.handleLoadClove", "When do we receive load test cloves", "Tunnels", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); } public void distribute(I2NPMessage msg, Hash target) { @@ -65,6 +66,8 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec // targetting us either implicitly (no target) or explicitly (no tunnel) // make sure we don't honor any remote requests directly (garlic instructions, etc) if (msg.getType() == GarlicMessage.MESSAGE_TYPE) { + // in case we're looking for replies to a garlic message (cough load tests cough) + _context.inNetMessagePool().handleReplies(msg); if (_log.shouldLog(Log.DEBUG)) _log.debug("received garlic message in the tunnel, parse it out"); _receiver.receive((GarlicMessage)msg); @@ -149,6 +152,11 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec if (_log.shouldLog(Log.WARN)) _log.warn("Bad store attempt", iae); } + } else if (data instanceof DataMessage) { + // a data message targetting the local router is how we send load tests (real + // data messages target destinations) + _context.statManager().addRateData("tunnel.handleLoadClove", 1, 0); + _context.inNetMessagePool().add(data, null, null); } else { if ( (_client != null) && (data.getType() != DeliveryStatusMessage.MESSAGE_TYPE) ) { // drop it, since the data we receive shouldn't include other stuff, diff --git a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java index e2a7613b40bcac9e645e832fa5c41f266f8ebc70..b8c996eac77f79c332e6c3296016982f6668b9e4 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java @@ -15,9 +15,11 @@ class ClientPeerSelector extends TunnelPeerSelector { if (length < 0) return null; HashSet matches = new HashSet(length); - - if (shouldSelectExplicit(settings)) - return selectExplicit(ctx, settings, length); + + if (length > 0) { + if (shouldSelectExplicit(settings)) + return selectExplicit(ctx, settings, length); + } Set exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory()); ctx.profileOrganizer().selectFastPeers(length, exclude, matches); @@ -31,5 +33,4 @@ class ClientPeerSelector extends TunnelPeerSelector { rv.add(ctx.routerHash()); return rv; } - } diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java index 82331957d172011119a4d095dcdf593e25c41f7f..5e1d0a57f18c179f7b8ddd236f24b5887cf90d37 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java @@ -20,7 +20,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { return null; } - if (shouldSelectExplicit(settings)) { + if (false && shouldSelectExplicit(settings)) { List rv = selectExplicit(ctx, settings, length); if (l.shouldLog(Log.DEBUG)) l.debug("Explicit peers selected: " + rv); diff --git a/router/java/src/net/i2p/router/tunnel/pool/OnCreatedJob.java b/router/java/src/net/i2p/router/tunnel/pool/OnCreatedJob.java index 74eaee0d7c5f7bb4a69e7ce930d786ed7fc3b5c8..62d431730ff33391a5f321cfef09f8fc709343e0 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/OnCreatedJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/OnCreatedJob.java @@ -29,7 +29,7 @@ class OnCreatedJob extends JobImpl { getContext().tunnelDispatcher().joinOutbound(_cfg); } - _pool.getManager().buildComplete(); + _pool.getManager().buildComplete(_cfg); _pool.addTunnel(_cfg); TestJob testJob = (_cfg.getLength() > 1 ? new TestJob(getContext(), _cfg, _pool) : null); RebuildJob rebuildJob = new RebuildJob(getContext(), _cfg, _pool); diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java index 0a07dc3eaf887533a8431da2682d134d1f44f421..1373099d34c3c6cb6094912056b0b12eb86fe57f 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java @@ -1,6 +1,7 @@ package net.i2p.router.tunnel.pool; import java.util.*; +import net.i2p.I2PAppContext; import net.i2p.data.DataFormatException; import net.i2p.data.Hash; import net.i2p.router.Router; @@ -62,9 +63,12 @@ abstract class TunnelPeerSelector { } protected boolean shouldSelectExplicit(TunnelPoolSettings settings) { + if (settings.isExploratory()) return false; Properties opts = settings.getUnknownOptions(); if (opts != null) { String peers = opts.getProperty("explicitPeers"); + if (peers == null) + peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers"); if (peers != null) return true; } @@ -77,6 +81,9 @@ abstract class TunnelPeerSelector { if (opts != null) peers = opts.getProperty("explicitPeers"); + if (peers == null) + peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers"); + Log log = ctx.logManager().getLog(ClientPeerSelector.class); List rv = new ArrayList(); StringTokenizer tok = new StringTokenizer(peers, ","); @@ -90,8 +97,8 @@ abstract class TunnelPeerSelector { if (ctx.profileOrganizer().isSelectable(peer)) { rv.add(peer); } else { - if (log.shouldLog(Log.WARN)) - log.warn("Explicit peer is not selectable: " + peerStr); + if (log.shouldLog(Log.DEBUG)) + log.debug("Explicit peer is not selectable: " + peerStr); } } catch (DataFormatException dfe) { if (log.shouldLog(Log.ERROR)) @@ -99,19 +106,34 @@ abstract class TunnelPeerSelector { } } + int sz = rv.size(); Collections.shuffle(rv, ctx.random()); - while (rv.size() > length) rv.remove(0); + if (log.shouldLog(Log.INFO)) { + StringBuffer buf = new StringBuffer(); + if (settings.getDestinationNickname() != null) + buf.append("peers for ").append(settings.getDestinationNickname()); + else if (settings.getDestination() != null) + buf.append("peers for ").append(settings.getDestination().toBase64()); + else + buf.append("peers for exploratory "); + if (settings.isInbound()) + buf.append(" inbound"); + else + buf.append(" outbound"); + buf.append(" peers: ").append(rv); + buf.append(", out of ").append(sz).append(" (not including self)"); + log.info(buf.toString()); + } + if (settings.isInbound()) rv.add(0, ctx.routerHash()); else rv.add(ctx.routerHash()); - if (log.shouldLog(Log.INFO)) - log.info(toString() + ": Selecting peers explicitly: " + rv); return rv; } diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index a2a9de019573be3eea76fb2a4fd5fb020f6d6b27..126b10d83fc8432d66b570947327136e99c63ecc 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -17,11 +17,13 @@ import net.i2p.stat.RateStat; import net.i2p.router.ClientTunnelSettings; import net.i2p.router.HandlerJobBuilder; import net.i2p.router.JobImpl; +import net.i2p.router.LoadTestManager; import net.i2p.router.RouterContext; import net.i2p.router.TunnelInfo; import net.i2p.router.TunnelManagerFacade; import net.i2p.router.TunnelPoolSettings; import net.i2p.router.tunnel.HopConfig; +import net.i2p.router.tunnel.TunnelCreatorConfig; import net.i2p.util.Log; /** @@ -40,6 +42,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { private int _outstandingBuilds; /** max # of concurrent build requests */ private int _maxOutstandingBuilds; + private LoadTestManager _loadTestManager; private static final String PROP_MAX_OUTSTANDING_BUILDS = "router.tunnel.maxConcurrentBuilds"; private static final int DEFAULT_MAX_OUTSTANDING_BUILDS = 20; @@ -70,6 +73,8 @@ public class TunnelPoolManager implements TunnelManagerFacade { } } + _loadTestManager = new LoadTestManager(_context); + ctx.statManager().createRateStat("tunnel.testSuccessTime", "How long do successful tunnel tests take?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000l }); @@ -139,6 +144,10 @@ public class TunnelPoolManager implements TunnelManagerFacade { return info; } } + info = _inboundExploratory.getTunnel(id); + if (info != null) return info; + info = _outboundExploratory.getTunnel(id); + if (info != null) return info; return null; } @@ -332,6 +341,10 @@ public class TunnelPoolManager implements TunnelManagerFacade { return rv.booleanValue(); } + void buildComplete(TunnelCreatorConfig cfg) { + buildComplete(); + _loadTestManager.addTunnelTestCandidate(cfg); + } void buildComplete() { synchronized (this) { if (_outstandingBuilds > 0) @@ -339,6 +352,9 @@ public class TunnelPoolManager implements TunnelManagerFacade { } } + + private static final String PROP_LOAD_TEST = "router.loadTest"; + public void startup() { TunnelBuilder builder = new TunnelBuilder(); ExploratoryPeerSelector selector = new ExploratoryPeerSelector(); @@ -359,6 +375,10 @@ public class TunnelPoolManager implements TunnelManagerFacade { // try to build up longer tunnels _context.jobQueue().addJob(new BootstrapPool(_context, _inboundExploratory)); _context.jobQueue().addJob(new BootstrapPool(_context, _outboundExploratory)); + + if (Boolean.valueOf(_context.getProperty(PROP_LOAD_TEST, "true")).booleanValue()) { + _context.jobQueue().addJob(_loadTestManager.getTestJob()); + } } private class BootstrapPool extends JobImpl {