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)
This commit is contained in:
@ -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();
|
||||
|
@ -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" +
|
||||
|
@ -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);
|
||||
|
@ -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) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"));
|
||||
}
|
||||
int replies = handleReplies(messageBody);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user