diff --git a/apps/i2psnark/icons/arrow_down.png b/apps/i2psnark/icons/arrow_down.png deleted file mode 100644 index 2c4e27937..000000000 Binary files a/apps/i2psnark/icons/arrow_down.png and /dev/null differ diff --git a/apps/i2psnark/icons/basket_put.png b/apps/i2psnark/icons/basket_put.png new file mode 100644 index 000000000..be62faaaa Binary files /dev/null and b/apps/i2psnark/icons/basket_put.png differ diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index 2bf4f2d41..229132a7f 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -122,6 +122,9 @@ public class I2PSnarkUtil { } ******/ + /** @since 0.9.1 */ + public I2PAppContext getContext() { return _context; } + public boolean configured() { return _configured; } public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) { diff --git a/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java b/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java index da89f5577..c33dc4dff 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java +++ b/apps/i2psnark/java/src/org/klomp/snark/MagnetState.java @@ -5,10 +5,10 @@ import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import java.util.Random; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; +import net.i2p.util.RandomSource; import org.klomp.snark.bencode.BDecoder; import org.klomp.snark.bencode.BEValue; @@ -27,7 +27,6 @@ import org.klomp.snark.bencode.BEValue; */ class MagnetState { public static final int CHUNK_SIZE = 16*1024; - private static final Random random = I2PAppContext.getGlobalContext().random(); private final byte[] infohash; private boolean complete; @@ -129,7 +128,7 @@ class MagnetState { throw new IllegalArgumentException("not initialized"); if (complete) throw new IllegalArgumentException("complete"); - int rand = random.nextInt(totalChunks); + int rand = RandomSource.getInstance().nextInt(totalChunks); for (int i = 0; i < totalChunks; i++) { int chk = (i + rand) % totalChunks; if (!(have.get(chk) || requested.get(chk))) { diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java index 9efce9f07..ede3be57b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.Random; import net.i2p.I2PAppContext; +import net.i2p.data.DataHelper; +import net.i2p.util.Log; /** * TimerTask that checks for good/bad up/downloader. Works together @@ -36,16 +38,18 @@ class PeerCheckerTask implements Runnable private final PeerCoordinator coordinator; private final I2PSnarkUtil _util; + private final Log _log; + private final Random random; private int _runCount; PeerCheckerTask(I2PSnarkUtil util, PeerCoordinator coordinator) { _util = util; + _log = util.getContext().logManager().getLog(PeerCheckerTask.class); + random = util.getContext().random(); this.coordinator = coordinator; } - private static final Random random = I2PAppContext.getGlobalContext().random(); - public void run() { _runCount++; @@ -82,6 +86,14 @@ class PeerCheckerTask implements Runnable continue; } + if (peer.getInactiveTime() > PeerCoordinator.MAX_INACTIVE) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Disconnecting peer idle " + + DataHelper.formatDuration(peer.getInactiveTime()) + ": " + peer); + peer.disconnect(); + continue; + } + if (!peer.isChoking()) uploaders++; @@ -92,14 +104,15 @@ class PeerCheckerTask implements Runnable peer.setRateHistory(upload, download); peer.resetCounters(); - _util.debug(peer + ":", Snark.DEBUG); - _util.debug(" ul: " + upload*1024/KILOPERSECOND + if (_log.shouldLog(Log.DEBUG)) { + _log.debug(peer + ":" + + " ul: " + upload*1024/KILOPERSECOND + " dl: " + download*1024/KILOPERSECOND + " i: " + peer.isInterested() + " I: " + peer.isInteresting() + " c: " + peer.isChoking() - + " C: " + peer.isChoked(), - Snark.DEBUG); + + " C: " + peer.isChoked()); + } // Choke a percentage of them rather than all so it isn't so drastic... // unless this torrent is over the limit all by itself. @@ -120,8 +133,8 @@ class PeerCheckerTask implements Runnable // Check if it still wants pieces from us. if (!peer.isInterested()) { - _util.debug("Choke uninterested peer: " + peer, - Snark.INFO); + if (_log.shouldLog(Log.INFO)) + _log.debug("Choke uninterested peer: " + peer); peer.setChoking(true); uploaders--; coordinator.uploaders--; @@ -131,8 +144,8 @@ class PeerCheckerTask implements Runnable } else if (overBWLimitChoke) { - _util.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer, - Snark.INFO); + if (_log.shouldLog(Log.INFO)) + _log.debug("BW limit (" + upload + "/" + uploaded + "), choke peer: " + peer); peer.setChoking(true); uploaders--; coordinator.uploaders--; @@ -144,7 +157,8 @@ class PeerCheckerTask implements Runnable else if (peer.isInteresting() && peer.isChoked()) { // If they are choking us make someone else a downloader - _util.debug("Choke choking peer: " + peer, Snark.DEBUG); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Choke choking peer: " + peer); peer.setChoking(true); uploaders--; coordinator.uploaders--; @@ -156,7 +170,8 @@ class PeerCheckerTask implements Runnable else if (!peer.isInteresting() && !coordinator.completed()) { // If they aren't interesting make someone else a downloader - _util.debug("Choke uninteresting peer: " + peer, Snark.DEBUG); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Choke uninteresting peer: " + peer); peer.setChoking(true); uploaders--; coordinator.uploaders--; @@ -170,8 +185,8 @@ class PeerCheckerTask implements Runnable && download == 0) { // We are downloading but didn't receive anything... - _util.debug("Choke downloader that doesn't deliver:" - + peer, Snark.DEBUG); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Choke downloader that doesn't deliver: " + peer); peer.setChoking(true); uploaders--; coordinator.uploaders--; @@ -198,7 +213,10 @@ class PeerCheckerTask implements Runnable // send PEX if ((_runCount % 17) == 0 && !peer.isCompleted()) coordinator.sendPeers(peer); - peer.keepAlive(); + // cheap failsafe for seeds connected to seeds, stop pinging and hopefully + // the inactive checker (above) will eventually disconnect it + if (coordinator.getNeededLength() > 0 || !peer.isCompleted()) + peer.keepAlive(); // announce them to local tracker (TrackerClient does this too) if (_util.getDHT() != null && (_runCount % 5) == 0) { _util.getDHT().announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash()); @@ -215,8 +233,8 @@ class PeerCheckerTask implements Runnable || uploaders > uploadLimit) && worstDownloader != null) { - _util.debug("Choke worst downloader: " + worstDownloader, - Snark.DEBUG); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Choke worst downloader: " + worstDownloader); worstDownloader.setChoking(true); coordinator.uploaders--; diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index fe924f79c..8ded86247 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -68,6 +68,7 @@ class PeerCoordinator implements PeerListener // package local for access by CheckDownLoadersTask final static long CHECK_PERIOD = 40*1000; // 40 seconds final static int MAX_UPLOADERS = 6; + public static final long MAX_INACTIVE = 8*60*1000; /** * Approximation of the number of current uploaders. @@ -130,7 +131,7 @@ class PeerCoordinator implements PeerListener private final MagnetState magnetState; private final CoordinatorListener listener; private final I2PSnarkUtil _util; - private static final Random _random = I2PAppContext.getGlobalContext().random(); + private final Random _random; /** * @param metainfo null if in magnet mode @@ -140,6 +141,7 @@ class PeerCoordinator implements PeerListener CoordinatorListener listener, Snark torrent) { _util = util; + _random = util.getContext().random(); this.id = id; this.infohash = infohash; this.metainfo = metainfo; @@ -377,8 +379,10 @@ class PeerCoordinator implements PeerListener } /** - * Reduce max if huge pieces to keep from ooming when leeching - * @return 512K: 16; 1M: 11; 2M: 6 + * Formerly used to + * reduce max if huge pieces to keep from ooming when leeching + * but now we don't + * @return usually 16 */ private int getMaxConnections() { if (metainfo == null) @@ -388,7 +392,7 @@ class PeerCoordinator implements PeerListener return 4; if (pieces <= 5) return 6; - int size = metainfo.getPieceLength(0); + //int size = metainfo.getPieceLength(0); int max = _util.getMaxConnections(); // Now that we use temp files, no memory concern //if (size <= 512*1024 || completed()) @@ -449,7 +453,7 @@ class PeerCoordinator implements PeerListener synchronized(peers) { Peer old = peerIDInList(peer.getPeerID(), peers); - if ( (old != null) && (old.getInactiveTime() > 8*60*1000) ) { + if ( (old != null) && (old.getInactiveTime() > MAX_INACTIVE) ) { // idle for 8 minutes, kill the old con (32KB/8min = 68B/sec minimum for one block) if (_log.shouldLog(Log.WARN)) _log.warn("Remomving old peer: " + peer + ": " + old + ", inactive for " + old.getInactiveTime()); @@ -543,7 +547,7 @@ class PeerCoordinator implements PeerListener need_more = (!peer.isConnected()) && peersize < getMaxConnections(); // Check if we already have this peer before we build the connection Peer old = peerIDInList(peer.getPeerID(), peers); - need_more = need_more && ((old == null) || (old.getInactiveTime() > 8*60*1000)); + need_more = need_more && ((old == null) || (old.getInactiveTime() > MAX_INACTIVE)); } if (need_more) @@ -974,11 +978,8 @@ class PeerCoordinator implements PeerListener // Announce to the world we have it! // Disconnect from other seeders when we get the last piece - List toDisconnect = new ArrayList(); - Iterator it = peers.iterator(); - while (it.hasNext()) - { - Peer p = it.next(); + List toDisconnect = done ? new ArrayList() : null; + for (Peer p : peers) { if (p.isConnected()) { if (done && p.isCompleted()) @@ -986,15 +987,13 @@ class PeerCoordinator implements PeerListener else p.have(piece); } - } - it = toDisconnect.iterator(); - while (it.hasNext()) - { - Peer p = it.next(); - p.disconnect(true); - } - + } + if (done) { + for (Peer p : toDisconnect) { + p.disconnect(true); + } + // put msg on the console if partial, since Storage won't do it if (!completed()) snark.storageCompleted(storage); diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java b/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java index abf007a53..c5f31d8f5 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java @@ -243,11 +243,12 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl } /** - * @return torrent file bytes remaining or -1 + * @return -1 when done so the web will list us as "complete" instead of "seeding" */ @Override public long getRemainingLength() { - return _remaining; + long rv = _remaining; + return rv > 0 ? rv : -1; } /** diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 06c9501d7..3b667da42 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -1073,7 +1073,7 @@ public class I2PSnarkServlet extends DefaultServlet { else if (isValid) icon = toIcon(meta.getName()); else if (snark instanceof FetchAndAdd) - icon = "arrow_down"; + icon = "basket_put"; else icon = "magnet"; if (isValid) { @@ -1104,7 +1104,7 @@ public class I2PSnarkServlet extends DefaultServlet { out.write(""); if(isRunning && remainingSeconds > 0) - out.write(DataHelper.formatDuration2(remainingSeconds*1000)); // (eta 6h) + out.write(DataHelper.formatDuration2(Math.max(remainingSeconds, 10) * 1000)); // (eta 6h) out.write("\n\t"); out.write(""); if (remaining > 0) diff --git a/history.txt b/history.txt index 1456fa838..9468144ae 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,12 @@ +2012-07-01 zzz + * i2psnark: + - Don't send a keepalive to a peer we are going to disconnect + - Disconnect peer when idle a long time + - PeerCheckerTask cleanup + - Static ref cleanup + - Don't show a downloaded torrent file as "seeding" + - Better torrent file download icon + 2012-06-29 zzz * HTTP Proxy: Change the error code for unknown host from 404 to 500 * SimpleTimer: Fix logging diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index f5c926454..43dca9240 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -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 = 20; + public final static long BUILD = 21; /** for example "-test" */ public final static String EXTRA = "";