forked from I2P_Developers/i2p.i2p
merge of '03e8a3d066ce112bb4ddaa98c0387dfefde94a0e'
and '751ff97c62634ee13a8f8baf3d7947e373d5368a'
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -9,8 +9,6 @@ public class Address extends DataStructureImpl {
|
||||
private Destination _destination;
|
||||
|
||||
public Address() {
|
||||
_hostname = null;
|
||||
_destination = null;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
*********/
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -27,8 +27,6 @@ public class Payload extends DataStructureImpl {
|
||||
private byte[] _unencryptedData;
|
||||
|
||||
public Payload() {
|
||||
setUnencryptedData(null);
|
||||
setEncryptedData(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@ public class RouterAddress extends DataStructureImpl {
|
||||
|
||||
public RouterAddress() {
|
||||
setCost(-1);
|
||||
setExpiration(null);
|
||||
setTransportStyle(null);
|
||||
setOptions(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ public class AbuseReason extends DataStructureImpl {
|
||||
private String _reason;
|
||||
|
||||
public AbuseReason() {
|
||||
setReason(null);
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -27,7 +27,6 @@ public class DestroySessionMessage extends I2CPMessageImpl {
|
||||
private SessionId _sessionId;
|
||||
|
||||
public DestroySessionMessage() {
|
||||
setSessionId(null);
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
|
||||
@@ -27,7 +27,6 @@ public class DisconnectMessage extends I2CPMessageImpl {
|
||||
private String _reason;
|
||||
|
||||
public DisconnectMessage() {
|
||||
setReason(null);
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
|
||||
@@ -31,7 +31,6 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
public MessagePayloadMessage() {
|
||||
setSessionId(-1);
|
||||
setMessageId(-1);
|
||||
setPayload(null);
|
||||
}
|
||||
|
||||
public long getSessionId() {
|
||||
|
||||
@@ -28,8 +28,6 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl {
|
||||
private SessionConfig _sessionConfig;
|
||||
|
||||
public ReconfigureSessionMessage() {
|
||||
_sessionId = null;
|
||||
_sessionConfig = null;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -34,9 +34,7 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
|
||||
private Date _end;
|
||||
|
||||
public RequestLeaseSetMessage() {
|
||||
setSessionId(null);
|
||||
_endpoints = new ArrayList();
|
||||
setEndDate(null);
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
|
||||
@@ -34,7 +34,6 @@ public class SendMessageExpiresMessage extends SendMessageMessage {
|
||||
|
||||
public SendMessageExpiresMessage() {
|
||||
super();
|
||||
setExpiration(null);
|
||||
}
|
||||
|
||||
public Date getExpiration() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,6 @@ public class SessionStatusMessage extends I2CPMessageImpl {
|
||||
public final static int STATUS_INVALID = 3;
|
||||
|
||||
public SessionStatusMessage() {
|
||||
setSessionId(null);
|
||||
setStatus(STATUS_INVALID);
|
||||
}
|
||||
|
||||
|
||||
@@ -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? */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -625,4 +625,12 @@ public class LogManager {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for LogRecordFormatter
|
||||
* @since 0.7.14
|
||||
*/
|
||||
I2PAppContext getContext() {
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
history.txt
20
history.txt
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -24,7 +24,6 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
|
||||
public DataMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
_data = null;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -26,7 +26,6 @@ public class GarlicMessage extends I2NPMessageImpl {
|
||||
|
||||
public GarlicMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
setData(null);
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
|
||||
@@ -33,7 +33,6 @@ public class I2NPMessageHandler {
|
||||
public I2NPMessageHandler(I2PAppContext context) {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(I2NPMessageHandler.class);
|
||||
_messageBuffer = null;
|
||||
_lastSize = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -76,5 +76,5 @@ interface KBucket {
|
||||
*/
|
||||
public Hash generateRandomKey();
|
||||
|
||||
public Hash getLocal();
|
||||
public LocalHash getLocal();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
208
router/java/src/net/i2p/router/networkdb/kademlia/LocalHash.java
Normal file
208
router/java/src/net/i2p/router/networkdb/kademlia/LocalHash.java
Normal 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");
|
||||
}
|
||||
*********/
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user