forked from I2P_Developers/i2p.i2p
merge of '398a24f487b61ef778a2e849660e953ef7e43b39'
and '598d00efae4c9b675b64fd626bc2eab2b921e0c5'
This commit is contained in:
@ -152,6 +152,9 @@ public class I2PSnarkUtil {
|
||||
*/
|
||||
synchronized public boolean connect() {
|
||||
if (_manager == null) {
|
||||
// try to find why reconnecting after stop
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Connecting to I2P", new Exception("I did it"));
|
||||
Properties opts = new Properties();
|
||||
if (_opts != null) {
|
||||
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
||||
@ -163,6 +166,10 @@ public class I2PSnarkUtil {
|
||||
opts.setProperty("inbound.nickname", "I2PSnark");
|
||||
if (opts.getProperty("outbound.nickname") == null)
|
||||
opts.setProperty("outbound.nickname", "I2PSnark");
|
||||
// Dont do this for now, it is set in I2PSocketEepGet for announces,
|
||||
// we don't need fast handshake for peer connections.
|
||||
//if (opts.getProperty("i2p.streaming.connectDelay") == null)
|
||||
// opts.setProperty("i2p.streaming.connectDelay", "500");
|
||||
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
|
||||
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
|
||||
@ -186,6 +193,7 @@ public class I2PSnarkUtil {
|
||||
*/
|
||||
public void disconnect() {
|
||||
I2PSocketManager mgr = _manager;
|
||||
// FIXME this can cause race NPEs elsewhere
|
||||
_manager = null;
|
||||
_shitlist.clear();
|
||||
mgr.destroySocketManager();
|
||||
@ -197,6 +205,9 @@ public class I2PSnarkUtil {
|
||||
|
||||
/** connect to the given destination */
|
||||
I2PSocket connect(PeerID peer) throws IOException {
|
||||
I2PSocketManager mgr = _manager;
|
||||
if (mgr == null)
|
||||
throw new IOException("No socket manager");
|
||||
Destination addr = peer.getAddress();
|
||||
if (addr == null)
|
||||
throw new IOException("Null address");
|
||||
|
@ -56,8 +56,8 @@ public class Peer implements Comparable
|
||||
private long _id;
|
||||
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
||||
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
||||
private long uploaded_old[] = {-1,-1,-1};
|
||||
private long downloaded_old[] = {-1,-1,-1};
|
||||
|
||||
/**
|
||||
* Creates a disconnected peer given a PeerID, your own id and the
|
||||
@ -117,10 +117,15 @@ public class Peer implements Comparable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns socket (for debug printing)
|
||||
* @return socket debug string (for debug printing)
|
||||
*/
|
||||
public String getSocket()
|
||||
{
|
||||
if (state != null) {
|
||||
String r = state.getRequests();
|
||||
if (r != null)
|
||||
return sock.toString() + "<br>Requests: " + r;
|
||||
}
|
||||
return sock.toString();
|
||||
}
|
||||
|
||||
@ -387,6 +392,37 @@ public class Peer implements Comparable
|
||||
s.havePiece(piece);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the other side that we are no longer interested in any of
|
||||
* the outstanding requests (if any) for this piece.
|
||||
* @since 0.8.1
|
||||
*/
|
||||
void cancel(int piece) {
|
||||
PeerState s = state;
|
||||
if (s != null)
|
||||
s.cancelPiece(piece);
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we currently requesting the piece?
|
||||
* @since 0.8.1
|
||||
*/
|
||||
boolean isRequesting(int p) {
|
||||
PeerState s = state;
|
||||
return s != null && s.isRequesting(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the request queue.
|
||||
* Call after adding wanted pieces.
|
||||
* @since 0.8.1
|
||||
*/
|
||||
void request() {
|
||||
PeerState s = state;
|
||||
if (s != null)
|
||||
s.addRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the peer is interested in pieces we have. Returns
|
||||
* false if not connected.
|
||||
@ -545,17 +581,8 @@ public class Peer implements Comparable
|
||||
*/
|
||||
public void setRateHistory(long up, long down)
|
||||
{
|
||||
setRate(up, uploaded_old);
|
||||
setRate(down, downloaded_old);
|
||||
}
|
||||
|
||||
private void setRate(long val, long array[])
|
||||
{
|
||||
synchronized(array) {
|
||||
for (int i = RATE_DEPTH-1; i > 0; i--)
|
||||
array[i] = array[i-1];
|
||||
array[0] = val;
|
||||
}
|
||||
PeerCoordinator.setRate(up, uploaded_old);
|
||||
PeerCoordinator.setRate(down, downloaded_old);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -563,28 +590,11 @@ public class Peer implements Comparable
|
||||
*/
|
||||
public long getUploadRate()
|
||||
{
|
||||
return getRate(uploaded_old);
|
||||
return PeerCoordinator.getRate(uploaded_old);
|
||||
}
|
||||
|
||||
public long getDownloadRate()
|
||||
{
|
||||
return getRate(downloaded_old);
|
||||
}
|
||||
|
||||
private long getRate(long array[])
|
||||
{
|
||||
long rate = 0;
|
||||
int i = 0;
|
||||
synchronized(array) {
|
||||
for ( ; i < RATE_DEPTH; i++){
|
||||
if (array[i] < 0)
|
||||
break;
|
||||
rate += array[i];
|
||||
return PeerCoordinator.getRate(downloaded_old);
|
||||
}
|
||||
}
|
||||
if (i == 0)
|
||||
return 0;
|
||||
return rate / (i * CHECK_PERIOD / 1000);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ public class PeerCoordinator implements PeerListener
|
||||
|
||||
private long uploaded;
|
||||
private long downloaded;
|
||||
final static int RATE_DEPTH = 6; // make following arrays RATE_DEPTH long
|
||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
||||
final static int RATE_DEPTH = 3; // make following arrays RATE_DEPTH long
|
||||
private long uploaded_old[] = {-1,-1,-1};
|
||||
private long downloaded_old[] = {-1,-1,-1};
|
||||
|
||||
// synchronize on this when changing peers or downloaders
|
||||
final List<Peer> peers = new ArrayList();
|
||||
@ -78,6 +78,7 @@ public class PeerCoordinator implements PeerListener
|
||||
|
||||
private final CoordinatorListener listener;
|
||||
public I2PSnarkUtil _util;
|
||||
private static final Random _random = I2PAppContext.getGlobalContext().random();
|
||||
|
||||
public String trackerProblems = null;
|
||||
public int trackerSeenPeers = 0;
|
||||
@ -97,20 +98,29 @@ public class PeerCoordinator implements PeerListener
|
||||
// Install a timer to check the uploaders.
|
||||
// Randomize the first start time so multiple tasks are spread out,
|
||||
// this will help the behavior with global limits
|
||||
Random r = I2PAppContext.getGlobalContext().random();
|
||||
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
|
||||
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
|
||||
}
|
||||
|
||||
// only called externally from Storage after the double-check fails
|
||||
public void setWantedPieces()
|
||||
{
|
||||
// Make a list of pieces
|
||||
// FIXME synchronize, clear and re-add instead?
|
||||
// Don't replace something we are synchronizing on.
|
||||
wantedPieces = new ArrayList();
|
||||
BitField bitfield = storage.getBitField();
|
||||
for(int i = 0; i < metainfo.getPieces(); i++)
|
||||
if (!bitfield.get(i))
|
||||
wantedPieces.add(new Piece(i));
|
||||
Collections.shuffle(wantedPieces);
|
||||
int[] pri = storage.getPiecePriorities();
|
||||
for(int i = 0; i < metainfo.getPieces(); i++) {
|
||||
// only add if we don't have and the priority is >= 0
|
||||
if ((!bitfield.get(i)) &&
|
||||
(pri == null || pri[i] >= 0)) {
|
||||
Piece p = new Piece(i);
|
||||
if (pri != null)
|
||||
p.setPriority(pri[i]);
|
||||
wantedPieces.add(p);
|
||||
}
|
||||
}
|
||||
Collections.shuffle(wantedPieces, _random);
|
||||
}
|
||||
|
||||
public Storage getStorage() { return storage; }
|
||||
@ -183,7 +193,7 @@ public class PeerCoordinator implements PeerListener
|
||||
setRate(down, downloaded_old);
|
||||
}
|
||||
|
||||
private static void setRate(long val, long array[])
|
||||
static void setRate(long val, long array[])
|
||||
{
|
||||
synchronized(array) {
|
||||
for (int i = RATE_DEPTH-1; i > 0; i--)
|
||||
@ -214,20 +224,23 @@ public class PeerCoordinator implements PeerListener
|
||||
return (r * 1000) / CHECK_PERIOD;
|
||||
}
|
||||
|
||||
private long getRate(long array[])
|
||||
static long getRate(long array[])
|
||||
{
|
||||
long rate = 0;
|
||||
int i = 0;
|
||||
int factor = 0;
|
||||
synchronized(array) {
|
||||
for ( ; i < RATE_DEPTH; i++) {
|
||||
if (array[i] < 0)
|
||||
break;
|
||||
rate += array[i];
|
||||
int f = RATE_DEPTH - i;
|
||||
rate += array[i] * f;
|
||||
factor += f;
|
||||
}
|
||||
}
|
||||
if (i == 0)
|
||||
return 0;
|
||||
return rate / (i * CHECK_PERIOD / 1000);
|
||||
return rate / (factor * CHECK_PERIOD / 1000);
|
||||
}
|
||||
|
||||
public MetaInfo getMetaInfo()
|
||||
@ -454,7 +467,7 @@ public class PeerCoordinator implements PeerListener
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we don't have the given piece yet.
|
||||
* @return true if we still want the given piece
|
||||
*/
|
||||
public boolean gotHave(Peer peer, int piece)
|
||||
{
|
||||
@ -499,6 +512,12 @@ public class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
private static final int END_GAME_THRESHOLD = 8;
|
||||
|
||||
/**
|
||||
* Max number of peers to get a piece from when in end game
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static final int MAX_PARALLEL_REQUESTS = 4;
|
||||
|
||||
/**
|
||||
* Returns one of pieces in the given BitField that is still wanted or
|
||||
* -1 if none of the given pieces are wanted.
|
||||
@ -520,6 +539,9 @@ public class PeerCoordinator implements PeerListener
|
||||
while (piece == null && it.hasNext())
|
||||
{
|
||||
Piece p = it.next();
|
||||
// sorted by priority, so when we hit a disabled piece we are done
|
||||
if (p.isDisabled())
|
||||
break;
|
||||
if (havePieces.get(p.getId()) && !p.isRequested())
|
||||
{
|
||||
piece = p;
|
||||
@ -538,13 +560,30 @@ public class PeerCoordinator implements PeerListener
|
||||
if (wantedPieces.size() > END_GAME_THRESHOLD)
|
||||
return -1; // nothing to request and not in end game
|
||||
// let's not all get on the same piece
|
||||
Collections.shuffle(requested);
|
||||
// Even better would be to sort by number of requests
|
||||
Collections.shuffle(requested, _random);
|
||||
Iterator<Piece> it2 = requested.iterator();
|
||||
while (piece == null && it2.hasNext())
|
||||
{
|
||||
Piece p = it2.next();
|
||||
if (havePieces.get(p.getId()))
|
||||
{
|
||||
if (havePieces.get(p.getId())) {
|
||||
// limit number of parallel requests
|
||||
int requestedCount = 0;
|
||||
synchronized(peers) {
|
||||
for (Peer pr : peers) {
|
||||
if (pr.isRequesting(p.getId())) {
|
||||
if (pr.equals(peer)) {
|
||||
// don't give it to him again
|
||||
requestedCount = MAX_PARALLEL_REQUESTS;
|
||||
break;
|
||||
}
|
||||
if (++requestedCount >= MAX_PARALLEL_REQUESTS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (requestedCount >= MAX_PARALLEL_REQUESTS)
|
||||
continue;
|
||||
piece = p;
|
||||
}
|
||||
}
|
||||
@ -555,7 +594,7 @@ public class PeerCoordinator implements PeerListener
|
||||
// + " wanted = " + wantedPieces + " peerHas = " + havePieces);
|
||||
return -1; //If we still can't find a piece we want, so be it.
|
||||
} else {
|
||||
// Should be a lot smarter here - limit # of parallel attempts and
|
||||
// Should be a lot smarter here -
|
||||
// share blocks rather than starting from 0 with each peer.
|
||||
// This is where the flaws of the snark data model are really exposed.
|
||||
// Could also randomize within the duplicate set rather than strict rarest-first
|
||||
@ -563,11 +602,83 @@ public class PeerCoordinator implements PeerListener
|
||||
_log.debug("parallel request (end game?) for " + peer + ": piece = " + piece);
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Now requesting: piece " + piece + " priority " + piece.getPriority());
|
||||
piece.setRequested(true);
|
||||
return piece.getId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps file priorities to piece priorities.
|
||||
* Call after updating file priorities Storage.setPriority()
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void updatePiecePriorities() {
|
||||
int[] pri = storage.getPiecePriorities();
|
||||
if (pri == null) {
|
||||
_log.debug("Updated piece priorities called but no priorities to set?");
|
||||
return;
|
||||
}
|
||||
synchronized(wantedPieces) {
|
||||
// Add incomplete and previously unwanted pieces to the list
|
||||
// Temp to avoid O(n**2)
|
||||
BitField want = new BitField(pri.length);
|
||||
for (Piece p : wantedPieces) {
|
||||
want.set(p.getId());
|
||||
}
|
||||
BitField bitfield = storage.getBitField();
|
||||
for (int i = 0; i < pri.length; i++) {
|
||||
if (pri[i] >= 0 && !bitfield.get(i)) {
|
||||
if (!want.get(i)) {
|
||||
Piece piece = new Piece(i);
|
||||
wantedPieces.add(piece);
|
||||
// As connections are already up, new Pieces will
|
||||
// not have their PeerID list populated, so do that.
|
||||
synchronized(peers) {
|
||||
for (Peer p : peers) {
|
||||
PeerState s = p.state;
|
||||
if (s != null) {
|
||||
BitField bf = s.bitfield;
|
||||
if (bf != null && bf.get(i))
|
||||
piece.addPeer(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// now set the new priorities and remove newly unwanted pieces
|
||||
for (Iterator<Piece> iter = wantedPieces.iterator(); iter.hasNext(); ) {
|
||||
Piece p = iter.next();
|
||||
int priority = pri[p.getId()];
|
||||
if (priority >= 0) {
|
||||
p.setPriority(priority);
|
||||
} else {
|
||||
iter.remove();
|
||||
// cancel all peers
|
||||
synchronized(peers) {
|
||||
for (Peer peer : peers) {
|
||||
peer.cancel(p.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Updated piece priorities, now wanted: " + wantedPieces);
|
||||
// if we added pieces, they will be in-order unless we shuffle
|
||||
Collections.shuffle(wantedPieces, _random);
|
||||
|
||||
// update request queues, in case we added wanted pieces
|
||||
// and we were previously uninterested
|
||||
synchronized(peers) {
|
||||
for (Peer peer : peers) {
|
||||
peer.request();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the requested piece or null of
|
||||
* the piece is unknown.
|
||||
@ -632,6 +743,9 @@ public class PeerCoordinator implements PeerListener
|
||||
|
||||
// No need to announce have piece to peers.
|
||||
// Assume we got a good piece, we don't really care anymore.
|
||||
// Well, this could be caused by a change in priorities, so
|
||||
// only return true if we already have it, otherwise might as well keep it.
|
||||
if (storage.getBitField().get(piece))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -639,6 +753,7 @@ public class PeerCoordinator implements PeerListener
|
||||
{
|
||||
if (storage.putPiece(piece, bs))
|
||||
{
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Got valid piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
|
||||
}
|
||||
else
|
||||
|
@ -55,12 +55,10 @@ class PeerState
|
||||
final PeerConnectionOut out;
|
||||
|
||||
// Outstanding request
|
||||
private final List outstandingRequests = new ArrayList();
|
||||
private final List<Request> outstandingRequests = new ArrayList();
|
||||
/** the tail (NOT the head) of the request queue */
|
||||
private Request lastRequest = null;
|
||||
|
||||
// If we have te resend outstanding requests (true after we got choked).
|
||||
private boolean resend = false;
|
||||
|
||||
private final static int MAX_PIPELINE = 5; // this is for outbound requests
|
||||
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
|
||||
public final static int PARTSIZE = 16*1024; // outbound request
|
||||
@ -91,14 +89,13 @@ class PeerState
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(peer + " rcv " + (choke ? "" : "un") + "choked");
|
||||
|
||||
boolean resend = choked && !choke;
|
||||
choked = choke;
|
||||
if (choked)
|
||||
resend = true;
|
||||
|
||||
listener.gotChoke(peer, choke);
|
||||
|
||||
if (!choked && interesting)
|
||||
request();
|
||||
if (interesting && !choked)
|
||||
request(resend);
|
||||
}
|
||||
|
||||
void interestedMessage(boolean interest)
|
||||
@ -278,7 +275,7 @@ class PeerState
|
||||
synchronized private int getFirstOutstandingRequest(int piece)
|
||||
{
|
||||
for (int i = 0; i < outstandingRequests.size(); i++)
|
||||
if (((Request)outstandingRequests.get(i)).piece == piece)
|
||||
if (outstandingRequests.get(i).piece == piece)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
@ -313,12 +310,12 @@ class PeerState
|
||||
Request req;
|
||||
synchronized(this)
|
||||
{
|
||||
req = (Request)outstandingRequests.get(r);
|
||||
req = outstandingRequests.get(r);
|
||||
while (req.piece == piece && req.off != begin
|
||||
&& r < outstandingRequests.size() - 1)
|
||||
{
|
||||
r++;
|
||||
req = (Request)outstandingRequests.get(r);
|
||||
req = outstandingRequests.get(r);
|
||||
}
|
||||
|
||||
// Something wrong?
|
||||
@ -342,7 +339,7 @@ class PeerState
|
||||
+ ", wanted for peer: " + peer);
|
||||
for (int i = 0; i < r; i++)
|
||||
{
|
||||
Request dropReq = (Request)outstandingRequests.remove(0);
|
||||
Request dropReq = outstandingRequests.remove(0);
|
||||
outstandingRequests.add(dropReq);
|
||||
if (!choked)
|
||||
out.sendRequest(dropReq);
|
||||
@ -366,11 +363,11 @@ class PeerState
|
||||
{
|
||||
Request req = null;
|
||||
for (int i = 0; i < outstandingRequests.size(); i++) {
|
||||
Request r1 = (Request)outstandingRequests.get(i);
|
||||
Request r1 = outstandingRequests.get(i);
|
||||
int j = getFirstOutstandingRequest(r1.piece);
|
||||
if (j == -1)
|
||||
continue;
|
||||
Request r2 = (Request)outstandingRequests.get(j);
|
||||
Request r2 = outstandingRequests.get(j);
|
||||
if (r2.off > 0 && ((req == null) || (r2.off > req.off)))
|
||||
req = r2;
|
||||
}
|
||||
@ -398,7 +395,7 @@ class PeerState
|
||||
}
|
||||
Request req = null;
|
||||
for (int i = 0; i < size; i++) {
|
||||
Request r1 = (Request)outstandingRequests.get(i);
|
||||
Request r1 = outstandingRequests.get(i);
|
||||
if (pc != r1.piece) {
|
||||
pc = r1.piece;
|
||||
arr[pos++] = pc;
|
||||
@ -423,31 +420,18 @@ class PeerState
|
||||
+ " length: " + bs.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* We now have this piece.
|
||||
* Tell the peer and cancel any requests for the piece.
|
||||
*/
|
||||
void havePiece(int piece)
|
||||
{
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Tell " + peer + " havePiece(" + piece + ")");
|
||||
|
||||
synchronized(this)
|
||||
{
|
||||
// Tell the other side that we are no longer interested in any of
|
||||
// the outstanding requests for this piece.
|
||||
if (lastRequest != null && lastRequest.piece == piece)
|
||||
lastRequest = null;
|
||||
|
||||
Iterator it = outstandingRequests.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Request req = (Request)it.next();
|
||||
if (req.piece == piece)
|
||||
{
|
||||
it.remove();
|
||||
// Send cancel even when we are choked to make sure that it is
|
||||
// really never ever send.
|
||||
out.sendCancel(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
cancelPiece(piece);
|
||||
|
||||
// Tell the other side that we really have this piece.
|
||||
out.sendHave(piece);
|
||||
@ -463,8 +447,46 @@ class PeerState
|
||||
}
|
||||
}
|
||||
|
||||
// Starts or resumes requesting pieces.
|
||||
private void request()
|
||||
/**
|
||||
* Tell the other side that we are no longer interested in any of
|
||||
* the outstanding requests (if any) for this piece.
|
||||
* @since 0.8.1
|
||||
*/
|
||||
synchronized void cancelPiece(int piece) {
|
||||
if (lastRequest != null && lastRequest.piece == piece)
|
||||
lastRequest = null;
|
||||
|
||||
Iterator<Request> it = outstandingRequests.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Request req = it.next();
|
||||
if (req.piece == piece)
|
||||
{
|
||||
it.remove();
|
||||
// Send cancel even when we are choked to make sure that it is
|
||||
// really never ever send.
|
||||
out.sendCancel(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we currently requesting the piece?
|
||||
* @since 0.8.1
|
||||
*/
|
||||
synchronized boolean isRequesting(int piece) {
|
||||
for (Request req : outstandingRequests) {
|
||||
if (req.piece == piece)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts or resumes requesting pieces.
|
||||
* @param resend should we resend outstanding requests?
|
||||
*/
|
||||
private void request(boolean resend)
|
||||
{
|
||||
// Are there outstanding requests that have to be resend?
|
||||
if (resend)
|
||||
@ -472,7 +494,6 @@ class PeerState
|
||||
synchronized (this) {
|
||||
out.sendRequests(outstandingRequests);
|
||||
}
|
||||
resend = false;
|
||||
}
|
||||
|
||||
// Add/Send some more requests if necessary.
|
||||
@ -481,8 +502,11 @@ class PeerState
|
||||
|
||||
/**
|
||||
* Adds a new request to the outstanding requests list.
|
||||
* Then send interested if we weren't.
|
||||
* Then send new requests if not choked.
|
||||
* If nothing to request, send not interested if we were.
|
||||
*/
|
||||
synchronized private void addRequest()
|
||||
synchronized void addRequest()
|
||||
{
|
||||
boolean more_pieces = true;
|
||||
while (more_pieces)
|
||||
@ -526,6 +550,7 @@ class PeerState
|
||||
/**
|
||||
* Starts requesting first chunk of next piece. Returns true if
|
||||
* something has been added to the requests, false otherwise.
|
||||
* Caller should synchronize.
|
||||
*/
|
||||
private boolean requestNextPiece()
|
||||
{
|
||||
@ -553,11 +578,10 @@ class PeerState
|
||||
}
|
||||
}
|
||||
int nextPiece = listener.wantPiece(peer, bitfield);
|
||||
if (nextPiece != -1
|
||||
&& (lastRequest == null || lastRequest.piece != nextPiece)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(peer + " want piece " + nextPiece);
|
||||
if (nextPiece != -1
|
||||
&& (lastRequest == null || lastRequest.piece != nextPiece))
|
||||
{
|
||||
// Fail safe to make sure we are interested
|
||||
// When we transition into the end game we may not be interested...
|
||||
if (!interesting) {
|
||||
@ -584,9 +608,25 @@ class PeerState
|
||||
out.sendRequest(req);
|
||||
lastRequest = req;
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(peer + " no more pieces to request");
|
||||
}
|
||||
}
|
||||
|
||||
// failsafe
|
||||
if (outstandingRequests.isEmpty())
|
||||
lastRequest = null;
|
||||
|
||||
// If we are not in the end game, we may run out of things to request
|
||||
// because we are asking other peers. Set not-interesting now rather than
|
||||
// wait for those other requests to be satisfied via havePiece()
|
||||
if (interesting && lastRequest == null) {
|
||||
interesting = false;
|
||||
out.sendInterest(false);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(peer + " nothing more to request, now uninteresting");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -601,7 +641,7 @@ class PeerState
|
||||
out.sendInterest(interest);
|
||||
|
||||
if (interesting && !choked)
|
||||
request();
|
||||
request(true); // we shouldnt have any pending requests, but if we do, resend them
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,4 +667,16 @@ class PeerState
|
||||
if (interesting && !choked)
|
||||
out.retransmitRequests(outstandingRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* debug
|
||||
* @return string or null
|
||||
* @since 0.8.1
|
||||
*/
|
||||
synchronized String getRequests() {
|
||||
if (outstandingRequests.isEmpty())
|
||||
return null;
|
||||
else
|
||||
return outstandingRequests.toString();
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,31 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
|
||||
public class Piece implements Comparable {
|
||||
|
||||
private int id;
|
||||
private Set peers;
|
||||
private Set<PeerID> peers;
|
||||
private boolean requested;
|
||||
/** @since 0.8.1 */
|
||||
private int priority;
|
||||
|
||||
public Piece(int id) {
|
||||
this.id = id;
|
||||
this.peers = Collections.synchronizedSet(new HashSet());
|
||||
this.requested = false;
|
||||
this.peers = new ConcurrentHashSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Highest priority first,
|
||||
* then rarest first
|
||||
*/
|
||||
public int compareTo(Object o) throws ClassCastException {
|
||||
int pdiff = ((Piece)o).priority - this.priority; // reverse
|
||||
if (pdiff != 0)
|
||||
return pdiff;
|
||||
return this.peers.size() - ((Piece)o).peers.size();
|
||||
}
|
||||
|
||||
@ -37,12 +46,25 @@ public class Piece implements Comparable {
|
||||
}
|
||||
|
||||
public int getId() { return this.id; }
|
||||
public Set getPeers() { return this.peers; }
|
||||
/** @deprecated unused */
|
||||
public Set<PeerID> getPeers() { return this.peers; }
|
||||
public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
|
||||
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
|
||||
public boolean isRequested() { return this.requested; }
|
||||
public void setRequested(boolean requested) { this.requested = requested; }
|
||||
|
||||
/** @return default 0 @since 0.8.1 */
|
||||
public int getPriority() { return this.priority; }
|
||||
|
||||
/** @since 0.8.1 */
|
||||
public void setPriority(int p) { this.priority = p; }
|
||||
|
||||
/** @since 0.8.1 */
|
||||
public boolean isDisabled() { return this.priority < 0; }
|
||||
|
||||
/** @since 0.8.1 */
|
||||
public void setDisabled() { this.priority = -1; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(id);
|
||||
|
@ -54,6 +54,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public static final String PROP_DIR = "i2psnark.dir";
|
||||
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
||||
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||
|
||||
private static final String CONFIG_FILE = "i2psnark.config";
|
||||
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
||||
@ -510,6 +511,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
torrent = new Snark(_util, filename, null, -1, null, null, this,
|
||||
_peerCoordinatorSet, _connectionAcceptor,
|
||||
false, dataDir.getPath());
|
||||
loadSavedFilePriorities(torrent);
|
||||
torrent.completeListener = this;
|
||||
synchronized (_snarks) {
|
||||
_snarks.put(filename, torrent);
|
||||
@ -587,6 +589,33 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
return new BitField(bitfield, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the saved priorities for a torrent from the config file.
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void loadSavedFilePriorities(Snark snark) {
|
||||
MetaInfo metainfo = snark.meta;
|
||||
if (metainfo.getFiles() == null)
|
||||
return;
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String pri = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
|
||||
if (pri == null)
|
||||
return;
|
||||
int filecount = metainfo.getFiles().size();
|
||||
int[] rv = new int[filecount];
|
||||
String[] arr = pri.split(",");
|
||||
for (int i = 0; i < filecount && i < arr.length; i++) {
|
||||
if (arr[i].length() > 0) {
|
||||
try {
|
||||
rv[i] = Integer.parseInt(arr[i]);
|
||||
} catch (Throwable t) {}
|
||||
}
|
||||
}
|
||||
snark.storage.setFilePriorities(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the completion status of a torrent and the current time in the config file
|
||||
* in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
|
||||
@ -595,8 +624,9 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
* The time is a standard long converted to string.
|
||||
* The status is either a bitfield converted to Base64 or "." for a completed
|
||||
* torrent to save space in the config file and in memory.
|
||||
* @param priorities may be null
|
||||
*/
|
||||
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) {
|
||||
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
@ -609,6 +639,34 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
bfs = Base64.encode(bf);
|
||||
}
|
||||
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
|
||||
|
||||
// now the file priorities
|
||||
String prop = PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX;
|
||||
if (priorities != null) {
|
||||
boolean nonzero = false;
|
||||
for (int i = 0; i < priorities.length; i++) {
|
||||
if (priorities[i] != 0) {
|
||||
nonzero = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nonzero) {
|
||||
// generate string like -5,,4,3,,,,,,-2 where no number is zero.
|
||||
StringBuilder buf = new StringBuilder(2 * priorities.length);
|
||||
for (int i = 0; i < priorities.length; i++) {
|
||||
if (priorities[i] != 0)
|
||||
buf.append(Integer.toString(priorities[i]));
|
||||
if (i != priorities.length - 1)
|
||||
buf.append(',');
|
||||
}
|
||||
_config.setProperty(prop, buf.toString());
|
||||
} else {
|
||||
_config.remove(prop);
|
||||
}
|
||||
} else {
|
||||
_config.remove(prop);
|
||||
}
|
||||
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
@ -621,6 +679,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
_config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
_config.remove(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
@ -742,7 +801,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
}
|
||||
|
||||
public void updateStatus(Snark snark) {
|
||||
saveTorrentStatus(snark.meta, snark.storage.getBitField());
|
||||
saveTorrentStatus(snark.meta, snark.storage.getBitField(), snark.storage.getFilePriorities());
|
||||
}
|
||||
|
||||
private void monitorTorrents(File dir) {
|
||||
|
@ -42,6 +42,8 @@ public class Storage
|
||||
private Object[] RAFlock; // lock on RAF access
|
||||
private long[] RAFtime; // when was RAF last accessed, or 0 if closed
|
||||
private File[] RAFfile; // File to make it easier to reopen
|
||||
/** priorities by file; default 0; may be null. @since 0.8.1 */
|
||||
private int[] priorities;
|
||||
|
||||
private final StorageListener listener;
|
||||
private I2PSnarkUtil _util;
|
||||
@ -228,6 +230,8 @@ public class Storage
|
||||
RAFlock = new Object[size];
|
||||
RAFtime = new long[size];
|
||||
RAFfile = new File[size];
|
||||
priorities = new int[size];
|
||||
|
||||
|
||||
int i = 0;
|
||||
Iterator it = files.iterator();
|
||||
@ -330,6 +334,102 @@ public class Storage
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file canonical path (non-directory)
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int getPriority(String file) {
|
||||
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||
return 0;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
if (f != null) {
|
||||
try {
|
||||
String canonical = f.getCanonicalPath();
|
||||
if (canonical.equals(file))
|
||||
return priorities[i];
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call setPiecePriorities() after calling this
|
||||
* @param file canonical path (non-directory)
|
||||
* @param priority default 0; <0 to disable
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public void setPriority(String file, int pri) {
|
||||
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||
return;
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
File f = RAFfile[i];
|
||||
// use canonical in case snark dir or sub dirs are symlinked
|
||||
if (f != null) {
|
||||
try {
|
||||
String canonical = f.getCanonicalPath();
|
||||
if (canonical.equals(file)) {
|
||||
priorities[i] = pri;
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file priorities array.
|
||||
* @return null on error, if complete, or if only one file
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int[] getFilePriorities() {
|
||||
return priorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file priorities array.
|
||||
* Only call this when stopped, but after check()
|
||||
* @param p may be null
|
||||
* @since 0.8.1
|
||||
*/
|
||||
void setFilePriorities(int[] p) {
|
||||
priorities = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call setPriority() for all changed files first,
|
||||
* then call this.
|
||||
* Set the piece priority to the highest priority
|
||||
* of all files spanning the piece.
|
||||
* Caller must pass array to the PeerCoordinator.
|
||||
* @return null on error, if complete, or if only one file
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public int[] getPiecePriorities() {
|
||||
if (complete() || metainfo.getFiles() == null || priorities == null)
|
||||
return null;
|
||||
int[] rv = new int[metainfo.getPieces()];
|
||||
int file = 0;
|
||||
long pcEnd = -1;
|
||||
long fileEnd = lengths[0] - 1;
|
||||
int psz = metainfo.getPieceLength(0);
|
||||
for (int i = 0; i < rv.length; i++) {
|
||||
pcEnd += psz;
|
||||
int pri = priorities[file];
|
||||
while (fileEnd <= pcEnd && file < lengths.length - 1) {
|
||||
file++;
|
||||
long oldFileEnd = fileEnd;
|
||||
fileEnd += lengths[file];
|
||||
if (priorities[file] > pri && oldFileEnd < pcEnd)
|
||||
pri = priorities[file];
|
||||
}
|
||||
rv[i] = pri;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* The BitField that tells which pieces this storage contains.
|
||||
* Do not change this since this is the current state of the storage.
|
||||
@ -436,11 +536,15 @@ public class Storage
|
||||
changed = true;
|
||||
checkCreateFiles();
|
||||
}
|
||||
if (complete())
|
||||
if (complete()) {
|
||||
_util.debug("Torrent is complete", Snark.NOTICE);
|
||||
else
|
||||
} else {
|
||||
// fixme saved priorities
|
||||
if (files != null)
|
||||
priorities = new int[files.size()];
|
||||
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reopen the file descriptors for a restart
|
||||
@ -565,6 +669,10 @@ public class Storage
|
||||
changed = true;
|
||||
synchronized(RAFlock[i]) {
|
||||
allocateFile(i);
|
||||
// close as we go so we don't run out of file descriptors
|
||||
try {
|
||||
closeRAF(i);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
} else {
|
||||
_util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
|
||||
@ -573,8 +681,10 @@ public class Storage
|
||||
synchronized(RAFlock[i]) {
|
||||
checkRAF(i);
|
||||
rafs[i].setLength(lengths[i]);
|
||||
try {
|
||||
closeRAF(i);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
// will be closed below
|
||||
}
|
||||
}
|
||||
|
||||
@ -583,10 +693,25 @@ public class Storage
|
||||
{
|
||||
pieces = metainfo.getPieces();
|
||||
byte[] piece = new byte[metainfo.getPieceLength(0)];
|
||||
int file = 0;
|
||||
long fileEnd = lengths[0];
|
||||
long pieceEnd = 0;
|
||||
for (int i = 0; i < pieces; i++)
|
||||
{
|
||||
int length = getUncheckedPiece(i, piece);
|
||||
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
|
||||
// close as we go so we don't run out of file descriptors
|
||||
pieceEnd += length;
|
||||
while (fileEnd <= pieceEnd) {
|
||||
synchronized(RAFlock[file]) {
|
||||
try {
|
||||
closeRAF(file);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
if (++file >= rafs.length)
|
||||
break;
|
||||
fileEnd += lengths[file];
|
||||
}
|
||||
if (correctHash)
|
||||
{
|
||||
bitfield.set(i);
|
||||
@ -601,13 +726,14 @@ public class Storage
|
||||
_probablyComplete = complete();
|
||||
// close all the files so we don't end up with a zillion open ones;
|
||||
// we will reopen as needed
|
||||
for (int i = 0; i < rafs.length; i++) {
|
||||
synchronized(RAFlock[i]) {
|
||||
try {
|
||||
closeRAF(i);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
// Now closed above to avoid running out of file descriptors
|
||||
//for (int i = 0; i < rafs.length; i++) {
|
||||
// synchronized(RAFlock[i]) {
|
||||
// try {
|
||||
// closeRAF(i);
|
||||
// } catch (IOException ioe) {}
|
||||
// }
|
||||
//}
|
||||
|
||||
if (listener != null) {
|
||||
listener.storageAllChecked(this);
|
||||
@ -616,6 +742,7 @@ public class Storage
|
||||
}
|
||||
}
|
||||
|
||||
/** this calls openRAF(); caller must synnchronize and call closeRAF() */
|
||||
private void allocateFile(int nr) throws IOException
|
||||
{
|
||||
// caller synchronized
|
||||
@ -624,7 +751,12 @@ public class Storage
|
||||
// the whole file?
|
||||
listener.storageCreateFile(this, names[nr], lengths[nr]);
|
||||
final int ZEROBLOCKSIZE = metainfo.getPieceLength(0);
|
||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
||||
byte[] zeros;
|
||||
try {
|
||||
zeros = new byte[ZEROBLOCKSIZE];
|
||||
} catch (OutOfMemoryError oom) {
|
||||
throw new IOException(oom.toString());
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < lengths[nr]/ZEROBLOCKSIZE; i++)
|
||||
{
|
||||
|
@ -266,9 +266,9 @@ public class TrackerClient extends I2PAppThread
|
||||
// we only want to talk to new people if we need things
|
||||
// from them (duh)
|
||||
List ordered = new ArrayList(peers);
|
||||
Collections.shuffle(ordered);
|
||||
Collections.shuffle(ordered, r);
|
||||
Iterator it = ordered.iterator();
|
||||
while (it.hasNext()) {
|
||||
while ((!stop) && it.hasNext()) {
|
||||
Peer cur = (Peer)it.next();
|
||||
// FIXME if id == us || dest == us continue;
|
||||
// only delay if we actually make an attempt to add peer
|
||||
@ -357,7 +357,7 @@ public class TrackerClient extends I2PAppThread
|
||||
+ "&uploaded=" + uploaded
|
||||
+ "&downloaded=" + downloaded
|
||||
+ "&left=" + left
|
||||
+ "&compact"
|
||||
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
|
||||
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
||||
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
||||
s += "&numwant=0";
|
||||
|
@ -133,6 +133,7 @@ public class I2PSnarkServlet extends Default {
|
||||
// bypass the horrid Resource.getListHTML()
|
||||
String pathInfo = req.getPathInfo();
|
||||
String pathInContext = URI.addPaths(path, pathInfo);
|
||||
req.setCharacterEncoding("UTF-8");
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
Resource resource = getResource(pathInContext);
|
||||
@ -140,7 +141,7 @@ public class I2PSnarkServlet extends Default {
|
||||
resp.sendError(HttpResponse.__404_Not_Found);
|
||||
} else {
|
||||
String base = URI.addPaths(req.getRequestURI(), "/");
|
||||
String listing = getListHTML(resource, base, true);
|
||||
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null);
|
||||
if (listing != null)
|
||||
resp.getWriter().write(listing);
|
||||
else // shouldn't happen
|
||||
@ -241,7 +242,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(TABLE_HEADER);
|
||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\"");
|
||||
out.write(" title=\"");
|
||||
out.write(_("Torrent Status"));
|
||||
out.write(_("Status"));
|
||||
out.write("\">");
|
||||
out.write(_("Status"));
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
@ -250,14 +251,14 @@ public class I2PSnarkServlet extends Default {
|
||||
if (peerParam != null) {
|
||||
out.write("\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/showpeers.png\" title=\"");
|
||||
out.write(_("Toggle Peer Visibility"));
|
||||
out.write(_("Hide Peers"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Hide Peers"));
|
||||
out.write("\">");
|
||||
} else {
|
||||
out.write("?p=1\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/hidepeers.png\" title=\"");
|
||||
out.write(_("Toggle Peer Visibility"));
|
||||
out.write(_("Show Peers"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Show Peers"));
|
||||
out.write("\">");
|
||||
@ -266,7 +267,7 @@ public class I2PSnarkServlet extends Default {
|
||||
}
|
||||
out.write("</th>\n<th align=\"left\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/torrent.png\" title=\"");
|
||||
out.write(_("Loaded Torrents"));
|
||||
out.write(_("Torrent"));
|
||||
out.write("\">");
|
||||
out.write(_("Torrent"));
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
@ -276,21 +277,21 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("ETA"));
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
|
||||
out.write(_("Data Downloaded"));
|
||||
out.write(_("Downloaded"));
|
||||
out.write("\">");
|
||||
out.write(_("RX"));
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
|
||||
out.write(_("Data Uploaded"));
|
||||
out.write(_("Uploaded"));
|
||||
out.write("\">");
|
||||
out.write(_("TX"));
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
|
||||
out.write(_("Download Speed"));
|
||||
out.write(_("Down Rate"));
|
||||
out.write("\">Rate");
|
||||
out.write("</th>\n<th align=\"center\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
|
||||
out.write(_("Upload Speed"));
|
||||
out.write(_("Up Rate"));
|
||||
out.write("\">");
|
||||
out.write(_("Rate"));
|
||||
out.write("</th>\n");
|
||||
@ -301,7 +302,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Stop all torrents and the I2P tunnel"));
|
||||
out.write("\">");
|
||||
out.write("<img src=\"/themes/snark/ubergine/images/stop_all.png\" title=\"");
|
||||
out.write(_("Stop All Torrents"));
|
||||
out.write(_("Stop all torrents and the I2P tunnel"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Stop All"));
|
||||
out.write("\">");
|
||||
@ -312,7 +313,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Start all torrents and the I2P tunnel"));
|
||||
out.write("\">");
|
||||
out.write("<img src=\"/themes/snark/ubergine/images/start_all.png\" title=\"");
|
||||
out.write(_("Start All Torrents"));
|
||||
out.write(_("Start all torrents and the I2P tunnel"));
|
||||
out.write("\" alt=\"Start All\">");
|
||||
out.write("</a>");
|
||||
} else {
|
||||
@ -537,7 +538,7 @@ public class I2PSnarkServlet extends Default {
|
||||
File torrentFile = new File(baseFile.getParent(), baseFile.getName() + ".torrent");
|
||||
if (torrentFile.exists())
|
||||
throw new IOException("Cannot overwrite an existing .torrent file: " + torrentFile.getPath());
|
||||
_manager.saveTorrentStatus(info, s.getBitField()); // so addTorrent won't recheck
|
||||
_manager.saveTorrentStatus(info, s.getBitField(), null); // so addTorrent won't recheck
|
||||
// DirMonitor could grab this first, maybe hold _snarks lock?
|
||||
FileOutputStream out = new FileOutputStream(torrentFile);
|
||||
out.write(info.getTorrentData());
|
||||
@ -564,6 +565,8 @@ public class I2PSnarkServlet extends Default {
|
||||
_manager.stopTorrent(snark.torrent, false);
|
||||
}
|
||||
if (_manager.util().connected()) {
|
||||
// Give the stopped announces time to get out
|
||||
try { Thread.sleep(2000); } catch (InterruptedException ie) {}
|
||||
_manager.util().disconnect();
|
||||
_manager.addMessage(_("I2P tunnel closed."));
|
||||
}
|
||||
@ -718,7 +721,7 @@ public class I2PSnarkServlet extends Default {
|
||||
curPeers + "/" +
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else
|
||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/complete.png\" title=\"" + _("Complete") + "\">" + _("Not Seeding");
|
||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/complete.png\" title=\"" + _("Complete") + "\">" + _("Complete");
|
||||
} else {
|
||||
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
|
||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
|
||||
@ -727,7 +730,7 @@ public class I2PSnarkServlet extends Default {
|
||||
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
|
||||
else if (isRunning && curPeers > 0 && downBps > 0)
|
||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
|
||||
" (" + curPeers + "/" +
|
||||
curPeers + "/" +
|
||||
ngettext("1 peer", "{0} peers", knownPeers);
|
||||
else if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/stalled.png\" title=\"" + _("Stalled") + "\">" +
|
||||
@ -790,7 +793,7 @@ public class I2PSnarkServlet extends Default {
|
||||
baseURL = baseURL.substring(e + 1);
|
||||
out.write(" <a href=\"" + baseURL + "details.php?dllist=1&filelist=1&info_hash=");
|
||||
out.write(TrackerClient.urlencode(snark.meta.getInfoHash()));
|
||||
out.write("\" title=\"" + name + _("Tracker") + "\" target=\"_blank\">");
|
||||
out.write("\" title=\"" + name + ' ' + _("Tracker") + "\" target=\"_blank\">");
|
||||
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/details.png\">");
|
||||
out.write("</a>");
|
||||
break;
|
||||
@ -804,7 +807,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; thin space so it will line break well
|
||||
else
|
||||
out.write(formatSize(total)); // 3GB
|
||||
out.write("</td>\n\t");
|
||||
@ -828,7 +831,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Stop the torrent"));
|
||||
out.write("\">");
|
||||
out.write("<img src=\"/themes/snark/ubergine/images/stop.png\" title=\"");
|
||||
out.write(_("Stop Torrent"));
|
||||
out.write(_("Stop"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Stop"));
|
||||
out.write("\">");
|
||||
@ -840,7 +843,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Start the torrent"));
|
||||
out.write("\">");
|
||||
out.write("<img src=\"/themes/snark/ubergine/images/start.png\" title=\"");
|
||||
out.write(_("Start Torrent"));
|
||||
out.write(_("Start the torrent"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Start"));
|
||||
out.write("\">");
|
||||
@ -856,7 +859,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
|
||||
out.write("')) { return false; }\">");
|
||||
out.write("<img src=\"/themes/snark/ubergine/images/remove.png\" title=\"");
|
||||
out.write(_("Remove Torrent"));
|
||||
out.write(_("Remove"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Remove"));
|
||||
out.write("\">");
|
||||
@ -871,7 +874,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
|
||||
out.write("')) { return false; }\">");
|
||||
out.write("<img src=\"/themes/snark/ubergine/images/delete.png\" title=\"");
|
||||
out.write(_("Delete Torrent + Data"));
|
||||
out.write(_("Delete"));
|
||||
out.write("\" alt=\"");
|
||||
out.write(_("Delete"));
|
||||
out.write("\">");
|
||||
@ -1007,7 +1010,7 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write(_("Add torrent"));
|
||||
out.write("\" name=\"foo\" ><br>\n");
|
||||
out.write("<tr><td> <td><span class=\"snarkAddInfo\">");
|
||||
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath ())) + "</code>";
|
||||
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath () + "</code>"));
|
||||
out.write("\n");
|
||||
out.write(_("Removing a .torrent will cause it to stop."));
|
||||
out.write("<br></span></table>\n");
|
||||
@ -1262,19 +1265,21 @@ public class I2PSnarkServlet extends Default {
|
||||
// rounding makes us look faster :)
|
||||
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";
|
||||
}
|
||||
|
||||
/** @since 0.7.14 */
|
||||
private static String urlify(String s) {
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("<a href=\"").append(s).append("\">").append(s).append("</a>");
|
||||
// browsers seem to work without doing this but let's be strict
|
||||
String link = s.replace("&", "&");
|
||||
buf.append("<a href=\"").append(link).append("\">").append(link).append("</a>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@ -1313,10 +1318,11 @@ public class I2PSnarkServlet extends Default {
|
||||
* @param r The Resource
|
||||
* @param base The base URL
|
||||
* @param parent True if the parent directory should be included
|
||||
* @param postParams map of POST parameters or null if not a POST
|
||||
* @return String of HTML
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String getListHTML(Resource r, String base, boolean parent)
|
||||
private String getListHTML(Resource r, String base, boolean parent, Map postParams)
|
||||
throws IOException
|
||||
{
|
||||
if (!r.isDirectory())
|
||||
@ -1341,6 +1347,10 @@ public class I2PSnarkServlet extends Default {
|
||||
else
|
||||
torrentName = title;
|
||||
Snark snark = _manager.getTorrentByBaseName(torrentName);
|
||||
|
||||
if (snark != null && postParams != null)
|
||||
savePriorities(snark, postParams);
|
||||
|
||||
if (title.endsWith("/"))
|
||||
title = title.substring(0, title.length() - 1);
|
||||
title = _("Torrent") + ": " + title;
|
||||
@ -1348,12 +1358,29 @@ public class I2PSnarkServlet extends Default {
|
||||
buf.append("</TITLE>").append(HEADER).append("<link rel=\"shortcut icon\" href=\"/themes/snark/ubergine/favicon.ico\"></HEAD><BODY>\n<center><div class=\"snarknavbar\"> <a href=\"/i2psnark/\" title=\"Torrents\"");
|
||||
buf.append(" class=\"snarkRefresh\">I2PSnark</a>").append("</div>");
|
||||
|
||||
buf.append("<div class=\"page\"><div class=\"mainsection\">" +
|
||||
"<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
|
||||
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\">");
|
||||
boolean showPriority = snark != null && !snark.storage.complete();
|
||||
if (showPriority)
|
||||
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
|
||||
buf.append("<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
|
||||
"<thead><tr><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/file.png\" title=\"").append(_("File")).append("\" alt=\"").append(_("File")).append("\"> ").append(title).append("</th><th align=\"right\">").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/size.png\" title=\"").append(_("FileSize")).append("\" alt=\"").append(_("FileSize")).append("\">").append(_("Size"));
|
||||
buf.append("</th><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\" title=\"").append(_("Download Status")).append("\">").append(_("Status")).append("</th></tr></thead>");
|
||||
buf.append("</th><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\" title=\"").append(_("Download Status")).append("\">").append(_("Status")).append("</th>");
|
||||
if (showPriority)
|
||||
buf.append("<th>").append(_("Priority")).append("</th>");
|
||||
buf.append("</tr></thead>\n");
|
||||
//DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||
// DateFormat.MEDIUM);
|
||||
boolean showSaveButton = false;
|
||||
for (int i=0 ; i< ls.length ; i++)
|
||||
{
|
||||
String encoded=URI.encodePath(ls[i]);
|
||||
@ -1391,7 +1418,8 @@ public class I2PSnarkServlet extends Default {
|
||||
complete = true;
|
||||
status = toImg("tick") + _("Complete");
|
||||
} else {
|
||||
status = toImg("clock") +
|
||||
status =
|
||||
(snark.storage.getPriority(f.getCanonicalPath()) < 0 ? toImg("cancel") : toImg("clock")) +
|
||||
(100 * (length - remaining) / length) + "% " + _("complete") +
|
||||
" (" + DataHelper.formatSize2(remaining) + _("bytes remaining") + ")";
|
||||
}
|
||||
@ -1435,22 +1463,41 @@ public class I2PSnarkServlet extends Default {
|
||||
buf.append("</TD><TD class=\"").append(rowClass).append(" snarkFileStatus\">");
|
||||
//buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
buf.append(status);
|
||||
buf.append("</TD></TR>\n");
|
||||
buf.append("</TD>");
|
||||
if (showPriority) {
|
||||
buf.append("<td>");
|
||||
File f = item.getFile();
|
||||
if ((!complete) && (!item.isDirectory()) && f != null) {
|
||||
int pri = snark.storage.getPriority(f.getCanonicalPath());
|
||||
buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||
if (pri > 0)
|
||||
buf.append("checked=\"true\"");
|
||||
buf.append('>').append(_("High"));
|
||||
|
||||
buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||
if (pri == 0)
|
||||
buf.append("checked=\"true\"");
|
||||
buf.append('>').append(_("Normal"));
|
||||
|
||||
buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||
if (pri < 0)
|
||||
buf.append("checked=\"true\"");
|
||||
buf.append('>').append(_("Do not download"));
|
||||
showSaveButton = true;
|
||||
}
|
||||
|
||||
if (parent)
|
||||
{
|
||||
buf.append("<tfoot align=\"left\"><tr><td colspan=\"3\"><A HREF=\"");
|
||||
// corrupts utf-8
|
||||
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
|
||||
buf.append(URI.addPaths(base,"../"));
|
||||
buf.append("\"><img border=\"0\" src=\"/themes/snark/ubergine/images/up.png\"> ")
|
||||
.append(_("Up to higher level directory")).append("</A></td></tr></thead>\n");
|
||||
buf.append("</td>");
|
||||
}
|
||||
buf.append("</TR>\n");
|
||||
}
|
||||
if (showSaveButton) {
|
||||
buf.append("<thead><tr><th colspan=\"3\"> </th><th align=\"center\"><input type=\"submit\" value=\"");
|
||||
buf.append(_("Save priorities"));
|
||||
buf.append("\" name=\"foo\" ></th></tr></thead>\n");
|
||||
}
|
||||
|
||||
|
||||
buf.append("</TABLE>\n");
|
||||
buf.append("</div></div></center></BODY></HTML>\n");
|
||||
if (showPriority)
|
||||
buf.append("</form>");
|
||||
buf.append("</div></div></BODY></HTML>\n");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
@ -1515,6 +1562,26 @@ buf.append("</div></div></center></BODY></HTML>\n");
|
||||
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\"> ";
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
private void savePriorities(Snark snark, Map postParams) {
|
||||
Set<Map.Entry> entries = postParams.entrySet();
|
||||
for (Map.Entry entry : entries) {
|
||||
String key = (String)entry.getKey();
|
||||
if (key.startsWith("pri.")) {
|
||||
try {
|
||||
String file = key.substring(4);
|
||||
String val = ((String[])entry.getValue())[0]; // jetty arrays
|
||||
int pri = Integer.parseInt(val);
|
||||
snark.storage.setPriority(file, pri);
|
||||
//System.err.println("Priority now " + pri + " for " + file);
|
||||
} catch (Throwable t) { t.printStackTrace(); }
|
||||
}
|
||||
}
|
||||
if (snark.coordinator != null)
|
||||
snark.coordinator.updatePiecePriorities();
|
||||
_manager.saveTorrentStatus(snark.storage.getMetaInfo(), snark.storage.getBitField(), snark.storage.getFilePriorities());
|
||||
}
|
||||
|
||||
|
||||
/** inner class, don't bother reindenting */
|
||||
private static class FetchAndAdd implements Runnable {
|
||||
|
@ -175,7 +175,7 @@ public class EditBean extends IndexBean {
|
||||
}
|
||||
|
||||
public String getAccessList(int tunnel) {
|
||||
return getProperty(tunnel, "i2cp.accessList", "").replaceAll(",", "\n");
|
||||
return getProperty(tunnel, "i2cp.accessList", "").replace(",", "\n");
|
||||
}
|
||||
|
||||
public boolean getClose(int tunnel) {
|
||||
|
@ -666,7 +666,7 @@ public class IndexBean {
|
||||
}
|
||||
public void setAccessList(String val) {
|
||||
if (val != null)
|
||||
_otherOptions.put("i2cp.accessList", val.trim().replaceAll("\r\n", ",").replaceAll("\n", ",").replaceAll(" ", ","));
|
||||
_otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
|
||||
}
|
||||
public void setCloseTime(String val) {
|
||||
if (val != null) {
|
||||
|
@ -159,7 +159,7 @@
|
||||
%>
|
||||
</label>
|
||||
<input type="text" size="30" id="targetDestination" name="targetDestination" title="Destination of the Tunnel" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
|
||||
<span class="comment">(<%=intl._("name or destination")%>)</span>
|
||||
<span class="comment">(<%=intl._("name or destination")%>; <%=intl._("b32 not recommended")%>)</span>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if (!"streamrclient".equals(tunnelType)) { %>
|
||||
|
@ -45,6 +45,10 @@ public class I2PSocketEepGet extends EepGet {
|
||||
/** this replaces _proxy in the superclass. Sadly, I2PSocket does not extend Socket. */
|
||||
private I2PSocket _socket;
|
||||
|
||||
/** from ConnectionOptions */
|
||||
private static final String PROP_CONNECT_DELAY = "i2p.streaming.connectDelay";
|
||||
private static final String CONNECT_DELAY = "500";
|
||||
|
||||
public I2PSocketEepGet(I2PAppContext ctx, I2PSocketManager mgr, int numRetries, String outputFile, String url) {
|
||||
this(ctx, mgr, numRetries, -1, -1, outputFile, null, url);
|
||||
}
|
||||
@ -123,6 +127,10 @@ public class I2PSocketEepGet extends EepGet {
|
||||
Properties props = new Properties();
|
||||
props.setProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT, "" + CONNECT_TIMEOUT);
|
||||
props.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + INACTIVITY_TIMEOUT);
|
||||
// This is important - even if the underlying socket doesn't have a connect delay,
|
||||
// we want to set it for this connection, so the request headers will go out
|
||||
// in the SYN packet, saving one RTT.
|
||||
props.setProperty(PROP_CONNECT_DELAY, CONNECT_DELAY);
|
||||
I2PSocketOptions opts = _socketManager.buildOptions(props);
|
||||
_socket = _socketManager.connect(dest, opts);
|
||||
} else {
|
||||
|
@ -24,7 +24,10 @@ public class ConfigLoggingHelper extends HelperBase {
|
||||
public String getMaxFileSize() {
|
||||
int bytes = _context.logManager().getFileSize();
|
||||
if (bytes <= 0) return "1.00 MB";
|
||||
return DataHelper.formatSize2(bytes) + 'B';
|
||||
// " " comes back in the POST as 0xc2 0xa0
|
||||
// non-breaking space is U+00A0 which is 0xc2 0xa0 in UTF-8.
|
||||
// we could figure out where the UTF-8 problem is but why bother.
|
||||
return DataHelper.formatSize2(bytes).replace(" ", " ") + 'B';
|
||||
}
|
||||
public String getLogLevelTable() {
|
||||
StringBuilder buf = new StringBuilder(32*1024);
|
||||
|
@ -142,7 +142,7 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
}
|
||||
|
||||
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
|
||||
_updateURL = _updateURL.replaceAll("\r\n", ",").replaceAll("\n", ",");
|
||||
_updateURL = _updateURL.replace("\r\n", ",").replace("\n", ",");
|
||||
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
|
||||
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
|
||||
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
|
||||
@ -151,7 +151,7 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
}
|
||||
|
||||
if ( (_trustedKeys != null) && (_trustedKeys.length() > 0) ) {
|
||||
_trustedKeys = _trustedKeys.replaceAll("\r\n", ",").replaceAll("\n", ",");
|
||||
_trustedKeys = _trustedKeys.replace("\r\n", ",").replace("\n", ",");
|
||||
String oldKeys = new TrustedUpdate(_context).getTrustedKeysString();
|
||||
if ( (oldKeys == null) || (!_trustedKeys.equals(oldKeys)) ) {
|
||||
_context.router().setConfigSetting(PROP_TRUSTED_KEYS, _trustedKeys);
|
||||
|
@ -40,7 +40,7 @@ public class ConfigUpdateHelper extends HelperBase {
|
||||
public String getUpdateURL() {
|
||||
String url = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL);
|
||||
if (url != null)
|
||||
return url.replaceAll(",", "\n");
|
||||
return url.replace(",", "\n");
|
||||
else
|
||||
return ConfigUpdateHandler.DEFAULT_UPDATE_URL;
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ public class LogsHelper extends HelperBase {
|
||||
}
|
||||
String str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false);
|
||||
if (str == null)
|
||||
return "";
|
||||
return _("File not found") + ": <b><code>" + f.getAbsolutePath() + "</code></b>";
|
||||
else {
|
||||
str = str.replaceAll("<", "<").replaceAll(">", ">");
|
||||
str = str.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||
return _("File location") + ": <b><code>" + f.getAbsolutePath() + "</code></b> <pre>" + str + "</pre>";
|
||||
}
|
||||
}
|
||||
@ -54,12 +54,14 @@ public class LogsHelper extends HelperBase {
|
||||
buf.append("<code>\n");
|
||||
for (int i = msgs.size(); i > 0; i--) {
|
||||
String msg = msgs.get(i - 1);
|
||||
msg = msg.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||
buf.append("<li>");
|
||||
if (colorize) {
|
||||
String color;
|
||||
// Homeland Security Advisory System
|
||||
// http://www.dhs.gov/xinfoshare/programs/Copy_of_press_release_0046.shtm
|
||||
// but pink instead of yellow for WARN
|
||||
// FIXME doesnt work for translated levels
|
||||
if (msg.contains("CRIT"))
|
||||
color = "#cc0000";
|
||||
else if (msg.contains("ERROR"))
|
||||
@ -71,7 +73,7 @@ public class LogsHelper extends HelperBase {
|
||||
else
|
||||
color = "#006600";
|
||||
buf.append("<font color=\"").append(color).append("\">");
|
||||
buf.append(msg.replaceAll("<", "<").replaceAll(">", ">"));
|
||||
buf.append(msg);
|
||||
buf.append("</font>");
|
||||
} else {
|
||||
buf.append(msg);
|
||||
|
@ -15,6 +15,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"X-Poedit-Language: German\n"
|
||||
|
||||
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126
|
||||
|
@ -15,8 +15,8 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-Language: German\n"
|
||||
"X-Poedit-Basepath: /home/lee/work/i2p/monotone/i2p.i2p/apps/routerconsole/java\n"
|
||||
"X-Poedit-Language: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:106
|
||||
msgid "config networking"
|
||||
|
@ -15,6 +15,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"X-Poedit-Language: Dutch\n"
|
||||
|
||||
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126
|
||||
|
@ -47,6 +47,9 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
private int _maxTotalConnsPerHour;
|
||||
private int _maxTotalConnsPerDay;
|
||||
|
||||
// NOTE - almost all the options are below, but see
|
||||
// I2PSocketOptions in ministreaming for a few more
|
||||
|
||||
public static final int PROFILE_BULK = 1;
|
||||
public static final int PROFILE_INTERACTIVE = 2;
|
||||
|
||||
|
21
apps/systray/doc/README.txt
Normal file
21
apps/systray/doc/README.txt
Normal file
@ -0,0 +1,21 @@
|
||||
Bundled in ../java/lib/ are the binaries for systray4j version 2.4.1 2004-03-28,
|
||||
which is still the latest.
|
||||
|
||||
Files are from systray4j-2.4.1-win32.zip.
|
||||
|
||||
SHA1Sums:
|
||||
28acaea97816f53d188d01fd88b72e670e67286b systray4j-2.4.1-win32.zip
|
||||
a7f5e02c3652f3f1a72559e54ee69226b8b97859 systray4j.dll
|
||||
947bd91c483494256cf48ad87c211e8701b4f85b systray4j.jar
|
||||
|
||||
|
||||
systray4j is GPLv2, see LICENSE.systray4j.txt.
|
||||
I2P systray code in ../java/src is public domain.
|
||||
|
||||
SysTray is really obsolete. It supports Windows and kde3 only.
|
||||
We only instantiate it on Windows.
|
||||
|
||||
The java.awt.SystemTray classes added in Java 6
|
||||
(and used by apps/desktopgui) are the way to go now.
|
||||
|
||||
We could either rewrite this to use SystemTray, or switch to desktopgui.
|
@ -184,4 +184,20 @@ public class SysTray implements SysTrayMenuListener {
|
||||
_sysTrayMenu.addItem(_itemOpenConsole);
|
||||
refreshDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts SysTray, even on linux (but requires kde3 libsystray4j.so to do anything)
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
System.err.println("SysTray4j version " + SysTrayMenu.VERSION);
|
||||
System.err.println("Hit ^C to exit");
|
||||
new SysTray();
|
||||
Thread t = Thread.currentThread();
|
||||
synchronized(t) {
|
||||
try {
|
||||
t.wait();
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,14 @@
|
||||
<!--
|
||||
<property name="javac.compilerargs" value="-warn:-unchecked,raw,unused,serial" />
|
||||
-->
|
||||
<!-- Add Apache Harmony's Pack200 library if you don't have java.util.jar.Pack200
|
||||
See core/java/src/net/i2p/util/FileUtil.java for code changes required
|
||||
to use this library instead of Sun's version.
|
||||
Or to comment it all out if you don't have either.
|
||||
-->
|
||||
<!--
|
||||
<property name="javac.classpath" value="/PATH/TO/pack200.jar" />
|
||||
-->
|
||||
|
||||
<!-- You probably don't want to change anything from here down -->
|
||||
<target name="help" depends="all" />
|
||||
|
@ -17,10 +17,11 @@
|
||||
</target>
|
||||
<!-- only used if not set by a higher build.xml -->
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.classpath" value="" />
|
||||
<target name="compile" depends="depend">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" >
|
||||
<javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" classpath="${javac.classpath}" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
|
@ -1076,7 +1076,8 @@ public class DataHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Like formatSize but with a space after the number
|
||||
* Like formatSize but with a non-breaking space after the number
|
||||
* Use only in HTML
|
||||
* @since 0.7.14
|
||||
*/
|
||||
public static String formatSize2(long bytes) {
|
||||
@ -1091,11 +1092,11 @@ public class DataHelper {
|
||||
|
||||
String str = fmt.format(val);
|
||||
switch (scale) {
|
||||
case 1: return str + " K";
|
||||
case 2: return str + " M";
|
||||
case 3: return str + " G";
|
||||
case 4: return str + " T";
|
||||
default: return bytes + " ";
|
||||
case 1: return str + " K";
|
||||
case 2: return str + " M";
|
||||
case 3: return str + " G";
|
||||
case 4: return str + " T";
|
||||
default: return bytes + " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,18 @@ import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Pack200;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
// Pack200 import
|
||||
// you must also uncomment the correct line in unpack() below
|
||||
// For gcj, gij, etc., comment both out
|
||||
//
|
||||
// For Sun, OpenJDK, IcedTea, etc, use this
|
||||
import java.util.jar.Pack200;
|
||||
|
||||
// For Apache Harmony or if you put its pack200.jar in your library directory use this
|
||||
//import org.apache.harmony.unpack200.Archive;
|
||||
|
||||
/**
|
||||
* General helper methods for messing with files
|
||||
@ -119,7 +127,7 @@ public class FileUtil {
|
||||
if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) {
|
||||
target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length()));
|
||||
JarOutputStream fos = new JarOutputStream(new FileOutputStream(target));
|
||||
Pack200.newUnpacker().unpack(in, fos);
|
||||
unpack(in, fos);
|
||||
fos.close();
|
||||
System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked");
|
||||
} else {
|
||||
@ -189,9 +197,7 @@ public class FileUtil {
|
||||
} else {
|
||||
if (p200TestRequired &&
|
||||
(entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack"))) {
|
||||
try {
|
||||
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
|
||||
} catch (Exception e) { // ClassNotFoundException but compiler not happy with that
|
||||
if (!isPack200Supported()) {
|
||||
System.err.println("ERROR: Zip verify failed, your JVM does not support unpack200");
|
||||
return false;
|
||||
}
|
||||
@ -224,6 +230,40 @@ public class FileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This won't work right if one of the two options in unpack() is commented out.
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static boolean isPack200Supported() {
|
||||
try {
|
||||
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
|
||||
return true;
|
||||
} catch (Exception e) {}
|
||||
try {
|
||||
Class.forName("org.apache.harmony.pack200.Archive", false, ClassLoader.getSystemClassLoader());
|
||||
return true;
|
||||
} catch (Exception e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller must close streams
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static void unpack(InputStream in, JarOutputStream out) throws Exception {
|
||||
// For Sun, OpenJDK, IcedTea, etc, use this
|
||||
Pack200.newUnpacker().unpack(in, out);
|
||||
|
||||
// ------------------
|
||||
// For Apache Harmony or if you put its pack200.jar in your library directory use this
|
||||
//(new Archive(in, out)).unpack();
|
||||
|
||||
|
||||
// ------------------
|
||||
// For gcj, gij, etc., use this
|
||||
//throw new IOException("Pack200 not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in the last few lines of a (newline delimited) textfile, or null if
|
||||
* the file doesn't exist.
|
||||
@ -352,6 +392,18 @@ public class FileUtil {
|
||||
boolean copied = FileUtil.copy(args[1], args[2], false);
|
||||
if (!copied)
|
||||
System.err.println("Error copying [" + args[1] + "] to [" + args[2] + "]");
|
||||
} else if ("unzip".equals(args[0])) {
|
||||
File f = new File(args[1]);
|
||||
File to = new File("tmp");
|
||||
to.mkdir();
|
||||
boolean copied = verifyZip(f);
|
||||
if (!copied)
|
||||
System.err.println("Error verifying " + args[1]);
|
||||
copied = extractZip(f, to);
|
||||
if (copied)
|
||||
System.err.println("Unzipped [" + args[1] + "] to [" + to + "]");
|
||||
else
|
||||
System.err.println("Error unzipping [" + args[1] + "] to [" + to + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -429,7 +430,8 @@ public class LogManager {
|
||||
v = v.substring(0, v.length() - 1);
|
||||
char mod = v.charAt(v.length() - 1);
|
||||
if (!Character.isDigit(mod)) v = v.substring(0, v.length() - 1);
|
||||
double val = Double.parseDouble(v);
|
||||
// output to form was in current locale, so have to parse it back that way
|
||||
double val = (new DecimalFormat()).parse(v.trim()).doubleValue();
|
||||
switch (mod) {
|
||||
case 'K':
|
||||
val *= 1024;
|
||||
|
@ -56,7 +56,7 @@ public class SecureFileOutputStream extends FileOutputStream {
|
||||
* Tries to set the permissions to 600,
|
||||
* ignores errors
|
||||
*/
|
||||
private static void setPerms(File f) {
|
||||
public static void setPerms(File f) {
|
||||
if (!canSetPerms)
|
||||
return;
|
||||
try {
|
||||
|
154
history.txt
154
history.txt
@ -1,3 +1,157 @@
|
||||
2010-11-03 zzz
|
||||
* Merge and snark fixups
|
||||
|
||||
2010-11-01 zzz
|
||||
* ClientConnectionRunner: Add synch to fix race causing AIOOBE
|
||||
(http://forum.i2p/viewtopic.php?t=5061)
|
||||
* configlogging.jsp: Parse log limit with current locale
|
||||
(ticket #118)
|
||||
* i2psnark:
|
||||
- Limit number of parallel requests of a single piece when in the end game
|
||||
- Shorten and weight the speed tracker so the display is more
|
||||
reflective of current speed
|
||||
* logs.jsp: Add message if wrapper log not found
|
||||
(ticket #103)
|
||||
|
||||
2010-10-30 zzz
|
||||
* i2psnark:
|
||||
- Priority mapping bugfix
|
||||
- Close files as we go when creating/checking
|
||||
so we don't run out of file descriptors
|
||||
- Update request queues after priority change
|
||||
- Only add wanted pieces to wanted list at startup
|
||||
- Make sure lastRequest is null when it should be
|
||||
- Delay during StopAll so we don't close the tunnel before the
|
||||
stopped announces go out and reopen it
|
||||
- Logging tweaks
|
||||
|
||||
2010-10-27 zzz
|
||||
* i2psnark:
|
||||
- Don't stay interested if we run out of pieces
|
||||
to request (thanks sponge)
|
||||
- Enhance debug mode to show requests
|
||||
- Priority mapping bugfix
|
||||
* Transport: Avoid rare NPE at startup
|
||||
|
||||
2010-10-24 zzz
|
||||
* FileUtil: Make it easier to compile without Pack200, or with
|
||||
Apache Harmony's Pack200, add unzip to main()
|
||||
* i2psnark: Catch a race after disconnect()
|
||||
* NTCP: Catch a race after stop()
|
||||
* Router: Set permissions on wrapper.log when not called by RouterLaunch
|
||||
* Systray: New doc and main()
|
||||
|
||||
2010-10-19 zzz
|
||||
* Escape & in logs and i2psnark (much more to do)
|
||||
* JobImpl: Deprecate two debugging methods
|
||||
* replaceAll() -> replace() when we don't need regex
|
||||
|
||||
2010-10-15 zzz
|
||||
* i2psnark: Add file priority feature
|
||||
* I2PSocketEepGet: Set connect delay to save a RTT, will
|
||||
speed announces in i2psnark
|
||||
|
||||
2010-10-12 zzz
|
||||
*** 1.6 or higher JDK now required to build
|
||||
* configlogging.jsp:
|
||||
- Add easy way to add an override
|
||||
- Make file size specifier more flexible
|
||||
* Console:
|
||||
- Sort RouterAddress options on netdb.jsp and peers.jsp
|
||||
- Remove unused web-*.xml file from war
|
||||
* Crypto:
|
||||
- Convert all ArrayList caching to LBQs in YKGenerator,
|
||||
HMACGenerator, and AESKeyCache.
|
||||
- Change DSAEngine params from Hash to new SHA1Hash, since
|
||||
these were really 20 byte hashes, not 32 byte Hashes.
|
||||
- Add stats to track YKGenerator caching success
|
||||
- Fix YKGenerator precalculation to be much more useful by
|
||||
increasing the cache size and dramatically shortening the delay
|
||||
- Option cleanups
|
||||
- YKGenerator cleanups
|
||||
- Mark HMAC256Generator unused
|
||||
* EepGet: Reset length variable on redirect
|
||||
* Files: Change permissions to 600/700 for all written files/directories.
|
||||
Now requires Java 1.6 to build, but only 1.5+ to run.
|
||||
(requires 1.6 to set permissiomns)
|
||||
* GeoIP: Fix locking bug causing lookups to stop
|
||||
* Hash: Throw IAE if data length is not 32 bytes,
|
||||
now that DSAEngine abuse is gone
|
||||
* HTTPResponseOutputStream:
|
||||
- More caching
|
||||
- Stats cleanup
|
||||
- Max header length check
|
||||
- Catch OOM
|
||||
- Initializer cleanup
|
||||
- Javadoc
|
||||
* I2CP:
|
||||
- Add new option i2cp.messageReliability=none, which prevents the
|
||||
router from sending MessageStatusMessages back in reply to an
|
||||
outbound SendMessageMessage. Since the streaming lib always ignored
|
||||
the MSMs anyway, make it the default for streaming.
|
||||
This will reduce the I2CP traffic significantly.
|
||||
MSM handling now avoided, but it is still fairly broken, see
|
||||
comments in I2PSessionImpl2.
|
||||
- Cleanups to replace method calls with fields
|
||||
- More cleanups, javadoc, rate reduction
|
||||
* i2psnark:
|
||||
- Compact response format
|
||||
- Add link to finished torrent in message box
|
||||
- Don't let one bad torrent prevent others from
|
||||
starting or stopping
|
||||
- Sort peers by completion %
|
||||
- Add some missing mime types to web.xml
|
||||
- shouldLog() cleanup
|
||||
* i2ptunnel:
|
||||
- Now that streaming flush() is fixed, use it in IRCClient, and
|
||||
for initial data in I2PTunnel runner, to avoid the 250 ms
|
||||
passive flush delay
|
||||
- Add hostname DSA signature field, to be used for addkey forms.
|
||||
Experimental, may be commented out later.
|
||||
- More header blocking (thanks telecomix!)
|
||||
- Remove unused web-*.xml file from war
|
||||
* Installer: Add startup hint for non-x86
|
||||
* Javadoc updates all over the place
|
||||
* LogConsoleBuffer: Java 5
|
||||
* Naming:
|
||||
- Increase cache size and expiration time
|
||||
- Add clearCache() method
|
||||
- Don't use EepGet or Exec for b32
|
||||
- Javadoc updates
|
||||
* NetDB:
|
||||
- Expire unreachable routers quickly, even if they don't have introducers,
|
||||
so we don't have old data on routers that ran out of introducers.
|
||||
- Fix rare NPEs at shutdown
|
||||
* NTCP:
|
||||
- Cleanups
|
||||
* Streaming:
|
||||
- Make flush() block less, by waiting only for "accept" into the
|
||||
streaming queue rather than "completion" (i.e. ACK from the far end).
|
||||
This prevents complete stalls when flushing, and should help performance
|
||||
of apps that use flush(), like i2psnark (and SAM?).
|
||||
close() still does a flush that waits for completion, as i2ptunnel
|
||||
doesn't like a fast return from close().
|
||||
- cleanups
|
||||
* SusiDNS:
|
||||
- Remove unused web-*.xml file from war
|
||||
* TransportManager: Convert _transports from a List to a CHM
|
||||
to prevent a rare concurrent exception
|
||||
* Tunnels:
|
||||
- Don't use peers < 0.7.9 for tunnels due to the old
|
||||
message corruption bugs
|
||||
- Javadoc
|
||||
- Cleanups
|
||||
* UDP:
|
||||
- Beginnings of destroy message support
|
||||
- Try to avoid running out of introducers by relaxing selection criteria
|
||||
and increasing minimum number of potential introducers
|
||||
- Avoid rare AIOOBE
|
||||
- PacketBuilder refactor
|
||||
- Make most classes package private
|
||||
- Comments
|
||||
- Logging cleanup
|
||||
- Comment out a main()
|
||||
|
||||
2010-10-22 sponge
|
||||
* Sanity and some fixs for slackware package
|
||||
|
||||
|
@ -39,11 +39,21 @@ public abstract class JobImpl implements Job {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* As of 0.8.1, this is a noop, as it just adds classes to the log manager
|
||||
* class list for no good reason. Logging in jobs is almost always
|
||||
* set explicitly rather than by class name.
|
||||
*/
|
||||
void addedToQueue() {
|
||||
if (_context.logManager().getLog(getClass()).shouldLog(Log.DEBUG))
|
||||
_addedBy = new Exception();
|
||||
//if (_context.logManager().getLog(getClass()).shouldLog(Log.DEBUG))
|
||||
// _addedBy = new Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return null always
|
||||
*/
|
||||
public Exception getAddedBy() { return _addedBy; }
|
||||
public long getMadeReadyOn() { return _madeReadyOn; }
|
||||
public void madeReady() { _madeReadyOn = _context.clock().now(); }
|
||||
|
@ -142,8 +142,9 @@ public class JobQueue {
|
||||
public void addJob(Job job) {
|
||||
if (job == null || !_alive) return;
|
||||
|
||||
if (job instanceof JobImpl)
|
||||
((JobImpl)job).addedToQueue();
|
||||
// This does nothing
|
||||
//if (job instanceof JobImpl)
|
||||
// ((JobImpl)job).addedToQueue();
|
||||
|
||||
long numReady = 0;
|
||||
boolean alreadyExists = false;
|
||||
|
@ -218,6 +218,19 @@ public class Router {
|
||||
// NOW we start all the activity
|
||||
_context.initAll();
|
||||
|
||||
// Set wrapper.log permissions.
|
||||
// Just hope this is the right location, we don't know for sure,
|
||||
// but this is the same method used in LogsHelper and we have no complaints.
|
||||
// (we could look for the wrapper.config file and parse it I guess...)
|
||||
// If we don't have a wrapper, RouterLaunch does this for us.
|
||||
if (System.getProperty("wrapper.version") != null) {
|
||||
File f = new File(System.getProperty("java.io.tmpdir"), "wrapper.log");
|
||||
if (!f.exists())
|
||||
f = new File(_context.getBaseDir(), "wrapper.log");
|
||||
if (f.exists())
|
||||
SecureFileOutputStream.setPerms(f);
|
||||
}
|
||||
|
||||
_routerInfo = null;
|
||||
_higherVersionSeen = false;
|
||||
_log = _context.logManager().getLog(Router.class);
|
||||
|
@ -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 = 05;
|
||||
public final static long BUILD = 9;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -361,6 +361,8 @@ public class ClientConnectionRunner {
|
||||
// TunnelPool.locked_buildNewLeaseSet() ensures that leases are sorted,
|
||||
// so the comparison will always work.
|
||||
int leases = set.getLeaseCount();
|
||||
// synch so _currentLeaseSet isn't changed out from under us
|
||||
synchronized (this) {
|
||||
if (_currentLeaseSet != null && _currentLeaseSet.getLeaseCount() == leases) {
|
||||
for (int i = 0; i < leases; i++) {
|
||||
if (! _currentLeaseSet.getLease(i).getTunnelId().equals(set.getLease(i).getTunnelId()))
|
||||
@ -376,6 +378,7 @@ public class ClientConnectionRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Current leaseSet " + _currentLeaseSet + "\nNew leaseSet " + set);
|
||||
LeaseRequestState state = null;
|
||||
@ -590,7 +593,7 @@ public class ClientConnectionRunner {
|
||||
+ " for session [" + _sessionId.getSessionId()
|
||||
+ "] (with nonce=2), retrying after ["
|
||||
+ (_context.clock().now() - _lastTried)
|
||||
+ "]", getAddedBy());
|
||||
+ "]");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Updating message status for message " + _messageId + " to "
|
||||
|
@ -76,7 +76,7 @@ public class SendMessageDirectJob extends JobImpl {
|
||||
if (_expiration < now) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Timed out sending message " + _message + " directly (expiration = "
|
||||
+ new Date(_expiration) + ") to " + _targetHash.toBase64(), getAddedBy());
|
||||
+ new Date(_expiration) + ") to " + _targetHash.toBase64());
|
||||
if (_onFail != null)
|
||||
getContext().jobQueue().addJob(_onFail);
|
||||
return;
|
||||
@ -104,7 +104,7 @@ public class SendMessageDirectJob extends JobImpl {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to find the router to send to: " + _targetHash
|
||||
+ " after searching for " + (getContext().clock().now()-_searchOn)
|
||||
+ "ms, message: " + _message, getAddedBy());
|
||||
+ "ms, message: " + _message);
|
||||
if (_onFail != null)
|
||||
getContext().jobQueue().addJob(_onFail);
|
||||
}
|
||||
|
@ -136,17 +136,17 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
|
||||
@Override
|
||||
public boolean isBacklogged(Hash dest) {
|
||||
return _manager.isBacklogged(dest);
|
||||
return _manager != null && _manager.isBacklogged(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEstablished(Hash dest) {
|
||||
return _manager.isEstablished(dest);
|
||||
return _manager != null && _manager.isEstablished(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasUnreachable(Hash dest) {
|
||||
return _manager.wasUnreachable(dest);
|
||||
return _manager != null && _manager.wasUnreachable(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -615,8 +615,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Type " + msg.getMessage().getType() + " pri " + msg.getPriority() + " slot " + slot);
|
||||
boolean removed = _outbound.remove(msg);
|
||||
if ((!removed) && _log.shouldLog(Log.ERROR))
|
||||
_log.info("Already removed??? " + msg.getMessage().getType());
|
||||
if ((!removed) && _log.shouldLog(Log.WARN))
|
||||
_log.warn("Already removed??? " + msg.getMessage().getType());
|
||||
}
|
||||
_currentOutbound = msg;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.i2p.router.transport.ntcp;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
@ -24,9 +25,9 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class NTCPSendFinisher {
|
||||
private static final int THREADS = 4;
|
||||
private I2PAppContext _context;
|
||||
private NTCPTransport _transport;
|
||||
private Log _log;
|
||||
private final I2PAppContext _context;
|
||||
private final NTCPTransport _transport;
|
||||
private final Log _log;
|
||||
private int _count;
|
||||
private ThreadPoolExecutor _executor;
|
||||
|
||||
@ -47,7 +48,12 @@ public class NTCPSendFinisher {
|
||||
}
|
||||
|
||||
public void add(OutNetMessage msg) {
|
||||
try {
|
||||
_executor.execute(new RunnableEvent(msg));
|
||||
} catch (RejectedExecutionException ree) {
|
||||
// race with stop()
|
||||
_log.warn("NTCP send finisher stopped, discarding msg.afterSend()");
|
||||
}
|
||||
}
|
||||
|
||||
// not really needed for now but in case we want to add some hooks like afterExecute()
|
||||
|
@ -317,7 +317,7 @@ class TestJob extends JobImpl {
|
||||
public String getName() { return "Tunnel test timeout"; }
|
||||
public void runJob() {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Timeout: found? " + _found, getAddedBy());
|
||||
_log.warn("Timeout: found? " + _found);
|
||||
if (!_found) {
|
||||
// don't clog up the SKM with old one-tag tagsets
|
||||
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
|
||||
|
@ -81,7 +81,7 @@ public class SendGarlicJob extends JobImpl {
|
||||
long after = getContext().clock().now();
|
||||
if ( (after - before) > 1000) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Building the garlic took too long [" + (after-before)+" ms]", getAddedBy());
|
||||
_log.warn("Building the garlic took too long [" + (after-before)+" ms]");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Building the garlic was fast! " + (after - before) + " ms");
|
||||
|
Reference in New Issue
Block a user