forked from I2P_Developers/i2p.i2p
merge of 'd09201283ea0356bf5b1d3aedc4795a202414930'
and 'e2f50f8cb50f8593ca882e94cb661c54b87d2468'
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
package org.klomp.snark;
|
package org.klomp.snark;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -393,6 +394,46 @@ public class I2PSnarkUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch to memory
|
||||||
|
* @param retries if < 0, set timeout to a few seconds
|
||||||
|
* @param initialSize buffer size
|
||||||
|
* @param maxSize fails if greater
|
||||||
|
* @return null on error
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public byte[] get(String url, boolean rewrite, int retries, int initialSize, int maxSize) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Fetching [" + url + "] to memory");
|
||||||
|
String fetchURL = url;
|
||||||
|
if (rewrite)
|
||||||
|
fetchURL = rewriteAnnounce(url);
|
||||||
|
int timeout;
|
||||||
|
if (retries < 0) {
|
||||||
|
if (!connected())
|
||||||
|
return null;
|
||||||
|
timeout = EEPGET_CONNECT_TIMEOUT_SHORT;
|
||||||
|
retries = 0;
|
||||||
|
} else {
|
||||||
|
timeout = EEPGET_CONNECT_TIMEOUT;
|
||||||
|
if (!connected()) {
|
||||||
|
if (!connect())
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(initialSize);
|
||||||
|
EepGet get = new I2PSocketEepGet(_context, _manager, retries, -1, maxSize, null, out, fetchURL);
|
||||||
|
if (get.fetch(timeout)) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Fetch successful [" + url + "]: size=" + out.size());
|
||||||
|
return out.toByteArray();
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Fetch failed [" + url + "]");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public I2PServerSocket getServerSocket() {
|
public I2PServerSocket getServerSocket() {
|
||||||
I2PSocketManager mgr = _manager;
|
I2PSocketManager mgr = _manager;
|
||||||
if (mgr != null)
|
if (mgr != null)
|
||||||
@ -524,6 +565,15 @@ public class I2PSnarkUtil {
|
|||||||
return _openTrackers;
|
return _openTrackers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of open trackers to use as backups even if disabled
|
||||||
|
* @return non-null
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public List<String> getBackupTrackers() {
|
||||||
|
return _openTrackers;
|
||||||
|
}
|
||||||
|
|
||||||
public void setUseOpenTrackers(boolean yes) {
|
public void setUseOpenTrackers(boolean yes) {
|
||||||
_shouldUseOT = yes;
|
_shouldUseOT = yes;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
package org.klomp.snark;
|
package org.klomp.snark;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -43,6 +44,7 @@ import net.i2p.util.I2PAppThread;
|
|||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.SimpleTimer2;
|
import net.i2p.util.SimpleTimer2;
|
||||||
|
|
||||||
|
import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||||
import org.klomp.snark.dht.DHT;
|
import org.klomp.snark.dht.DHT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,6 +72,8 @@ public class TrackerClient implements Runnable {
|
|||||||
private static final String COMPLETED_EVENT = "completed";
|
private static final String COMPLETED_EVENT = "completed";
|
||||||
private static final String STOPPED_EVENT = "stopped";
|
private static final String STOPPED_EVENT = "stopped";
|
||||||
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
|
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
|
||||||
|
/** this is our equivalent to router.utorrent.com for bootstrap */
|
||||||
|
private static final String DEFAULT_BACKUP_TRACKER = "http://tracker.welterde.i2p/a";
|
||||||
|
|
||||||
private final static int SLEEP = 5; // 5 minutes.
|
private final static int SLEEP = 5; // 5 minutes.
|
||||||
private final static int DELAY_MIN = 2000; // 2 secs.
|
private final static int DELAY_MIN = 2000; // 2 secs.
|
||||||
@ -78,7 +82,7 @@ public class TrackerClient implements Runnable {
|
|||||||
private final static int INITIAL_SLEEP = 90*1000;
|
private final static int INITIAL_SLEEP = 90*1000;
|
||||||
private final static int MAX_CONSEC_FAILS = 5; // slow down after this
|
private final static int MAX_CONSEC_FAILS = 5; // slow down after this
|
||||||
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
|
private final static int LONG_SLEEP = 30*60*1000; // sleep a while after lots of fails
|
||||||
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 10*60*1000;
|
private final static long MIN_TRACKER_ANNOUNCE_INTERVAL = 15*60*1000;
|
||||||
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
|
private final static long MIN_DHT_ANNOUNCE_INTERVAL = 10*60*1000;
|
||||||
|
|
||||||
private final I2PSnarkUtil _util;
|
private final I2PSnarkUtil _util;
|
||||||
@ -106,6 +110,7 @@ public class TrackerClient implements Runnable {
|
|||||||
private volatile boolean _fastUnannounce;
|
private volatile boolean _fastUnannounce;
|
||||||
private long lastDHTAnnounce;
|
private long lastDHTAnnounce;
|
||||||
private final List<Tracker> trackers;
|
private final List<Tracker> trackers;
|
||||||
|
private final List<Tracker> backupTrackers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call start() to start it.
|
* Call start() to start it.
|
||||||
@ -131,6 +136,7 @@ public class TrackerClient implements Runnable {
|
|||||||
this.infoHash = urlencode(snark.getInfoHash());
|
this.infoHash = urlencode(snark.getInfoHash());
|
||||||
this.peerID = urlencode(snark.getID());
|
this.peerID = urlencode(snark.getID());
|
||||||
this.trackers = new ArrayList(2);
|
this.trackers = new ArrayList(2);
|
||||||
|
this.backupTrackers = new ArrayList(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void start() {
|
public synchronized void start() {
|
||||||
@ -233,7 +239,7 @@ public class TrackerClient implements Runnable {
|
|||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
// FIXME only when starting everybody at once, not for a single torrent
|
// FIXME only when starting everybody at once, not for a single torrent
|
||||||
long delay = I2PAppContext.getGlobalContext().random().nextInt(30*1000);
|
long delay = _util.getContext().random().nextInt(30*1000);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(delay);
|
Thread.sleep(delay);
|
||||||
} catch (InterruptedException ie) {}
|
} catch (InterruptedException ie) {}
|
||||||
@ -267,18 +273,20 @@ public class TrackerClient implements Runnable {
|
|||||||
if (primary != null) {
|
if (primary != null) {
|
||||||
if (isValidAnnounce(primary)) {
|
if (isValidAnnounce(primary)) {
|
||||||
trackers.add(new Tracker(primary, true));
|
trackers.add(new Tracker(primary, true));
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Announce: [" + primary + "] infoHash: " + infoHash);
|
_log.debug("Announce: [" + primary + "] infoHash: " + infoHash);
|
||||||
} else {
|
} else {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Skipping invalid or non-i2p announce: " + primary);
|
_log.warn("Skipping invalid or non-i2p announce: " + primary);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_log.warn("No primary announce");
|
_log.warn("No primary announce");
|
||||||
primary = "";
|
primary = "";
|
||||||
}
|
}
|
||||||
List tlist = _util.getOpenTrackers();
|
if (meta == null || !meta.isPrivate()) {
|
||||||
if (tlist != null && (meta == null || !meta.isPrivate())) {
|
List<String> tlist = _util.getOpenTrackers();
|
||||||
for (int i = 0; i < tlist.size(); i++) {
|
for (int i = 0; i < tlist.size(); i++) {
|
||||||
String url = (String)tlist.get(i);
|
String url = tlist.get(i);
|
||||||
if (!isValidAnnounce(url)) {
|
if (!isValidAnnounce(url)) {
|
||||||
_log.error("Bad announce URL: [" + url + "]");
|
_log.error("Bad announce URL: [" + url + "]");
|
||||||
continue;
|
continue;
|
||||||
@ -301,9 +309,37 @@ public class TrackerClient implements Runnable {
|
|||||||
continue;
|
continue;
|
||||||
// opentrackers are primary if we don't have primary
|
// opentrackers are primary if we don't have primary
|
||||||
trackers.add(new Tracker(url, primary.equals("")));
|
trackers.add(new Tracker(url, primary.equals("")));
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
_log.debug("Additional announce: [" + url + "] for infoHash: " + infoHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// backup trackers if DHT needs bootstrapping
|
||||||
|
if (trackers.isEmpty() && (meta == null || !meta.isPrivate())) {
|
||||||
|
List<String> tlist = _util.getBackupTrackers();
|
||||||
|
for (int i = 0; i < tlist.size(); i++) {
|
||||||
|
String url = tlist.get(i);
|
||||||
|
if (!isValidAnnounce(url)) {
|
||||||
|
_log.error("Bad announce URL: [" + url + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int slash = url.indexOf('/', 7);
|
||||||
|
if (slash <= 7) {
|
||||||
|
_log.error("Bad announce URL: [" + url + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String dest = _util.lookup(url.substring(7, slash));
|
||||||
|
if (dest == null) {
|
||||||
|
_log.error("Announce host unknown: [" + url.substring(7, slash) + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
backupTrackers.add(new Tracker(url, false));
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Backup announce: [" + url + "] for infoHash: " + infoHash);
|
||||||
|
}
|
||||||
|
if (backupTrackers.isEmpty())
|
||||||
|
backupTrackers.add(new Tracker(DEFAULT_BACKUP_TRACKER, false));
|
||||||
|
}
|
||||||
this.completed = coordinator.getLeft() == 0;
|
this.completed = coordinator.getLeft() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +351,7 @@ public class TrackerClient implements Runnable {
|
|||||||
private void loop() {
|
private void loop() {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Random r = I2PAppContext.getGlobalContext().random();
|
// normally this will only go once, then call queueLoop() and return
|
||||||
while(!stop)
|
while(!stop)
|
||||||
{
|
{
|
||||||
if (!verifyConnected()) {
|
if (!verifyConnected()) {
|
||||||
@ -325,186 +361,24 @@ public class TrackerClient implements Runnable {
|
|||||||
|
|
||||||
// Local DHT tracker announce
|
// Local DHT tracker announce
|
||||||
DHT dht = _util.getDHT();
|
DHT dht = _util.getDHT();
|
||||||
if (dht != null)
|
if (dht != null && (meta == null || !meta.isPrivate()))
|
||||||
dht.announce(snark.getInfoHash());
|
dht.announce(snark.getInfoHash());
|
||||||
|
|
||||||
long uploaded = coordinator.getUploaded();
|
|
||||||
long downloaded = coordinator.getDownloaded();
|
|
||||||
long left = coordinator.getLeft(); // -1 in magnet mode
|
|
||||||
|
|
||||||
// First time we got a complete download?
|
|
||||||
String event;
|
|
||||||
if (!completed && left == 0)
|
|
||||||
{
|
|
||||||
completed = true;
|
|
||||||
event = COMPLETED_EVENT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
event = NO_EVENT;
|
|
||||||
|
|
||||||
// *** loop once for each tracker
|
|
||||||
int maxSeenPeers = 0;
|
int maxSeenPeers = 0;
|
||||||
for (Tracker tr : trackers) {
|
if (!trackers.isEmpty())
|
||||||
if ((!stop) && (!tr.stop) &&
|
maxSeenPeers = getPeersFromTrackers(trackers);
|
||||||
(completed || coordinator.needOutboundPeers() || !tr.started) &&
|
int p = getPeersFromPEX();
|
||||||
(event.equals(COMPLETED_EVENT) || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
|
if (p > maxSeenPeers)
|
||||||
{
|
maxSeenPeers = p;
|
||||||
try
|
p = getPeersFromDHT();
|
||||||
{
|
if (p > maxSeenPeers)
|
||||||
if (!tr.started)
|
maxSeenPeers = p;
|
||||||
event = STARTED_EVENT;
|
// backup if DHT needs bootstrapping
|
||||||
TrackerInfo info = doRequest(tr, infoHash, peerID,
|
if (trackers.isEmpty() && !backupTrackers.isEmpty() && dht != null && dht.size() < 16) {
|
||||||
uploaded, downloaded, left,
|
p = getPeersFromTrackers(backupTrackers);
|
||||||
event);
|
if (p > maxSeenPeers)
|
||||||
|
maxSeenPeers = p;
|
||||||
snark.setTrackerProblems(null);
|
|
||||||
tr.trackerProblems = null;
|
|
||||||
tr.registerFails = 0;
|
|
||||||
tr.consecutiveFails = 0;
|
|
||||||
if (tr.isPrimary)
|
|
||||||
consecutiveFails = 0;
|
|
||||||
runStarted = true;
|
|
||||||
tr.started = true;
|
|
||||||
|
|
||||||
Set<Peer> peers = info.getPeers();
|
|
||||||
tr.seenPeers = info.getPeerCount();
|
|
||||||
if (snark.getTrackerSeenPeers() < tr.seenPeers) // update rising number quickly
|
|
||||||
snark.setTrackerSeenPeers(tr.seenPeers);
|
|
||||||
|
|
||||||
// pass everybody over to our tracker
|
|
||||||
dht = _util.getDHT();
|
|
||||||
if (dht != null) {
|
|
||||||
for (Peer peer : peers) {
|
|
||||||
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (coordinator.needOutboundPeers()) {
|
|
||||||
// we only want to talk to new people if we need things
|
|
||||||
// from them (duh)
|
|
||||||
List<Peer> ordered = new ArrayList(peers);
|
|
||||||
Collections.shuffle(ordered, r);
|
|
||||||
Iterator<Peer> it = ordered.iterator();
|
|
||||||
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
|
||||||
Peer cur = it.next();
|
|
||||||
// FIXME if id == us || dest == us continue;
|
|
||||||
// only delay if we actually make an attempt to add peer
|
|
||||||
if(coordinator.addPeer(cur) && it.hasNext()) {
|
|
||||||
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
|
||||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe)
|
|
||||||
{
|
|
||||||
// Probably not fatal (if it doesn't last to long...)
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn
|
|
||||||
("WARNING: Could not contact tracker at '"
|
|
||||||
+ tr.announce + "': " + ioe);
|
|
||||||
tr.trackerProblems = ioe.getMessage();
|
|
||||||
// don't show secondary tracker problems to the user
|
|
||||||
if (tr.isPrimary)
|
|
||||||
snark.setTrackerProblems(tr.trackerProblems);
|
|
||||||
if (tr.trackerProblems.toLowerCase(Locale.US).startsWith(NOT_REGISTERED)) {
|
|
||||||
// Give a guy some time to register it if using opentrackers too
|
|
||||||
if (trackers.size() == 1) {
|
|
||||||
stop = true;
|
|
||||||
snark.stopTorrent();
|
|
||||||
} else { // hopefully each on the opentrackers list is really open
|
|
||||||
if (tr.registerFails++ > MAX_REGISTER_FAILS)
|
|
||||||
tr.stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (++tr.consecutiveFails == MAX_CONSEC_FAILS) {
|
|
||||||
tr.seenPeers = 0;
|
|
||||||
if (tr.interval < LONG_SLEEP)
|
|
||||||
tr.interval = LONG_SLEEP; // slow down
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Not announcing to " + tr.announce + " last announce was " +
|
|
||||||
new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval));
|
|
||||||
}
|
|
||||||
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
|
|
||||||
maxSeenPeers = tr.seenPeers;
|
|
||||||
} // *** end of trackers loop here
|
|
||||||
|
|
||||||
// Get peers from PEX
|
|
||||||
if (coordinator.needOutboundPeers() && (meta == null || !meta.isPrivate()) && !stop) {
|
|
||||||
Set<PeerID> pids = coordinator.getPEXPeers();
|
|
||||||
if (!pids.isEmpty()) {
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Got " + pids.size() + " from PEX");
|
|
||||||
List<Peer> peers = new ArrayList(pids.size());
|
|
||||||
for (PeerID pID : pids) {
|
|
||||||
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
|
||||||
}
|
|
||||||
Collections.shuffle(peers, r);
|
|
||||||
Iterator<Peer> it = peers.iterator();
|
|
||||||
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
|
||||||
Peer cur = it.next();
|
|
||||||
if (coordinator.addPeer(cur) && it.hasNext()) {
|
|
||||||
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
|
||||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Not getting PEX peers");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get peers from DHT
|
|
||||||
// FIXME this needs to be in its own thread
|
|
||||||
dht = _util.getDHT();
|
|
||||||
if (dht != null && (meta == null || !meta.isPrivate()) && (!stop) &&
|
|
||||||
_util.getContext().clock().now() > lastDHTAnnounce + MIN_DHT_ANNOUNCE_INTERVAL) {
|
|
||||||
int numwant;
|
|
||||||
if (event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
|
|
||||||
numwant = 1;
|
|
||||||
else
|
|
||||||
numwant = _util.getMaxConnections();
|
|
||||||
Collection<Hash> hashes = dht.getPeers(snark.getInfoHash(), numwant, 2*60*1000);
|
|
||||||
if (!hashes.isEmpty()) {
|
|
||||||
runStarted = true;
|
|
||||||
lastDHTAnnounce = _util.getContext().clock().now();
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Got " + hashes + " from DHT");
|
|
||||||
// announce ourselves while the token is still good
|
|
||||||
// FIXME this needs to be in its own thread
|
|
||||||
if (!stop) {
|
|
||||||
// announce only to the 1 closest
|
|
||||||
int good = dht.announce(snark.getInfoHash(), 1, 5*60*1000);
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Sent " + good + " good announces to DHT");
|
|
||||||
}
|
|
||||||
|
|
||||||
// now try these peers
|
|
||||||
if ((!stop) && !hashes.isEmpty()) {
|
|
||||||
List<Peer> peers = new ArrayList(hashes.size());
|
|
||||||
for (Hash h : hashes) {
|
|
||||||
PeerID pID = new PeerID(h.getData(), _util);
|
|
||||||
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
|
||||||
}
|
|
||||||
Collections.shuffle(peers, r);
|
|
||||||
Iterator<Peer> it = peers.iterator();
|
|
||||||
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
|
||||||
Peer cur = it.next();
|
|
||||||
if (coordinator.addPeer(cur) && it.hasNext()) {
|
|
||||||
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
|
||||||
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Not getting DHT peers");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// we could try and total the unique peers but that's too hard for now
|
// we could try and total the unique peers but that's too hard for now
|
||||||
snark.setTrackerSeenPeers(maxSeenPeers);
|
snark.setTrackerSeenPeers(maxSeenPeers);
|
||||||
@ -516,6 +390,7 @@ public class TrackerClient implements Runnable {
|
|||||||
// Sleep some minutes...
|
// Sleep some minutes...
|
||||||
// Sleep the minimum interval for all the trackers, but 60s minimum
|
// Sleep the minimum interval for all the trackers, but 60s minimum
|
||||||
int delay;
|
int delay;
|
||||||
|
Random r = _util.getContext().random();
|
||||||
int random = r.nextInt(120*1000);
|
int random = r.nextInt(120*1000);
|
||||||
if (completed && runStarted)
|
if (completed && runStarted)
|
||||||
delay = 3*SLEEP*60*1000 + random;
|
delay = 3*SLEEP*60*1000 + random;
|
||||||
@ -547,6 +422,212 @@ public class TrackerClient implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return max peers seen
|
||||||
|
*/
|
||||||
|
private int getPeersFromTrackers(List<Tracker> trckrs) {
|
||||||
|
long uploaded = coordinator.getUploaded();
|
||||||
|
long downloaded = coordinator.getDownloaded();
|
||||||
|
long left = coordinator.getLeft(); // -1 in magnet mode
|
||||||
|
|
||||||
|
// First time we got a complete download?
|
||||||
|
String event;
|
||||||
|
if (!completed && left == 0)
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
event = COMPLETED_EVENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
event = NO_EVENT;
|
||||||
|
|
||||||
|
// *** loop once for each tracker
|
||||||
|
int maxSeenPeers = 0;
|
||||||
|
for (Tracker tr : trckrs) {
|
||||||
|
if ((!stop) && (!tr.stop) &&
|
||||||
|
(completed || coordinator.needOutboundPeers() || !tr.started) &&
|
||||||
|
(event.equals(COMPLETED_EVENT) || System.currentTimeMillis() > tr.lastRequestTime + tr.interval))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!tr.started)
|
||||||
|
event = STARTED_EVENT;
|
||||||
|
TrackerInfo info = doRequest(tr, infoHash, peerID,
|
||||||
|
uploaded, downloaded, left,
|
||||||
|
event);
|
||||||
|
|
||||||
|
snark.setTrackerProblems(null);
|
||||||
|
tr.trackerProblems = null;
|
||||||
|
tr.registerFails = 0;
|
||||||
|
tr.consecutiveFails = 0;
|
||||||
|
if (tr.isPrimary)
|
||||||
|
consecutiveFails = 0;
|
||||||
|
runStarted = true;
|
||||||
|
tr.started = true;
|
||||||
|
|
||||||
|
Set<Peer> peers = info.getPeers();
|
||||||
|
tr.seenPeers = info.getPeerCount();
|
||||||
|
if (snark.getTrackerSeenPeers() < tr.seenPeers) // update rising number quickly
|
||||||
|
snark.setTrackerSeenPeers(tr.seenPeers);
|
||||||
|
|
||||||
|
// pass everybody over to our tracker
|
||||||
|
DHT dht = _util.getDHT();
|
||||||
|
if (dht != null) {
|
||||||
|
for (Peer peer : peers) {
|
||||||
|
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coordinator.needOutboundPeers()) {
|
||||||
|
// we only want to talk to new people if we need things
|
||||||
|
// from them (duh)
|
||||||
|
List<Peer> ordered = new ArrayList(peers);
|
||||||
|
Random r = _util.getContext().random();
|
||||||
|
Collections.shuffle(ordered, r);
|
||||||
|
Iterator<Peer> it = ordered.iterator();
|
||||||
|
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
||||||
|
Peer cur = it.next();
|
||||||
|
// FIXME if id == us || dest == us continue;
|
||||||
|
// only delay if we actually make an attempt to add peer
|
||||||
|
if(coordinator.addPeer(cur) && it.hasNext()) {
|
||||||
|
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
||||||
|
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ioe)
|
||||||
|
{
|
||||||
|
// Probably not fatal (if it doesn't last to long...)
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn
|
||||||
|
("WARNING: Could not contact tracker at '"
|
||||||
|
+ tr.announce + "': " + ioe);
|
||||||
|
tr.trackerProblems = ioe.getMessage();
|
||||||
|
// don't show secondary tracker problems to the user
|
||||||
|
if (tr.isPrimary)
|
||||||
|
snark.setTrackerProblems(tr.trackerProblems);
|
||||||
|
if (tr.trackerProblems.toLowerCase(Locale.US).startsWith(NOT_REGISTERED)) {
|
||||||
|
// Give a guy some time to register it if using opentrackers too
|
||||||
|
//if (trckrs.size() == 1) {
|
||||||
|
// stop = true;
|
||||||
|
// snark.stopTorrent();
|
||||||
|
//} else { // hopefully each on the opentrackers list is really open
|
||||||
|
if (tr.registerFails++ > MAX_REGISTER_FAILS)
|
||||||
|
tr.stop = true;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
if (++tr.consecutiveFails == MAX_CONSEC_FAILS) {
|
||||||
|
tr.seenPeers = 0;
|
||||||
|
if (tr.interval < LONG_SLEEP)
|
||||||
|
tr.interval = LONG_SLEEP; // slow down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Not announcing to " + tr.announce + " last announce was " +
|
||||||
|
new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval));
|
||||||
|
}
|
||||||
|
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
|
||||||
|
maxSeenPeers = tr.seenPeers;
|
||||||
|
} // *** end of trackers loop here
|
||||||
|
|
||||||
|
return maxSeenPeers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return max peers seen
|
||||||
|
*/
|
||||||
|
private int getPeersFromPEX() {
|
||||||
|
// Get peers from PEX
|
||||||
|
int rv = 0;
|
||||||
|
if (coordinator.needOutboundPeers() && (meta == null || !meta.isPrivate()) && !stop) {
|
||||||
|
Set<PeerID> pids = coordinator.getPEXPeers();
|
||||||
|
if (!pids.isEmpty()) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Got " + pids.size() + " from PEX");
|
||||||
|
List<Peer> peers = new ArrayList(pids.size());
|
||||||
|
for (PeerID pID : pids) {
|
||||||
|
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
||||||
|
}
|
||||||
|
Random r = _util.getContext().random();
|
||||||
|
Collections.shuffle(peers, r);
|
||||||
|
Iterator<Peer> it = peers.iterator();
|
||||||
|
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
||||||
|
Peer cur = it.next();
|
||||||
|
if (coordinator.addPeer(cur) && it.hasNext()) {
|
||||||
|
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
||||||
|
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rv = pids.size();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Not getting PEX peers");
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return max peers seen
|
||||||
|
*/
|
||||||
|
private int getPeersFromDHT() {
|
||||||
|
// Get peers from DHT
|
||||||
|
// FIXME this needs to be in its own thread
|
||||||
|
int rv = 0;
|
||||||
|
DHT dht = _util.getDHT();
|
||||||
|
if (dht != null && (meta == null || !meta.isPrivate()) && (!stop) &&
|
||||||
|
_util.getContext().clock().now() > lastDHTAnnounce + MIN_DHT_ANNOUNCE_INTERVAL) {
|
||||||
|
int numwant;
|
||||||
|
if (!coordinator.needOutboundPeers())
|
||||||
|
numwant = 1;
|
||||||
|
else
|
||||||
|
numwant = _util.getMaxConnections();
|
||||||
|
Collection<Hash> hashes = dht.getPeers(snark.getInfoHash(), numwant, 2*60*1000);
|
||||||
|
if (!hashes.isEmpty()) {
|
||||||
|
runStarted = true;
|
||||||
|
lastDHTAnnounce = _util.getContext().clock().now();
|
||||||
|
rv = hashes.size();
|
||||||
|
}
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Got " + hashes + " from DHT");
|
||||||
|
// announce ourselves while the token is still good
|
||||||
|
// FIXME this needs to be in its own thread
|
||||||
|
if (!stop) {
|
||||||
|
// announce only to the 1 closest
|
||||||
|
int good = dht.announce(snark.getInfoHash(), 1, 5*60*1000);
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Sent " + good + " good announces to DHT");
|
||||||
|
}
|
||||||
|
|
||||||
|
// now try these peers
|
||||||
|
if ((!stop) && !hashes.isEmpty()) {
|
||||||
|
List<Peer> peers = new ArrayList(hashes.size());
|
||||||
|
for (Hash h : hashes) {
|
||||||
|
try {
|
||||||
|
PeerID pID = new PeerID(h.getData(), _util);
|
||||||
|
peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo()));
|
||||||
|
} catch (InvalidBEncodingException ibe) {}
|
||||||
|
}
|
||||||
|
Random r = _util.getContext().random();
|
||||||
|
Collections.shuffle(peers, r);
|
||||||
|
Iterator<Peer> it = peers.iterator();
|
||||||
|
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
||||||
|
Peer cur = it.next();
|
||||||
|
if (coordinator.addPeer(cur) && it.hasNext()) {
|
||||||
|
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
||||||
|
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Not getting DHT peers");
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a thread for each tracker in parallel if tunnel is still open
|
* Creates a thread for each tracker in parallel if tunnel is still open
|
||||||
* @since 0.9.1
|
* @since 0.9.1
|
||||||
@ -630,7 +711,8 @@ public class TrackerClient implements Runnable {
|
|||||||
if (! event.equals(NO_EVENT))
|
if (! event.equals(NO_EVENT))
|
||||||
buf.append("&event=").append(event);
|
buf.append("&event=").append(event);
|
||||||
buf.append("&numwant=");
|
buf.append("&numwant=");
|
||||||
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
|
boolean small = left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers();
|
||||||
|
if (small)
|
||||||
buf.append('0');
|
buf.append('0');
|
||||||
else
|
else
|
||||||
buf.append(_util.getMaxConnections());
|
buf.append(_util.getMaxConnections());
|
||||||
@ -641,14 +723,12 @@ public class TrackerClient implements Runnable {
|
|||||||
tr.lastRequestTime = System.currentTimeMillis();
|
tr.lastRequestTime = System.currentTimeMillis();
|
||||||
// Don't wait for a response to stopped when shutting down
|
// Don't wait for a response to stopped when shutting down
|
||||||
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
|
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
|
||||||
File fetched = _util.get(s, true, fast ? -1 : 0);
|
byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 8*1024);
|
||||||
if (fetched == null) {
|
if (fetched == null) {
|
||||||
throw new IOException("Error fetching " + s);
|
throw new IOException("Error fetching " + s);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream in = null;
|
InputStream in = new ByteArrayInputStream(fetched);
|
||||||
try {
|
|
||||||
in = new FileInputStream(fetched);
|
|
||||||
|
|
||||||
TrackerInfo info = new TrackerInfo(in, snark.getID(),
|
TrackerInfo info = new TrackerInfo(in, snark.getID(),
|
||||||
snark.getInfoHash(), snark.getMetaInfo(), _util);
|
snark.getInfoHash(), snark.getMetaInfo(), _util);
|
||||||
@ -661,10 +741,6 @@ public class TrackerClient implements Runnable {
|
|||||||
|
|
||||||
tr.interval = Math.max(MIN_TRACKER_ANNOUNCE_INTERVAL, info.getInterval() * 1000l);
|
tr.interval = Math.max(MIN_TRACKER_ANNOUNCE_INTERVAL, info.getInterval() * 1000l);
|
||||||
return info;
|
return info;
|
||||||
} finally {
|
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
|
||||||
fetched.delete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,6 +143,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
|||||||
/** how long since generated do we delete - BEP 5 says 10 minutes */
|
/** how long since generated do we delete - BEP 5 says 10 minutes */
|
||||||
private static final long MAX_TOKEN_AGE = 10*60*1000;
|
private static final long MAX_TOKEN_AGE = 10*60*1000;
|
||||||
private static final long MAX_INBOUND_TOKEN_AGE = MAX_TOKEN_AGE - 2*60*1000;
|
private static final long MAX_INBOUND_TOKEN_AGE = MAX_TOKEN_AGE - 2*60*1000;
|
||||||
|
private static final int MAX_OUTBOUND_TOKENS = 5000;
|
||||||
/** how long since sent do we wait for a reply */
|
/** how long since sent do we wait for a reply */
|
||||||
private static final long MAX_MSGID_AGE = 2*60*1000;
|
private static final long MAX_MSGID_AGE = 2*60*1000;
|
||||||
/** how long since sent do we wait for a reply */
|
/** how long since sent do we wait for a reply */
|
||||||
@ -1208,7 +1209,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle and respond to the query.
|
* Handle and respond to the query.
|
||||||
* We have no node info here, it came on response port, we have to get it from the token
|
* We have no node info here, it came on response port, we have to get it from the token.
|
||||||
|
* So we can't verify that it came from the same peer, as BEP 5 specifies.
|
||||||
*/
|
*/
|
||||||
private void receiveAnnouncePeer(MsgID msgID, InfoHash ih, byte[] tok) throws InvalidBEncodingException {
|
private void receiveAnnouncePeer(MsgID msgID, InfoHash ih, byte[] tok) throws InvalidBEncodingException {
|
||||||
Token token = new Token(tok);
|
Token token = new Token(tok);
|
||||||
@ -1216,8 +1218,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
|||||||
if (nInfo == null) {
|
if (nInfo == null) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Unknown token in announce_peer: " + token);
|
_log.warn("Unknown token in announce_peer: " + token);
|
||||||
if (_log.shouldLog(Log.INFO))
|
//if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Current known tokens: " + _outgoingTokens.keySet());
|
// _log.info("Current known tokens: " + _outgoingTokens.keySet());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
@ -1282,8 +1284,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
|||||||
* @throws NPE, IllegalArgumentException, and others too
|
* @throws NPE, IllegalArgumentException, and others too
|
||||||
*/
|
*/
|
||||||
private List<NodeInfo> receiveNodes(NodeInfo nInfo, byte[] ids) throws InvalidBEncodingException {
|
private List<NodeInfo> receiveNodes(NodeInfo nInfo, byte[] ids) throws InvalidBEncodingException {
|
||||||
List<NodeInfo> rv = new ArrayList(ids.length / NodeInfo.LENGTH);
|
int max = Math.min(K, ids.length / NodeInfo.LENGTH);
|
||||||
for (int off = 0; off < ids.length; off += NodeInfo.LENGTH) {
|
List<NodeInfo> rv = new ArrayList(max);
|
||||||
|
for (int off = 0; off < ids.length && rv.size() < max; off += NodeInfo.LENGTH) {
|
||||||
NodeInfo nInf = new NodeInfo(ids, off);
|
NodeInfo nInf = new NodeInfo(ids, off);
|
||||||
if (_blacklist.contains(nInf.getNID())) {
|
if (_blacklist.contains(nInf.getNID())) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
@ -1305,12 +1308,15 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
|||||||
private List<Hash> receivePeers(NodeInfo nInfo, List<BEValue> peers) throws InvalidBEncodingException {
|
private List<Hash> receivePeers(NodeInfo nInfo, List<BEValue> peers) throws InvalidBEncodingException {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Rcvd peers from: " + nInfo);
|
_log.info("Rcvd peers from: " + nInfo);
|
||||||
List<Hash> rv = new ArrayList(peers.size());
|
int max = Math.min(MAX_WANT, peers.size());
|
||||||
|
List<Hash> rv = new ArrayList(max);
|
||||||
for (BEValue bev : peers) {
|
for (BEValue bev : peers) {
|
||||||
byte[] b = bev.getBytes();
|
byte[] b = bev.getBytes();
|
||||||
//Hash h = new Hash(b);
|
//Hash h = new Hash(b);
|
||||||
Hash h = Hash.create(b);
|
Hash h = Hash.create(b);
|
||||||
rv.add(h);
|
rv.add(h);
|
||||||
|
if (rv.size() >= max)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Rcvd peers from: " + nInfo + ": " + DataHelper.toString(rv));
|
_log.info("Rcvd peers from: " + nInfo + ": " + DataHelper.toString(rv));
|
||||||
@ -1535,20 +1541,28 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
|||||||
_blacklist.size() + " in blacklist, " +
|
_blacklist.size() + " in blacklist, " +
|
||||||
_outgoingTokens.size() + " sent Tokens, " +
|
_outgoingTokens.size() + " sent Tokens, " +
|
||||||
_incomingTokens.size() + " rcvd Tokens");
|
_incomingTokens.size() + " rcvd Tokens");
|
||||||
|
int cnt = 0;
|
||||||
|
long expire = now - MAX_TOKEN_AGE;
|
||||||
for (Iterator<Token> iter = _outgoingTokens.keySet().iterator(); iter.hasNext(); ) {
|
for (Iterator<Token> iter = _outgoingTokens.keySet().iterator(); iter.hasNext(); ) {
|
||||||
Token tok = iter.next();
|
Token tok = iter.next();
|
||||||
if (tok.lastSeen() < now - MAX_TOKEN_AGE)
|
// just delete at random if we have too many
|
||||||
|
// TODO reduce the expire time and iterate again?
|
||||||
|
if (tok.lastSeen() < expire || cnt >= MAX_OUTBOUND_TOKENS)
|
||||||
iter.remove();
|
iter.remove();
|
||||||
|
else
|
||||||
|
cnt++;
|
||||||
}
|
}
|
||||||
|
expire = now - MAX_INBOUND_TOKEN_AGE;
|
||||||
for (Iterator<Token> iter = _incomingTokens.values().iterator(); iter.hasNext(); ) {
|
for (Iterator<Token> iter = _incomingTokens.values().iterator(); iter.hasNext(); ) {
|
||||||
Token tok = iter.next();
|
Token tok = iter.next();
|
||||||
if (tok.lastSeen() < now - MAX_INBOUND_TOKEN_AGE)
|
if (tok.lastSeen() < expire)
|
||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
|
expire = now - BLACKLIST_CLEAN_TIME;
|
||||||
for (Iterator<NID> iter = _blacklist.iterator(); iter.hasNext(); ) {
|
for (Iterator<NID> iter = _blacklist.iterator(); iter.hasNext(); ) {
|
||||||
NID nid = iter.next();
|
NID nid = iter.next();
|
||||||
// lastSeen() is actually when-added
|
// lastSeen() is actually when-added
|
||||||
if (now > nid.lastSeen() + BLACKLIST_CLEAN_TIME)
|
if (nid.lastSeen() < expire)
|
||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
// TODO sent queries?
|
// TODO sent queries?
|
||||||
|
@ -53,6 +53,8 @@ class UnsignedUpdateRunner extends UpdateRunner {
|
|||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
_log.error("Error updating", t);
|
_log.error("Error updating", t);
|
||||||
}
|
}
|
||||||
|
if (!this.done)
|
||||||
|
_mgr.notifyTaskFailed(this, "", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** eepget listener callback Overrides */
|
/** eepget listener callback Overrides */
|
||||||
|
@ -3,6 +3,8 @@ package net.i2p.router.web;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import net.i2p.util.RandomSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copied and modded from I2PTunnel IndexBean (GPL)
|
* Copied and modded from I2PTunnel IndexBean (GPL)
|
||||||
* @author zzz
|
* @author zzz
|
||||||
@ -11,8 +13,6 @@ public class CSSHelper extends HelperBase {
|
|||||||
|
|
||||||
private static final Map<String, Boolean> _UACache = new ConcurrentHashMap();
|
private static final Map<String, Boolean> _UACache = new ConcurrentHashMap();
|
||||||
|
|
||||||
public CSSHelper() {}
|
|
||||||
|
|
||||||
public static final String PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
public static final String PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||||
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
||||||
public static final String DEFAULT_THEME = "light";
|
public static final String DEFAULT_THEME = "light";
|
||||||
@ -24,6 +24,16 @@ public class CSSHelper extends HelperBase {
|
|||||||
public static final String PROP_DISABLE_REFRESH = "routerconsole.summaryDisableRefresh";
|
public static final String PROP_DISABLE_REFRESH = "routerconsole.summaryDisableRefresh";
|
||||||
private static final String PROP_XFRAME = "routerconsole.disableXFrame";
|
private static final String PROP_XFRAME = "routerconsole.disableXFrame";
|
||||||
|
|
||||||
|
private static final String _consoleNonce = Long.toString(RandomSource.getInstance().nextLong());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* formerly stored in System.getProperty("router.consoleNonce")
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public static String getNonce() {
|
||||||
|
return _consoleNonce;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTheme(String userAgent) {
|
public String getTheme(String userAgent) {
|
||||||
String url = BASE_THEME_PATH;
|
String url = BASE_THEME_PATH;
|
||||||
if (userAgent != null && userAgent.contains("MSIE")) {
|
if (userAgent != null && userAgent.contains("MSIE")) {
|
||||||
|
@ -3,6 +3,7 @@ package net.i2p.router.web;
|
|||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.Router;
|
import net.i2p.router.Router;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.util.RandomSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* simple helper to control restarts/shutdowns in the left hand nav
|
* simple helper to control restarts/shutdowns in the left hand nav
|
||||||
@ -12,20 +13,18 @@ public class ConfigRestartBean {
|
|||||||
/** all these are tagged below so no need to _x them here.
|
/** all these are tagged below so no need to _x them here.
|
||||||
* order is: form value, form class, display text.
|
* order is: form value, form class, display text.
|
||||||
*/
|
*/
|
||||||
static final String[] SET1 = {"shutdownImmediate", "stop", "Shutdown immediately", "cancelShutdown", "cancel", "Cancel shutdown"};
|
private static final String[] SET1 = {"shutdownImmediate", "stop", "Shutdown immediately", "cancelShutdown", "cancel", "Cancel shutdown"};
|
||||||
static final String[] SET2 = {"restartImmediate", "reload", "Restart immediately", "cancelShutdown", "cancel", "Cancel restart"};
|
private static final String[] SET2 = {"restartImmediate", "reload", "Restart immediately", "cancelShutdown", "cancel", "Cancel restart"};
|
||||||
static final String[] SET3 = {"restart", "reload", "Restart", "shutdown", "stop", "Shutdown"};
|
private static final String[] SET3 = {"restart", "reload", "Restart", "shutdown", "stop", "Shutdown"};
|
||||||
static final String[] SET4 = {"shutdown", "stop", "Shutdown"};
|
private static final String[] SET4 = {"shutdown", "stop", "Shutdown"};
|
||||||
|
|
||||||
|
private static final String _systemNonce = Long.toString(RandomSource.getInstance().nextLong());
|
||||||
|
|
||||||
|
/** formerly System.getProperty("console.nonce") */
|
||||||
public static String getNonce() {
|
public static String getNonce() {
|
||||||
RouterContext ctx = ContextHelper.getContext(null);
|
return _systemNonce;
|
||||||
String nonce = System.getProperty("console.nonce");
|
|
||||||
if (nonce == null) {
|
|
||||||
nonce = ""+ctx.random().nextLong();
|
|
||||||
System.setProperty("console.nonce", nonce);
|
|
||||||
}
|
|
||||||
return nonce;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String renderStatus(String urlBase, String action, String nonce) {
|
public static String renderStatus(String urlBase, String action, String nonce) {
|
||||||
RouterContext ctx = ContextHelper.getContext(null);
|
RouterContext ctx = ContextHelper.getContext(null);
|
||||||
String systemNonce = getNonce();
|
String systemNonce = getNonce();
|
||||||
|
@ -194,8 +194,8 @@ public class FormHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sharedNonce = System.getProperty("router.consoleNonce");
|
String sharedNonce = CSSHelper.getNonce();
|
||||||
if ( (sharedNonce != null) && (sharedNonce.equals(_nonce) ) ) {
|
if (sharedNonce.equals(_nonce)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ public class NewsHelper extends ContentHelper {
|
|||||||
ctx));
|
ctx));
|
||||||
}
|
}
|
||||||
buf.append("</i>");
|
buf.append("</i>");
|
||||||
String consoleNonce = System.getProperty("router.consoleNonce");
|
String consoleNonce = CSSHelper.getNonce();
|
||||||
if (lastUpdated > 0 && consoleNonce != null) {
|
if (lastUpdated > 0 && consoleNonce != null) {
|
||||||
if (shouldShowNews(ctx)) {
|
if (shouldShowNews(ctx)) {
|
||||||
buf.append(" <a href=\"/?news=0&consoleNonce=").append(consoleNonce).append("\">")
|
buf.append(" <a href=\"/?news=0&consoleNonce=").append(consoleNonce).append("\">")
|
||||||
|
@ -606,7 +606,7 @@ public class SummaryBarRenderer {
|
|||||||
NewsHelper newshelper = _helper.getNewsHelper();
|
NewsHelper newshelper = _helper.getNewsHelper();
|
||||||
if (newshelper == null || newshelper.shouldShowNews()) return "";
|
if (newshelper == null || newshelper.shouldShowNews()) return "";
|
||||||
StringBuilder buf = new StringBuilder(512);
|
StringBuilder buf = new StringBuilder(512);
|
||||||
String consoleNonce = System.getProperty("router.consoleNonce");
|
String consoleNonce = CSSHelper.getNonce();
|
||||||
if (consoleNonce != null) {
|
if (consoleNonce != null) {
|
||||||
// Set up title and pre-headings stuff.
|
// Set up title and pre-headings stuff.
|
||||||
buf.append("<h3><a href=\"/configupdate\">")
|
buf.append("<h3><a href=\"/configupdate\">")
|
||||||
|
@ -809,7 +809,13 @@ public class SummaryHelper extends HelperBase {
|
|||||||
|
|
||||||
private String _requestURI;
|
private String _requestURI;
|
||||||
public void setRequestURI(String s) { _requestURI = s; }
|
public void setRequestURI(String s) { _requestURI = s; }
|
||||||
public String getRequestURI() { return _requestURI; }
|
|
||||||
|
/**
|
||||||
|
* @return non-null; "/home" if (strangely) not set by jsp
|
||||||
|
*/
|
||||||
|
public String getRequestURI() {
|
||||||
|
return _requestURI != null ? _requestURI : "/home";
|
||||||
|
}
|
||||||
|
|
||||||
public String getConfigTable() {
|
public String getConfigTable() {
|
||||||
String[] allSections = SummaryBarRenderer.ALL_SECTIONS;
|
String[] allSections = SummaryBarRenderer.ALL_SECTIONS;
|
||||||
|
@ -31,15 +31,7 @@ input.default {
|
|||||||
<%@include file="formhandler.jsi" %>
|
<%@include file="formhandler.jsi" %>
|
||||||
<div class="configure"><div class="topshimten"><h3><%=uihelper._("Router Console Theme")%></h3></div>
|
<div class="configure"><div class="topshimten"><h3><%=uihelper._("Router Console Theme")%></h3></div>
|
||||||
<form action="" method="POST">
|
<form action="" method="POST">
|
||||||
<%
|
<input type="hidden" name="consoleNonce" value="<%=intl.getNonce()%>" >
|
||||||
/** lang setting is done in css.jsi, not in ConfigUIHandler */
|
|
||||||
String consoleNonce = System.getProperty("router.consoleNonce");
|
|
||||||
if (consoleNonce == null) {
|
|
||||||
consoleNonce = Long.toString(new java.util.Random().nextLong());
|
|
||||||
System.setProperty("router.consoleNonce", consoleNonce);
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
<input type="hidden" name="consoleNonce" value="<%=consoleNonce%>" >
|
|
||||||
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||||
<input type="hidden" name="action" value="blah" >
|
<input type="hidden" name="action" value="blah" >
|
||||||
<%
|
<%
|
||||||
|
@ -10,11 +10,7 @@
|
|||||||
<%@include file="summaryajax.jsi" %>
|
<%@include file="summaryajax.jsi" %>
|
||||||
</head><body onload="initAjax()">
|
</head><body onload="initAjax()">
|
||||||
<%
|
<%
|
||||||
String consoleNonce = System.getProperty("router.consoleNonce");
|
String consoleNonce = intl.getNonce();
|
||||||
if (consoleNonce == null) {
|
|
||||||
consoleNonce = Long.toString(new java.util.Random().nextLong());
|
|
||||||
System.setProperty("router.consoleNonce", consoleNonce);
|
|
||||||
}
|
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%@include file="summary.jsi" %>
|
<%@include file="summary.jsi" %>
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||||
|
|
||||||
String conNonceParam = request.getParameter("consoleNonce");
|
String conNonceParam = request.getParameter("consoleNonce");
|
||||||
if (conNonceParam != null && conNonceParam.equals(System.getProperty("router.consoleNonce"))) {
|
if (intl.getNonce().equals(conNonceParam)) {
|
||||||
intl.setLang(request.getParameter("lang"));
|
intl.setLang(request.getParameter("lang"));
|
||||||
intl.setNews(request.getParameter("news"));
|
intl.setNews(request.getParameter("news"));
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,7 @@
|
|||||||
<%@include file="summaryajax.jsi" %>
|
<%@include file="summaryajax.jsi" %>
|
||||||
</head><body onload="initAjax()">
|
</head><body onload="initAjax()">
|
||||||
<%
|
<%
|
||||||
String consoleNonce = System.getProperty("router.consoleNonce");
|
String consoleNonce = intl.getNonce();
|
||||||
if (consoleNonce == null) {
|
|
||||||
consoleNonce = Long.toString(new java.util.Random().nextLong());
|
|
||||||
System.setProperty("router.consoleNonce", consoleNonce);
|
|
||||||
}
|
|
||||||
%>
|
%>
|
||||||
<jsp:useBean class="net.i2p.router.web.NewsHelper" id="newshelper" scope="request" />
|
<jsp:useBean class="net.i2p.router.web.NewsHelper" id="newshelper" scope="request" />
|
||||||
<jsp:setProperty name="newshelper" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" />
|
<jsp:setProperty name="newshelper" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" />
|
||||||
|
18
history.txt
18
history.txt
@ -1,6 +1,20 @@
|
|||||||
|
2012-11-05 zzz
|
||||||
|
* Console:
|
||||||
|
- Fix NPE after restart (ticket #763)
|
||||||
|
- Move more nonces out of system properties
|
||||||
|
* i2psnark:
|
||||||
|
- More DHT limits
|
||||||
|
- Announce to backup trackers if DHT is empty
|
||||||
|
- Use PEX and DHT info in torrent peer count
|
||||||
|
- Don't use temp files for announces
|
||||||
|
* PeerManager: Don't reorganize as often if it takes too long (ticket #765)
|
||||||
|
* RequestLeaseSetJob: Only disconnect client after multiple dropped
|
||||||
|
lease set requests; reduce timeout, other cleanups
|
||||||
|
* Unsigned Update: Fix notification on failure
|
||||||
|
|
||||||
2012-11-02 kytv
|
2012-11-02 kytv
|
||||||
* German, Portuguese, and Swedish translation updates from Transifex
|
* German, Portuguese, and Swedish translation updates from Transifex
|
||||||
* Refreshed English po files to push to TX.
|
* Refreshed English po files to push to TX.
|
||||||
|
|
||||||
2012-11-02 zzz
|
2012-11-02 zzz
|
||||||
* configstats: Fix group sorting, translate groups
|
* configstats: Fix group sorting, translate groups
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 4;
|
public final static long BUILD = 5;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -71,6 +71,7 @@ class ClientConnectionRunner {
|
|||||||
private final Map<MessageId, Payload> _messages;
|
private final Map<MessageId, Payload> _messages;
|
||||||
/** lease set request state, or null if there is no request pending on at the moment */
|
/** lease set request state, or null if there is no request pending on at the moment */
|
||||||
private LeaseRequestState _leaseRequest;
|
private LeaseRequestState _leaseRequest;
|
||||||
|
private int _consecutiveLeaseRequestFails;
|
||||||
/** currently allocated leaseSet, or null if none is allocated */
|
/** currently allocated leaseSet, or null if none is allocated */
|
||||||
private LeaseSet _currentLeaseSet;
|
private LeaseSet _currentLeaseSet;
|
||||||
/** set of messageIds created but not yet ACCEPTED */
|
/** set of messageIds created but not yet ACCEPTED */
|
||||||
@ -100,6 +101,7 @@ class ClientConnectionRunner {
|
|||||||
// e.g. on local access
|
// e.g. on local access
|
||||||
private static final int MAX_MESSAGE_ID = 0x4000000;
|
private static final int MAX_MESSAGE_ID = 0x4000000;
|
||||||
|
|
||||||
|
private static final int MAX_LEASE_FAILS = 5;
|
||||||
private static final int BUF_SIZE = 32*1024;
|
private static final int BUF_SIZE = 32*1024;
|
||||||
|
|
||||||
/** @since 0.9.2 */
|
/** @since 0.9.2 */
|
||||||
@ -191,13 +193,18 @@ class ClientConnectionRunner {
|
|||||||
/** data for the current leaseRequest, or null if there is no active leaseSet request */
|
/** data for the current leaseRequest, or null if there is no active leaseSet request */
|
||||||
LeaseRequestState getLeaseRequest() { return _leaseRequest; }
|
LeaseRequestState getLeaseRequest() { return _leaseRequest; }
|
||||||
|
|
||||||
void setLeaseRequest(LeaseRequestState req) {
|
/** @param req non-null */
|
||||||
|
public void failLeaseRequest(LeaseRequestState req) {
|
||||||
|
boolean disconnect = false;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if ( (_leaseRequest != null) && (req != _leaseRequest) )
|
if (_leaseRequest == req) {
|
||||||
_log.error("Changing leaseRequest from " + _leaseRequest + " to " + req);
|
_leaseRequest = null;
|
||||||
_leaseRequest = req;
|
disconnect = ++_consecutiveLeaseRequestFails > MAX_LEASE_FAILS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (disconnect)
|
||||||
|
disconnectClient("Too many leaseset request fails");
|
||||||
|
}
|
||||||
|
|
||||||
/** already closed? */
|
/** already closed? */
|
||||||
boolean isDead() { return _dead; }
|
boolean isDead() { return _dead; }
|
||||||
@ -287,6 +294,7 @@ class ClientConnectionRunner {
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("LeaseSet created fully: " + state + " / " + ls);
|
_log.debug("LeaseSet created fully: " + state + " / " + ls);
|
||||||
_leaseRequest = null;
|
_leaseRequest = null;
|
||||||
|
_consecutiveLeaseRequestFails = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( (state != null) && (state.getOnGranted() != null) )
|
if ( (state != null) && (state.getOnGranted() != null) )
|
||||||
@ -351,9 +359,8 @@ class ClientConnectionRunner {
|
|||||||
_acceptedPending.add(id);
|
_acceptedPending.add(id);
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("** Receiving message [" + id.getMessageId() + "] with payload of size ["
|
_log.debug("** Receiving message " + id.getMessageId() + " with payload of size "
|
||||||
+ payload.getSize() + "]" + " for session [" + _sessionId.getSessionId()
|
+ payload.getSize() + " for session " + _sessionId.getSessionId());
|
||||||
+ "]");
|
|
||||||
//long beforeDistribute = _context.clock().now();
|
//long beforeDistribute = _context.clock().now();
|
||||||
// the following blocks as described above
|
// the following blocks as described above
|
||||||
SessionConfig cfg = _config;
|
SessionConfig cfg = _config;
|
||||||
@ -380,7 +387,7 @@ class ClientConnectionRunner {
|
|||||||
if (sid == null) return;
|
if (sid == null) return;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Acking message send [accepted]" + id + " / " + nonce + " for sessionId "
|
_log.debug("Acking message send [accepted]" + id + " / " + nonce + " for sessionId "
|
||||||
+ sid, new Exception("sendAccepted"));
|
+ sid);
|
||||||
MessageStatusMessage status = new MessageStatusMessage();
|
MessageStatusMessage status = new MessageStatusMessage();
|
||||||
status.setMessageId(id.getMessageId());
|
status.setMessageId(id.getMessageId());
|
||||||
status.setSessionId(sid.getSessionId());
|
status.setSessionId(sid.getSessionId());
|
||||||
@ -486,10 +493,11 @@ class ClientConnectionRunner {
|
|||||||
return; // already requesting
|
return; // already requesting
|
||||||
} else {
|
} else {
|
||||||
_leaseRequest = state = new LeaseRequestState(onCreateJob, onFailedJob, _context.clock().now() + expirationTime, set);
|
_leaseRequest = state = new LeaseRequestState(onCreateJob, onFailedJob, _context.clock().now() + expirationTime, set);
|
||||||
_log.debug("Not already requesting, continue to request " + set);
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("New request: " + state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_context.jobQueue().addJob(new RequestLeaseSetJob(_context, this, set, _context.clock().now() + expirationTime, onCreateJob, onFailedJob, state));
|
_context.jobQueue().addJob(new RequestLeaseSetJob(_context, this, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Rerequest implements SimpleTimer.TimedEvent {
|
private class Rerequest implements SimpleTimer.TimedEvent {
|
||||||
@ -646,8 +654,8 @@ class ClientConnectionRunner {
|
|||||||
if (!alreadyAccepted(_messageId)) {
|
if (!alreadyAccepted(_messageId)) {
|
||||||
_log.warn("Almost send an update for message " + _messageId + " to "
|
_log.warn("Almost send an update for message " + _messageId + " to "
|
||||||
+ MessageStatusMessage.getStatusString(msg.getStatus())
|
+ MessageStatusMessage.getStatusString(msg.getStatus())
|
||||||
+ " for session [" + _sessionId.getSessionId()
|
+ " for session " + _sessionId.getSessionId()
|
||||||
+ "] before they knew the messageId! delaying .5s");
|
+ " before they knew the messageId! delaying .5s");
|
||||||
_lastTried = _context.clock().now();
|
_lastTried = _context.clock().now();
|
||||||
requeue(REQUEUE_DELAY);
|
requeue(REQUEUE_DELAY);
|
||||||
return;
|
return;
|
||||||
@ -680,15 +688,14 @@ class ClientConnectionRunner {
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.info("Updating message status for message " + _messageId + " to "
|
_log.info("Updating message status for message " + _messageId + " to "
|
||||||
+ MessageStatusMessage.getStatusString(msg.getStatus())
|
+ MessageStatusMessage.getStatusString(msg.getStatus())
|
||||||
+ " for session [" + _sessionId.getSessionId()
|
+ " for session " + _sessionId.getSessionId()
|
||||||
+ "] (with nonce=2), retrying after ["
|
+ " (with nonce=2), retrying after "
|
||||||
+ (_context.clock().now() - _lastTried)
|
+ (_context.clock().now() - _lastTried));
|
||||||
+ "]");
|
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Updating message status for message " + _messageId + " to "
|
_log.debug("Updating message status for message " + _messageId + " to "
|
||||||
+ MessageStatusMessage.getStatusString(msg.getStatus())
|
+ MessageStatusMessage.getStatusString(msg.getStatus())
|
||||||
+ " for session [" + _sessionId.getSessionId() + "] (with nonce=2)");
|
+ " for session " + _sessionId.getSessionId() + " (with nonce=2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -62,6 +62,8 @@ class ClientManager {
|
|||||||
|
|
||||||
private static final int INTERNAL_QUEUE_SIZE = 256;
|
private static final int INTERNAL_QUEUE_SIZE = 256;
|
||||||
|
|
||||||
|
private static final long REQUEST_LEASESET_TIMEOUT = 60*1000;
|
||||||
|
|
||||||
public ClientManager(RouterContext context, int port) {
|
public ClientManager(RouterContext context, int port) {
|
||||||
_ctx = context;
|
_ctx = context;
|
||||||
_log = context.logManager().getLog(ClientManager.class);
|
_log = context.logManager().getLog(ClientManager.class);
|
||||||
@ -275,6 +277,8 @@ class ClientManager {
|
|||||||
* within the timeout specified, queue up the onFailedJob. This call does not
|
* within the timeout specified, queue up the onFailedJob. This call does not
|
||||||
* block.
|
* block.
|
||||||
*
|
*
|
||||||
|
* UNUSED, the call below without jobs is always used.
|
||||||
|
*
|
||||||
* @param dest Destination from which the LeaseSet's authorization should be requested
|
* @param dest Destination from which the LeaseSet's authorization should be requested
|
||||||
* @param set LeaseSet with requested leases - this object must be updated to contain the
|
* @param set LeaseSet with requested leases - this object must be updated to contain the
|
||||||
* signed version (as well as any changed/added/removed Leases)
|
* signed version (as well as any changed/added/removed Leases)
|
||||||
@ -290,12 +294,10 @@ class ClientManager {
|
|||||||
+ dest.calculateHash().toBase64() + ". disconnected?");
|
+ dest.calculateHash().toBase64() + ". disconnected?");
|
||||||
_ctx.jobQueue().addJob(onFailedJob);
|
_ctx.jobQueue().addJob(onFailedJob);
|
||||||
} else {
|
} else {
|
||||||
runner.requestLeaseSet(set, _ctx.clock().now() + timeout, onCreateJob, onFailedJob);
|
runner.requestLeaseSet(set, timeout, onCreateJob, onFailedJob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int REQUEST_LEASESET_TIMEOUT = 120*1000;
|
|
||||||
|
|
||||||
public void requestLeaseSet(Hash dest, LeaseSet ls) {
|
public void requestLeaseSet(Hash dest, LeaseSet ls) {
|
||||||
ClientConnectionRunner runner = getRunner(dest);
|
ClientConnectionRunner runner = getRunner(dest);
|
||||||
if (runner != null) {
|
if (runner != null) {
|
||||||
|
@ -110,6 +110,8 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade implements Inte
|
|||||||
* within the timeout specified, queue up the onFailedJob. This call does not
|
* within the timeout specified, queue up the onFailedJob. This call does not
|
||||||
* block.
|
* block.
|
||||||
*
|
*
|
||||||
|
* UNUSED, the call below without jobs is always used.
|
||||||
|
*
|
||||||
* @param dest Destination from which the LeaseSet's authorization should be requested
|
* @param dest Destination from which the LeaseSet's authorization should be requested
|
||||||
* @param set LeaseSet with requested leases - this object must be updated to contain the
|
* @param set LeaseSet with requested leases - this object must be updated to contain the
|
||||||
* signed version (as well as any changed/added/removed Leases)
|
* signed version (as well as any changed/added/removed Leases)
|
||||||
|
@ -28,6 +28,9 @@ class LeaseRequestState {
|
|||||||
private final long _expiration;
|
private final long _expiration;
|
||||||
private boolean _successful;
|
private boolean _successful;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param expiration absolute time
|
||||||
|
*/
|
||||||
public LeaseRequestState(Job onGranted, Job onFailed, long expiration, LeaseSet requested) {
|
public LeaseRequestState(Job onGranted, Job onFailed, long expiration, LeaseSet requested) {
|
||||||
_onGranted = onGranted;
|
_onGranted = onGranted;
|
||||||
_onFailed = onFailed;
|
_onFailed = onFailed;
|
||||||
|
@ -31,7 +31,7 @@ class RequestLeaseSetJob extends JobImpl {
|
|||||||
private final ClientConnectionRunner _runner;
|
private final ClientConnectionRunner _runner;
|
||||||
private final LeaseRequestState _requestState;
|
private final LeaseRequestState _requestState;
|
||||||
|
|
||||||
public RequestLeaseSetJob(RouterContext ctx, ClientConnectionRunner runner, LeaseSet set, long expiration, Job onCreate, Job onFail, LeaseRequestState state) {
|
public RequestLeaseSetJob(RouterContext ctx, ClientConnectionRunner runner, LeaseRequestState state) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
_log = ctx.logManager().getLog(RequestLeaseSetJob.class);
|
_log = ctx.logManager().getLog(RequestLeaseSetJob.class);
|
||||||
_runner = runner;
|
_runner = runner;
|
||||||
@ -42,6 +42,7 @@ class RequestLeaseSetJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return "Request Lease Set"; }
|
public String getName() { return "Request Lease Set"; }
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
if (_runner.isDead()) return;
|
if (_runner.isDead()) return;
|
||||||
|
|
||||||
@ -59,21 +60,23 @@ class RequestLeaseSetJob extends JobImpl {
|
|||||||
msg.setSessionId(_runner.getSessionId());
|
msg.setSessionId(_runner.getSessionId());
|
||||||
|
|
||||||
for (int i = 0; i < _requestState.getRequested().getLeaseCount(); i++) {
|
for (int i = 0; i < _requestState.getRequested().getLeaseCount(); i++) {
|
||||||
msg.addEndpoint(_requestState.getRequested().getLease(i).getGateway(), _requestState.getRequested().getLease(i).getTunnelId());
|
msg.addEndpoint(_requestState.getRequested().getLease(i).getGateway(),
|
||||||
|
_requestState.getRequested().getLease(i).getTunnelId());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//_runner.setLeaseRequest(state);
|
//_runner.setLeaseRequest(state);
|
||||||
_runner.doSend(msg);
|
_runner.doSend(msg);
|
||||||
getContext().jobQueue().addJob(new CheckLeaseRequestStatus(getContext(), _requestState));
|
getContext().jobQueue().addJob(new CheckLeaseRequestStatus());
|
||||||
return;
|
|
||||||
} catch (I2CPMessageException ime) {
|
} catch (I2CPMessageException ime) {
|
||||||
getContext().statManager().addRateData("client.requestLeaseSetDropped", 1, 0);
|
getContext().statManager().addRateData("client.requestLeaseSetDropped", 1, 0);
|
||||||
_log.error("Error sending I2CP message requesting the lease set", ime);
|
_log.error("Error sending I2CP message requesting the lease set", ime);
|
||||||
_requestState.setIsSuccessful(false);
|
_requestState.setIsSuccessful(false);
|
||||||
_runner.setLeaseRequest(null);
|
if (_requestState.getOnFailed() != null)
|
||||||
_runner.disconnectClient("I2CP error requesting leaseSet");
|
RequestLeaseSetJob.this.getContext().jobQueue().addJob(_requestState.getOnFailed());
|
||||||
return;
|
_runner.failLeaseRequest(_requestState);
|
||||||
|
// Don't disconnect, the tunnel will retry
|
||||||
|
//_runner.disconnectClient("I2CP error requesting leaseSet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,14 +87,12 @@ class RequestLeaseSetJob extends JobImpl {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private class CheckLeaseRequestStatus extends JobImpl {
|
private class CheckLeaseRequestStatus extends JobImpl {
|
||||||
private final LeaseRequestState _req;
|
|
||||||
private final long _start;
|
private final long _start;
|
||||||
|
|
||||||
public CheckLeaseRequestStatus(RouterContext enclosingContext, LeaseRequestState state) {
|
public CheckLeaseRequestStatus() {
|
||||||
super(enclosingContext);
|
super(RequestLeaseSetJob.this.getContext());
|
||||||
_req = state;
|
|
||||||
_start = System.currentTimeMillis();
|
_start = System.currentTimeMillis();
|
||||||
getTiming().setStartAfter(state.getExpiration());
|
getTiming().setStartAfter(_requestState.getExpiration());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
@ -100,20 +101,22 @@ class RequestLeaseSetJob extends JobImpl {
|
|||||||
_log.debug("Already dead, dont try to expire the leaseSet lookup");
|
_log.debug("Already dead, dont try to expire the leaseSet lookup");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_req.getIsSuccessful()) {
|
if (_requestState.getIsSuccessful()) {
|
||||||
// we didn't fail
|
// we didn't fail
|
||||||
RequestLeaseSetJob.CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetSuccess", 1, 0);
|
CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetSuccess", 1);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
RequestLeaseSetJob.CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetTimeout", 1, 0);
|
CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetTimeout", 1);
|
||||||
if (_log.shouldLog(Log.ERROR)) {
|
if (_log.shouldLog(Log.ERROR)) {
|
||||||
long waited = System.currentTimeMillis() - _start;
|
long waited = System.currentTimeMillis() - _start;
|
||||||
_log.error("Failed to receive a leaseSet in the time allotted (" + waited + "): " + _req + " for "
|
_log.error("Failed to receive a leaseSet in the time allotted (" + waited + "): " + _requestState + " for "
|
||||||
+ _runner.getConfig().getDestination().calculateHash().toBase64());
|
+ _runner.getConfig().getDestination().calculateHash().toBase64());
|
||||||
}
|
}
|
||||||
_runner.disconnectClient("Took too long to request leaseSet");
|
if (_requestState.getOnFailed() != null)
|
||||||
if (_req.getOnFailed() != null)
|
RequestLeaseSetJob.this.getContext().jobQueue().addJob(_requestState.getOnFailed());
|
||||||
RequestLeaseSetJob.this.getContext().jobQueue().addJob(_req.getOnFailed());
|
_runner.failLeaseRequest(_requestState);
|
||||||
|
// Don't disconnect, the tunnel will retry
|
||||||
|
//_runner.disconnectClient("Took too long to request leaseSet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public String getName() { return "Check LeaseRequest Status"; }
|
public String getName() { return "Check LeaseRequest Status"; }
|
||||||
|
@ -89,14 +89,16 @@ class PeerManager {
|
|||||||
super(_context.simpleTimer2(), REORGANIZE_TIME);
|
super(_context.simpleTimer2(), REORGANIZE_TIME);
|
||||||
}
|
}
|
||||||
public void timeReached() {
|
public void timeReached() {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
_organizer.reorganize(true);
|
_organizer.reorganize(true);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
_log.log(Log.CRIT, "Error evaluating profiles", t);
|
_log.log(Log.CRIT, "Error evaluating profiles", t);
|
||||||
}
|
}
|
||||||
|
long orgtime = System.currentTimeMillis() - start;
|
||||||
long uptime = _context.router().getUptime();
|
long uptime = _context.router().getUptime();
|
||||||
long delay;
|
long delay;
|
||||||
if (uptime > 2*60*60*1000)
|
if (orgtime > 1000 || uptime > 2*60*60*1000)
|
||||||
delay = REORGANIZE_TIME_LONG;
|
delay = REORGANIZE_TIME_LONG;
|
||||||
else if (uptime > 10*60*1000)
|
else if (uptime > 10*60*1000)
|
||||||
delay = REORGANIZE_TIME_MEDIUM;
|
delay = REORGANIZE_TIME_MEDIUM;
|
||||||
|
Reference in New Issue
Block a user