merge of 'b002d4a942128fdd4994a2cfba1c554ba9cb81d8'

and 'e6547920e2da9f540c79fcafc7ca7c82d25eae23'
This commit is contained in:
zzz
2010-11-26 15:57:05 +00:00
37 changed files with 607 additions and 383 deletions

View File

@ -0,0 +1,102 @@
package org.klomp.snark;
/**
* This is the class passed from PeerCoordinator to PeerState so
* PeerState may start requests.
*
* It is also passed from PeerState to PeerCoordinator when
* a piece is not completely downloaded, for example
* when the Peer disconnects or chokes.
*/
class PartialPiece implements Comparable {
private final int piece;
private final byte[] bs;
private final int off;
private final long createdTime;
/**
* Used by PeerCoordinator.
* Creates a new PartialPiece, with no chunks yet downloaded.
* Allocates the data.
*
* @param piece Piece number requested.
* @param bs length must be equal to the piece length
*/
public PartialPiece (int piece, int len) throws OutOfMemoryError {
this.piece = piece;
this.bs = new byte[len];
this.off = 0;
this.createdTime = 0;
}
/**
* Used by PeerState.
* Creates a new PartialPiece, with chunks up to but not including
* firstOutstandingRequest already downloaded and stored in the Request byte array.
*
* Note that this cannot handle gaps; chunks after a missing chunk cannot be saved.
* That would be harder.
*
* @param firstOutstandingRequest the first request not fulfilled for the piece
*/
public PartialPiece (Request firstOutstandingRequest) {
this.piece = firstOutstandingRequest.piece;
this.bs = firstOutstandingRequest.bs;
this.off = firstOutstandingRequest.off;
this.createdTime = System.currentTimeMillis();
}
/**
* Convert this PartialPiece to a request for the next chunk.
* Used by PeerState only.
*/
public Request getRequest() {
return new Request(this.piece, this.bs, this.off, Math.min(this.bs.length - this.off, PeerState.PARTSIZE));
}
/** piece number */
public int getPiece() {
return this.piece;
}
/** how many bytes are good */
public int getDownloaded() {
return this.off;
}
public long getCreated() {
return this.createdTime;
}
/**
* Highest downloaded first
*/
public int compareTo(Object o) throws ClassCastException {
return ((PartialPiece)o).off - this.off; // reverse
}
@Override
public int hashCode() {
return piece * 7777;
}
/**
* Make this simple so PeerCoordinator can keep a List.
* Warning - compares piece number only!
*/
@Override
public boolean equals(Object o) {
if (o instanceof PartialPiece) {
PartialPiece pp = (PartialPiece)o;
return pp.piece == this.piece;
}
return false;
}
@Override
public String toString() {
return "Partial(" + piece + ',' + off + ',' + bs.length + ')';
}
}

View File

@ -27,6 +27,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocket;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -368,8 +369,11 @@ public class Peer implements Comparable
if (this.deregister) { if (this.deregister) {
PeerListener p = s.listener; PeerListener p = s.listener;
if (p != null) { if (p != null) {
p.savePeerPartial(s); List<PartialPiece> pcs = s.returnPartialPieces();
p.markUnrequested(this); if (!pcs.isEmpty())
p.savePartialPieces(this, pcs);
// now covered by savePartialPieces
//p.markUnrequested(this);
} }
} }
state = null; state = null;

View File

@ -74,6 +74,9 @@ public class PeerCoordinator implements PeerListener
// Some random wanted pieces // Some random wanted pieces
private List<Piece> wantedPieces; private List<Piece> wantedPieces;
/** partial pieces */
private final List<PartialPiece> partialPieces;
private boolean halted = false; private boolean halted = false;
private final CoordinatorListener listener; private final CoordinatorListener listener;
@ -94,6 +97,7 @@ public class PeerCoordinator implements PeerListener
this.snark = torrent; this.snark = torrent;
setWantedPieces(); setWantedPieces();
partialPieces = new ArrayList(getMaxConnections() + 1);
// Install a timer to check the uploaders. // Install a timer to check the uploaders.
// Randomize the first start time so multiple tasks are spread out, // Randomize the first start time so multiple tasks are spread out,
@ -293,7 +297,9 @@ public class PeerCoordinator implements PeerListener
removePeerFromPieces(peer); removePeerFromPieces(peer);
} }
// delete any saved orphan partial piece // delete any saved orphan partial piece
savedRequest = null; synchronized (partialPieces) {
partialPieces.clear();
}
} }
public void connected(Peer peer) public void connected(Peer peer)
@ -773,6 +779,9 @@ public class PeerCoordinator implements PeerListener
wantedPieces.remove(p); wantedPieces.remove(p);
} }
// just in case
removePartialPiece(piece);
// Announce to the world we have it! // Announce to the world we have it!
// Disconnect from other seeders when we get the last piece // Disconnect from other seeders when we get the last piece
synchronized(peers) synchronized(peers)
@ -866,70 +875,123 @@ public class PeerCoordinator implements PeerListener
} }
} }
/**
/** Simple method to save a partial piece on peer disconnection * Save partial pieces on peer disconnection
* and hopefully restart it later. * and hopefully restart it later.
* Only one partial piece is saved at a time. * Replace a partial piece in the List if the new one is bigger.
* Replace it if a new one is bigger or the old one is too old.
* Storage method is private so we can expand to save multiple partials * Storage method is private so we can expand to save multiple partials
* if we wish. * if we wish.
*
* Also mark the piece unrequested if this peer was the only one.
*
* @param peer partials, must include the zero-offset (empty) ones too
* @since 0.8.2
*/ */
private Request savedRequest = null; public void savePartialPieces(Peer peer, List<PartialPiece> partials)
private long savedRequestTime = 0;
public void savePeerPartial(PeerState state)
{ {
if (halted) if (halted)
return; return;
Request req = state.getPartialRequest(); if (_log.shouldLog(Log.INFO))
if (req == null) _log.info("Partials received from " + peer + ": " + partials);
return; synchronized(partialPieces) {
if (savedRequest == null || for (PartialPiece pp : partials) {
req.off > savedRequest.off || if (pp.getDownloaded() > 0) {
System.currentTimeMillis() > savedRequestTime + (15 * 60 * 1000)) { // PartialPiece.equals() only compares piece number, which is what we want
if (savedRequest == null || (req.piece != savedRequest.piece && req.off != savedRequest.off)) { int idx = partialPieces.indexOf(pp);
if (_log.shouldLog(Log.DEBUG)) { if (idx < 0) {
_log.debug(" Saving orphaned partial piece " + req); partialPieces.add(pp);
if (savedRequest != null) if (_log.shouldLog(Log.INFO))
_log.debug(" (Discarding previously saved orphan) " + savedRequest); _log.info("Saving orphaned partial piece (new) " + pp);
} } else if (idx >= 0 && pp.getDownloaded() > partialPieces.get(idx).getDownloaded()) {
// replace what's there now
partialPieces.set(idx, pp);
if (_log.shouldLog(Log.INFO))
_log.info("Saving orphaned partial piece (bigger) " + pp);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Discarding partial piece (not bigger)" + pp);
}
int max = getMaxConnections();
if (partialPieces.size() > max) {
// sorts by remaining bytes, least first
Collections.sort(partialPieces);
PartialPiece gone = partialPieces.remove(max);
if (_log.shouldLog(Log.INFO))
_log.info("Discarding orphaned partial piece (list full)" + gone);
}
} // else drop the empty partial piece
// synchs on wantedPieces...
markUnrequestedIfOnlyOne(peer, pp.getPiece());
}
if (_log.shouldLog(Log.INFO))
_log.info("Partial list size now: " + partialPieces.size());
} }
savedRequest = req;
savedRequestTime = System.currentTimeMillis();
} else {
if (req.piece != savedRequest.piece)
if (_log.shouldLog(Log.DEBUG))
_log.debug(" Discarding orphaned partial piece " + req);
}
} }
/** Return partial piece if it's still wanted and peer has it. /**
* Return partial piece to the PeerState if it's still wanted and peer has it.
* @param havePieces pieces the peer has, the rv will be one of these
*
* @return PartialPiece or null
* @since 0.8.2
*/ */
public Request getPeerPartial(BitField havePieces) { public PartialPiece getPartialPiece(Peer peer, BitField havePieces) {
if (savedRequest == null) // do it in this order to avoid deadlock (same order as in savePartialPieces())
return null; synchronized(partialPieces) {
if (! havePieces.get(savedRequest.piece)) { synchronized(wantedPieces) {
if (_log.shouldLog(Log.DEBUG)) // sorts by remaining bytes, least first
_log.debug("Peer doesn't have orphaned piece " + savedRequest); Collections.sort(partialPieces);
return null; for (Iterator<PartialPiece> iter = partialPieces.iterator(); iter.hasNext(); ) {
} PartialPiece pp = iter.next();
synchronized(wantedPieces) int savedPiece = pp.getPiece();
{ if (havePieces.get(savedPiece)) {
for(Iterator<Piece> iter = wantedPieces.iterator(); iter.hasNext(); ) { // this is just a double-check, it should be in there
Piece piece = iter.next(); for(Piece piece : wantedPieces) {
if (piece.getId() == savedRequest.piece) { if (piece.getId() == savedPiece) {
Request req = savedRequest; piece.setRequested(true);
piece.setRequested(true); iter.remove();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.INFO)) {
_log.debug("Restoring orphaned partial piece " + req); _log.info("Restoring orphaned partial piece " + pp +
savedRequest = null; " Partial list size now: " + partialPieces.size());
return req; }
return pp;
}
}
}
}
}
}
// ...and this section turns this into the general move-requests-around code!
// Temporary? So PeerState never calls wantPiece() directly for now...
int piece = wantPiece(peer, havePieces);
if (piece >= 0) {
try {
return new PartialPiece(piece, metainfo.getPieceLength(piece));
} catch (OutOfMemoryError oom) {
if (_log.shouldLog(Log.WARN))
_log.warn("OOM creating new partial piece");
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("We have no partial piece to return");
return null;
}
/**
* Remove saved state for this piece.
* Unless we are in the end game there shouldnt be anything in there.
* Do not call with wantedPieces lock held (deadlock)
*/
private void removePartialPiece(int piece) {
synchronized(partialPieces) {
for (Iterator<PartialPiece> iter = partialPieces.iterator(); iter.hasNext(); ) {
PartialPiece pp = iter.next();
if (pp.getPiece() == piece) {
iter.remove();
// there should be only one but keep going to be sure
}
} }
}
} }
if (_log.shouldLog(Log.DEBUG))
_log.debug("We no longer want orphaned piece " + savedRequest);
savedRequest = null;
return null;
} }
/** Clear the requested flag for a piece if the peer /** Clear the requested flag for a piece if the peer
@ -947,13 +1009,12 @@ public class PeerCoordinator implements PeerListener
continue; continue;
if (p.state == null) if (p.state == null)
continue; continue;
int[] arr = p.state.getRequestedPieces(); // FIXME don't go into the state
for (int i = 0; arr[i] >= 0; i++) if (p.state.getRequestedPieces().contains(Integer.valueOf(piece))) {
if(arr[i] == piece) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Another peer is requesting piece " + piece); _log.debug("Another peer is requesting piece " + piece);
return; return;
} }
} }
} }
@ -973,20 +1034,6 @@ public class PeerCoordinator implements PeerListener
} }
} }
/** Mark a peer's requested pieces unrequested when it is disconnected
** Once for each piece
** This is enough trouble, maybe would be easier just to regenerate
** the requested list from scratch instead.
*/
public void markUnrequested(Peer peer)
{
if (halted || peer.state == null)
return;
int[] arr = peer.state.getRequestedPieces();
for (int i = 0; arr[i] >= 0; i++)
markUnrequestedIfOnlyOne(peer, arr[i]);
}
/** Return number of allowed uploaders for this torrent. /** Return number of allowed uploaders for this torrent.
** Check with Snark to see if we are over the total upload limit. ** Check with Snark to see if we are over the total upload limit.
*/ */

View File

@ -20,10 +20,12 @@
package org.klomp.snark; package org.klomp.snark;
import java.util.List;
/** /**
* Listener for Peer events. * Listener for Peer events.
*/ */
public interface PeerListener interface PeerListener
{ {
/** /**
* Called when the connection to the peer has started and the * Called when the connection to the peer has started and the
@ -151,7 +153,7 @@ public interface PeerListener
* *
* @param state the PeerState for the peer * @param state the PeerState for the peer
*/ */
void savePeerPartial(PeerState state); /* FIXME Exporting non-public type through public API FIXME */ void savePartialPieces(Peer peer, List<PartialPiece> pcs);
/** /**
* Called when a peer has connected and there may be a partially * Called when a peer has connected and there may be a partially
@ -161,12 +163,5 @@ public interface PeerListener
* *
* @return request (contains the partial data and valid length) * @return request (contains the partial data and valid length)
*/ */
Request getPeerPartial(BitField havePieces); /* FIXME Exporting non-public type through public API FIXME */ PartialPiece getPartialPiece(Peer peer, BitField havePieces);
/** Mark a peer's requested pieces unrequested when it is disconnected
* This prevents premature end game
*
* @param peer the peer that is disconnecting
*/
void markUnrequested(Peer peer);
} }

View File

@ -23,9 +23,11 @@ package org.klomp.snark;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -36,9 +38,9 @@ import org.klomp.snark.bencode.BEValue;
class PeerState implements DataLoader class PeerState implements DataLoader
{ {
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class); private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class);
final Peer peer; private final Peer peer;
final PeerListener listener; final PeerListener listener;
final MetaInfo metainfo; private final MetaInfo metainfo;
// Interesting and choking describes whether we are interested in or // Interesting and choking describes whether we are interested in or
// are choking the other side. // are choking the other side.
@ -54,6 +56,7 @@ class PeerState implements DataLoader
long downloaded; long downloaded;
long uploaded; long uploaded;
/** the pieces the peer has */
BitField bitfield; BitField bitfield;
// Package local for use by Peer. // Package local for use by Peer.
@ -102,6 +105,12 @@ class PeerState implements DataLoader
if (interesting && !choked) if (interesting && !choked)
request(resend); request(resend);
if (choked) {
// TODO
// savePartialPieces
// clear request list
}
} }
void interestedMessage(boolean interest) void interestedMessage(boolean interest)
@ -308,8 +317,11 @@ class PeerState implements DataLoader
} }
} }
/**
* @return index in outstandingRequests or -1
*/
synchronized private int getFirstOutstandingRequest(int piece) synchronized private int getFirstOutstandingRequest(int piece)
{ {
for (int i = 0; i < outstandingRequests.size(); i++) for (int i = 0; i < outstandingRequests.size(); i++)
if (outstandingRequests.get(i).piece == piece) if (outstandingRequests.get(i).piece == piece)
return i; return i;
@ -397,54 +409,56 @@ class PeerState implements DataLoader
} }
// get longest partial piece /**
synchronized Request getPartialRequest() * @return lowest offset of any request for the piece
{ * @since 0.8.2
Request req = null; */
for (int i = 0; i < outstandingRequests.size(); i++) { synchronized private Request getLowestOutstandingRequest(int piece) {
Request r1 = outstandingRequests.get(i); Request rv = null;
int j = getFirstOutstandingRequest(r1.piece); int lowest = Integer.MAX_VALUE;
if (j == -1) for (Request r : outstandingRequests) {
continue; if (r.piece == piece && r.off < lowest) {
Request r2 = outstandingRequests.get(j); lowest = r.off;
if (r2.off > 0 && ((req == null) || (r2.off > req.off))) rv = r;
req = r2; }
} }
if (pendingRequest != null && req != null && pendingRequest.off < req.off) { if (pendingRequest != null &&
if (pendingRequest.off != 0) pendingRequest.piece == piece && pendingRequest.off < lowest)
req = pendingRequest; rv = pendingRequest;
else
req = null; if (_log.shouldLog(Log.DEBUG))
} _log.debug(peer + " lowest for " + piece + " is " + rv + " out of " + pendingRequest + " and " + outstandingRequests);
return req; return rv;
} }
/** /**
* return array of pieces terminated by -1 * get partial pieces, give them back to PeerCoordinator
* remove most duplicates * @return List of PartialPieces, even those with an offset == 0, or empty list
* but still could be some duplicates, not guaranteed * @since 0.8.2
* TODO rework this Java-style to return a Set or a List
*/ */
synchronized int[] getRequestedPieces() synchronized List<PartialPiece> returnPartialPieces()
{ {
int size = outstandingRequests.size(); Set<Integer> pcs = getRequestedPieces();
int[] arr = new int[size+2]; List<PartialPiece> rv = new ArrayList(pcs.size());
int pc = -1; for (Integer p : pcs) {
int pos = 0; Request req = getLowestOutstandingRequest(p.intValue());
if (pendingRequest != null) { if (req != null)
pc = pendingRequest.piece; rv.add(new PartialPiece(req));
arr[pos++] = pc;
}
Request req = null;
for (int i = 0; i < size; i++) {
Request r1 = outstandingRequests.get(i);
if (pc != r1.piece) {
pc = r1.piece;
arr[pos++] = pc;
} }
} return rv;
arr[pos] = -1; }
return(arr);
/**
* @return all pieces we are currently requesting, or empty Set
*/
synchronized Set<Integer> getRequestedPieces() {
Set<Integer> rv = new HashSet(outstandingRequests.size() + 1);
for (Request req : outstandingRequests) {
rv.add(Integer.valueOf(req.piece));
if (pendingRequest != null)
rv.add(Integer.valueOf(pendingRequest.piece));
}
return rv;
} }
void cancelMessage(int piece, int begin, int length) void cancelMessage(int piece, int begin, int length)
@ -555,6 +569,8 @@ class PeerState implements DataLoader
{ {
synchronized (this) { synchronized (this) {
out.sendRequests(outstandingRequests); out.sendRequests(outstandingRequests);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Resending requests to " + peer + outstandingRequests);
} }
} }
@ -620,24 +636,17 @@ class PeerState implements DataLoader
if (bitfield != null) if (bitfield != null)
{ {
// Check for adopting an orphaned partial piece // Check for adopting an orphaned partial piece
Request r = listener.getPeerPartial(bitfield); PartialPiece pp = listener.getPartialPiece(peer, bitfield);
if (r != null) { if (pp != null) {
// Check that r not already in outstandingRequests // Double-check that r not already in outstandingRequests
int[] arr = getRequestedPieces(); if (!getRequestedPieces().contains(Integer.valueOf(pp.getPiece()))) {
boolean found = false; Request r = pp.getRequest();
for (int i = 0; arr[i] >= 0; i++) {
if (arr[i] == r.piece) {
found = true;
break;
}
}
if (!found) {
outstandingRequests.add(r); outstandingRequests.add(r);
if (!choked) if (!choked)
out.sendRequest(r); out.sendRequest(r);
lastRequest = r; lastRequest = r;
return true; return true;
} }
} }
// Note that in addition to the bitfield, PeerCoordinator uses // Note that in addition to the bitfield, PeerCoordinator uses

View File

@ -5,7 +5,10 @@ import java.util.Set;
import net.i2p.util.ConcurrentHashSet; import net.i2p.util.ConcurrentHashSet;
public class Piece implements Comparable { /**
* This class is used solely by PeerCoordinator.
*/
class Piece implements Comparable {
private int id; private int id;
private Set<PeerID> peers; private Set<PeerID> peers;

View File

@ -22,6 +22,7 @@ package org.klomp.snark;
/** /**
* Holds all information needed for a partial piece request. * Holds all information needed for a partial piece request.
* This class should be used only by PeerState, PeerConnectionIn, and PeerConnectionOut.
*/ */
class Request class Request
{ {

View File

@ -92,6 +92,11 @@
<!-- jar again to get the latest messages_*.class files --> <!-- jar again to get the latest messages_*.class files -->
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class" update="true" /> <jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class" update="true" />
</target> </target>
<target name="jarWithJavadoc" depends="jar">
<jar destfile="build/routerconsole.war" basedir="../../../build/" includes="javadoc/**/*" update="true" />
</target>
<target name="poupdate" depends="build"> <target name="poupdate" depends="build">
<ant target="war" /> <ant target="war" />
<!-- Update the messages_*.po files. <!-- Update the messages_*.po files.

View File

@ -6,13 +6,11 @@ import java.util.Locale;
import net.i2p.util.FileUtil; import net.i2p.util.FileUtil;
public class ContentHelper extends HelperBase { public class ContentHelper extends HelperBase {
private String _page; protected String _page;
private int _maxLines; private int _maxLines;
private boolean _startAtBeginning; private boolean _startAtBeginning;
private String _lang; private String _lang;
public ContentHelper() {}
/** /**
* Caution, use absolute paths only, do not assume files are in CWD * Caution, use absolute paths only, do not assume files are in CWD
*/ */

View File

@ -0,0 +1,20 @@
package net.i2p.router.web;
import java.io.File;
/**
* If news file does not exist, use file from the initialNews directory
* in $I2P
*
* @since 0.8.2
*/
public class NewsHelper extends ContentHelper {
@Override
public String getContent() {
File news = new File(_page);
if (!news.exists())
_page = (new File(_context.getBaseDir(), "docs/initialNews/initialNews.xml")).getAbsolutePath();
return super.getContent();
}
}

View File

@ -14,7 +14,8 @@ if (System.getProperty("router.consoleNonce") == null) {
<%@include file="summary.jsi" %><h1><%=intl._("I2P Router Console")%></h1> <%@include file="summary.jsi" %><h1><%=intl._("I2P Router Console")%></h1>
<div class="news" id="news"> <div class="news" id="news">
<jsp:useBean class="net.i2p.router.web.ContentHelper" id="newshelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.NewsHelper" id="newshelper" scope="request" />
<jsp:setProperty name="newshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<% java.io.File fpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getRouterDir(), "docs/news.xml"); %> <% java.io.File fpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getRouterDir(), "docs/news.xml"); %>
<jsp:setProperty name="newshelper" property="page" value="<%=fpath.getAbsolutePath()%>" /> <jsp:setProperty name="newshelper" property="page" value="<%=fpath.getAbsolutePath()%>" />
<jsp:setProperty name="newshelper" property="maxLines" value="300" /> <jsp:setProperty name="newshelper" property="maxLines" value="300" />

View File

@ -413,8 +413,6 @@
<copy file="history.txt" todir="pkg-temp/" overwrite="true" /> <copy file="history.txt" todir="pkg-temp/" overwrite="true" />
<mkdir dir="pkg-temp/scripts" /> <mkdir dir="pkg-temp/scripts" />
<copy file="apps/proxyscript/i2pProxy.pac" todir="pkg-temp/scripts/" /> <copy file="apps/proxyscript/i2pProxy.pac" todir="pkg-temp/scripts/" />
<!-- overwrite the news put in by the updater -->
<copy file="installer/resources/initialNews.xml" tofile="pkg-temp/docs/news.xml" overwrite="true" />
<copy file="installer/resources/startconsole.html" todir="pkg-temp/docs/" /> <copy file="installer/resources/startconsole.html" todir="pkg-temp/docs/" />
<copy file="installer/resources/start.ico" todir="pkg-temp/docs/" /> <copy file="installer/resources/start.ico" todir="pkg-temp/docs/" />
<copy file="installer/resources/console.ico" todir="pkg-temp/docs/" /> <copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
@ -476,8 +474,12 @@
<delete dir="pkg-temp" /> <delete dir="pkg-temp" />
</target> </target>
<!-- readme and proxy error page files, GeoIP files, and flag icons --> <!-- readme and proxy error page files, initialNews.xml files, GeoIP files, and flag icons -->
<target name="prepConsoleDocs" depends="prepConsoleDocUpdates, prepgeoupdate" /> <target name="prepConsoleDocs" depends="prepConsoleDocUpdates, prepgeoupdate" >
<copy todir="pkg-temp/docs/" >
<fileset dir="installer/resources/initialNews/" />
</copy>
</target>
<!-- readme and proxy error page files --> <!-- readme and proxy error page files -->
<target name="prepConsoleDocUpdates"> <target name="prepConsoleDocUpdates">
@ -766,7 +768,6 @@
<exec executable="ls" failonerror="true"> <exec executable="ls" failonerror="true">
<arg value="-l" /> <arg value="-l" />
<arg value="history.txt" /> <arg value="history.txt" />
<arg value="installer/resources/initialNews.xml" />
<arg value="installer/install.xml" /> <arg value="installer/install.xml" />
<arg value="installer/resources/news.xml" /> <arg value="installer/resources/news.xml" />
<arg value="core/java/src/net/i2p/CoreVersion.java" /> <arg value="core/java/src/net/i2p/CoreVersion.java" />

View File

@ -19,6 +19,10 @@ import net.i2p.util.Log;
* Defines the actual payload of a message being delivered, including the * Defines the actual payload of a message being delivered, including the
* standard encryption wrapping, as defined by the I2P data structure spec. * standard encryption wrapping, as defined by the I2P data structure spec.
* *
* This is used mostly in I2CP, where we used to do end-to-end encryption.
* Since we don't any more, you probably just want to use the
* get/set EncryptedData methods.
*
* @author jrandom * @author jrandom
*/ */
public class Payload extends DataStructureImpl { public class Payload extends DataStructureImpl {
@ -32,6 +36,9 @@ public class Payload extends DataStructureImpl {
/** /**
* Retrieve the unencrypted body of the message. * Retrieve the unencrypted body of the message.
* *
* Deprecated.
* Unless you are doing encryption, use getEncryptedData() instead.
*
* @return body of the message, or null if the message has either not been * @return body of the message, or null if the message has either not been
* decrypted yet or if the hash is not correct * decrypted yet or if the hash is not correct
*/ */
@ -43,15 +50,19 @@ public class Payload extends DataStructureImpl {
* Populate the message body with data. This does not automatically encrypt * Populate the message body with data. This does not automatically encrypt
* yet. * yet.
* *
* Deprecated.
* Unless you are doing encryption, use setEncryptedData() instead.
*/ */
public void setUnencryptedData(byte[] data) { public void setUnencryptedData(byte[] data) {
_unencryptedData = data; _unencryptedData = data;
} }
/** the real data */
public byte[] getEncryptedData() { public byte[] getEncryptedData() {
return _encryptedData; return _encryptedData;
} }
/** the real data */
public void setEncryptedData(byte[] data) { public void setEncryptedData(byte[] data) {
_encryptedData = data; _encryptedData = data;
} }
@ -100,7 +111,7 @@ public class Payload extends DataStructureImpl {
@Override @Override
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_unencryptedData); return DataHelper.hashCode(_encryptedData != null ? _encryptedData : _unencryptedData);
} }
@Override @Override

View File

@ -23,6 +23,7 @@ import net.i2p.crypto.DSAEngine;
* This helper class reads and writes files in the * This helper class reads and writes files in the
* same "eepPriv.dat" format used by the client code. * same "eepPriv.dat" format used by the client code.
* The format is: * The format is:
*<pre>
* - Destination (387 bytes if no certificate, otherwise longer) * - Destination (387 bytes if no certificate, otherwise longer)
* - Public key (256 bytes) * - Public key (256 bytes)
* - Signing Public key (128 bytes) * - Signing Public key (128 bytes)
@ -32,6 +33,7 @@ import net.i2p.crypto.DSAEngine;
* - Private key (256 bytes) * - Private key (256 bytes)
* - Signing Private key (20 bytes) * - Signing Private key (20 bytes)
* Total 663 bytes * Total 663 bytes
*</pre>
* *
* @author welterde, zzz * @author welterde, zzz
*/ */

View File

@ -66,8 +66,8 @@ public class LogManager {
public final static String DEFAULT_DEFAULTLEVEL = Log.STR_ERROR; public final static String DEFAULT_DEFAULTLEVEL = Log.STR_ERROR;
public final static String DEFAULT_ONSCREENLEVEL = Log.STR_CRIT; public final static String DEFAULT_ONSCREENLEVEL = Log.STR_CRIT;
private I2PAppContext _context; private final I2PAppContext _context;
private Log _log; private final Log _log;
/** when was the config file last read (or -1 if never) */ /** when was the config file last read (or -1 if never) */
private long _configLastRead; private long _configLastRead;
@ -75,11 +75,11 @@ public class LogManager {
/** the config file */ /** the config file */
private File _locationFile; private File _locationFile;
/** Ordered list of LogRecord elements that have not been written out yet */ /** Ordered list of LogRecord elements that have not been written out yet */
private LinkedBlockingQueue<LogRecord> _records; private final LinkedBlockingQueue<LogRecord> _records;
/** List of explicit overrides of log levels (LogLimit objects) */ /** List of explicit overrides of log levels (LogLimit objects) */
private Set<LogLimit> _limits; private final Set<LogLimit> _limits;
/** String (scope) or Log.LogScope to Log object */ /** String (scope) or Log.LogScope to Log object */
private ConcurrentHashMap<Object, Log> _logs; private final ConcurrentHashMap<Object, Log> _logs;
/** who clears and writes our records */ /** who clears and writes our records */
private LogWriter _writer; private LogWriter _writer;
@ -108,7 +108,7 @@ public class LogManager {
/** how many records we want to buffer in the "recent logs" list */ /** how many records we want to buffer in the "recent logs" list */
private int _consoleBufferSize; private int _consoleBufferSize;
/** the actual "recent logs" list */ /** the actual "recent logs" list */
private LogConsoleBuffer _consoleBuffer; private final LogConsoleBuffer _consoleBuffer;
private boolean _alreadyNoticedMissingConfig; private boolean _alreadyNoticedMissingConfig;
@ -119,17 +119,17 @@ public class LogManager {
_limits = new ConcurrentHashSet(); _limits = new ConcurrentHashSet();
_logs = new ConcurrentHashMap(128); _logs = new ConcurrentHashMap(128);
_defaultLimit = Log.ERROR; _defaultLimit = Log.ERROR;
_configLastRead = 0;
_context = context; _context = context;
_log = getLog(LogManager.class); _log = getLog(LogManager.class);
String location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT); String location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT);
setConfig(location); setConfig(location);
_consoleBuffer = new LogConsoleBuffer(context); _consoleBuffer = new LogConsoleBuffer(context);
_writer = new LogWriter(this); // If we aren't in the router context, delay creating the LogWriter until required,
Thread t = new I2PThread(_writer); // so it doesn't create a log directory and log files unless there is output.
t.setName("LogWriter"); // In the router context, we have to rotate to a new log file at startup or the logs.jsp
t.setDaemon(true); // page will display the old log.
t.start(); if (context.isRouterContext())
startLogWriter();
try { try {
Runtime.getRuntime().addShutdownHook(new ShutdownHook()); Runtime.getRuntime().addShutdownHook(new ShutdownHook());
} catch (IllegalStateException ise) { } catch (IllegalStateException ise) {
@ -138,9 +138,16 @@ public class LogManager {
//System.out.println("Created logManager " + this + " with context: " + context); //System.out.println("Created logManager " + this + " with context: " + context);
} }
private LogManager() { // nop /** @since 0.8.2 */
private synchronized void startLogWriter() {
// yeah, this doesn't always work, _writer should be volatile
if (_writer != null)
return;
_writer = new LogWriter(this);
Thread t = new I2PThread(_writer, "LogWriter", true);
t.start();
} }
public Log getLog(Class cls) { return getLog(cls, null); } public Log getLog(Class cls) { return getLog(cls, null); }
public Log getLog(String name) { return getLog(null, name); } public Log getLog(String name) { return getLog(null, name); }
public Log getLog(Class cls, String name) { public Log getLog(Class cls, String name) {
@ -169,6 +176,7 @@ public class LogManager {
public LogConsoleBuffer getBuffer() { return _consoleBuffer; } public LogConsoleBuffer getBuffer() { return _consoleBuffer; }
/** @deprecated unused */
public void setDisplayOnScreen(boolean yes) { public void setDisplayOnScreen(boolean yes) {
_displayOnScreen = yes; _displayOnScreen = yes;
} }
@ -181,6 +189,7 @@ public class LogManager {
return _onScreenLimit; return _onScreenLimit;
} }
/** @deprecated unused */
public void setDisplayOnScreenLevel(int level) { public void setDisplayOnScreenLevel(int level) {
_onScreenLimit = level; _onScreenLimit = level;
} }
@ -189,6 +198,7 @@ public class LogManager {
return _consoleBufferSize; return _consoleBufferSize;
} }
/** @deprecated unused */
public void setConsoleBufferSize(int numRecords) { public void setConsoleBufferSize(int numRecords) {
_consoleBufferSize = numRecords; _consoleBufferSize = numRecords;
} }
@ -201,6 +211,8 @@ public class LogManager {
} }
public String currentFile() { public String currentFile() {
if (_writer == null)
return ("No log file created yet");
return _writer.currentFile(); return _writer.currentFile();
} }
@ -209,6 +221,9 @@ public class LogManager {
* *
*/ */
void addRecord(LogRecord record) { void addRecord(LogRecord record) {
if ((!_context.isRouterContext()) && _writer == null)
startLogWriter();
_records.offer(record); _records.offer(record);
int numRecords = _records.size(); int numRecords = _records.size();
@ -617,9 +632,11 @@ public class LogManager {
} }
public void shutdown() { public void shutdown() {
_log.log(Log.WARN, "Shutting down logger"); if (_writer != null) {
_writer.flushRecords(false); _log.log(Log.WARN, "Shutting down logger");
_writer.stopWriting(); _writer.flushRecords(false);
_writer.stopWriting();
}
} }
private static int __id = 0; private static int __id = 0;

View File

@ -1,3 +1,12 @@
2010-11-27 zzz
* Console: Split initialNews.xml into a file for each language
don't copy to config dir at install.
* i2psnark: Clean up and enhance the PeerCoordinator's partial piece handling,
in preparation for more improvements
* LogManager: When not in router context, delay creating log file until required
* NetDb: Lower RouterInfo expiration time again
* Router: Prevent NCDFE after unzipping update file
2010-11-26 dr|z3d 2010-11-26 dr|z3d
* Readme: Overhaul (English) layout and text. * Readme: Overhaul (English) layout and text.

View File

@ -1,127 +0,0 @@
<!--
<i2p.news date="$Date: 2010-11-15 00:00:00 $">
<i2p.release version="0.8.1" date="2010/11/15" minVersion="0.6"/>
-->
<div lang="en">
<h4><ul><li>Congratulations on getting I2P installed!</li></ul></h4>
<p>
<b>Welcome to I2P!</b>
Please <b>have patience</b> as I2P boots up and finds peers.
</p>
<p>
While you are waiting, please <b>adjust your bandwidth settings</b> on the
<a href="config.jsp">configuration page</a>.
</p>
<p>
Once you have a "shared clients" destination listed on the left,
please <b>check out</b> our
<a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
Point your IRC client to <b>localhost:6668</b> and say hi to us on
<a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> or <a href="irc://127.0.0.1:6668/i2p">#i2p</a>.
</p>
</div>
<div lang="de">
<h4><ul><li>Wir gratulieren zur erfolgreichen Installation von I2P!</li></ul></h4>
<p>
<b>Willkommen im I2P!</b>
Hab noch <b>etwas Geduld</b>, w&auml;hrend I2P startet und weitere I2P-Router findet.
</p>
<p>
Passe bitte in der Zwischenzeit <b>deine Einstellungen zur Bandbreite</b> auf der
<a href="config.jsp">Einstellungsseite</a> an!
</p>
<p>
Sobald auf der linken Seite eine Verbindung namens "versch. Klienten" aufgelistet ist, kannst Du unsere <a href="http://www.i2p2.i2p/faq_de.html">FAQ</a> besuchen.
</p>
<p>
Verbinde deinen IRC-Klienten mit dem Server auf <b>localhost:6668</b> und schau bei uns im Kanal
<a href="irc://127.0.0.1:6668/i2p-de">#i2p-de</a>, <a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> oder <a href="irc://127.0.0.1:6668/i2p">#i2p</a> vorbei!
</p>
<p>
Wir wünschen Dir viel Freude an unserem Netz und hoffen, Deine Erwartungen zu erfüllen.
</p>
</div>
<div lang="es">
<h4><ul><li>&iexcl;Felicidades!, has instalado el enrutador I2P con &eacute;xito.</li></ul></h4>
<p>
<b>&iexcl;Bienvenido a I2P!</b><br>
&iexcl;Ten todav&iacute;a <b>paciencia</b> mientras I2P est&eacute; arrancando y encontrando otros enrutadores I2P!
</p>
<p>
Este es el momento ideal para adaptar tu <b>configuraci&oacute;n de ancho de banda</b> en la
<a href="config.jsp">p&aacute;gina de configuraci&oacute;n</a>.
</p>
<p>
En cuanto veas a la izquierda una conexi&oacute;n llamada "shared clients", puedes visitar nuestros <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
&iexcl;Con&eacute;cta tu cliente IRC con el servidor <b>localhost:6668</b> y ven a saludarnos en los canales
<a href="irc://127.0.0.1:6668/i2p-es">#i2p-es</a>, <a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> o <a href="irc://127.0.0.1:6668/i2p">#i2p</a>!
</p>
<p>
Esperemos que tengas una buena experiencia con y en I2P.
</p>
</div>
<div lang="br">
<h4><ul><li>Parabéns, você instalou o roteador I2P com êxito!</li></ul></h4>
<p>
<b>Bem-vindo ao I2P!</b><br>
Seja <b>paciente</b> enquanto I2P ainda está iniciando-se e enquanto continuam sendo encontrados outros roteadores I2P!
</p>
<p>
Este é o momento ideal para personalizar a <b>configuração de largura de banda</b> na
<a href="config.jsp">página de configuração</a>.
</p>
<p>
Quando você vê uma conexão no lado esquerdo chamada "shared clients", você pode visitar os nossos <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
Conecte seu cliente de IRC para o servidor <b>localhost:6668</b> e vem para nos cumprimentar aos canais
<a href="irc://127.0.0.1:6668/i2p-br">#i2p-br</a>, <a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> ou <a href="irc://127.0.0.1:6668/i2p">#i2p</a>!
</p>
<p>
Esperamos que você tenha uma boa experiência e I2P.
</p>
</div>
<div lang="nl">
<h4><ul><li>Gefeliciteerd met de installatie van I2P!</li></ul></h4>
<p>
<b>Welkom bij I2P!</b>
Heb <b>wat geduld</b> terwijl I2P opstart en peers zoekt.
</p>
<p>
Terwijl je wacht, <b>pas je bandbreedte instellingen aan</b> op de
<a href="config.jsp">configuratie pagina</a>.
</p>
<p>
Wanneer je een "gedeelde clients" destination in de linker lijst hebt,
<b>lees dan aub</b> onze <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
Verbind je IRC client met <b>localhost:6668</b> en zeg Hallo in
<a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> of <a href="irc://127.0.0.1:6668/i2p">#i2p</a>.
</p>
</div>
<div lang="ru">
<h4><ul><li>Поздравляем с успешным завершением установки I2P!</li></ul></h4>
<p>
<b>Добро пожаловать в I2P!</b> Немного терпения! I2P-маршрутизатору потребуется пара минут для запуска модулей и первого подключения к сети I2P.
</p>
<p>
Пока Вы ждете, самое время зайти в <a href="config.jsp">сетевые настройки</a> и <b>выставить ограничение скорости</b> в соответствии со скоростью Вашего подключения к интернету.
</p>
<p>
Как только в панели слева в разделе «Локальные туннели» появится запись «коллективные клиенты» — I2P готов к работе. Подключайте Ваш IRC-клиент к серверу <b>localhost:6668</b> и заходите сказать нам привет на канал
<a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> и <a href="irc://127.0.0.1:6668/i2p">#i2p</a>.
</p>
<p>
<b>Не забудьте заглянуть</b> в наш <a href="http://www.i2p2.i2p/faq_ru.html">FAQ</a>.
</p>
</div>

View File

@ -0,0 +1,20 @@
<div lang="en">
<h4><ul><li>Congratulations on getting I2P installed!</li></ul></h4>
<p>
<b>Welcome to I2P!</b>
Please <b>have patience</b> as I2P boots up and finds peers.
</p>
<p>
While you are waiting, please <b>adjust your bandwidth settings</b> on the
<a href="config.jsp">configuration page</a>.
</p>
<p>
Once you have a "shared clients" destination listed on the left,
please <b>check out</b> our
<a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
Point your IRC client to <b>localhost:6668</b> and say hi to us on
<a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> or <a href="irc://127.0.0.1:6668/i2p">#i2p</a>.
</p>
</div>

View File

@ -0,0 +1,21 @@
<div lang="br">
<h4><ul><li>Parabéns, você instalou o roteador I2P com êxito!</li></ul></h4>
<p>
<b>Bem-vindo ao I2P!</b><br>
Seja <b>paciente</b> enquanto I2P ainda está iniciando-se e enquanto continuam sendo encontrados outros roteadores I2P!
</p>
<p>
Este é o momento ideal para personalizar a <b>configuração de largura de banda</b> na
<a href="config.jsp">página de configuração</a>.
</p>
<p>
Quando você vê uma conexão no lado esquerdo chamada "shared clients", você pode visitar os nossos <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
Conecte seu cliente de IRC para o servidor <b>localhost:6668</b> e vem para nos cumprimentar aos canais
<a href="irc://127.0.0.1:6668/i2p-br">#i2p-br</a>, <a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> ou <a href="irc://127.0.0.1:6668/i2p">#i2p</a>!
</p>
<p>
Esperamos que você tenha uma boa experiência e I2P.
</p>
</div>

View File

@ -0,0 +1,21 @@
<div lang="de">
<h4><ul><li>Wir gratulieren zur erfolgreichen Installation von I2P!</li></ul></h4>
<p>
<b>Willkommen im I2P!</b>
Hab noch <b>etwas Geduld</b>, w&auml;hrend I2P startet und weitere I2P-Router findet.
</p>
<p>
Passe bitte in der Zwischenzeit <b>deine Einstellungen zur Bandbreite</b> auf der
<a href="config.jsp">Einstellungsseite</a> an!
</p>
<p>
Sobald auf der linken Seite eine Verbindung namens "versch. Klienten" aufgelistet ist, kannst Du unsere <a href="http://www.i2p2.i2p/faq_de.html">FAQ</a> besuchen.
</p>
<p>
Verbinde deinen IRC-Klienten mit dem Server auf <b>localhost:6668</b> und schau bei uns im Kanal
<a href="irc://127.0.0.1:6668/i2p-de">#i2p-de</a>, <a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> oder <a href="irc://127.0.0.1:6668/i2p">#i2p</a> vorbei!
</p>
<p>
Wir wünschen Dir viel Freude an unserem Netz und hoffen, Deine Erwartungen zu erfüllen.
</p>
</div>

View File

@ -0,0 +1,21 @@
<div lang="es">
<h4><ul><li>&iexcl;Felicidades!, has instalado el enrutador I2P con &eacute;xito.</li></ul></h4>
<p>
<b>&iexcl;Bienvenido a I2P!</b><br>
&iexcl;Ten todav&iacute;a <b>paciencia</b> mientras I2P est&eacute; arrancando y encontrando otros enrutadores I2P!
</p>
<p>
Este es el momento ideal para adaptar tu <b>configuraci&oacute;n de ancho de banda</b> en la
<a href="config.jsp">p&aacute;gina de configuraci&oacute;n</a>.
</p>
<p>
En cuanto veas a la izquierda una conexi&oacute;n llamada "shared clients", puedes visitar nuestros <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
&iexcl;Con&eacute;cta tu cliente IRC con el servidor <b>localhost:6668</b> y ven a saludarnos en los canales
<a href="irc://127.0.0.1:6668/i2p-es">#i2p-es</a>, <a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> o <a href="irc://127.0.0.1:6668/i2p">#i2p</a>!
</p>
<p>
Esperemos que tengas una buena experiencia con y en I2P.
</p>
</div>

View File

@ -0,0 +1,19 @@
<div lang="nl">
<h4><ul><li>Gefeliciteerd met de installatie van I2P!</li></ul></h4>
<p>
<b>Welkom bij I2P!</b>
Heb <b>wat geduld</b> terwijl I2P opstart en peers zoekt.
</p>
<p>
Terwijl je wacht, <b>pas je bandbreedte instellingen aan</b> op de
<a href="config.jsp">configuratie pagina</a>.
</p>
<p>
Wanneer je een "gedeelde clients" destination in de linker lijst hebt,
<b>lees dan aub</b> onze <a href="http://www.i2p2.i2p/faq.html">FAQ</a>.
</p>
<p>
Verbind je IRC client met <b>localhost:6668</b> en zeg Hallo in
<a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> of <a href="irc://127.0.0.1:6668/i2p">#i2p</a>.
</p>
</div>

View File

@ -0,0 +1,16 @@
<div lang="ru">
<h4><ul><li>Поздравляем с успешным завершением установки I2P!</li></ul></h4>
<p>
<b>Добро пожаловать в I2P!</b> Немного терпения! I2P-маршрутизатору потребуется пара минут для запуска модулей и первого подключения к сети I2P.
</p>
<p>
Пока Вы ждете, самое время зайти в <a href="config.jsp">сетевые настройки</a> и <b>выставить ограничение скорости</b> в соответствии со скоростью Вашего подключения к интернету.
</p>
<p>
Как только в панели слева в разделе «Локальные туннели» появится запись «коллективные клиенты» — I2P готов к работе. Подключайте Ваш IRC-клиент к серверу <b>localhost:6668</b> и заходите сказать нам привет на канал
<a href="irc://127.0.0.1:6668/i2p-help">#i2p-help</a> и <a href="irc://127.0.0.1:6668/i2p">#i2p</a>.
</p>
<p>
<b>Не забудьте заглянуть</b> в наш <a href="http://www.i2p2.i2p/faq_ru.html">FAQ</a>.
</p>
</div>

View File

@ -191,7 +191,9 @@ public class Router {
// This is here so that we can get the directory location from the context // This is here so that we can get the directory location from the context
// for the ping file // for the ping file
if (!beginMarkingLiveliness()) { // Check for other router but do not start a thread yet so the update doesn't cause
// a NCDFE
if (!isOnlyRouterRunning()) {
System.err.println("ERROR: There appears to be another router already running!"); System.err.println("ERROR: There appears to be another router already running!");
System.err.println(" Please make sure to shut down old instances before starting up"); System.err.println(" Please make sure to shut down old instances before starting up");
System.err.println(" a new one. If you are positive that no other instance is running,"); System.err.println(" a new one. If you are positive that no other instance is running,");
@ -215,6 +217,11 @@ public class Router {
// overwrite an existing running router's jar files. Other than ours. // overwrite an existing running router's jar files. Other than ours.
installUpdates(); installUpdates();
// ********* Start no threads before here ********* //
//
// NOW we can start the ping file thread.
beginMarkingLiveliness();
// Apps may use this as an easy way to determine if they are in the router JVM // Apps may use this as an easy way to determine if they are in the router JVM
// But context.isRouterContext() is even easier... // But context.isRouterContext() is even easier...
// Both of these as of 0.7.9 // Both of these as of 0.7.9
@ -1163,38 +1170,50 @@ public class Router {
// verify the whole thing first // verify the whole thing first
// we could remember this fails, and not bother restarting, but who cares... // we could remember this fails, and not bother restarting, but who cares...
boolean ok = FileUtil.verifyZip(updateFile); boolean ok = FileUtil.verifyZip(updateFile);
if (ok)
ok = FileUtil.extractZip(updateFile, _context.getBaseDir());
if (ok)
System.out.println("INFO: Update installed");
else
System.out.println("ERROR: Update failed!");
if (!ok) {
// we can't leave the file in place or we'll continually restart, so rename it
File bad = new File(_context.getRouterDir(), "BAD-" + UPDATE_FILE);
boolean renamed = updateFile.renameTo(bad);
if (renamed) {
System.out.println("Moved update file to " + bad.getAbsolutePath());
} else {
System.out.println("Deleting file " + updateFile.getAbsolutePath());
ok = true; // so it will be deleted
}
}
if (ok) { if (ok) {
// This may be useful someday. First added in 0.8.2 // This may be useful someday. First added in 0.8.2
// Moved above the extract so we don't NCDFE
_config.put("router.updateLastInstalled", "" + System.currentTimeMillis()); _config.put("router.updateLastInstalled", "" + System.currentTimeMillis());
saveConfig(); saveConfig();
boolean deleted = updateFile.delete(); ok = FileUtil.extractZip(updateFile, _context.getBaseDir());
if (!deleted) { }
System.out.println("ERROR: Unable to delete the update file!");
updateFile.deleteOnExit(); // Very important - we have now trashed our jars.
} // After this point, do not use any new I2P classes, or they will fail to load
// and we will die with NCDFE.
// Ideally, do not use I2P classes at all, new or not.
try {
if (ok)
System.out.println("INFO: Update installed");
else
System.out.println("ERROR: Update failed!");
if (!ok) {
// we can't leave the file in place or we'll continually restart, so rename it
File bad = new File(_context.getRouterDir(), "BAD-" + UPDATE_FILE);
boolean renamed = updateFile.renameTo(bad);
if (renamed) {
System.out.println("Moved update file to " + bad.getAbsolutePath());
} else {
System.out.println("Deleting file " + updateFile.getAbsolutePath());
ok = true; // so it will be deleted
}
}
if (ok) {
boolean deleted = updateFile.delete();
if (!deleted) {
System.out.println("ERROR: Unable to delete the update file!");
updateFile.deleteOnExit();
}
}
// exit whether ok or not
if (System.getProperty("wrapper.version") != null)
System.out.println("INFO: Restarting after update");
else
System.out.println("WARNING: Exiting after update, restart I2P");
} catch (Throwable t) {
// hide the NCDFE
// hopefully the update file got deleted or we will loop
} }
// exit whether ok or not
if (System.getProperty("wrapper.version") != null)
System.out.println("INFO: Restarting after update");
else
System.out.println("WARNING: Exiting after update, restart I2P");
System.exit(EXIT_HARD_RESTART); System.exit(EXIT_HARD_RESTART);
} }
} }
@ -1230,13 +1249,14 @@ public class Router {
static final long LIVELINESS_DELAY = 60*1000; static final long LIVELINESS_DELAY = 60*1000;
/** /**
* Start a thread that will periodically update the file "router.ping", but if * Check the file "router.ping", but if
* that file already exists and was recently written to, return false as there is * that file already exists and was recently written to, return false as there is
* another instance running * another instance running.
* *
* @return true if the router is the only one running * @return true if the router is the only one running
* @since 0.8.2
*/ */
private boolean beginMarkingLiveliness() { private boolean isOnlyRouterRunning() {
File f = getPingFile(); File f = getPingFile();
if (f.exists()) { if (f.exists()) {
long lastWritten = f.lastModified(); long lastWritten = f.lastModified();
@ -1247,12 +1267,20 @@ public class Router {
return false; return false;
} }
} }
return true;
}
/**
* Start a thread that will periodically update the file "router.ping".
* isOnlyRouterRunning() MUST have been called previously.
*/
private void beginMarkingLiveliness() {
File f = getPingFile();
// not an I2PThread for context creation issues // not an I2PThread for context creation issues
Thread t = new Thread(new MarkLiveliness(_context, this, f)); Thread t = new Thread(new MarkLiveliness(_context, this, f));
t.setName("Mark router liveliness"); t.setName("Mark router liveliness");
t.setDaemon(true); t.setDaemon(true);
t.start(); t.start();
return true;
} }
public static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage"; public static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";

View File

@ -686,6 +686,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
return rv; return rv;
} }
private static final int MIN_ROUTERS = 90;
/** /**
* Determine whether this routerInfo will be accepted as valid and current * Determine whether this routerInfo will be accepted as valid and current
* given what we know now. * given what we know now.
@ -694,9 +696,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
String validate(Hash key, RouterInfo routerInfo) throws IllegalArgumentException { String validate(Hash key, RouterInfo routerInfo) throws IllegalArgumentException {
long now = _context.clock().now(); long now = _context.clock().now();
boolean upLongEnough = _context.router().getUptime() > 60*60*1000; boolean upLongEnough = _context.router().getUptime() > 60*60*1000;
// Once we're over 120 routers, reduce the expiration time down from the default, // Once we're over MIN_ROUTERS routers, reduce the expiration time down from the default,
// as a crude way of limiting memory usage. // as a crude way of limiting memory usage.
// i.e. at 300 routers the expiration time will be about half the default, etc. // i.e. at 2*MIN_ROUTERS routers the expiration time will be about half the default, etc.
// And if we're floodfill, we can keep the expiration really short, since // And if we're floodfill, we can keep the expiration really short, since
// we are always getting the latest published to us. // we are always getting the latest published to us.
// As the net grows this won't be sufficient, and we'll have to implement // As the net grows this won't be sufficient, and we'll have to implement
@ -708,7 +710,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
// _kb.size() includes leasesets but that's ok // _kb.size() includes leasesets but that's ok
adjustedExpiration = Math.min(ROUTER_INFO_EXPIRATION, adjustedExpiration = Math.min(ROUTER_INFO_EXPIRATION,
ROUTER_INFO_EXPIRATION_MIN + ROUTER_INFO_EXPIRATION_MIN +
((ROUTER_INFO_EXPIRATION - ROUTER_INFO_EXPIRATION_MIN) * 120 / (_kb.size() + 1))); ((ROUTER_INFO_EXPIRATION - ROUTER_INFO_EXPIRATION_MIN) * MIN_ROUTERS / (_kb.size() + 1)));
if (!key.equals(routerInfo.getIdentity().getHash())) { if (!key.equals(routerInfo.getIdentity().getHash())) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))

View File

@ -149,7 +149,8 @@ public class WorkingDir {
// this one must be after MIGRATE_BASE // this one must be after MIGRATE_BASE
success &= migrateJettyXml(oldDirf, dirf); success &= migrateJettyXml(oldDirf, dirf);
success &= migrateClientsConfig(oldDirf, dirf); success &= migrateClientsConfig(oldDirf, dirf);
success &= copy(new File(oldDirf, "docs/news.xml"), new SecureDirectory(dirf, "docs")); // for later news.xml updates (we don't copy initialNews.xml over anymore)
success &= (new SecureDirectory(dirf, "docs")) .mkdir();
// Report success or failure // Report success or failure
if (success) { if (success) {

View File

@ -234,7 +234,7 @@ public class EstablishState {
System.arraycopy(_X, 0, xy, 0, _X.length); System.arraycopy(_X, 0, xy, 0, _X.length);
System.arraycopy(_Y, 0, xy, _X.length, _Y.length); System.arraycopy(_Y, 0, xy, _X.length, _Y.length);
Hash hxy = _context.sha().calculateHash(xy); Hash hxy = _context.sha().calculateHash(xy);
_tsB = _context.clock().now()/1000l; // our (Bob's) timestamp in seconds _tsB = (_context.clock().now() + 500) / 1000l; // our (Bob's) timestamp in seconds
byte padding[] = new byte[12]; // the encrypted data needs an extra 12 bytes byte padding[] = new byte[12]; // the encrypted data needs an extra 12 bytes
_context.random().nextBytes(padding); _context.random().nextBytes(padding);
byte toEncrypt[] = new byte[hxy.getData().length+4+padding.length]; byte toEncrypt[] = new byte[hxy.getData().length+4+padding.length];
@ -387,7 +387,7 @@ public class EstablishState {
return; return;
} }
_tsB = DataHelper.fromLong(hXY_tsB, Hash.HASH_LENGTH, 4); // their (Bob's) timestamp in seconds _tsB = DataHelper.fromLong(hXY_tsB, Hash.HASH_LENGTH, 4); // their (Bob's) timestamp in seconds
_tsA = _context.clock().now()/1000; // our (Alice's) timestamp in seconds _tsA = (_context.clock().now() + 500) / 1000; // our (Alice's) timestamp in seconds
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug(prefix()+"h(X+Y) is correct, tsA-tsB=" + (_tsA-_tsB)); _log.debug(prefix()+"h(X+Y) is correct, tsA-tsB=" + (_tsA-_tsB));

View File

@ -1035,7 +1035,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
* *
*/ */
private void readMeta(byte unencrypted[]) { private void readMeta(byte unencrypted[]) {
long ourTs = _context.clock().now()/1000; long ourTs = (_context.clock().now() + 500) / 1000;
long ts = DataHelper.fromLong(unencrypted, 2, 4); long ts = DataHelper.fromLong(unencrypted, 2, 4);
Adler32 crc = new Adler32(); Adler32 crc = new Adler32();
crc.update(unencrypted, 0, unencrypted.length-4); crc.update(unencrypted, 0, unencrypted.length-4);
@ -1068,7 +1068,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
synchronized (_meta) { synchronized (_meta) {
_context.random().nextBytes(_meta); // randomize the uninterpreted, then overwrite w/ data _context.random().nextBytes(_meta); // randomize the uninterpreted, then overwrite w/ data
DataHelper.toLong(_meta, 0, 2, 0); DataHelper.toLong(_meta, 0, 2, 0);
DataHelper.toLong(_meta, 2, 4, _context.clock().now()/1000); DataHelper.toLong(_meta, 2, 4, (_context.clock().now() + 500) / 1000);
Adler32 crc = new Adler32(); Adler32 crc = new Adler32();
crc.update(_meta, 0, _meta.length-4); crc.update(_meta, 0, _meta.length-4);
DataHelper.toLong(_meta, _meta.length-4, 4, crc.getValue()); DataHelper.toLong(_meta, _meta.length-4, 4, crc.getValue());

View File

@ -72,9 +72,6 @@ class InboundEstablishState {
_alicePort = remotePort; _alicePort = remotePort;
_remoteHostId = new RemoteHostId(_aliceIP, _alicePort); _remoteHostId = new RemoteHostId(_aliceIP, _alicePort);
_bobPort = localPort; _bobPort = localPort;
_keyBuilder = null;
_verificationAttempted = false;
_complete = false;
_currentState = STATE_UNKNOWN; _currentState = STATE_UNKNOWN;
_establishBegin = ctx.clock().now(); _establishBegin = ctx.clock().now();
} }

View File

@ -86,13 +86,11 @@ class OutboundEstablishState {
} }
_remotePeer = remotePeer; _remotePeer = remotePeer;
_introKey = introKey; _introKey = introKey;
_keyBuilder = null;
_queuedMessages = new LinkedBlockingQueue(); _queuedMessages = new LinkedBlockingQueue();
_currentState = STATE_UNKNOWN; _currentState = STATE_UNKNOWN;
_establishBegin = ctx.clock().now(); _establishBegin = ctx.clock().now();
_remoteAddress = addr; _remoteAddress = addr;
_introductionNonce = -1; _introductionNonce = -1;
_complete = false;
prepareSessionRequest(); prepareSessionRequest();
if ( (addr != null) && (addr.getIntroducerCount() > 0) ) { if ( (addr != null) && (addr.getIntroducerCount() > 0) ) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))

View File

@ -47,7 +47,6 @@ class OutboundMessageFragments {
_transport = transport; _transport = transport;
// _throttle = throttle; // _throttle = throttle;
_activePeers = new ArrayList(256); _activePeers = new ArrayList(256);
_nextPeer = 0;
_builder = new PacketBuilder(ctx, transport); _builder = new PacketBuilder(ctx, transport);
_alive = true; _alive = true;
// _allowExcess = false; // _allowExcess = false;

View File

@ -47,9 +47,6 @@ class OutboundMessageState {
public OutboundMessageState(I2PAppContext context) { public OutboundMessageState(I2PAppContext context) {
_context = context; _context = context;
_log = _context.logManager().getLog(OutboundMessageState.class); _log = _context.logManager().getLog(OutboundMessageState.class);
_pushCount = 0;
_maxSends = 0;
// _nextSendFragment = 0;
} }
public boolean initialize(OutNetMessage msg) { public boolean initialize(OutNetMessage msg) {

View File

@ -1084,7 +1084,7 @@ class PacketBuilder {
// header // header
data[off] = flagByte; data[off] = flagByte;
off++; off++;
long now = _context.clock().now() / 1000; long now = (_context.clock().now() + 500) / 1000;
DataHelper.toLong(data, off, 4, now); DataHelper.toLong(data, off, 4, now);
// todo: add support for rekeying and extended options // todo: add support for rekeying and extended options
return packet; return packet;

View File

@ -63,8 +63,13 @@ class PeerState {
private boolean _rekeyBeganLocally; private boolean _rekeyBeganLocally;
/** when were the current cipher and MAC keys established/rekeyed? */ /** when were the current cipher and MAC keys established/rekeyed? */
private long _keyEstablishedTime; private long _keyEstablishedTime;
/** how far off is the remote peer from our clock, in milliseconds? */
/**
* How far off is the remote peer from our clock, in milliseconds?
* A positive number means our clock is ahead of theirs.
*/
private long _clockSkew; private long _clockSkew;
/** what is the current receive second, for congestion control? */ /** what is the current receive second, for congestion control? */
private long _currentReceiveSecond; private long _currentReceiveSecond;
/** when did we last send them a packet? */ /** when did we last send them a packet? */
@ -241,36 +246,19 @@ class PeerState {
_context = ctx; _context = ctx;
_log = ctx.logManager().getLog(PeerState.class); _log = ctx.logManager().getLog(PeerState.class);
_transport = transport; _transport = transport;
_remotePeer = null;
_currentMACKey = null;
_currentCipherKey = null;
_nextMACKey = null;
_nextCipherKey = null;
_nextKeyingMaterial = null;
_rekeyBeganLocally = false;
_keyEstablishedTime = -1; _keyEstablishedTime = -1;
_clockSkew = 0;
_currentReceiveSecond = -1; _currentReceiveSecond = -1;
_lastSendTime = -1; _lastSendTime = -1;
_lastReceiveTime = -1; _lastReceiveTime = -1;
_currentACKs = new ConcurrentHashSet(); _currentACKs = new ConcurrentHashSet();
_currentACKsResend = new LinkedBlockingQueue(); _currentACKsResend = new LinkedBlockingQueue();
_currentSecondECNReceived = false;
_remoteWantsPreviousACKs = false;
_sendWindowBytes = DEFAULT_SEND_WINDOW_BYTES; _sendWindowBytes = DEFAULT_SEND_WINDOW_BYTES;
_sendWindowBytesRemaining = DEFAULT_SEND_WINDOW_BYTES; _sendWindowBytesRemaining = DEFAULT_SEND_WINDOW_BYTES;
_slowStartThreshold = MAX_SEND_WINDOW_BYTES/2; _slowStartThreshold = MAX_SEND_WINDOW_BYTES/2;
_lastSendRefill = _context.clock().now(); _lastSendRefill = _context.clock().now();
_receivePeriodBegin = _lastSendRefill; _receivePeriodBegin = _lastSendRefill;
_sendBps = 0;
_sendBytes = 0;
_receiveBps = 0;
_lastCongestionOccurred = -1; _lastCongestionOccurred = -1;
_remoteIP = null;
_remotePort = -1; _remotePort = -1;
_remoteRequiresIntroduction = false;
_weRelayToThemAs = 0;
_theyRelayToUsAs = 0;
_mtu = getDefaultMTU(); _mtu = getDefaultMTU();
_mtuReceive = _mtu; _mtuReceive = _mtu;
_mtuLastChecked = -1; _mtuLastChecked = -1;
@ -278,19 +266,8 @@ class PeerState {
_rto = MIN_RTO; _rto = MIN_RTO;
_rtt = _rto/2; _rtt = _rto/2;
_rttDeviation = _rtt; _rttDeviation = _rtt;
_messagesReceived = 0;
_messagesSent = 0;
_packetsTransmitted = 0;
_packetsRetransmitted = 0;
_packetRetransmissionRate = 0;
_retransmissionPeriodStart = 0;
_packetsReceived = 0;
_packetsReceivedDuplicate = 0;
_inboundMessages = new HashMap(8); _inboundMessages = new HashMap(8);
_outboundMessages = new ArrayList(32); _outboundMessages = new ArrayList(32);
_dead = false;
_isInbound = false;
_lastIntroducerTime = 0;
_context.statManager().createRateStat("udp.congestionOccurred", "How large the cwin was when congestion occurred (duration == sendBps)", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.congestionOccurred", "How large the cwin was when congestion occurred (duration == sendBps)", "udp", UDPTransport.RATES);
_context.statManager().createRateStat("udp.congestedRTO", "retransmission timeout after congestion (duration == rtt dev)", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.congestedRTO", "retransmission timeout after congestion (duration == rtt dev)", "udp", UDPTransport.RATES);
_context.statManager().createRateStat("udp.sendACKPartial", "Number of partial ACKs sent (duration == number of full ACKs in that ack packet)", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.sendACKPartial", "Number of partial ACKs sent (duration == number of full ACKs in that ack packet)", "udp", UDPTransport.RATES);
@ -346,8 +323,13 @@ class PeerState {
public boolean getRekeyBeganLocally() { return _rekeyBeganLocally; } public boolean getRekeyBeganLocally() { return _rekeyBeganLocally; }
/** when were the current cipher and MAC keys established/rekeyed? */ /** when were the current cipher and MAC keys established/rekeyed? */
public long getKeyEstablishedTime() { return _keyEstablishedTime; } public long getKeyEstablishedTime() { return _keyEstablishedTime; }
/** how far off is the remote peer from our clock, in milliseconds? */
/**
* How far off is the remote peer from our clock, in milliseconds?
* A positive number means our clock is ahead of theirs.
*/
public long getClockSkew() { return _clockSkew ; } public long getClockSkew() { return _clockSkew ; }
/** what is the current receive second, for congestion control? */ /** what is the current receive second, for congestion control? */
public long getCurrentReceiveSecond() { return _currentReceiveSecond; } public long getCurrentReceiveSecond() { return _currentReceiveSecond; }
/** when did we last send them a packet? */ /** when did we last send them a packet? */
@ -444,10 +426,17 @@ class PeerState {
public void setRekeyBeganLocally(boolean local) { _rekeyBeganLocally = local; } public void setRekeyBeganLocally(boolean local) { _rekeyBeganLocally = local; }
/** when were the current cipher and MAC keys established/rekeyed? */ /** when were the current cipher and MAC keys established/rekeyed? */
public void setKeyEstablishedTime(long when) { _keyEstablishedTime = when; } public void setKeyEstablishedTime(long when) { _keyEstablishedTime = when; }
/** how far off is the remote peer from our clock, in milliseconds? */
/**
* Update the moving-average clock skew based on the current difference.
* The raw skew will be adjusted for RTT/2 here.
* @param skew milliseconds, NOT adjusted for RTT.
* A positive number means our clock is ahead of theirs.
*/
public void adjustClockSkew(long skew) { public void adjustClockSkew(long skew) {
_clockSkew = (long) (0.9*(float)_clockSkew + 0.1*(float)skew); _clockSkew = (long) (0.9*(float)_clockSkew + 0.1*(float)(skew - (_rtt / 2)));
} }
/** what is the current receive second, for congestion control? */ /** what is the current receive second, for congestion control? */
public void setCurrentReceiveSecond(long sec) { _currentReceiveSecond = sec; } public void setCurrentReceiveSecond(long sec) { _currentReceiveSecond = sec; }
/** when did we last send them a packet? */ /** when did we last send them a packet? */
@ -679,6 +668,7 @@ class PeerState {
* *
*/ */
public List<Long> getCurrentFullACKs() { public List<Long> getCurrentFullACKs() {
// no such element exception seen here
ArrayList<Long> rv = new ArrayList(_currentACKs); ArrayList<Long> rv = new ArrayList(_currentACKs);
// include some for retransmission // include some for retransmission
rv.addAll(_currentACKsResend); rv.addAll(_currentACKsResend);

View File

@ -118,8 +118,6 @@ class PeerTestManager {
_activeTests = new ConcurrentHashMap(); _activeTests = new ConcurrentHashMap();
_recentTests = new LinkedBlockingQueue(); _recentTests = new LinkedBlockingQueue();
_packetBuilder = new PacketBuilder(context, transport); _packetBuilder = new PacketBuilder(context, transport);
_currentTest = null;
_currentTestComplete = false;
_context.statManager().createRateStat("udp.statusKnownCharlie", "How often the bob we pick passes us to a charlie we already have a session with?", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.statusKnownCharlie", "How often the bob we pick passes us to a charlie we already have a session with?", "udp", UDPTransport.RATES);
_context.statManager().createRateStat("udp.receiveTestReply", "How often we get a reply to our peer test?", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.receiveTestReply", "How often we get a reply to our peer test?", "udp", UDPTransport.RATES);
_context.statManager().createRateStat("udp.receiveTest", "How often we get a packet requesting us to participate in a peer test?", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.receiveTest", "How often we get a packet requesting us to participate in a peer test?", "udp", UDPTransport.RATES);

View File

@ -76,7 +76,6 @@ class TimedWeightedPriorityMessageQueue implements MessageQueue, OutboundMessage
} }
_alive = true; _alive = true;
_nextLock = this; _nextLock = this;
_nextQueue = 0;
_chokedPeers = Collections.synchronizedSet(new HashSet(16)); _chokedPeers = Collections.synchronizedSet(new HashSet(16));
_listener = lsnr; _listener = lsnr;
_context.statManager().createRateStat("udp.timeToEntrance", "Message lifetime until it reaches the UDP system", "udp", UDPTransport.RATES); _context.statManager().createRateStat("udp.timeToEntrance", "Message lifetime until it reaches the UDP system", "udp", UDPTransport.RATES);

View File

@ -177,7 +177,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_peersByIdent = new ConcurrentHashMap(128); _peersByIdent = new ConcurrentHashMap(128);
_peersByRemoteHost = new ConcurrentHashMap(128); _peersByRemoteHost = new ConcurrentHashMap(128);
_dropList = new ConcurrentHashSet(2); _dropList = new ConcurrentHashSet(2);
_endpoint = null;
// See comments in DQAT.java // See comments in DQAT.java
if (USE_PRIORITY) { if (USE_PRIORITY) {