* I2CP: Implement optional reduce tunnels on idle - not hooked

in to i2ptunnel GUI yet - still needs tweaks
This commit is contained in:
zzz
2009-02-02 14:03:17 +00:00
parent 1ee2b5e899
commit 8d7340500f
9 changed files with 228 additions and 23 deletions

View File

@@ -10,6 +10,7 @@ package net.i2p.client;
*/
import java.util.Date;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
@@ -27,6 +28,7 @@ import net.i2p.data.i2cp.CreateLeaseSetMessage;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.DestroySessionMessage;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.ReconfigureSessionMessage;
import net.i2p.data.i2cp.ReportAbuseMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SendMessageExpiresMessage;
@@ -188,4 +190,33 @@ class I2CPMessageProducer {
msg.setSessionId(session.getSessionId());
session.sendMessage(msg);
}
/**
* Update number of tunnels
*
* @param tunnels 0 for original configured number
*/
public void updateTunnels(I2PSessionImpl session, int tunnels) throws I2PSessionException {
ReconfigureSessionMessage msg = new ReconfigureSessionMessage();
SessionConfig cfg = new SessionConfig(session.getMyDestination());
Properties props = session.getOptions();
if (tunnels > 0) {
Properties newprops = new Properties();
newprops.putAll(props);
props = newprops;
props.setProperty("inbound.quantity", "" + tunnels);
props.setProperty("outbound.quantity", "" + tunnels);
props.setProperty("inbound.backupQuantity", "0");
props.setProperty("outbound.backupQuantity", "0");
}
cfg.setOptions(props);
try {
cfg.signSessionConfig(session.getPrivateKey());
} catch (DataFormatException dfe) {
throw new I2PSessionException("Unable to sign the session config", dfe);
}
msg.setSessionConfig(cfg);
msg.setSessionId(session.getSessionId());
session.sendMessage(msg);
}
}

View File

@@ -110,6 +110,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
*/
protected AvailabilityNotifier _availabilityNotifier;
private long _lastActivity;
private boolean _isReduced;
void dateUpdated() {
_dateReceived = true;
synchronized (_dateReceivedLock) {
@@ -290,6 +293,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_log.info(getPrefix() + "Lease set created with inbound tunnels after "
+ (connected - startConnect)
+ "ms - ready to participate in the network!");
startIdleMonitor();
} catch (UnknownHostException uhe) {
_closed = true;
throw new I2PSessionException(getPrefix() + "Invalid session configuration", uhe);
@@ -316,6 +320,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_log.error("Receive message " + msgId + " had no matches, remaining=" + remaining);
return null;
}
updateActivity();
return msg.getPayload().getUnencryptedData();
}
@@ -668,4 +673,34 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public Destination lookupDest(Hash h) throws I2PSessionException {
return null;
}
protected void updateActivity() {
_lastActivity = _context.clock().now();
if (_isReduced) {
_isReduced = false;
try {
_producer.updateTunnels(this, 0);
} catch (I2PSessionException ise) {
_log.error(getPrefix() + "bork restore from reduced");
}
}
}
public long lastActivity() {
return _lastActivity;
}
public void setReduced() {
_isReduced = true;
}
private void startIdleMonitor() {
_isReduced = false;
boolean reduce = Boolean.valueOf(_options.getProperty("i2cp.reduceOnIdle")).booleanValue();
boolean close = Boolean.valueOf(_options.getProperty("i2cp.closeOnIdle")).booleanValue();
if (reduce || close) {
updateActivity();
SimpleScheduler.getInstance().addEvent(new SessionIdleTimer(_context, this, reduce, close), SessionIdleTimer.MINIMUM_TIME);
}
}
}

View File

@@ -122,6 +122,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
throws I2PSessionException {
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
if (isClosed()) throw new I2PSessionException("Already closed");
updateActivity();
// Sadly there is no way to send something completely uncompressed in a backward-compatible way,
// so we have to still send it in a gzip format, which adds 23 bytes (2.4% for a 960-byte msg)

View File

@@ -0,0 +1,106 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
*
*/
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
/**
* Reduce tunnels or shutdown the session on idle if so configured
*
* @author zzz
*/
public class SessionIdleTimer implements SimpleTimer.TimedEvent {
public static final long MINIMUM_TIME = 5*60*1000;
private static final long DEFAULT_REDUCE_TIME = 20*60*1000;
private static final long DEFAULT_CLOSE_TIME = 30*60*1000;
private final static Log _log = new Log(SessionIdleTimer.class);
private I2PAppContext _context;
private I2PSessionImpl _session;
private boolean _reduceEnabled;
private int _reduceQuantity;
private long _reduceTime;
private boolean _shutdownEnabled;
private long _shutdownTime;
private long _minimumTime;
/**
* reduce, shutdown, or both must be true
*/
public SessionIdleTimer(I2PAppContext context, I2PSessionImpl session, boolean reduce, boolean shutdown) {
_context = context;
_session = session;
_reduceEnabled = reduce;
_shutdownEnabled = shutdown;
if (! (reduce || shutdown))
throw new IllegalArgumentException("At least one must be enabled");
Properties props = session.getOptions();
_minimumTime = Long.MAX_VALUE;
if (reduce) {
_reduceQuantity = 1;
String p = props.getProperty("i2cp.reduceQuantity");
if (p != null) {
try {
_reduceQuantity = Math.max(Integer.parseInt(p), 1);
// also check vs. configured quantities?
} catch (NumberFormatException nfe) {}
}
_reduceTime = DEFAULT_REDUCE_TIME;
p = props.getProperty("i2cp.reduceTime");
if (p != null) {
try {
_reduceTime = Math.max(Long.parseLong(p), MINIMUM_TIME);
} catch (NumberFormatException nfe) {}
}
_minimumTime = _reduceTime;
}
if (shutdown) {
_shutdownTime = DEFAULT_CLOSE_TIME;
String p = props.getProperty("i2cp.closeTime");
if (p != null) {
try {
_shutdownTime = Math.max(Long.parseLong(p), MINIMUM_TIME);
} catch (NumberFormatException nfe) {}
}
_minimumTime = Math.min(_minimumTime, _shutdownTime);
if (reduce && _shutdownTime <= _reduceTime)
reduce = false;
}
}
public void timeReached() {
if (_session.isClosed())
return;
long now = _context.clock().now();
long lastActivity = _session.lastActivity();
if (_log.shouldLog(Log.INFO))
_log.info("Fire idle timer, last activity: " + DataHelper.formatDuration(now - lastActivity) + " ago ");
if (_shutdownEnabled && now - lastActivity >= _shutdownTime) {
if (_log.shouldLog(Log.WARN))
_log.warn("Closing on idle " + _session);
_session.destroySession();
} else if (_reduceEnabled && now - lastActivity >= _reduceTime) {
if (_log.shouldLog(Log.WARN))
_log.warn("Reducing quantity on idle " + _session);
try {
_session.getProducer().updateTunnels(_session, _reduceQuantity);
} catch (I2PSessionException ise) {
_log.error("bork idle reduction " + ise);
}
_session.setReduced();
if (_shutdownEnabled)
SimpleScheduler.getInstance().addEvent(this, _shutdownTime - (now - lastActivity));
// else sessionimpl must reschedule??
} else {
SimpleScheduler.getInstance().addEvent(this, _minimumTime - (now - lastActivity));
}
}
}

View File

@@ -69,6 +69,8 @@ public class I2CPMessageHandler {
return new ReceiveMessageBeginMessage();
case ReceiveMessageEndMessage.MESSAGE_TYPE:
return new ReceiveMessageEndMessage();
case ReconfigureSessionMessage.MESSAGE_TYPE:
return new ReconfigureSessionMessage();
case ReportAbuseMessage.MESSAGE_TYPE:
return new ReportAbuseMessage();
case RequestLeaseSetMessage.MESSAGE_TYPE: