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:
zzz
2018-02-18 13:53:50 +00:00
parent acebd2ea68
commit 826d8ca07f
10 changed files with 71 additions and 10 deletions

View File

@@ -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()

View File

@@ -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.

View File

@@ -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.
*/

View File

@@ -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();
}