* I2CP/I2PTunnel locking fixes (partial fixes for tickets 650. 815, 946, 947, 953):

- I2PSocketManagerFactory: New createDisconnectedManager(), javadocs
   - I2PSessionImpl: Rewrite state management and locking, prevent multiple
     connect() calls, but allow disconnect() to interrupt connect()
   - I2PSimpleSession: Changes to match I2PSessionImpl
   - I2PTunnelServer: Don't connect in constructor, use createDisconnectedManager()
     for a final manager, finals and cleanups
   Lightly tested.
   Todo: I2PTunnelClientBase
This commit is contained in:
zzz
2013-07-10 18:54:25 +00:00
parent 9a1e1a92ca
commit f3c4a26483
4 changed files with 312 additions and 200 deletions

View File

@@ -26,7 +26,6 @@ import net.i2p.util.Log;
public class I2PSocketManagerFactory {
public static final String PROP_MANAGER = "i2p.streaming.manager";
//public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
/**
@@ -47,7 +46,7 @@ public class I2PSocketManagerFactory {
*
* Blocks for a long time while the router builds tunnels.
*
* @param opts I2CP options
* @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(Properties opts) {
@@ -60,8 +59,8 @@ public class I2PSocketManagerFactory {
*
* Blocks for a long time while the router builds tunnels.
*
* @param host I2CP host
* @param port I2CP port
* @param host I2CP host null to use default
* @param port I2CP port <= 0 to use default
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(String host, int port) {
@@ -74,9 +73,9 @@ public class I2PSocketManagerFactory {
*
* Blocks for a long time while the router builds tunnels.
*
* @param i2cpHost I2CP host
* @param i2cpPort I2CP port
* @param opts I2CP options
* @param i2cpHost I2CP host null to use default
* @param i2cpPort I2CP port <= 0 to use default
* @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
@@ -102,6 +101,7 @@ public class I2PSocketManagerFactory {
* Blocks for a long time while the router builds tunnels.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* or null for a transient destination. Caller must close.
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream) {
@@ -115,7 +115,8 @@ public class I2PSocketManagerFactory {
* Blocks for a long time while the router builds tunnels.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param opts I2CP options
* or null for a transient destination. Caller must close.
* @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, Properties opts) {
@@ -130,13 +131,73 @@ public class I2PSocketManagerFactory {
* Blocks for a long time while the router builds tunnels.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param i2cpHost I2CP host
* @param i2cpPort I2CP port
* @param opts I2CP options
* or null for a transient destination. Caller must close.
* @param i2cpHost I2CP host null to use default
* @param i2cpPort I2CP port <= 0 to use default
* @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
Properties opts) {
try {
return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, true);
} catch (I2PSessionException ise) {
getLog().error("Error creating session for socket manager", ise);
return null;
}
}
/**
* Create a disconnected socket manager using the destination loaded from the given private key
* stream, or null for a transient destination.
*
* Non-blocking. Does not connect to the router or build tunnels.
* For servers, caller MUST call getSession().connect() to build tunnels and start listening.
* For clients, caller may do that to build tunnels in advance;
* otherwise, the first call to connect() will initiate a connection to the router,
* with significant delay for tunnel building.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* or null for a transient destination. Caller must close.
* @param i2cpHost I2CP host null to use default
* @param i2cpPort I2CP port <= 0 to use default
* @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, non-null (throws on error)
* @since 0.9.8
*/
public static I2PSocketManager createDisconnectedManager(InputStream myPrivateKeyStream, String i2cpHost,
int i2cpPort, Properties opts) throws I2PSessionException {
if (myPrivateKeyStream == null) {
I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
try {
client.createDestination(keyStream);
} catch (Exception e) {
throw new I2PSessionException("Error creating keys", e);
}
myPrivateKeyStream = new ByteArrayInputStream(keyStream.toByteArray());
}
return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, false);
}
/**
* Create a socket manager using the destination loaded from the given private key
* stream and connected to the I2CP router on the specified machine on the given
* port.
*
* Blocks for a long time while the router builds tunnels if connect is true.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* non-null. Caller must close.
* @param i2cpHost I2CP host null to use default
* @param i2cpPort I2CP port <= 0 to use default
* @param opts Streaming and I2CP options, may be null
* @param connect true to connect (blocking)
* @return the newly created socket manager, non-null (throws on error)
* @since 0.9.7
*/
private static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
Properties opts, boolean connect) throws I2PSessionException {
I2PClient client = I2PClientFactory.createClient();
if (opts == null)
opts = new Properties();
@@ -146,34 +207,20 @@ public class I2PSocketManagerFactory {
if (!opts.containsKey(name))
opts.setProperty(name, (String) e.getValue());
}
//boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
//if (oldLib && false) {
// for the old streaming lib
// opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
//opts.setProperty("tunnels.depthInbound", "0");
//} else {
// for new streaming lib:
//opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
// as of 0.8.1 (I2CP default is BestEffort)
if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
//p.setProperty("tunnels.depthInbound", "0");
//}
// as of 0.8.1 (I2CP default is BestEffort)
if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
if (i2cpHost != null)
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
if (i2cpPort > 0)
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
try {
I2PSession session = client.createSession(myPrivateKeyStream, opts);
I2PSession session = client.createSession(myPrivateKeyStream, opts);
if (connect)
session.connect();
I2PSocketManager sockMgr = createManager(session, opts, "manager");
return sockMgr;
} catch (I2PSessionException ise) {
getLog().error("Error creating session for socket manager", ise);
return null;
}
I2PSocketManager sockMgr = createManager(session, opts, "manager");
return sockMgr;
}
private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) {