diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
index 6fc491e394acb65c4bbb7af19f07fe007e28b03e..34dc025d56f802e0904c0b0605efb485ba019404 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
@@ -887,13 +887,13 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
             if (portNum <= 0)
                 throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
 
-            I2PTunnelTask task;
             ownDest = !isShared;
             try {
                 String privateKeyFile = null;
                 if (args.length >= 4)
                     privateKeyFile = args[3];
-                task = new I2PTunnelClient(portNum, args[1], l, ownDest, this, this, privateKeyFile);
+                I2PTunnelClientBase task = new I2PTunnelClient(portNum, args[1], l, ownDest, this, this, privateKeyFile);
+                task.startRunning();
                 addtask(task);
                 notifyEvent("clientTaskId", Integer.valueOf(task.getId()));
             } catch (IllegalArgumentException iae) {
@@ -966,10 +966,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
                 }
             }
 
-            I2PTunnelTask task;
             ownDest = !isShared;
             try {
-                task = new I2PTunnelHTTPClient(clientPort, l, ownDest, proxy, this, this);
+                I2PTunnelClientBase task = new I2PTunnelHTTPClient(clientPort, l, ownDest, proxy, this, this);
+                task.startRunning();
                 addtask(task);
                 notifyEvent("httpclientTaskId", Integer.valueOf(task.getId()));
             } catch (IllegalArgumentException iae) {
@@ -1035,10 +1035,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
                 }
             }
 
-            I2PTunnelTask task;
             ownDest = !isShared;
             try {
-                task = new I2PTunnelConnectClient(_port, l, ownDest, proxy, this, this);
+                I2PTunnelClientBase task = new I2PTunnelConnectClient(_port, l, ownDest, proxy, this, this);
+                task.startRunning();
                 addtask(task);
             } catch (IllegalArgumentException iae) {
                 String msg = "Invalid I2PTunnel configuration to create a CONNECT client connecting to the router at " + host + ':'+ port +
@@ -1097,13 +1097,13 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
                 }
             }
 
-            I2PTunnelTask task;
             ownDest = !isShared;
             try {
                 String privateKeyFile = null;
                 if (args.length >= 4)
                     privateKeyFile = args[3];
-                task = new I2PTunnelIRCClient(_port, args[1], l, ownDest, this, this, privateKeyFile);
+                I2PTunnelClientBase task = new I2PTunnelIRCClient(_port, args[1], l, ownDest, this, this, privateKeyFile);
+                task.startRunning();
                 addtask(task);
                 notifyEvent("ircclientTaskId", Integer.valueOf(task.getId()));
             } catch (IllegalArgumentException iae) {
@@ -1160,7 +1160,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
             if (args.length == 3)
                 privateKeyFile = args[2];
             try {
-                I2PTunnelTask task = new I2PSOCKSTunnel(_port, l, ownDest, this, this, privateKeyFile);
+                I2PTunnelClientBase task = new I2PSOCKSTunnel(_port, l, ownDest, this, this, privateKeyFile);
+                task.startRunning();
                 addtask(task);
                 notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
             } catch (IllegalArgumentException iae) {
@@ -1207,7 +1208,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
             if (args.length == 3)
                 privateKeyFile = args[2];
             try {
-                I2PTunnelTask task = new I2PSOCKSIRCTunnel(_port, l, ownDest, this, this, privateKeyFile);
+                I2PTunnelClientBase task = new I2PSOCKSIRCTunnel(_port, l, ownDest, this, this, privateKeyFile);
+                task.startRunning();
                 addtask(task);
                 notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
             } catch (IllegalArgumentException iae) {
@@ -1670,7 +1672,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
                 if (!_clientOptions.containsKey("outbound.nickname"))
                     _clientOptions.setProperty("outbound.nickname", "I2Ping");
             }
-            I2PTunnelTask task = new I2Ping(l, ownDest, this, this);
+            I2PTunnelClientBase task = new I2Ping(l, ownDest, this, this);
+            task.startRunning();
             addtask(task);
             notifyEvent("pingTaskId", Integer.valueOf(task.getId()));
         } else {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java
index 6a552719ec1d0cf3bf7339a9bc4d4a47d396c1fd..93785e32659d957842660dfb0d96ef8c4c3d1989 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java
@@ -32,6 +32,9 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
     protected long readTimeout = DEFAULT_READ_TIMEOUT;
 
     /**
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     * or open the local socket. You MUST call startRunning() for that.
+     *
      * @param destinations peers we target, comma- or space-separated. Since 0.9.9, each dest may be appended with :port
      * @throws IllegalArgumentException if the I2PTunnel does not contain
      *                                  valid config to contact the router
@@ -44,11 +47,6 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
               tunnel, pkf);
 
         _addrs = new ArrayList<I2PSocketAddress>(1);
-        if (waitEventValue("openBaseClientResult").equals("error")) {
-            notifyEvent("openClientResult", "error");
-            return;
-        }
-
         dests = new ArrayList<Destination>(1);
         buildAddresses(destinations);
 
@@ -68,9 +66,6 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
         }
 
         setName(getLocalPort() + " -> " + destinations);
-
-        startRunning();
-
         notifyEvent("openClientResult", "ok");
     }
 
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
index 030e4a0b637da4aa3d4554a4831b50fd638225c2..a2c408973d82ec4be7fc817e4572d7bb20adb892 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
@@ -26,6 +26,7 @@ import javax.net.ssl.SSLServerSocketFactory;
 import net.i2p.I2PAppContext;
 import net.i2p.I2PException;
 import net.i2p.client.I2PSession;
+import net.i2p.client.I2PSessionException;
 import net.i2p.client.streaming.I2PSocket;
 import net.i2p.client.streaming.I2PSocketManager;
 import net.i2p.client.streaming.I2PSocketManagerFactory;
@@ -53,6 +54,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
     protected Destination dest;
     private int localPort;
+    private final String _handlerName;
 
     /**
      *  Protected for I2Ping since 0.9.11. Not for use outside package.
@@ -91,6 +93,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
         chained = true;
         sockMgr = sktMgr;
         _clientId = clientId;
+        _handlerName = "chained";
         this.localPort = localPort;
         this.l = l;
         _ownDest = true; // == ! shared client
@@ -101,31 +104,14 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
         _context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
         _log = _context.logManager().getLog(getClass());
 
-        Thread t = new I2PAppThread(this, "Client " + tunnel.listenHost + ':' + localPort);
-        t.start();
-        open = true;
-        synchronized (this) {
-            while (!listenerReady && open) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                    // ignore
-                }
-            }
-        }
-
-        if (open && listenerReady) {
-            l.log("Client ready, listening on " + tunnel.listenHost + ':' + localPort);
-            notifyEvent("openBaseClientResult", "ok");
-        } else {
-            l.log("Client error for " + tunnel.listenHost + ':' + localPort + ", check logs");
-            notifyEvent("openBaseClientResult", "error");
-        }
+        startup();
     }
 
     /**
      * The main constructor.
-     * This may take a LONG time if building and starting a new manager.
+     *
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     * or open the local socket. You MUST call startRunning() for that.
      *
      * @param localPort if 0, use any port, get actual port selected with getLocalPort()
      * @throws IllegalArgumentException if the I2CP configuration is b0rked so
@@ -139,7 +125,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
     /**
      * Use this to build a client with a persistent private key.
-     * This may take a LONG time if building and starting a new manager.
+     *
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     * or open the local socket. You MUST call startRunning() for that.
      *
      * @param localPort if 0, use any port, get actual port selected with getLocalPort()
      * @param pkf Path to the private key file, or null to generate a transient key
@@ -155,7 +143,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
         this.localPort = localPort;
         this.l = l;
         _ownDest = ownDest; // == ! shared client
-
+        _handlerName = handlerName;
 
         _context = tunnel.getContext();
         _context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
@@ -201,33 +189,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
         } // else delay creating session until createI2PSocket() is called
         
-        Thread t = new I2PAppThread(this);
-        t.setName("Client " + _clientId);
-        t.start();
-        open = true;
-        synchronized (this) {
-            while (!listenerReady && open) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                    // ignore
-                }
-            }
-        }
-
-        if (open && listenerReady) {
-            if (openNow)
-                l.log("Client ready, listening on " + tunnel.listenHost + ':' + localPort);
-            else
-                l.log("Client ready, listening on " + tunnel.listenHost + ':' + localPort + ", delaying tunnel open until required");
-            notifyEvent("openBaseClientResult", "ok");
-        } else {
-            l.log("Client error for " + tunnel.listenHost + ':' + localPort + ", check logs");
-            notifyEvent("openBaseClientResult", "error");
-        }
     }
     
     /**
+     * Create the manager if it doesn't exist, AND connect it to the router and
+     * build tunnels.
+     *
      * Sets the this.sockMgr field if it is null, or if we want a new one.
      * This may take a LONG time if building a new manager.
      *
@@ -269,6 +236,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
                     this.sockMgr = getSocketManager();
             }
         }
+        connectManager();
     }
 
     /** this is ONLY for shared clients */
@@ -277,7 +245,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
     /**
      * This is ONLY for shared clients.
-     * This may take a LONG time if building a new manager.
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @return non-null
      * @throws IllegalArgumentException if the I2CP configuration is b0rked so
@@ -289,7 +258,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
     /**
      * This is ONLY for shared clients.
-     * This may take a LONG time if building a new manager.
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @return non-null
      * @throws IllegalArgumentException if the I2CP configuration is b0rked so
@@ -301,7 +271,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
     /**
      * This is ONLY for shared clients.
-     * This may take a LONG time if building a new manager.
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @return non-null
      * @throws IllegalArgumentException if the I2CP configuration is b0rked so
@@ -347,7 +318,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
     }
 
     /**
-     * This may take a LONG time.
+     * For NON-SHARED clients (ownDest = true).
+     *
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @return non-null
      * @throws IllegalArgumentException if the I2CP configuration is b0rked so
@@ -356,8 +330,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
     protected I2PSocketManager buildSocketManager() {
         return buildSocketManager(getTunnel(), this.privKeyFile, this.l);
     }
+
     /**
-     * This may take a LONG time.
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @return non-null
      * @throws IllegalArgumentException if the I2CP configuration is b0rked so
@@ -371,7 +347,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
     private static final int MAX_RETRIES = 4;
 
     /**
-     * This may take a LONG time.
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @param pkf absolute path or null
      * @return non-null
@@ -383,7 +360,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
     }
 
     /**
-     * This may take a LONG time.
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
+     * Call verifySocketManager() for that.
      *
      * @param pkf absolute path or null
      * @return non-null
@@ -400,57 +378,79 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
             try {
                 portNum = Integer.parseInt(tunnel.port);
             } catch (NumberFormatException nfe) {
-                _log.log(Log.CRIT, "Invalid port specified [" + tunnel.port + "], reverting to " + portNum);
+                throw new IllegalArgumentException("Invalid port specified [" + tunnel.port + "]", nfe);
             }
         }
         
         I2PSocketManager sockManager = null;
-        // FIXME: Can't stop a tunnel from the UI while it's in this loop (no session yet)
-        int retries = 0;
-        while (sockManager == null) {
+        FileInputStream fis = null;
+        try {
             if (pkf != null) {
                 // Persistent client dest
-                FileInputStream fis = null;
-                try {
-                    fis = new FileInputStream(pkf);
-                    sockManager = I2PSocketManagerFactory.createManager(fis, tunnel.host, portNum, props);
-                } catch (IOException ioe) {
-                    if (log != null)
-                        log.log("Error opening key file " + ioe);
-                    _log.error("Error opening key file", ioe);
-                    throw new IllegalArgumentException("Error opening key file " + ioe);
-                } finally {
-                    if (fis != null)
-                        try { fis.close(); } catch (IOException ioe) {}
-                }
+                fis = new FileInputStream(pkf);
+                sockManager = I2PSocketManagerFactory.createDisconnectedManager(fis, tunnel.host, portNum, props);
             } else {
-                sockManager = I2PSocketManagerFactory.createManager(tunnel.host, portNum, props);
+                sockManager = I2PSocketManagerFactory.createDisconnectedManager(null, tunnel.host, portNum, props);
             }
-            
-            if (sockManager == null) {
-                // try to make this error sensible as it will happen... sadly we can't get to the listenPort, only the listenHost
-                String msg = "Unable to connect to the router at " + tunnel.host + ':' + portNum +
+        } catch (I2PSessionException ise) {
+            throw new IllegalArgumentException("Can't create socket manager", ise);
+        } catch (IOException ioe) {
+            if (log != null)
+                log.log("Error opening key file " + ioe);
+            _log.error("Error opening key file", ioe);
+            throw new IllegalArgumentException("Error opening key file", ioe);
+        } finally {
+            if (fis != null)
+                try { fis.close(); } catch (IOException ioe) {}
+        }
+        sockManager.setName("Client");
+        if (_log.shouldLog(Log.INFO))
+            _log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Built a new socket manager [s=" + sockManager.getSession() + "]");
+        tunnel.addSession(sockManager.getSession());
+        return sockManager;
+    }
+
+
+    /**
+     * Warning, blocks while connecting to router and building tunnels;
+     * This may take a LONG time.
+     *
+     * @throws IllegalArgumentException if the I2CP configuration is b0rked so
+     *                                  badly that we cant create a socketManager
+     * @since 0.9.20
+     */
+    private void connectManager() {
+        int retries = 0;
+        while (sockMgr.getSession().isClosed()) {
+            try {
+                sockMgr.getSession().connect();
+            } catch (I2PSessionException ise) {
+                // shadows instance _log
+                Log _log = getTunnel().getContext().logManager().getLog(I2PTunnelClientBase.class);
+                Logging log = this.l;
+                // try to make this error sensible as it will happen...
+                String portNum = getTunnel().port;
+                if (portNum == null)
+                    portNum = "7654";
+                String msg;
+                if (getTunnel().getContext().isRouterContext())
+                    msg = "Unable to connect to the router at " + getTunnel().host + ':' + portNum +
                              " and build tunnels for the client";
+                else
+                    msg = "Unable to build tunnels for the client";
                 if (++retries < MAX_RETRIES) {
                     if (log != null)
                         log.log(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds");
-                    _log.error(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds");
+                    _log.error(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds", ise);
                 } else {
                     if (log != null)
                         log.log(msg + ", giving up");
-                    _log.log(Log.CRIT, msg + ", giving up");
-                    // not clear if callers can handle null
-                    //return null;
-                    throw new IllegalArgumentException(msg);
+                    _log.log(Log.CRIT, msg + ", giving up", ise);
+                    throw new IllegalArgumentException(msg, ise);
                 }
                 try { Thread.sleep(RETRY_DELAY); } catch (InterruptedException ie) {}
             }
         }
-        sockManager.setName("Client");
-        if (_log.shouldLog(Log.INFO))
-            _log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Built a new socket manager [s=" + sockManager.getSession() + "]");
-        tunnel.addSession(sockManager.getSession());
-        return sockManager;
     }
 
     public final int getLocalPort() {
@@ -469,11 +469,67 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
     }
 
     /**
-     * Actually start working on incoming connections.  *Must* be
+     * Actually open the local socket and start working on incoming connections.  *Must* be
      * called by derived classes after initialization.
      *
+     * (this wasn't actually true until 0.9.20)
+     *
+     * This will be fast if i2cp.delayOpen is true, but could take
+     * a LONG TIME if it is false, as it connects to the router and builds tunnels.
+     *
      */
     public void startRunning() {
+        boolean openNow = !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty("i2cp.delayOpen"));
+        if (openNow) {
+            while (sockMgr == null) {
+                verifySocketManager();
+                if (sockMgr == null) {
+                    _log.error("Unable to connect to router and build tunnels for " + _handlerName);
+                    // FIXME there is a loop in buildSocketManager(), do we really need another one here?
+                    // no matter, buildSocketManager() now throws an IllegalArgumentException
+                    try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
+                }
+            }
+            // can't be null unless we limit the loop above
+            //if (sockMgr == null) {
+            //    l.log("Invalid I2CP configuration");
+            //    throw new IllegalArgumentException("Socket manager could not be created");
+            //}
+            l.log("Tunnels ready for client: " + _handlerName);
+
+        } // else delay creating session until createI2PSocket() is called
+        startup();
+    }
+        
+    private void startup() {
+        if (_log.shouldLog(Log.DEBUG))
+            _log.debug("startup " + _clientId, new Exception("I did it"));
+        // prevent JVM exit when running outside the router
+        boolean isDaemon = getTunnel().getContext().isRouterContext();
+        Thread t = new I2PAppThread(this, "Client " + _clientId, isDaemon);
+        t.start();
+        open = true;
+        synchronized (this) {
+            while (!listenerReady && open) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+        }
+
+        if (open && listenerReady) {
+            boolean openNow = !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty("i2cp.delayOpen"));
+            if (openNow)
+                l.log("Client ready, listening on " + getTunnel().listenHost + ':' + localPort);
+            else
+                l.log("Client ready, listening on " + getTunnel().listenHost + ':' + localPort + ", delaying tunnel open until required");
+            notifyEvent("openBaseClientResult", "ok");
+        } else {
+            l.log("Client error for " + getTunnel().listenHost + ':' + localPort + ", check logs");
+            notifyEvent("openBaseClientResult", "error");
+        }
         synchronized (startLock) {
             startRunning = true;
             startLock.notify();
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
index e10cfb55f6d717f1321157aefe9ebb1636e258c5..8763218662b4327eff0c4d14abdb5ab789b568e7 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
@@ -77,6 +77,9 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
          "Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>";
     
     /**
+     *  As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     *  or open the local socket. You MUST call startRunning() for that.
+     *
      * @throws IllegalArgumentException if the I2PTunnel does not contain
      *                                  valid config to contact the router
      */
@@ -85,11 +88,6 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
                                I2PTunnel tunnel) throws IllegalArgumentException {
         super(localPort, ownDest, l, notifyThis, "HTTPS Proxy on " + tunnel.listenHost + ':' + localPort, tunnel);
 
-        if (waitEventValue("openBaseClientResult").equals("error")) {
-            notifyEvent("openConnectClientResult", "error");
-            return;
-        }
-
         if (wwwProxy != null) {
             StringTokenizer tok = new StringTokenizer(wwwProxy, ", ");
             while (tok.hasMoreTokens())
@@ -97,8 +95,6 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
         }
 
         setName("HTTPS Proxy on " + tunnel.listenHost + ':' + localPort);
-
-        startRunning();
     }
 
     /** 
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java
index 56c2e7550c4242fb5146880a2968f198972f2f6c..4dbd5e654cdce76ed02115679be940f818244cd2 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java
@@ -30,6 +30,9 @@ public class I2PTunnelHTTPBidirProxy extends I2PTunnelHTTPClient implements Runn
   
 
     /**
+     *  As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     *  or open the local socket. You MUST call startRunning() for that.
+     *
      * @throws IllegalArgumentException if the I2PTunnel does not contain
      *                                  valid config to contact the router
      */
@@ -38,8 +41,6 @@ public class I2PTunnelHTTPBidirProxy extends I2PTunnelHTTPClient implements Runn
         // proxyList = new ArrayList();
 
         setName(getLocalPort() + " -> HTTPClient [NO PROXIES]");
-        startRunning();
-
         notifyEvent("openHTTPClientResult", "ok");
     }
 }
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java
index d97482e04e118cdf618b70067f415fb5d8e43bfe..2a3118567c963a66b0a32d90dbf4f837620bdc70 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java
@@ -32,7 +32,9 @@ public class I2PTunnelHTTPBidirServer extends I2PTunnelHTTPServer {
         bidir = true;
 
         /* start the httpclient */
-        task = new I2PTunnelHTTPBidirProxy(localPort, l, sockMgr, getTunnel(), getEventDispatcher(), __serverId);
+        I2PTunnelClientBase client = new I2PTunnelHTTPBidirProxy(localPort, l, sockMgr, getTunnel(), getEventDispatcher(), __serverId);
+        client.startRunning();
+        task = client;
         sockMgr.setName("Server"); // TO-DO: Need to change this to "Bidir"!
         getTunnel().addSession(sockMgr.getSession());
         l.log("Ready!");
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
index e8912cd112be40efb75e2e5260a29e325e98724a..372648b4ee45f6003e233e71465fe2a80817ed9a 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
@@ -192,6 +192,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
      *  This constructor always starts the tunnel (ignoring the i2cp.delayOpen option).
      *  It is used to add a client to an existing socket manager.
      *
+     *  As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     *  or open the local socket. You MUST call startRunning() for that.
+     *
      *  @param sockMgr the existing socket manager
      */
     public I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) {
@@ -200,12 +203,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
         // proxyList = new ArrayList();
 
         setName("HTTP Proxy on " + getTunnel().listenHost + ':' + localPort);
-        startRunning();
-
         notifyEvent("openHTTPClientResult", "ok");
     }
 
     /**
+     *  As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     *  or open the local socket. You MUST call startRunning() for that.
+     *
      * @throws IllegalArgumentException if the I2PTunnel does not contain
      *                                  valid config to contact the router
      */
@@ -216,10 +220,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
         _proxyNonce = Long.toString(_context.random().nextLong());
 
         //proxyList = new ArrayList(); // We won't use outside of i2p
-        if(waitEventValue("openBaseClientResult").equals("error")) {
-            notifyEvent("openHTTPClientResult", "error");
-            return;
-        }
 
         if(wwwProxy != null) {
             StringTokenizer tok = new StringTokenizer(wwwProxy, ", ");
@@ -229,9 +229,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
         }
 
         setName("HTTP Proxy on " + tunnel.listenHost + ':' + localPort);
-
-        startRunning();
-
         notifyEvent("openHTTPClientResult", "ok");
     }
 
@@ -611,6 +608,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                                                            _("i2paddresshelper cannot help you with a destination like that!") +
                                                            "</p>").getBytes("UTF-8"));
                                                 writeFooter(out);
+                                                reader.drain();
                                                 // XXX: should closeSocket(s) be in a finally block?
                                             } catch (IOException ioe) {
                                                 // ignore
@@ -725,7 +723,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                             out.write(getErrorPage("localhost", ERR_LOCALHOST).getBytes("UTF-8"));
                             writeFooter(out);
                             reader.drain();
-                            s.close();
                         } catch (IOException ioe) {
                             // ignore
                         } finally {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java
index 344c721971d42de85651596be45a84567ca8497b..e8fac6812b3aded6253952d2fadc22d6eee48221 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java
@@ -41,6 +41,9 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase {
     public static final String PROP_DCC = "i2ptunnel.ircclient.enableDCC";
 
     /**
+     *  As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     *  or open the local socket. You MUST call startRunning() for that.
+     *
      * @param destinations peers we target, comma- or space-separated. Since 0.9.9, each dest may be appended with :port
      * @throws IllegalArgumentException if the I2PTunnel does not contain
      *                                  valid config to contact the router
@@ -81,8 +84,6 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase {
         _dccEnabled = Boolean.parseBoolean(tunnel.getClientOptions().getProperty(PROP_DCC));
         // TODO add some prudent tunnel options (or is it too late?)
 
-        startRunning();
-
         notifyEvent("openIRCClientResult", "ok");
     }
     
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java
index 5d6dbcef3ec88f5c7699acd780df88ae4cb237dc..5c06cb41c2d5ead872c13ee45d977286b65a1f20 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java
@@ -276,7 +276,7 @@ public class TunnelControllerGroup implements ClientApp {
                     for (int i = 0; i < _controllers.size(); i++) {
                         TunnelController controller = _controllers.get(i);
                         if (controller.getStartOnLoad())
-                            controller.startTunnel();
+                            controller.startTunnelBackground();
                     }
                 } finally {
                     _controllersLock.readLock().unlock();
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/DCCClientManager.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/DCCClientManager.java
index 42af9abf220afd602473c5af32392db3d7ba6c54..753da65ec466f3c740804aa8c02204ce813085ce 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/DCCClientManager.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/DCCClientManager.java
@@ -109,6 +109,7 @@ public class DCCClientManager extends EventReceiver {
             I2PTunnelDCCClient cTunnel = new I2PTunnelDCCClient(b32, localPort, port, l, sockMgr,
                                                                 _dispatch, _tunnel, ++_id);
             cTunnel.attachEventDispatcher(this);
+            cTunnel.startRunning();
             int lport = cTunnel.getLocalPort();
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Opened client tunnel at port " + lport +
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/I2PTunnelDCCClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/I2PTunnelDCCClient.java
index fcca71cb9a5cb90aafc215dc43abe631366ec13d..f5b434e3be058c4d1ecff900da526e27bb4addfb 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/I2PTunnelDCCClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/irc/I2PTunnelDCCClient.java
@@ -37,6 +37,9 @@ public class I2PTunnelDCCClient extends I2PTunnelClientBase {
     public static final String CONNECT_STOP_EVENT = "connectionStopped";
 
     /**
+     * As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     * or open the local socket. You MUST call startRunning() for that.
+     *
      * @param dest the target, presumably b32
      * @param localPort if 0, use any port, get actual port selected with getLocalPort()
      * @throws IllegalArgumentException if the I2PTunnel does not contain
@@ -51,8 +54,6 @@ public class I2PTunnelDCCClient extends I2PTunnelClientBase {
         _expires = tunnel.getContext().clock().now() + INBOUND_EXPIRE;
 
         setName("DCC send -> " + dest + ':' + remotePort);
-
-        startRunning();
     }
 
     /**
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java
index 498d75a181cd8e0366eb51ba7d9a19cba8ed0c7f..6f8f6b3d302dccea16873dbf1ec86f6e712514ff 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java
@@ -33,7 +33,12 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
     //	  I2PSOCKSTunnel(localPort, l, ownDest, (EventDispatcher)null);
     //}
 
-    /** @param pkf private key file name or null for transient key */
+    /**
+     *  As of 0.9.20 this is fast, and does NOT connect the manager to the router,
+     *  or open the local socket. You MUST call startRunning() for that.
+     *
+     *  @param pkf private key file name or null for transient key
+     */
     public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel, String pkf) {
         super(localPort, ownDest, l, notifyThis, "SOCKS Proxy on " + tunnel.listenHost + ':' + localPort, tunnel, pkf);
 
@@ -44,8 +49,6 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
 
         setName("SOCKS Proxy on " + tunnel.listenHost + ':' + localPort);
         parseOptions();
-        startRunning();
-
         notifyEvent("openSOCKSTunnelResult", "ok");
     }