forked from I2P_Developers/i2p.i2p
i2ptunnel: Retry accept after router soft restart (ticket #2003)
This sends the router restart indication from I2CP router side to client side to streaming to I2PTunnelServer via a new streaming exception.
This commit is contained in:
@@ -6,6 +6,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.RouterRestartException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
@@ -27,6 +28,7 @@ class ConnectionHandler {
|
||||
private final SimpleTimer2 _timer;
|
||||
private volatile boolean _active;
|
||||
private int _acceptTimeout;
|
||||
private boolean _restartPending;
|
||||
|
||||
/** max time after receiveNewSyn() and before the matched accept() */
|
||||
private static final int DEFAULT_ACCEPT_TIMEOUT = 3*1000;
|
||||
@@ -48,14 +50,27 @@ class ConnectionHandler {
|
||||
_acceptTimeout = DEFAULT_ACCEPT_TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* The router told us it's going to restart.
|
||||
* Call instead of setActive(false).
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public synchronized void setRestartPending() {
|
||||
_restartPending = true;
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
public synchronized void setActive(boolean active) {
|
||||
// FIXME active=false this only kills for one thread in accept()
|
||||
// if there are more, they won't get a poison packet.
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("setActive(" + active + ") called, previously " + _active, new Exception("I did it"));
|
||||
// if starting, clear any old poison
|
||||
if (active && !_active)
|
||||
if (active && !_active) {
|
||||
_restartPending = false;
|
||||
_synQueue.clear();
|
||||
}
|
||||
boolean wasActive = _active;
|
||||
_active = active;
|
||||
if (wasActive && !active) {
|
||||
@@ -116,12 +131,13 @@ class ConnectionHandler {
|
||||
* than 1ms, wait indefinitely)
|
||||
* @return connection received. Prior to 0.9.17, or null if there was a timeout or the
|
||||
* handler was shut down. As of 0.9.17, never null.
|
||||
* @throws RouterRestartException (extends I2PException) if the router is apparently restarting, since 0.9.34
|
||||
* @throws ConnectException since 0.9.17, returned null before;
|
||||
* if the I2PServerSocket is closed, or if interrupted.
|
||||
* @throws SocketTimeoutException since 0.9.17, returned null before;
|
||||
* if a timeout was previously set with setSoTimeout and the timeout has been reached.
|
||||
*/
|
||||
public Connection accept(long timeoutMs) throws ConnectException, SocketTimeoutException {
|
||||
public Connection accept(long timeoutMs) throws RouterRestartException, ConnectException, SocketTimeoutException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Accept("+ timeoutMs+") called");
|
||||
|
||||
@@ -137,6 +153,8 @@ class ConnectionHandler {
|
||||
break;
|
||||
sendReset(packet);
|
||||
}
|
||||
if (_restartPending)
|
||||
throw new RouterRestartException();
|
||||
throw new ConnectException("ServerSocket closed");
|
||||
}
|
||||
|
||||
@@ -174,8 +192,11 @@ class ConnectionHandler {
|
||||
}
|
||||
|
||||
if (syn != null) {
|
||||
if (syn.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST)
|
||||
if (syn.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST) {
|
||||
if (_restartPending)
|
||||
throw new RouterRestartException();
|
||||
throw new ConnectException("ServerSocket closed");
|
||||
}
|
||||
|
||||
// deal with forged / invalid syn packets in _manager.receiveConnection()
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class I2PServerSocketFull implements I2PServerSocket {
|
||||
*
|
||||
* @throws I2PException if there is a problem with reading a new socket
|
||||
* from the data available (e.g. the I2PSession is closed)
|
||||
* @throws RouterRestartException (extends I2PException) if the router is apparently restarting, since 0.9.34
|
||||
* @throws ConnectException if the I2PServerSocket is closed, or if interrupted.
|
||||
* Not actually thrown through 0.9.16; thrown as of 0.9.17
|
||||
* @throws SocketTimeoutException if a timeout was previously set with setSoTimeout and the timeout has been reached.
|
||||
|
||||
@@ -350,6 +350,7 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
*
|
||||
* @return connected I2PSocket, or null through 0.9.16, non-null as of 0.9.17
|
||||
* @throws I2PException if session is closed
|
||||
* @throws RouterRestartException (extends I2PException) if the router is apparently restarting, since 0.9.34
|
||||
* @throws ConnectException (since 0.9.17; I2PServerSocket interface always declared it)
|
||||
* @throws SocketTimeoutException if a timeout was previously set with setSoTimeout and the timeout has been reached.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ class MessageHandler implements I2PSessionMuxedListener {
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final Set<I2PSocketManager.DisconnectListener> _listeners;
|
||||
private boolean _restartPending;
|
||||
|
||||
public MessageHandler(I2PAppContext ctx, ConnectionManager mgr) {
|
||||
_manager = mgr;
|
||||
@@ -104,7 +105,12 @@ class MessageHandler implements I2PSessionMuxedListener {
|
||||
_log.warn("I2PSession disconnected");
|
||||
_manager.disconnectAllHard();
|
||||
// kill anybody waiting in accept()
|
||||
_manager.getConnectionHandler().setActive(false);
|
||||
if (_restartPending) {
|
||||
_manager.getConnectionHandler().setRestartPending();
|
||||
_restartPending = false;
|
||||
} else {
|
||||
_manager.getConnectionHandler().setActive(false);
|
||||
}
|
||||
|
||||
for (I2PSocketManager.DisconnectListener lsnr : _listeners) {
|
||||
lsnr.sessionDisconnected();
|
||||
@@ -120,8 +126,9 @@ class MessageHandler implements I2PSessionMuxedListener {
|
||||
* @param error the actual error
|
||||
*/
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
_restartPending = message.contains("restart");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("error occurred: " + message + "- " + error.getMessage(), error);
|
||||
_log.warn("error occurred: " + message, error);
|
||||
//_manager.disconnectAllHard();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user