merge of '03e8a3d066ce112bb4ddaa98c0387dfefde94a0e'

and '751ff97c62634ee13a8f8baf3d7947e373d5368a'
This commit is contained in:
dev
2010-05-23 17:05:15 +00:00
70 changed files with 860 additions and 544 deletions

View File

@@ -522,6 +522,11 @@ public class PeerCoordinator implements PeerListener
//Only request a piece we've requested before if there's no other choice.
if (piece == null) {
// AND if there are almost no wanted pieces left (real end game).
// If we do end game all the time, we generate lots of extra traffic
// when the seeder is super-slow and all the peers are "caught up"
if (wantedPieces.size() > 4)
return -1; // nothing to request and not in end game
// let's not all get on the same piece
Collections.shuffle(requested);
Iterator it2 = requested.iterator();

View File

@@ -734,7 +734,7 @@ public class Snark
//if (debug >= INFO && t != null)
// t.printStackTrace();
stopTorrent();
throw new RuntimeException("die bart die");
throw new RuntimeException(s + (t == null ? "" : ": " + t.getMessage()));
}
/**

View File

@@ -286,6 +286,40 @@ public class Storage
return needed == 0;
}
/**
* @param file absolute path (non-directory)
* @return number of bytes remaining; -1 if unknown file
* @since 0.7.14
*/
public long remaining(String file) {
long bytes = 0;
for (int i = 0; i < rafs.length; i++) {
File f = RAFfile[i];
if (f != null && f.getAbsolutePath().equals(file)) {
if (complete())
return 0;
int psz = metainfo.getPieceLength(0);
long start = bytes;
long end = start + lengths[i];
int pc = (int) (bytes / psz);
long rv = 0;
if (!bitfield.get(pc))
rv = psz - (bytes % psz);
for (int j = pc + 1; j * psz < end; j++) {
if (!bitfield.get(j)) {
if ((j+1)*psz < end)
rv += psz;
else
rv += end - (j * psz);
}
}
return rv;
}
bytes += lengths[i];
}
return -1;
}
/**
* The BitField that tells which pieces this storage contains.
* Do not change this since this is the current state of the storage.

View File

@@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
@@ -37,7 +38,10 @@ import org.klomp.snark.SnarkManager;
import org.klomp.snark.Storage;
import org.klomp.snark.TrackerClient;
import org.mortbay.http.HttpResponse;
import org.mortbay.jetty.servlet.Default;
import org.mortbay.util.Resource;
import org.mortbay.util.URI;
/**
* We extend Default instead of HTTPServlet so we can handle
@@ -105,13 +109,52 @@ public class I2PSnarkServlet extends Default {
super.destroy();
}
/**
* Some parts modified from:
* <pre>
// ========================================================================
// $Id: Default.java,v 1.51 2006/10/08 14:13:18 gregwilkins Exp $
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
* </pre>
*
*/
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this is the part after /i2psnark
String path = req.getServletPath();
// index.jsp doesn't work, it is grabbed by the war handler before here
if (!(path == null || path.equals("/") || path.equals("/index.jsp") || path.equals("/index.html"))) {
super.service(req, resp);
if (path.endsWith("/")) {
// bypass the horrid Resource.getListHTML()
String pathInfo = req.getPathInfo();
String pathInContext = URI.addPaths(path, pathInfo);
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
Resource resource = getResource(pathInContext);
if (resource == null || (!resource.exists()) || !resource.isDirectory()) {
resp.sendError(HttpResponse.__404_Not_Found);
} else {
String base = URI.addPaths(req.getRequestURI(), "/");
String listing = getListHTML(resource, base, true);
if (listing != null)
resp.getWriter().write(listing);
else // shouldn't happen
resp.sendError(HttpResponse.__404_Not_Found);
}
} else {
super.service(req, resp);
}
return;
}
@@ -305,7 +348,7 @@ public class I2PSnarkServlet extends Default {
}
} else if (newURL != null) {
if (newURL.startsWith("http://")) {
_manager.addMessage(_("Fetching {0}", newURL));
_manager.addMessage(_("Fetching {0}", urlify(newURL)));
I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add");
fetch.start();
} else {
@@ -643,7 +686,7 @@ public class I2PSnarkServlet extends Default {
out.write(statusString + "</td>\n\t");
out.write("<td align=\"left\" class=\"snarkTorrentName " + rowClass + "\">");
if (remaining == 0) {
if (remaining == 0 || snark.meta.getFiles() != null) {
out.write("<a href=\"" + snark.meta.getName());
if (snark.meta.getFiles() != null)
out.write("/");
@@ -655,7 +698,7 @@ public class I2PSnarkServlet extends Default {
out.write("\">");
}
out.write(filename);
if (remaining == 0)
if (remaining == 0 || snark.meta.getFiles() != null)
out.write("</a>");
// temporarily hardcoded for postman* and anonymity, requires bytemonsoon patch for lookup by info_hash
String announce = snark.meta.getAnnounce();
@@ -689,7 +732,7 @@ public class I2PSnarkServlet extends Default {
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded " + rowClass + "\">");
if (remaining > 0)
out.write(formatSize(total-remaining) + "/" + formatSize(total)); // 18MB/3GB
out.write(formatSize(total-remaining) + " / " + formatSize(total)); // 18MB/3GB
else
out.write(formatSize(total)); // 3GB
out.write("</td>\n\t");
@@ -966,14 +1009,14 @@ public class I2PSnarkServlet extends Default {
*/
out.write("<tr><td>");
out.write(_("Total uploader limit"));
out.write(": <td><input type=\"text\" name=\"upLimit\" value=\""
out.write(": <td><input type=\"text\" name=\"upLimit\" class=\"r\" value=\""
+ _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" > ");
out.write(_("peers"));
out.write("<br>\n");
out.write("<tr><td>");
out.write(_("Up bandwidth limit"));
out.write(": <td><input type=\"text\" name=\"upBW\" value=\""
out.write(": <td><input type=\"text\" name=\"upBW\" class=\"r\" value=\""
+ _manager.util().getMaxUpBW() + "\" size=\"3\" maxlength=\"3\" > KBps <i>(");
out.write(_("Half available bandwidth recommended."));
out.write(" <a href=\"/config.jsp\" target=\"blank\">");
@@ -1020,7 +1063,7 @@ public class I2PSnarkServlet extends Default {
out.write("<tr><td>");
out.write(_("I2CP port"));
out.write(": <td><input type=\"text\" name=\"i2cpPort\" value=\"" +
out.write(": <td><input type=\"text\" name=\"i2cpPort\" class=\"r\" value=\"" +
+ _manager.util().getI2CPPort() + "\" size=\"5\" maxlength=\"5\" > <br>\n");
StringBuilder opts = new StringBuilder(64);
@@ -1090,18 +1133,24 @@ public class I2PSnarkServlet extends Default {
}
// rounding makes us look faster :)
private String formatSize(long bytes) {
private static String formatSize(long bytes) {
if (bytes < 5*1024)
return bytes + "B";
return bytes + " B";
else if (bytes < 5*1024*1024)
return ((bytes + 512)/1024) + "KB";
return ((bytes + 512)/1024) + " KB";
else if (bytes < 10*1024*1024*1024l)
return ((bytes + 512*1024)/(1024*1024)) + "MB";
return ((bytes + 512*1024)/(1024*1024)) + " MB";
else
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + "GB";
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + " GB";
}
private static final String HEADER = "<link href=\"../themes/console/snark.css\" rel=\"stylesheet\" type=\"text/css\" >";
private static String urlify(String s) {
StringBuilder buf = new StringBuilder(256);
buf.append("<a href=\"").append(s).append("\">").append(s).append("</a>");
return buf.toString();
}
private static final String HEADER = "<link href=\"/themes/console/snark.css\" rel=\"stylesheet\" type=\"text/css\" >";
private static final String TABLE_HEADER = "<table border=\"0\" class=\"snarkTorrents\" width=\"100%\" cellpadding=\"0 10px\">\n" +
@@ -1112,6 +1161,169 @@ public class I2PSnarkServlet extends Default {
private static final String FOOTER = "</div></div></div></center></body></html>";
/**
* Modded heavily from the Jetty version in Resource.java,
* pass Resource as 1st param
* All the xxxResource constructors are package local so we can't extend them.
*
* <pre>
// ========================================================================
// $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
* </pre>
*
* Get the resource list as a HTML directory listing.
* @param r The Resource
* @param base The base URL
* @param parent True if the parent directory should be included
* @return String of HTML
*/
private String getListHTML(Resource r, String base, boolean parent)
throws IOException
{
if (!r.isDirectory())
return null;
String[] ls = r.list();
if (ls==null)
return null;
Arrays.sort(ls, Collator.getInstance());
StringBuilder buf=new StringBuilder(4096);
buf.append("<HTML><HEAD><TITLE>");
String title = URI.decodePath(base);
if (title.startsWith("/i2psnark/"))
title = title.substring("/i2psnark/".length());
// Get the snark associated with this directory
Snark snark = null;
try {
String torrentName;
int slash = title.indexOf('/');
if (slash > 0)
torrentName = title.substring(0, slash) + ".torrent";
else
torrentName = title + ".torrent";
File dataDir = _manager.getDataDir();
String torrentAbsPath = (new File(dataDir, torrentName)).getCanonicalPath();
snark = _manager.getTorrent(torrentAbsPath);
} catch (IOException ioe) {}
if (title.endsWith("/"))
title = title.substring(0, title.length() - 1);
title = _("Torrent") + ": " + title;
buf.append(title);
buf.append("</TITLE>").append(HEADER).append("</HEAD><BODY>\n<div class=\"snarknavbar\">");
buf.append(title);
if (parent)
{
buf.append("\n<br><A HREF=\"");
// corrupts utf-8
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
buf.append(URI.addPaths(base,"../"));
buf.append("\"><img border=\"0\" src=\"/themes/console/images/outbound.png\"> ")
.append(_("Up to higher level directory")).append("</A>\n");
}
buf.append("</div><div class=\"page\"><div class=\"mainsection\">" +
"<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
"<thead><tr><th>").append(_("File")).append("</th><th>").append(_("Size"))
.append("</th><th>").append(_("Status")).append("</th></tr></thead>");
//DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
// DateFormat.MEDIUM);
for (int i=0 ; i< ls.length ; i++)
{
String encoded=URI.encodePath(ls[i]);
// bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
// http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
// See resource.diff attachment
//Resource item = addPath(encoded);
Resource item = r.addPath(ls[i]);
String rowClass = (i % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
buf.append("<TR class=\"").append(rowClass).append("\"><TD class=\"snarkFileName ")
.append(rowClass).append("\">");
// Get completeness and status string
boolean complete = false;
String status = "";
long length = item.length();
if (item.isDirectory()) {
complete = true;
status = _("Directory");
} else {
if (snark == null) {
status = "Snark not found?";
} else {
try {
File f = item.getFile();
if (f != null) {
long remaining = snark.storage.remaining(f.getCanonicalPath());
if (remaining == 0) {
complete = true;
status = _("Complete");
} else if (remaining < 0) {
complete = true;
status = _("File not found in torrent?");
} else if (length <= 0) {
complete = true;
status = _("Complete");
} else {
status = (100 - (100 * remaining / length)) + "% " + _("complete") +
" (" + DataHelper.formatSize(remaining) + " bytes remaining)";
}
} else {
status = "Not a file?";
}
} catch (IOException ioe) {
status = "Not a file? " + ioe;
}
}
}
String path=URI.addPaths(base,encoded);
if (item.isDirectory() && !path.endsWith("/"))
path=URI.addPaths(path,"/");
if (complete) {
// thumbnail ?
String plc = path.toLowerCase();
if (plc.endsWith(".jpg") || plc.endsWith(".png") || plc.endsWith(".gif")) {
buf.append("<a href=\"").append(path).append("\"><img alt=\"\" border=\"0\" class=\"thumb\" src=\"")
.append(path).append("\"></a> ");
}
buf.append("<A HREF=\"");
buf.append(path);
buf.append("\">");
}
buf.append(ls[i]);
if (complete)
buf.append("</a>");
buf.append("</TD><TD ALIGN=right class=\"").append(rowClass).append("\">");
if (!item.isDirectory())
buf.append(DataHelper.formatSize(length)).append(' ').append(_("Bytes"));
buf.append("</TD><TD>");
//buf.append(dfmt.format(new Date(item.lastModified())));
buf.append(status);
buf.append("</TD></TR>\n");
}
buf.append("</TABLE>\n");
buf.append("</div></div></BODY></HTML>\n");
return buf.toString();
}
/** inner class, don't bother reindenting */
private static class FetchAndAdd implements Runnable {
private SnarkManager _manager;
@@ -1126,7 +1338,7 @@ private static class FetchAndAdd implements Runnable {
File file = _manager.util().get(_url, false, 3);
try {
if ( (file != null) && (file.exists()) && (file.length() > 0) ) {
_manager.addMessage(_("Torrent fetched from {0}", _url));
_manager.addMessage(_("Torrent fetched from {0}", urlify(_url)));
FileInputStream in = null;
try {
in = new FileInputStream(file);
@@ -1154,12 +1366,12 @@ private static class FetchAndAdd implements Runnable {
_manager.addTorrent(canonical);
}
} catch (IOException ioe) {
_manager.addMessage(_("Torrent at {0} was not valid", _url) + ": " + ioe.getMessage());
_manager.addMessage(_("Torrent at {0} was not valid", urlify(_url)) + ": " + ioe.getMessage());
} finally {
try { in.close(); } catch (IOException ioe) {}
}
} else {
_manager.addMessage(_("Torrent was not retrieved from {0}", _url));
_manager.addMessage(_("Torrent was not retrieved from {0}", urlify(_url)));
}
} finally {
if (file != null) file.delete();

View File

@@ -149,6 +149,10 @@ public class StatSummarizer implements Runnable {
return false;
}
/**
* This does the two-data bandwidth graph only.
* For all other graphs see SummaryRenderer
*/
public boolean renderRatePng(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
long end = _context.clock().now() - 60*1000;
if (width > GraphHelper.MAX_X)
@@ -166,22 +170,22 @@ public class StatSummarizer implements Runnable {
def.setTimePeriod(start/1000, 0);
def.setLowerLimit(0d);
def.setBaseValue(1024);
String title = "Bandwidth usage";
String title = _("Bandwidth usage");
if (!hideTitle)
def.setTitle(title);
String sendName = SummaryListener.createName(_context, "bw.sendRate.60000");
String recvName = SummaryListener.createName(_context, "bw.recvRate.60000");
def.datasource(sendName, sendName, sendName, "AVERAGE", "MEMORY");
def.datasource(recvName, recvName, recvName, "AVERAGE", "MEMORY");
def.area(sendName, Color.BLUE, "Outbound bytes/sec");
def.area(sendName, Color.BLUE, _("Outbound bytes/sec"));
//def.line(sendName, Color.BLUE, "Outbound bytes/sec", 3);
def.line(recvName, Color.RED, "Inbound bytes/sec@r", 3);
def.line(recvName, Color.RED, _("Inbound bytes/sec") + "@r", 3);
//def.area(recvName, Color.RED, "Inbound bytes/sec@r");
if (!hideLegend) {
def.gprint(sendName, "AVERAGE", "out average: @2@sbytes/sec");
def.gprint(sendName, "MAX", " max: @2@sbytes/sec@r");
def.gprint(recvName, "AVERAGE", "in average: @2@sbytes/sec");
def.gprint(recvName, "MAX", " max: @2@sbytes/sec@r");
def.gprint(sendName, "AVERAGE", _("out average") + ": @2@s" + _("bytes/sec"));
def.gprint(sendName, "MAX", ' ' + _("max") + ": @2@s" + _("bytes/sec") + "@r");
def.gprint(recvName, "AVERAGE", _("in average") + ": @2@s" + _("bytes/sec"));
def.gprint(recvName, "MAX", ' ' + _("max") + ": @2@s" + _("bytes/sec") + "@r");
}
if (!showCredit)
def.setShowSignature(false);
@@ -248,4 +252,12 @@ public class StatSummarizer implements Runnable {
}
return rv;
}
/** translate a string */
private String _(String s) {
// the RRD font doesn't have zh chars, at least on my system
if ("zh".equals(Messages.getLanguage(_context)))
return s;
return Messages.getString(s, _context);
}
}

View File

@@ -142,127 +142,3 @@ class SummaryListener implements RateSummaryListener {
@Override
public int hashCode() { return _rate.hashCode(); }
}
class SummaryRenderer {
private Log _log;
private SummaryListener _listener;
public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) {
_log = ctx.logManager().getLog(SummaryRenderer.class);
_listener = lsnr;
}
/**
* Render the stats as determined by the specified JRobin xml config,
* but note that this doesn't work on stock jvms, as it requires
* DOM level 3 load and store support. Perhaps we can bundle that, or
* specify who can get it from where, etc.
*
*/
public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
long end = ctx.clock().now() - 60*1000;
long start = end - 60*1000*SummaryListener.PERIODS;
try {
RrdGraphDefTemplate template = new RrdGraphDefTemplate(filename);
RrdGraphDef def = template.getRrdGraphDef();
def.setTimePeriod(start/1000, end/1000); // ignore the periods in the template
RrdGraph graph = new RrdGraph(def);
byte img[] = graph.getPNGBytes();
out.write(img);
} catch (RrdException re) {
//_log.error("Error rendering " + filename, re);
throw new IOException("Error plotting: " + re.getMessage());
} catch (IOException ioe) {
//_log.error("Error rendering " + filename, ioe);
throw ioe;
}
}
public void render(OutputStream out) throws IOException { render(out, -1, -1, false, false, false, false, -1, false); }
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
long end = _listener.now() - 60*1000;
if (periodCount <= 0) periodCount = SummaryListener.PERIODS;
if (periodCount > SummaryListener.PERIODS)
periodCount = SummaryListener.PERIODS;
long start = end - _listener.getRate().getPeriod()*periodCount;
//long begin = System.currentTimeMillis();
try {
RrdGraphDef def = new RrdGraphDef();
def.setTimePeriod(start/1000, 0);
def.setLowerLimit(0d);
String name = _listener.getRate().getRateStat().getName();
// heuristic to set K=1024
if ((name.startsWith("bw.") || name.indexOf("Size") >= 0 || name.indexOf("Bps") >= 0 || name.indexOf("memory") >= 0)
&& !showEvents)
def.setBaseValue(1024);
String title = name;
if (showEvents)
title = title + " events in ";
else
title = title + " averaged for ";
title = title + DataHelper.formatDuration(_listener.getRate().getPeriod());
if (!hideTitle)
def.setTitle(title);
String path = _listener.getData().getPath();
String dsNames[] = _listener.getData().getDsNames();
String plotName = null;
String descr = null;
if (showEvents) {
// include the average event count on the plot
plotName = dsNames[1];
descr = "Events per period";
} else {
// include the average value
plotName = dsNames[0];
descr = _listener.getRate().getRateStat().getDescription();
}
def.datasource(plotName, path, plotName, "AVERAGE", "MEMORY");
def.area(plotName, Color.BLUE, descr + "@r");
if (!hideLegend) {
def.gprint(plotName, "AVERAGE", "avg: @2@s");
def.gprint(plotName, "MAX", " max: @2@s");
def.gprint(plotName, "LAST", " now: @2@s@r");
}
if (!showCredit)
def.setShowSignature(false);
/*
// these four lines set up a graph plotting both values and events on the same chart
// (but with the same coordinates, so the values may look pretty skewed)
def.datasource(dsNames[0], path, dsNames[0], "AVERAGE", "MEMORY");
def.datasource(dsNames[1], path, dsNames[1], "AVERAGE", "MEMORY");
def.area(dsNames[0], Color.BLUE, _listener.getRate().getRateStat().getDescription());
def.line(dsNames[1], Color.RED, "Events per period");
*/
if (hideLegend)
def.setShowLegend(false);
if (hideGrid) {
def.setGridX(false);
def.setGridY(false);
}
//System.out.println("rendering: path=" + path + " dsNames[0]=" + dsNames[0] + " dsNames[1]=" + dsNames[1] + " lsnr.getName=" + _listener.getName());
def.setAntiAliasing(false);
//System.out.println("Rendering: \n" + def.exportXmlTemplate());
//System.out.println("*****************\nData: \n" + _listener.getData().dump());
RrdGraph graph = new RrdGraph(def);
//System.out.println("Graph created");
byte data[] = null;
if ( (width <= 0) || (height <= 0) )
data = graph.getPNGBytes();
else
data = graph.getPNGBytes(width, height);
//long timeToPlot = System.currentTimeMillis() - begin;
out.write(data);
//File t = File.createTempFile("jrobinData", ".xml");
//_listener.getData().dumpXml(new FileOutputStream(t));
//System.out.println("plotted: " + (data != null ? data.length : 0) + " bytes in " + timeToPlot
// ); // + ", data written to " + t.getAbsolutePath());
} catch (RrdException re) {
_log.error("Error rendering", re);
throw new IOException("Error plotting: " + re.getMessage());
} catch (IOException ioe) {
_log.error("Error rendering", ioe);
throw ioe;
} catch (OutOfMemoryError oom) {
_log.error("Error rendering", oom);
throw new IOException("Error plotting: " + oom.getMessage());
}
}
}

View File

@@ -0,0 +1,170 @@
package net.i2p.router.web;
import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.stat.RateSummaryListener;
import net.i2p.util.Log;
import org.jrobin.core.RrdBackendFactory;
import org.jrobin.core.RrdDb;
import org.jrobin.core.RrdDef;
import org.jrobin.core.RrdException;
import org.jrobin.core.RrdMemoryBackendFactory;
import org.jrobin.core.Sample;
import org.jrobin.graph.RrdGraph;
import org.jrobin.graph.RrdGraphDef;
import org.jrobin.graph.RrdGraphDefTemplate;
class SummaryRenderer {
private Log _log;
private SummaryListener _listener;
private I2PAppContext _context;
public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) {
_log = ctx.logManager().getLog(SummaryRenderer.class);
_listener = lsnr;
_context = ctx;
}
/**
* Render the stats as determined by the specified JRobin xml config,
* but note that this doesn't work on stock jvms, as it requires
* DOM level 3 load and store support. Perhaps we can bundle that, or
* specify who can get it from where, etc.
*
*/
public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
long end = ctx.clock().now() - 60*1000;
long start = end - 60*1000*SummaryListener.PERIODS;
try {
RrdGraphDefTemplate template = new RrdGraphDefTemplate(filename);
RrdGraphDef def = template.getRrdGraphDef();
def.setTimePeriod(start/1000, end/1000); // ignore the periods in the template
RrdGraph graph = new RrdGraph(def);
byte img[] = graph.getPNGBytes();
out.write(img);
} catch (RrdException re) {
//_log.error("Error rendering " + filename, re);
throw new IOException("Error plotting: " + re.getMessage());
} catch (IOException ioe) {
//_log.error("Error rendering " + filename, ioe);
throw ioe;
}
}
public void render(OutputStream out) throws IOException { render(out, -1, -1, false, false, false, false, -1, false); }
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
long end = _listener.now() - 60*1000;
if (periodCount <= 0) periodCount = SummaryListener.PERIODS;
if (periodCount > SummaryListener.PERIODS)
periodCount = SummaryListener.PERIODS;
long start = end - _listener.getRate().getPeriod()*periodCount;
//long begin = System.currentTimeMillis();
try {
RrdGraphDef def = new RrdGraphDef();
def.setTimePeriod(start/1000, 0);
def.setLowerLimit(0d);
String name = _listener.getRate().getRateStat().getName();
// heuristic to set K=1024
if ((name.startsWith("bw.") || name.indexOf("Size") >= 0 || name.indexOf("Bps") >= 0 || name.indexOf("memory") >= 0)
&& !showEvents)
def.setBaseValue(1024);
if (!hideTitle) {
String title;
String p = DataHelper.formatDuration(_listener.getRate().getPeriod());
if (showEvents)
title = name + ' ' + _("events in {0}", p);
else
title = name + ' ' + _("averaged for {0}", p);
def.setTitle(title);
}
String path = _listener.getData().getPath();
String dsNames[] = _listener.getData().getDsNames();
String plotName = null;
String descr = null;
if (showEvents) {
// include the average event count on the plot
plotName = dsNames[1];
descr = _("Events per period");
} else {
// include the average value
plotName = dsNames[0];
descr = _listener.getRate().getRateStat().getDescription();
}
def.datasource(plotName, path, plotName, "AVERAGE", "MEMORY");
def.area(plotName, Color.BLUE, descr + "@r");
if (!hideLegend) {
def.gprint(plotName, "AVERAGE", _("avg") + ": @2@s");
def.gprint(plotName, "MAX", ' ' + _("max") + ": @2@s");
def.gprint(plotName, "LAST", ' ' + _("now") + ": @2@s@r");
}
if (!showCredit)
def.setShowSignature(false);
/*
// these four lines set up a graph plotting both values and events on the same chart
// (but with the same coordinates, so the values may look pretty skewed)
def.datasource(dsNames[0], path, dsNames[0], "AVERAGE", "MEMORY");
def.datasource(dsNames[1], path, dsNames[1], "AVERAGE", "MEMORY");
def.area(dsNames[0], Color.BLUE, _listener.getRate().getRateStat().getDescription());
def.line(dsNames[1], Color.RED, "Events per period");
*/
if (hideLegend)
def.setShowLegend(false);
if (hideGrid) {
def.setGridX(false);
def.setGridY(false);
}
//System.out.println("rendering: path=" + path + " dsNames[0]=" + dsNames[0] + " dsNames[1]=" + dsNames[1] + " lsnr.getName=" + _listener.getName());
def.setAntiAliasing(false);
//System.out.println("Rendering: \n" + def.exportXmlTemplate());
//System.out.println("*****************\nData: \n" + _listener.getData().dump());
RrdGraph graph = new RrdGraph(def);
//System.out.println("Graph created");
byte data[] = null;
if ( (width <= 0) || (height <= 0) )
data = graph.getPNGBytes();
else
data = graph.getPNGBytes(width, height);
//long timeToPlot = System.currentTimeMillis() - begin;
out.write(data);
//File t = File.createTempFile("jrobinData", ".xml");
//_listener.getData().dumpXml(new FileOutputStream(t));
//System.out.println("plotted: " + (data != null ? data.length : 0) + " bytes in " + timeToPlot
// ); // + ", data written to " + t.getAbsolutePath());
} catch (RrdException re) {
_log.error("Error rendering", re);
throw new IOException("Error plotting: " + re.getMessage());
} catch (IOException ioe) {
_log.error("Error rendering", ioe);
throw ioe;
} catch (OutOfMemoryError oom) {
_log.error("Error rendering", oom);
throw new IOException("Error plotting: " + oom.getMessage());
}
}
/** translate a string */
private String _(String s) {
// the RRD font doesn't have zh chars, at least on my system
if ("zh".equals(Messages.getLanguage(_context)))
return s;
return Messages.getString(s, _context);
}
/**
* translate a string with a parameter
*/
private String _(String s, String o) {
// the RRD font doesn't have zh chars, at least on my system
if ("zh".equals(Messages.getLanguage(_context)))
return s.replace("{0}", o);
return Messages.getString(s, o, _context);
}
}

View File

@@ -150,3 +150,27 @@ input[type=submit]:hover {
}
input[type=reset] {
border: 1px outset #999;
background: #ddf;
color: #001;
margin: 5px;
font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
padding: 1px 2px;
text-decoration: none;
min-width: 110px;
border-radius: 4px;
-moz-border-radius: 4px;
-khtml-border-radius: 4px;
-moz-box-shadow: inset 0px 2px 8px 0px #fff;
color: #006;
opacity: 0.9;
}
input[type=reset]:hover {
background: #22a;
color: #fff;
border: 1px solid #f60;
opacity: 1.0;
-moz-box-shadow: inset 0px 0px 0px 1px #fff;
}

View File

@@ -35,7 +35,9 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
public class AddressbookBean
{
@@ -254,15 +256,40 @@ public class AddressbookBean
boolean changed = false;
int deleted = 0;
String name = null;
if (action.equals(_("Add"))) {
if (action.equals(_("Add")) || action.equals(_("Replace"))) {
if( addressbook != null && hostname != null && destination != null ) {
addressbook.put( hostname, destination );
changed = true;
message = _("Destination added.");
// clear search when adding
search = null;
String oldDest = (String) addressbook.get(hostname);
if (destination.equals(oldDest)) {
message = _("Host name {0} is already in addressbook, unchanged.", hostname);
} else if (oldDest != null && !action.equals(_("Replace"))) {
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", hostname);
} else {
boolean valid = true;
try {
Destination dest = new Destination(destination);
} catch (DataFormatException dfe) {
valid = false;
}
if (valid) {
addressbook.put( hostname, destination );
changed = true;
if (oldDest == null)
message = _("Destination added for {0}.", hostname);
else
message = _("Destination changed for {0}.", hostname);
// clear form
hostname = null;
destination = null;
} else {
message = _("Invalid Base 64 destination.");
}
}
} else {
message = _("Please enter a host name and destination");
}
} else if (action.equals(_("Delete"))) {
// clear search when adding
search = null;
} else if (action.equals(_("Delete Selected"))) {
Iterator it = deletionMarks.iterator();
while( it.hasNext() ) {
name = (String)it.next();
@@ -340,7 +367,7 @@ public class AddressbookBean
return destination;
}
public void setDestination(String destination) {
this.destination = DataHelper.stripHTML(destination); // XSS
this.destination = DataHelper.stripHTML(destination).trim(); // XSS
}
public String getHostname() {
return hostname;
@@ -352,7 +379,7 @@ public class AddressbookBean
deletionMarks.addLast( name );
}
public void setHostname(String hostname) {
this.hostname = DataHelper.stripHTML(hostname); // XSS
this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS
}
private int getBeginInt() {
return Math.max(0, Math.min(entries.length - 1, beginIndex));

View File

@@ -160,7 +160,9 @@
<c:if test="${book.master || book.router || book.published || book.private}">
<div id="buttons">
<p class="buttons"><input type="submit" name="action" value="<%=intl._("Delete")%>" >
<p class="buttons">
<input type="reset" value="<%=intl._("Cancel")%>" >
<input type="submit" name="action" value="<%=intl._("Delete Selected")%>" >
</p>
</div>
</c:if>
@@ -179,6 +181,7 @@
<b><%=intl._("Hostname")%>:</b> <input type="text" name="hostname" value="${book.hostname}" size="20">
<b><%=intl._("Destination")%>:</b> <textarea name="destination" rows="1" style="height: 3em;" cols="40" wrap="off" >${book.destination}</textarea><br/>
</p><p>
<input type="submit" name="action" value="<%=intl._("Replace")%>" >
<input type="submit" name="action" value="<%=intl._("Add")%>" >
</p>
</div>

View File

@@ -69,8 +69,8 @@
<textarea name="config" rows="10" cols="80">${cfg.config}</textarea>
</div>
<div id="buttons">
<input type="submit" name="action" value="<%=intl._("Save")%>" >
<input type="submit" name="action" value="<%=intl._("Reload")%>" >
<input type="submit" name="action" value="<%=intl._("Save")%>" >
</div>
</form>
<div id="help">

View File

@@ -69,8 +69,8 @@
<textarea name="content" rows="10" cols="80">${subs.content}</textarea>
</div>
<div id="buttons">
<input type="submit" name="action" value="<%=intl._("Save")%>" >
<input type="submit" name="action" value="<%=intl._("Reload")%>" >
<input type="submit" name="action" value="<%=intl._("Save")%>" >
</div>
</form>
<div id="help">

View File

@@ -54,7 +54,7 @@ cp *jbigi???* ../../lib/
echo 'Library copied to lib/'
cd ../..
I2P=~/i2p
I2P=~i2p
if [ ! -f $I2P/lib/i2p.jar ]
then
echo "I2P installation not found in $I2P - correct \$I2P definition in script to run speed test"

View File

@@ -9,8 +9,6 @@ public class Address extends DataStructureImpl {
private Destination _destination;
public Address() {
_hostname = null;
_destination = null;
}
public String getHostname() {

View File

@@ -22,11 +22,9 @@ public class ByteArray implements Serializable, Comparable {
private int _offset;
public ByteArray() {
this(null);
}
public ByteArray(byte[] data) {
_offset = 0;
_data = data;
_valid = (data != null ? data.length : 0);
}
@@ -92,4 +90,4 @@ public class ByteArray implements Serializable, Comparable {
public final String toBase64() {
return Base64.encode(_data, _offset, _valid);
}
}
}

View File

@@ -42,8 +42,6 @@ public class Certificate extends DataStructureImpl {
public final static int CERTIFICATE_TYPE_MULTIPLE = 4;
public Certificate() {
_type = 0;
_payload = null;
}
public Certificate(int type, byte[] payload) {

View File

@@ -27,10 +27,6 @@ public class Destination extends DataStructureImpl {
protected Hash __calculatedHash;
public Destination() {
setCertificate(null);
setSigningPublicKey(null);
setPublicKey(null);
__calculatedHash = null;
}
/**
@@ -38,7 +34,6 @@ public class Destination extends DataStructureImpl {
* @param s a Base64 representation of the destination, as (eg) is used in hosts.txt
*/
public Destination(String s) throws DataFormatException {
this();
fromBase64(s);
}

View File

@@ -12,13 +12,6 @@ package net.i2p.data;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.i2p.util.Log;
/**
* Defines the hash as defined by the I2P data structure spec.
@@ -27,20 +20,15 @@ import net.i2p.util.Log;
* @author jrandom
*/
public class Hash extends DataStructureImpl {
private final static Log _log = new Log(Hash.class);
private byte[] _data;
private volatile String _stringified;
private volatile String _base64ed;
private /* FIXME final FIXME */ Map _xorCache;
private int _cachedHashCode;
public final static int HASH_LENGTH = 32;
public final static Hash FAKE_HASH = new Hash(new byte[HASH_LENGTH]);
private static final int MAX_CACHED_XOR = 1024;
public Hash() {
setData(null);
}
public Hash(byte data[]) {
@@ -58,77 +46,6 @@ public class Hash extends DataStructureImpl {
_cachedHashCode = calcHashCode();
}
/**
* Prepare this hash's cache for xor values - very few hashes will need it,
* so we don't want to waste the memory, and lazy initialization would incur
* online overhead to verify the initialization.
*
*/
public void prepareCache() {
synchronized (this) {
if (_xorCache == null)
_xorCache = new HashMap(MAX_CACHED_XOR);
}
}
/**
* Calculate the xor with the current object and the specified hash,
* caching values where possible. Currently this keeps up to MAX_CACHED_XOR
* (1024) entries, and uses an essentially random ejection policy. Later
* perhaps go for an LRU or FIFO?
*
* @throws IllegalStateException if you try to use the cache without first
* preparing this object's cache via .prepareCache()
*/
public byte[] cachedXor(Hash key) throws IllegalStateException {
if (_xorCache == null)
throw new IllegalStateException("To use the cache, you must first prepare it");
byte[] distance = (byte[])_xorCache.get(key);
if (distance == null) {
// not cached, lets cache it
int cached = 0;
synchronized (_xorCache) {
int toRemove = _xorCache.size() + 1 - MAX_CACHED_XOR;
if (toRemove > 0) {
Set keys = new HashSet(toRemove);
// this removes essentially random keys - we dont maintain any sort
// of LRU or age. perhaps we should?
int removed = 0;
for (Iterator iter = _xorCache.keySet().iterator(); iter.hasNext() && removed < toRemove; removed++)
keys.add(iter.next());
for (Iterator iter = keys.iterator(); iter.hasNext(); )
_xorCache.remove(iter.next());
}
distance = DataHelper.xor(key.getData(), getData());
_xorCache.put(key, (Object) distance);
cached = _xorCache.size();
}
if (_log.shouldLog(Log.DEBUG)) {
// explicit buffer, since the compiler can't guess how long it'll be
StringBuilder buf = new StringBuilder(128);
buf.append("miss [").append(cached).append("] from ");
buf.append(DataHelper.toHexString(getData())).append(" to ");
buf.append(DataHelper.toHexString(key.getData()));
_log.debug(buf.toString(), new Exception());
}
} else {
if (_log.shouldLog(Log.DEBUG)) {
// explicit buffer, since the compiler can't guess how long it'll be
StringBuilder buf = new StringBuilder(128);
buf.append("hit from ");
buf.append(DataHelper.toHexString(getData())).append(" to ");
buf.append(DataHelper.toHexString(key.getData()));
_log.debug(buf.toString());
}
}
return distance;
}
public void clearXorCache() {
_xorCache = null;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[HASH_LENGTH];
_stringified = null;
@@ -189,98 +106,4 @@ public class Hash extends DataStructureImpl {
}
return _base64ed;
}
/********
public static void main(String args[]) {
testFill();
testOverflow();
testFillCheck();
}
private static void testFill() {
Hash local = new Hash(new byte[HASH_LENGTH]); // all zeroes
local.prepareCache();
for (int i = 0; i < MAX_CACHED_XOR; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
local.cachedXor(cur);
if (local._xorCache.size() != i+1) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
}
_log.debug("Fill test passed");
}
private static void testOverflow() {
Hash local = new Hash(new byte[HASH_LENGTH]); // all zeroes
local.prepareCache();
for (int i = 0; i < MAX_CACHED_XOR*2; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
local.cachedXor(cur);
if (i < MAX_CACHED_XOR) {
if (local._xorCache.size() != i+1) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
} else {
if (local._xorCache.size() > MAX_CACHED_XOR) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
}
}
_log.debug("overflow test passed");
}
private static void testFillCheck() {
Set hashes = new HashSet();
Hash local = new Hash(new byte[HASH_LENGTH]); // all zeroes
local.prepareCache();
// fill 'er up
for (int i = 0; i < MAX_CACHED_XOR; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
hashes.add(cur);
local.cachedXor(cur);
if (local._xorCache.size() != i+1) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
}
// now lets recheck using those same hash objects
// and see if they're cached
for (Iterator iter = hashes.iterator(); iter.hasNext(); ) {
Hash cur = (Hash)iter.next();
if (!local._xorCache.containsKey(cur)) {
_log.error("checking the cache, we dont have "
+ DataHelper.toHexString(cur.getData()));
return;
}
}
// now lets recheck with new objects but the same values
// and see if they'return cached
for (int i = 0; i < MAX_CACHED_XOR; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
if (!local._xorCache.containsKey(cur)) {
_log.error("checking the cache, we do NOT have "
+ DataHelper.toHexString(cur.getData()));
return;
}
}
_log.debug("Fill check test passed");
}
*********/
}

View File

@@ -30,11 +30,6 @@ public class Lease extends DataStructureImpl {
private int _numFailure;
public Lease() {
setGateway(null);
setTunnelId(null);
setEndDate(null);
setNumSuccess(0);
setNumFailure(0);
}
/** Retrieve the router at which the destination can be contacted

View File

@@ -78,18 +78,8 @@ public class LeaseSet extends DataStructureImpl {
public final static int MAX_LEASES = 6;
public LeaseSet() {
setDestination(null);
setEncryptionKey(null);
setSigningKey(null);
setSignature(null);
setRoutingKey(null);
_leases = new ArrayList(MAX_LEASES);
_routingKeyGenMod = null;
_receivedAsPublished = false;
_firstExpiration = Long.MAX_VALUE;
_lastExpiration = 0;
_decrypted = false;
_checked = false;
}
public Destination getDestination() {

View File

@@ -27,8 +27,6 @@ public class Payload extends DataStructureImpl {
private byte[] _unencryptedData;
public Payload() {
setUnencryptedData(null);
setEncryptedData(null);
}
/**

View File

@@ -28,8 +28,8 @@ public class PrivateKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 256;
public PrivateKey() {
setData(null);
}
public PrivateKey(byte data[]) { setData(data); }
/** constructs from base64
@@ -37,7 +37,6 @@ public class PrivateKey extends DataStructureImpl {
* on a prior instance of PrivateKey
*/
public PrivateKey(String base64Data) throws DataFormatException {
this();
fromBase64(base64Data);
}

View File

@@ -26,8 +26,8 @@ public class PublicKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 256;
public PublicKey() {
setData(null);
}
public PublicKey(byte data[]) {
if ( (data == null) || (data.length != KEYSIZE_BYTES) )
throw new IllegalArgumentException("Data must be specified, and the correct size");
@@ -39,7 +39,6 @@ public class PublicKey extends DataStructureImpl {
* on a prior instance of PublicKey
*/
public PublicKey(String base64Data) throws DataFormatException {
this();
fromBase64(base64Data);
}

View File

@@ -29,9 +29,6 @@ public class RouterAddress extends DataStructureImpl {
public RouterAddress() {
setCost(-1);
setExpiration(null);
setTransportStyle(null);
setOptions(null);
}
/**

View File

@@ -31,10 +31,6 @@ public class RouterIdentity extends DataStructureImpl {
private Hash __calculatedHash;
public RouterIdentity() {
setCertificate(null);
setSigningPublicKey(null);
setPublicKey(null);
__calculatedHash = null;
}
public Certificate getCertificate() {

View File

@@ -60,18 +60,9 @@ public class RouterInfo extends DataStructureImpl {
public static final String BW_CAPABILITY_CHARS = "KLMNO";
public RouterInfo() {
setIdentity(null);
setPublished(0);
_addresses = new HashSet(2);
_peers = new HashSet(0);
_options = new OrderedProperties();
setSignature(null);
_validated = false;
_isValid = false;
_currentRoutingKey = null;
_stringified = null;
_byteified = null;
_hashCodeInitialized = false;
}
public RouterInfo(RouterInfo old) {

View File

@@ -27,8 +27,8 @@ public class SessionKey extends DataStructureImpl {
public static final SessionKey INVALID_KEY = new SessionKey(new byte[KEYSIZE_BYTES]);
public SessionKey() {
this(null);
}
public SessionKey(byte data[]) {
setData(data);
}

View File

@@ -30,7 +30,8 @@ public class Signature extends DataStructureImpl {
FAKE_SIGNATURE[i] = 0x00;
}
public Signature() { this(null); }
public Signature() {}
public Signature(byte data[]) { setData(data); }
public byte[] getData() {

View File

@@ -28,7 +28,8 @@ public class SigningPrivateKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 20;
public SigningPrivateKey() { this((byte[])null); }
public SigningPrivateKey() {}
public SigningPrivateKey(byte data[]) { setData(data); }
/** constructs from base64
@@ -36,7 +37,6 @@ public class SigningPrivateKey extends DataStructureImpl {
* on a prior instance of SigningPrivateKey
*/
public SigningPrivateKey(String base64Data) throws DataFormatException {
this();
fromBase64(base64Data);
}

View File

@@ -26,7 +26,8 @@ public class SigningPublicKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 128;
public SigningPublicKey() { this((byte[])null); }
public SigningPublicKey() {}
public SigningPublicKey(byte data[]) { setData(data); }
/** constructs from base64
@@ -34,7 +35,6 @@ public class SigningPublicKey extends DataStructureImpl {
* on a prior instance of SigningPublicKey
*/
public SigningPublicKey(String base64Data) throws DataFormatException {
this();
fromBase64(base64Data);
}

View File

@@ -27,7 +27,6 @@ public class AbuseReason extends DataStructureImpl {
private String _reason;
public AbuseReason() {
setReason(null);
}
public String getReason() {

View File

@@ -33,10 +33,6 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
private PrivateKey _privateKey;
public CreateLeaseSetMessage() {
setSessionId(null);
setLeaseSet(null);
setSigningPrivateKey(null);
setPrivateKey(null);
}
public SessionId getSessionId() {

View File

@@ -27,7 +27,6 @@ public class DestroySessionMessage extends I2CPMessageImpl {
private SessionId _sessionId;
public DestroySessionMessage() {
setSessionId(null);
}
public SessionId getSessionId() {

View File

@@ -27,7 +27,6 @@ public class DisconnectMessage extends I2CPMessageImpl {
private String _reason;
public DisconnectMessage() {
setReason(null);
}
public String getReason() {

View File

@@ -31,7 +31,6 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
public MessagePayloadMessage() {
setSessionId(-1);
setMessageId(-1);
setPayload(null);
}
public long getSessionId() {

View File

@@ -28,8 +28,6 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl {
private SessionConfig _sessionConfig;
public ReconfigureSessionMessage() {
_sessionId = null;
_sessionConfig = null;
}
public SessionId getSessionId() {

View File

@@ -30,10 +30,6 @@ public class ReportAbuseMessage extends I2CPMessageImpl {
private MessageId _messageId;
public ReportAbuseMessage() {
setSessionId(null);
setSeverity(null);
setReason(null);
setMessageId(null);
}
public SessionId getSessionId() {

View File

@@ -34,9 +34,7 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
private Date _end;
public RequestLeaseSetMessage() {
setSessionId(null);
_endpoints = new ArrayList();
setEndDate(null);
}
public SessionId getSessionId() {

View File

@@ -34,7 +34,6 @@ public class SendMessageExpiresMessage extends SendMessageMessage {
public SendMessageExpiresMessage() {
super();
setExpiration(null);
}
public Date getExpiration() {

View File

@@ -32,10 +32,6 @@ public class SendMessageMessage extends I2CPMessageImpl {
private long _nonce;
public SendMessageMessage() {
setSessionId(null);
setDestination(null);
setPayload(null);
setNonce(0);
}
public SessionId getSessionId() {

View File

@@ -51,9 +51,7 @@ public class SessionConfig extends DataStructureImpl {
}
public SessionConfig(Destination dest) {
_destination = dest;
_signature = null;
_creationDate = new Date(Clock.getInstance().now());
_options = null;
}
/**

View File

@@ -33,7 +33,6 @@ public class SessionStatusMessage extends I2CPMessageImpl {
public final static int STATUS_INVALID = 3;
public SessionStatusMessage() {
setSessionId(null);
setStatus(STATUS_INVALID);
}

View File

@@ -15,9 +15,6 @@ public class Frequency {
public Frequency(long period) {
setPeriod(period);
setLastEvent(0);
setAverageInterval(0);
setMinAverageInterval(0);
}
/** how long is this frequency averaged over? */

View File

@@ -120,18 +120,6 @@ public class Rate {
*/
public Rate(long period) throws IllegalArgumentException {
if (period <= 0) throw new IllegalArgumentException("The period must be strictly positive");
_currentTotalValue = 0.0d;
_currentEventCount = 0;
_currentTotalEventTime = 0;
_lastTotalValue = 0.0d;
_lastEventCount = 0;
_lastTotalEventTime = 0;
_extremeTotalValue = 0.0d;
_extremeEventCount = 0;
_extremeTotalEventTime = 0;
_lifetimeTotalValue = 0.0d;
_lifetimeEventCount = 0;
_lifetimeTotalEventTime = 0;
_creationDate = now();
_lastCoalesceDate = _creationDate;

View File

@@ -15,7 +15,7 @@ import net.i2p.data.ByteArray;
*
* Heap size control - survey of usage (April 2010) :
*
* </pre>
* <pre>
Size Max MaxMem From
16 16 256 CryptixAESEngine

View File

@@ -625,4 +625,12 @@ public class LogManager {
shutdown();
}
}
/**
* Convenience method for LogRecordFormatter
* @since 0.7.14
*/
I2PAppContext getContext() {
return _context;
}
}

View File

@@ -14,6 +14,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import net.i2p.I2PAppContext;
/**
* Render a log record according to the log manager's settings
*
@@ -44,7 +46,7 @@ class LogRecordFormatter {
buf.append(getThread(rec));
break;
case LogManager.PRIORITY:
buf.append(getPriority(rec));
buf.append(getPriority(rec, manager.getContext()));
break;
case LogManager.MESSAGE:
buf.append(getWhat(rec));
@@ -78,10 +80,23 @@ class LogRecordFormatter {
return manager.getDateFormat().format(new Date(logRecord.getDate()));
}
/** don't translate */
private static String getPriority(LogRecord rec) {
return toString(Log.toLevelString(rec.getPriority()), MAX_PRIORITY_LENGTH);
}
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
/** translate @since 0.7.14 */
private static String getPriority(LogRecord rec, I2PAppContext ctx) {
int len;
if (Translate.getLanguage(ctx).equals("de"))
len = 8; // KRITISCH
else
len = MAX_PRIORITY_LENGTH;
return toString(Translate.getString(Log.toLevelString(rec.getPriority()), ctx, BUNDLE_NAME), len);
}
private static String getWhat(LogRecord rec) {
return rec.getMessage();
}
@@ -92,6 +107,7 @@ class LogRecordFormatter {
return toString(src, MAX_WHERE_LENGTH);
}
/** truncates or pads to the specified size */
private static String toString(String str, int size) {
StringBuilder buf = new StringBuilder();
if (str == null) str = "";
@@ -101,4 +117,4 @@ class LogRecordFormatter {
buf.append(' ');
return buf.toString();
}
}
}

View File

@@ -1,3 +1,23 @@
2010-05-21 zzz
* i2psnark:
- Spiff up dir listings
- Urlify some messages
- Only go into end game at the end
2010-05-19 zzz
* Data: Remove lots of unnecessary initializers
* susidns: More validataion when adding entry
2010-05-15 zzz
* Console:
- Tag text in graphs
- Move SummaryRenderer to its own file
* Eepsite: Set no-cache in redirecting page
* Hash: Move caching XOR methods only used by KBucket
into netdb
* i2psnark: CSS tweaks
* Log: Translate priority
2010-05-13 zzz
* netdb.jsp debug tweaks
* Plugins: Try to prevent ZipErrors after upgrade

View File

@@ -7,8 +7,10 @@
#
-->
<head>
<!-- remove the following line to stop redirecting to the help page -->
<!-- remove the following three lines to stop redirecting to the help page -->
<meta http-equiv="refresh" content="1;url=/help/" />
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<title>I2P Anonymous Webserver</title>
</head>
<body>

View File

@@ -108,6 +108,15 @@ td {
font-size: 8pt;
}
.snarkFileName {
min-width: 25em;
}
.thumb {
max-height: 64px;
max-width: 96px;
}
.snarkNewTorrent {
font-size: 9pt;
}
@@ -185,13 +194,33 @@ input {
font-size: 9pt;
font-weight: bold;
text-align: left;
padding: 2px;
padding: 2px 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: 1px;
}
input.r {
text-align: right;
}
select {
background: #ffe;
color: #310;
font: 9pt "Lucida Sans Unicode","Bitstream Vera Sans",Verdana,Tahoma,Helvetica,sans-serif;
font-weight: bold;
padding: 2px 2px 2px 3px;
-moz-border-radius: 4px;
border-radius: 4px;
}
textarea {
background: #ffe;
color: #310;
font-weight: bold;
padding: 1px 4px 0px;
-moz-border-radius: 4px;
border-radius: 4px;
}
img {

View File

@@ -24,7 +24,6 @@ public class DataMessage extends I2NPMessageImpl {
public DataMessage(I2PAppContext context) {
super(context);
_data = null;
}
public byte[] getData() {

View File

@@ -34,9 +34,7 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
// do this in netdb if we need it
//_context.statManager().createRateStat("netDb.searchReplyMessageSend", "How many search reply messages we send", "NetworkDatabase", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
//_context.statManager().createRateStat("netDb.searchReplyMessageReceive", "How many search reply messages we receive", "NetworkDatabase", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
setSearchKey(null);
_peerHashes = new ArrayList(3);
setFromHash(null);
}
/**

View File

@@ -43,12 +43,6 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
public DatabaseStoreMessage(I2PAppContext context) {
super(context);
setValueType(-1);
setKey(null);
setLeaseSet(null);
setRouterInfo(null);
setReplyToken(0);
setReplyTunnel(null);
setReplyGateway(null);
}
/**

View File

@@ -51,14 +51,7 @@ public class DeliveryInstructions extends DataStructureImpl {
private final static long FLAG_DELAY = 16;
public DeliveryInstructions() {
setEncrypted(false);
setEncryptionKey(null);
setDeliveryMode(-1);
setDestination(null);
setRouter(null);
setTunnelId(null);
setDelayRequested(false);
setDelaySeconds(0);
}
public boolean getEncrypted() { return _encrypted; }

View File

@@ -40,11 +40,7 @@ public class GarlicClove extends DataStructureImpl {
_context = context;
_log = context.logManager().getLog(GarlicClove.class);
_handler = new I2NPMessageHandler(context);
setInstructions(null);
setData(null);
setCloveId(-1);
setExpiration(null);
setCertificate(null);
}
public DeliveryInstructions getInstructions() { return _instructions; }

View File

@@ -26,7 +26,6 @@ public class GarlicMessage extends I2NPMessageImpl {
public GarlicMessage(I2PAppContext context) {
super(context);
setData(null);
}
public byte[] getData() {

View File

@@ -33,7 +33,6 @@ public class I2NPMessageHandler {
public I2NPMessageHandler(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(I2NPMessageHandler.class);
_messageBuffer = null;
_lastSize = -1;
}

View File

@@ -54,8 +54,6 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
_log = context.logManager().getLog(I2NPMessageImpl.class);
_expiration = _context.clock().now() + DEFAULT_EXPIRATION_MS;
_uniqueId = _context.random().nextLong(MAX_ID_VALUE);
_written = false;
_read = false;
//_context.statManager().createRateStat("i2np.writeTime", "How long it takes to write an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
//_context.statManager().createRateStat("i2np.readTime", "How long it takes to read an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
}

View File

@@ -24,8 +24,6 @@ public abstract class JobImpl implements Job {
_context = context;
_timing = new JobTiming(context);
_id = ++_idSrc;
_addedBy = null;
_madeReadyOn = 0;
}
public long getJobId() { return _id; }

View File

@@ -15,11 +15,8 @@ class JobStats {
public JobStats(String name) {
_job = name;
_numRuns = 0;
_totalTime = 0;
_maxTime = -1;
_minTime = -1;
_totalPendingTime = 0;
_maxPendingTime = -1;
_minPendingTime = -1;
}

View File

@@ -23,8 +23,6 @@ public class JobTiming implements Clock.ClockUpdateListener {
public JobTiming(RouterContext context) {
_context = context;
_start = context.clock().now();
_actualStart = 0;
_actualEnd = 0;
//context.clock().addUpdateListener(this);
}

View File

@@ -66,18 +66,8 @@ public class OutNetMessage {
public OutNetMessage(RouterContext context) {
_context = context;
_log = context.logManager().getLog(OutNetMessage.class);
setTarget(null);
_message = null;
_messageSize = 0;
setPriority(-1);
setExpiration(-1);
setOnSendJob(null);
setOnFailedSendJob(null);
setOnReplyJob(null);
setOnFailedReplyJob(null);
setReplySelector(null);
_failedTransports = null;
_sendBegin = 0;
//_createdBy = new Exception("Created by");
_created = context.clock().now();
timestamp("Created");

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 6;
public final static long BUILD = 9;
/** for example "-test" */
public final static String EXTRA = "";

View File

@@ -60,12 +60,7 @@ public class TunnelPoolSettings {
_duration = DEFAULT_DURATION;
_length = DEFAULT_LENGTH;
_lengthVariance = DEFAULT_LENGTH_VARIANCE;
_lengthOverride = 0;
_allowZeroHop = DEFAULT_ALLOW_ZERO_HOP;
_isInbound = false;
_isExploratory = false;
_destination = null;
_destinationNickname = null;
_IPRestriction = DEFAULT_IP_RESTRICTION;
_unknownOptions = new Properties();
_randomKey = generateRandomKey();

View File

@@ -1,47 +0,0 @@
package net.i2p.router;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
/**
* Set of criteria for finding a tunnel from the Tunnel Manager
*
*/
public class TunnelSelectionCriteria {
public final static int MAX_PRIORITY = 100;
public final static int MIN_PRIORITY = 0;
private int _latencyPriority;
private int _anonymityPriority;
private int _reliabilityPriority;
private int _maxNeeded;
private int _minNeeded;
public TunnelSelectionCriteria() {
setLatencyPriority(0);
setAnonymityPriority(0);
setReliabilityPriority(0);
setMinimumTunnelsRequired(0);
setMaximumTunnelsRequired(0);
}
/** priority of the latency for the tunnel */
public int getLatencyPriority() { return _latencyPriority; }
public void setLatencyPriority(int latencyPriority) { _latencyPriority = latencyPriority; }
/** priority of the anonymity for the tunnel */
public int getAnonymityPriority() { return _anonymityPriority; }
public void setAnonymityPriority(int anonPriority) { _anonymityPriority = anonPriority; }
/** priority of the reliability for the tunnel */
public int getReliabilityPriority() { return _reliabilityPriority; }
public void setReliabilityPriority(int reliabilityPriority) { _reliabilityPriority = reliabilityPriority; }
/** max # of tunnels to return */
public int getMaximumTunnelsRequired() { return _maxNeeded; }
public void setMaximumTunnelsRequired(int maxNeeded) { _maxNeeded = maxNeeded; }
/** minimum # of tunnels to return */
public int getMinimumTunnelsRequired() { return _minNeeded; }
public void setMinimumTunnelsRequired(int minNeeded) { _minNeeded = minNeeded; }
}

View File

@@ -76,5 +76,5 @@ interface KBucket {
*/
public Hash generateRandomKey();
public Hash getLocal();
public LocalHash getLocal();
}

View File

@@ -39,7 +39,7 @@ class KBucketImpl implements KBucket {
*/
private final Set<Hash> _entries;
/** we center the kbucket set on the given hash, and derive distances from this */
private Hash _local;
private LocalHash _local;
/** include if any bits equal or higher to this bit (in big endian order) */
private int _begin;
/** include if no bits higher than this bit (inclusive) are set */
@@ -49,7 +49,7 @@ class KBucketImpl implements KBucket {
private static final int SHUFFLE_DELAY = 10*60*1000;
private I2PAppContext _context;
public KBucketImpl(I2PAppContext context, Hash local) {
public KBucketImpl(I2PAppContext context, LocalHash local) {
_context = context;
_log = context.logManager().getLog(KBucketImpl.class);
_entries = new ConcurrentHashSet(2); //all but the last 1 or 2 buckets will be empty
@@ -57,6 +57,11 @@ class KBucketImpl implements KBucket {
setLocal(local);
}
/** for testing - use above constructor for production to get common caching */
public KBucketImpl(I2PAppContext context, Hash local) {
this(context, new LocalHash(local));
}
public int getRangeBegin() { return _begin; }
public int getRangeEnd() { return _end; }
public void setRange(int lowOrderBitLimit, int highOrderBitLimit) {
@@ -67,8 +72,8 @@ class KBucketImpl implements KBucket {
return _entries.size();
}
public Hash getLocal() { return _local; }
private void setLocal(Hash local) {
public LocalHash getLocal() { return _local; }
private void setLocal(LocalHash local) {
_local = local;
// we want to make sure we've got the cache in place before calling cachedXor
_local.prepareCache();
@@ -378,7 +383,7 @@ class KBucketImpl implements KBucket {
int low = 1;
int high = 3;
Log log = I2PAppContext.getGlobalContext().logManager().getLog(KBucketImpl.class);
Hash local = Hash.FAKE_HASH;
LocalHash local = new LocalHash(Hash.FAKE_HASH);
local.prepareCache();
KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), local);
bucket.setRange(low, high);
@@ -415,7 +420,7 @@ class KBucketImpl implements KBucket {
int high = 200;
byte hash[] = new byte[Hash.HASH_LENGTH];
RandomSource.getInstance().nextBytes(hash);
Hash local = new Hash(hash);
LocalHash local = new LocalHash(hash);
local.prepareCache();
KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), local);
bucket.setRange(low, high);

View File

@@ -27,7 +27,7 @@ import net.i2p.util.Log;
class KBucketSet {
private Log _log;
private I2PAppContext _context;
private Hash _us;
private LocalHash _us;
private KBucket _buckets[];
private volatile int _size;
@@ -38,7 +38,7 @@ class KBucketSet {
public final static int BUCKET_SIZE = 500; // # values at which we start periodic trimming (500 ~= 250Kb)
public KBucketSet(I2PAppContext context, Hash us) {
_us = us;
_us = new LocalHash(us);
_context = context;
_log = context.logManager().getLog(KBucketSet.class);
createBuckets();

View File

@@ -0,0 +1,208 @@
package net.i2p.router.networkdb.kademlia;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.util.Log;
/**
* Pull the caching used only by KBucketImpl out of Hash and put it here.
*
* @since 0.7.14
* @author jrandom
* @author moved from Hash.java by zzz
*/
class LocalHash extends Hash {
private final static Log _log = new Log(LocalHash.class);
private /* FIXME final FIXME */ Map _xorCache;
private static final int MAX_CACHED_XOR = 1024;
public LocalHash(Hash h) {
super(h.getData());
}
public LocalHash(byte[] b) {
super(b);
}
/**
* Prepare this hash's cache for xor values - very few hashes will need it,
* so we don't want to waste the memory, and lazy initialization would incur
* online overhead to verify the initialization.
*
*/
public void prepareCache() {
synchronized (this) {
if (_xorCache == null)
_xorCache = new HashMap(MAX_CACHED_XOR);
}
}
/**
* Calculate the xor with the current object and the specified hash,
* caching values where possible. Currently this keeps up to MAX_CACHED_XOR
* (1024) entries, and uses an essentially random ejection policy. Later
* perhaps go for an LRU or FIFO?
*
* @throws IllegalStateException if you try to use the cache without first
* preparing this object's cache via .prepareCache()
*/
public byte[] cachedXor(Hash key) throws IllegalStateException {
if (_xorCache == null)
throw new IllegalStateException("To use the cache, you must first prepare it");
byte[] distance = (byte[])_xorCache.get(key);
if (distance == null) {
// not cached, lets cache it
int cached = 0;
synchronized (_xorCache) {
int toRemove = _xorCache.size() + 1 - MAX_CACHED_XOR;
if (toRemove > 0) {
Set keys = new HashSet(toRemove);
// this removes essentially random keys - we dont maintain any sort
// of LRU or age. perhaps we should?
int removed = 0;
for (Iterator iter = _xorCache.keySet().iterator(); iter.hasNext() && removed < toRemove; removed++)
keys.add(iter.next());
for (Iterator iter = keys.iterator(); iter.hasNext(); )
_xorCache.remove(iter.next());
}
distance = DataHelper.xor(key.getData(), getData());
_xorCache.put(key, (Object) distance);
cached = _xorCache.size();
}
if (_log.shouldLog(Log.DEBUG)) {
// explicit buffer, since the compiler can't guess how long it'll be
StringBuilder buf = new StringBuilder(128);
buf.append("miss [").append(cached).append("] from ");
buf.append(DataHelper.toHexString(getData())).append(" to ");
buf.append(DataHelper.toHexString(key.getData()));
_log.debug(buf.toString(), new Exception());
}
} else {
if (_log.shouldLog(Log.DEBUG)) {
// explicit buffer, since the compiler can't guess how long it'll be
StringBuilder buf = new StringBuilder(128);
buf.append("hit from ");
buf.append(DataHelper.toHexString(getData())).append(" to ");
buf.append(DataHelper.toHexString(key.getData()));
_log.debug(buf.toString());
}
}
return distance;
}
/** @deprecated unused */
public void clearXorCache() {
_xorCache = null;
}
/********
public static void main(String args[]) {
testFill();
testOverflow();
testFillCheck();
}
private static void testFill() {
Hash local = new Hash(new byte[HASH_LENGTH]); // all zeroes
local.prepareCache();
for (int i = 0; i < MAX_CACHED_XOR; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
local.cachedXor(cur);
if (local._xorCache.size() != i+1) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
}
_log.debug("Fill test passed");
}
private static void testOverflow() {
Hash local = new Hash(new byte[HASH_LENGTH]); // all zeroes
local.prepareCache();
for (int i = 0; i < MAX_CACHED_XOR*2; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
local.cachedXor(cur);
if (i < MAX_CACHED_XOR) {
if (local._xorCache.size() != i+1) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
} else {
if (local._xorCache.size() > MAX_CACHED_XOR) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
}
}
_log.debug("overflow test passed");
}
private static void testFillCheck() {
Set hashes = new HashSet();
Hash local = new Hash(new byte[HASH_LENGTH]); // all zeroes
local.prepareCache();
// fill 'er up
for (int i = 0; i < MAX_CACHED_XOR; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
hashes.add(cur);
local.cachedXor(cur);
if (local._xorCache.size() != i+1) {
_log.error("xor cache size where i=" + i + " isn't correct! size = "
+ local._xorCache.size());
return;
}
}
// now lets recheck using those same hash objects
// and see if they're cached
for (Iterator iter = hashes.iterator(); iter.hasNext(); ) {
Hash cur = (Hash)iter.next();
if (!local._xorCache.containsKey(cur)) {
_log.error("checking the cache, we dont have "
+ DataHelper.toHexString(cur.getData()));
return;
}
}
// now lets recheck with new objects but the same values
// and see if they'return cached
for (int i = 0; i < MAX_CACHED_XOR; i++) {
byte t[] = new byte[HASH_LENGTH];
for (int j = 0; j < HASH_LENGTH; j++)
t[j] = (byte)((i >> j) & 0xFF);
Hash cur = new Hash(t);
if (!local._xorCache.containsKey(cur)) {
_log.error("checking the cache, we do NOT have "
+ DataHelper.toHexString(cur.getData()));
return;
}
}
_log.debug("Fill check test passed");
}
*********/
}

View File

@@ -2,6 +2,8 @@ package net.i2p.router.transport.ntcp;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.DecimalFormat;
@@ -54,6 +56,9 @@ public class NTCPTransport extends TransportImpl {
*/
private final List<NTCPConnection> _establishing;
/** this is rarely if ever used, default is to bind to wildcard address */
public static final String PROP_BIND_INTERFACE = "i2np.ntcp.bindInterface";
private final NTCPSendFinisher _finisher;
private long _lastBadSkew;
private static final long[] RATES = { 10*60*1000 };
@@ -486,15 +491,29 @@ public class NTCPTransport extends TransportImpl {
/** call from synchronized method */
private RouterAddress bindAddress() {
if (_myAddress != null) {
InetAddress bindToAddr = null;
String bindTo = _context.getProperty(PROP_BIND_INTERFACE);
if (bindTo != null) {
try {
bindToAddr = InetAddress.getByName(bindTo);
} catch (UnknownHostException uhe) {
_log.log(Log.CRIT, "Invalid SSU bind interface specified [" + bindTo + "]", uhe);
// this can be implemented later, just updates some stats
// see udp/UDPTransport.java
//setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
return null;
}
}
try {
ServerSocketChannel chan = ServerSocketChannel.open();
chan.configureBlocking(false);
InetSocketAddress addr = null;
//if (bindAllInterfaces())
if(bindToAddr==null)
addr = new InetSocketAddress(_myAddress.getPort());
//else
// addr = new InetSocketAddress(_myAddress.getAddress(), _myAddress.getPort());
else
addr = new InetSocketAddress(bindToAddr, _myAddress.getPort());
chan.socket().bind(addr);
if (_log.shouldLog(Log.INFO))
_log.info("Listening on " + addr);