diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
index 083123b5fc698908d24a6504734094816295e628..d1dec5248e008e5b5f10ef03756896d1ca454667 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
@@ -45,7 +45,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
     private static final String[] CLIENT_SKIPHEADERS = {HASH_HEADER, DEST64_HEADER, DEST32_HEADER};
     private static final String SERVER_HEADER = "Server";
     private static final String[] SERVER_SKIPHEADERS = {SERVER_HEADER};
-    private static final long HEADER_TIMEOUT = 60*1000;
+    private static final long HEADER_TIMEOUT = 15*1000;
+    private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
     private static final long START_INTERVAL = (60 * 1000) * 3;
     private long _startedOn = 0L;
 
@@ -492,7 +493,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
     	}
     }
 
-    protected static Map<String, List<String>> readHeaders(InputStream in, StringBuilder command, String[] skipHeaders, I2PAppContext ctx) throws IOException {
+    protected static Map<String, List<String>> readHeaders(InputStream in, StringBuilder command,
+                                                           String[] skipHeaders, I2PAppContext ctx) throws IOException {
     	HashMap<String, List<String>> headers = new HashMap<String, List<String>>();
         StringBuilder buf = new StringBuilder(128);
         
@@ -516,6 +518,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
         if (trimmed > 0)
             ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed, 0);
         
+        // slowloris / darkloris
+        long expire = ctx.clock().now() + TOTAL_HEADER_TIMEOUT;
         int i = 0;
         while (true) {
             if (++i > MAX_HEADERS)
@@ -528,6 +532,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
                 // end of headers reached
                 return headers;
             } else {
+                if (ctx.clock().now() > expire)
+                    throw new IOException("Headers took too long [" + buf.toString() + "]");
                 int split = buf.indexOf(":");
                 if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
                 String name = buf.substring(0, split).trim();
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
index b592ebcf82dcc267f90ddbbb621329b6207403f1..31cc4c26c2ffe43f780ad1ba0497ea3efc968489 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
@@ -62,7 +62,8 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
 	public static final String PROP_WEBIRC_SPOOF_IP_DEFAULT="127.0.0.1";
     public static final String PROP_HOSTNAME="ircserver.fakeHostname";
     public static final String PROP_HOSTNAME_DEFAULT="%f.b32.i2p";
-    private static final long HEADER_TIMEOUT = 60*1000;
+    private static final long HEADER_TIMEOUT = 15*1000;
+    private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
     
     private final static byte[] ERR_UNAVAILABLE =
         (":ircserver.i2p 499 you :" +
@@ -188,12 +189,16 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
         StringBuilder buf = new StringBuilder(128);
         int lineCount = 0;
         
+        // slowloris / darkloris
+        long expire = System.currentTimeMillis() + TOTAL_HEADER_TIMEOUT;
         while (true) {
             String s = DataHelper.readLine(in);
             if (s == null)
                 throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
             if (++lineCount > 10)
                 throw new IOException("Too many lines before USER or SERVER, giving up");
+            if (System.currentTimeMillis() > expire)
+                throw new IOException("Headers took too long [" + buf.toString() + "]");
             s = s.trim();
             //if (_log.shouldLog(Log.DEBUG))
             //    _log.debug("Got line: " + s);
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index 2e3b55fa1bc5d31919607d6fb8fa6a2491cd235e..24e2c58eab5b04ca6abcd058218ebf34cc600d23 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -49,8 +49,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
 
     protected Logging l;
 
-    private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
-    /** default timeout to 3 minutes - override if desired */
+    private static final long DEFAULT_READ_TIMEOUT = 5*60*1000;
+    /** default timeout to 5 minutes - override if desired */
     protected long readTimeout = DEFAULT_READ_TIMEOUT;
 
     /** do we use threads? default true (ignored for standard servers, always false) */
diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java
index c20a89a591e6983cd75456f1c17d0979c839c334..1095a9334ef0cafd1266fecc78e327777d507f7d 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java
@@ -17,6 +17,9 @@ public interface I2PSocketOptions {
     /**
      * How long we will wait for the ACK from a SYN, in milliseconds.
      *
+     * Default 60 seconds. Max of 2 minutes enforced in Connection.java,
+     * and it also interprets <= 0 as default.
+     *
      * @return milliseconds to wait, or -1 if we will wait indefinitely
      */
     public long getConnectTimeout();
@@ -24,6 +27,9 @@ public interface I2PSocketOptions {
     /**
      * Define how long we will wait for the ACK from a SYN, in milliseconds.
      *
+     * Default 60 seconds. Max of 2 minutes enforced in Connection.java,
+     * and it also interprets <= 0 as default.
+     *
      * @param ms timeout in ms
      */
     public void setConnectTimeout(long ms);
@@ -32,6 +38,9 @@ public interface I2PSocketOptions {
      * What is the longest we'll block on the input stream while waiting
      * for more data.  If this value is exceeded, the read() throws 
      * InterruptedIOException
+     *
+     * WARNING: Default -1 (unlimited), which is probably not what you want.
+     *
      * @return timeout in ms
      */
     public long getReadTimeout();
@@ -40,6 +49,9 @@ public interface I2PSocketOptions {
      * What is the longest we'll block on the input stream while waiting
      * for more data.  If this value is exceeded, the read() throws 
      * InterruptedIOException
+     *
+     * WARNING: Default -1 (unlimited), which is probably not what you want.
+     *
      * @param ms timeout in ms
      */
     public void setReadTimeout(long ms);
@@ -50,6 +62,8 @@ public interface I2PSocketOptions {
      * either some data is removed or the connection is closed.  If this is 
      * less than or equal to zero, there is no limit (warning: can eat ram)
      *
+     * Default 64 KB
+     *
      * @return buffer size limit, in bytes
      */
     public int getMaxBufferSize();
@@ -60,6 +74,8 @@ public interface I2PSocketOptions {
      * either some data is removed or the connection is closed.  If this is 
      * less than or equal to zero, there is no limit (warning: can eat ram)
      *
+     * Default 64 KB
+     *
      * @param numBytes How much data will we accept that hasn't been written out yet.
      */
     public void setMaxBufferSize(int numBytes);
@@ -69,6 +85,9 @@ public interface I2PSocketOptions {
      * for the data to flush.  If this value is exceeded, the write() throws 
      * InterruptedIOException.  If this is less than or equal to zero, there 
      * is no timeout.
+     *
+     * Default -1 (unlimited)
+     *
      * @return wait time to block on the output stream while waiting for the data to flush.
      */
     public long getWriteTimeout();
@@ -78,6 +97,9 @@ public interface I2PSocketOptions {
      * for the data to flush.  If this value is exceeded, the write() throws 
      * InterruptedIOException.  If this is less than or equal to zero, there 
      * is no timeout.
+     *
+     * Default -1 (unlimited)
+     *
      * @param ms wait time to block on the output stream while waiting for the data to flush.
      */
     public void setWriteTimeout(long ms);
diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java
index 5d0636676be05648a04f407e6d0c58c0a8a5865a..3a1722dfb31942c1ffa174b71be64894139d843c 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java
@@ -96,6 +96,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
     /**
      * How long we will wait for the ACK from a SYN, in milliseconds.
      *
+     * Default 60 seconds. Max of 2 minutes enforced in Connection.java,
+     * and it also interprets <= 0 as default.
+     *
      * @return milliseconds to wait, or -1 if we will wait indefinitely
      */
     public long getConnectTimeout() {
@@ -105,6 +108,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
     /**
      * Define how long we will wait for the ACK from a SYN, in milliseconds.
      *
+     * Default 60 seconds. Max of 2 minutes enforced in Connection.java,
+     * and it also interprets <= 0 as default.
+     *
      */
     public void setConnectTimeout(long ms) {
         _connectTimeout = ms;
@@ -114,6 +120,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
      * What is the longest we'll block on the input stream while waiting
      * for more data.  If this value is exceeded, the read() throws 
      * InterruptedIOException
+     *
+     * WARNING: Default -1 (unlimited), which is probably not what you want.
      */
     public long getReadTimeout() {
         return _readTimeout;
@@ -123,6 +131,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
      * What is the longest we'll block on the input stream while waiting
      * for more data.  If this value is exceeded, the read() throws 
      * InterruptedIOException
+     *
+     * WARNING: Default -1 (unlimited), which is probably not what you want.
      */
     public void setReadTimeout(long ms) {
         _readTimeout = ms;
@@ -134,6 +144,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
      * either some data is removed or the connection is closed.  If this is 
      * less than or equal to zero, there is no limit (warning: can eat ram)
      *
+     * Default 64 KB
+     *
      * @return buffer size limit, in bytes
      */
     public int getMaxBufferSize() {
@@ -146,6 +158,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
      * either some data is removed or the connection is closed.  If this is 
      * less than or equal to zero, there is no limit (warning: can eat ram)
      *
+     * Default 64 KB
+     *
      */
     public void setMaxBufferSize(int numBytes) {
         _maxBufferSize = numBytes; 
@@ -156,6 +170,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
      * for the data to flush.  If this value is exceeded, the write() throws 
      * InterruptedIOException.  If this is less than or equal to zero, there 
      * is no timeout.
+     *
+     * Default -1 (unlimited)
      */
     public long getWriteTimeout() {
         return _writeTimeout;
@@ -166,6 +182,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
      * for the data to flush.  If this value is exceeded, the write() throws 
      * InterruptedIOException.  If this is less than or equal to zero, there 
      * is no timeout.
+     *
+     * Default -1 (unlimited)
      */
     public void setWriteTimeout(long ms) {
         _writeTimeout = ms;
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
index 29a05512f71737526e42be160bc264582b281042..99909b6b30c49c2f1ebbd5454518215402d1c144 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
@@ -87,7 +87,7 @@ class Connection {
     /** wait up to 5 minutes after disconnection so we can ack/close packets */
     public static final int DISCONNECT_TIMEOUT = 5*60*1000;
     
-    private static final long DEFAULT_CONNECT_TIMEOUT = 60*1000;
+    public static final int DEFAULT_CONNECT_TIMEOUT = 60*1000;
     private static final long MAX_CONNECT_TIMEOUT = 2*60*1000;
 
     public static final int MAX_WINDOW_SIZE = 128;
@@ -549,7 +549,15 @@ class Connection {
     
     public void setInbound() { _isInbound = true; }
     public boolean isInbound() { return _isInbound; }
+
+    /**
+     *  Always true at the start, even if we haven't gotten a reply on an
+     *  outbound connection. Only set to false on disconnect.
+     *  For outbound, use getHighestAckedThrough() >= 0 also,
+     *  to determine if the connection is up.
+     */
     public boolean getIsConnected() { return _connected; }
+
     public boolean getHardDisconnected() { return _hardDisconnected; }
     public boolean getResetSent() { return _resetSent; }
     public long getResetSentOn() { return _resetSentOn; }
@@ -1017,7 +1025,7 @@ class Connection {
 
     @Override
     public String toString() { 
-        StringBuilder buf = new StringBuilder(128);
+        StringBuilder buf = new StringBuilder(256);
         buf.append("[Connection ");
         if (_receiveStreamId > 0)
             buf.append(Packet.toId(_receiveStreamId));
@@ -1075,6 +1083,7 @@ class Connection {
             buf.append(" close received ").append(DataHelper.formatDuration(_context.clock().now() - getCloseReceivedOn())).append(" ago");
         buf.append(" sent: ").append(1 + _lastSendId.get());
         buf.append(" rcvd: ").append(1 + _inputStream.getHighestBlockId() - missing);
+        buf.append(" ackThru ").append(_highestAckedThrough);
         
         buf.append(" maxWin ").append(getOptions().getMaxWindowSize());
         buf.append(" MTU ").append(getOptions().getMaxMessageSize());
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java
index cd535cc310b70abc60c41b851fadbafe335cad35..aac78d99369a55e1828dc8c6b388abcd711ac707 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java
@@ -343,8 +343,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
                                                       DEFAULT_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR));
         setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR,
                                             DEFAULT_SLOW_START_GROWTH_RATE_FACTOR));
-        // overrides default in super()
-        setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
+        // overrides default in super()... why?
+        //setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
         setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS));
         setEnforceProtocol(getBool(opts, PROP_ENFORCE_PROTO, DEFAULT_ENFORCE_PROTO));
         initLists(opts);
@@ -399,9 +399,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
             setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR,
                                                 DEFAULT_SLOW_START_GROWTH_RATE_FACTOR));
         if (opts.containsKey(PROP_CONNECT_TIMEOUT))
-            // wow 5 minutes!!! FIXME!!
             // overrides default in super()
-            setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
+            setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DEFAULT_CONNECT_TIMEOUT));
         if (opts.containsKey(PROP_ANSWER_PINGS))
             setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS));
         if (opts.containsKey(PROP_ENFORCE_PROTO))
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java
index 17918a6e129219842da6e374e4b5c2f8dd09c5e2..1dd473907aa165f1e8e63a32c41a76e6722e0458 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java
@@ -29,7 +29,7 @@ class SchedulerChooser {
             TaskScheduler scheduler = _schedulers.get(i);
             if (scheduler.accept(con)) {
                 //if (_log.shouldLog(Log.DEBUG))
-                //    _log.debug("Scheduling for " + con + " with " + scheduler.getClass().getName());
+                //    _log.debug("Scheduling for " + con + " with " + scheduler.getClass().getSimpleName());
                 return scheduler;
             }
         }
diff --git a/core/java/src/net/i2p/util/SocketTimeout.java b/core/java/src/net/i2p/util/SocketTimeout.java
index 3a8f57400b3713c028a45c1a3a18ba52857c4ce0..e5c02d5c9713e2cf96f83d895814c5650aa329fd 100644
--- a/core/java/src/net/i2p/util/SocketTimeout.java
+++ b/core/java/src/net/i2p/util/SocketTimeout.java
@@ -7,7 +7,7 @@ import java.util.Date;
 
 /**
  *  This should be deprecated.
- *  It is only used by EepGet, and it uses the inefficient SimpleTimer.
+ *  It is only used by EepGet.
  *  The only advantage seems to be a total timeout period, which is the second
  *  argument to EepGet.fetch(headerTimeout, totalTimeout, inactivityTimeout),
  *  which is most likely always set to -1.
@@ -15,14 +15,16 @@ import java.util.Date;
  *  Use socket.setsotimeout instead?
  */
 public class SocketTimeout extends SimpleTimer2.TimedEvent {
-    private Socket _targetSocket;
-    private long _startTime;
-    private long _inactivityDelay;
-    private long _lastActivity;
-    private long _totalTimeoutTime;
-    private boolean _cancelled;
-    private Runnable _command;
+    private volatile Socket _targetSocket;
+    private final long _startTime;
+    private volatile long _inactivityDelay;
+    private volatile long _lastActivity;
+    private volatile long _totalTimeoutTime;
+    private volatile boolean _cancelled;
+    private volatile Runnable _command;
+
     public SocketTimeout(long delay) { this(null, delay); }
+
     public SocketTimeout(Socket socket, long delay) {
         super(SimpleTimer2.getInstance());
         _inactivityDelay = delay;
@@ -32,6 +34,7 @@ public class SocketTimeout extends SimpleTimer2.TimedEvent {
         _totalTimeoutTime = -1;
         schedule(delay);
     }
+
     public void timeReached() {
         if (_cancelled) return;
         
@@ -53,25 +56,29 @@ public class SocketTimeout extends SimpleTimer2.TimedEvent {
         _cancelled = true;
         return super.cancel();
     }
+
     public void setSocket(Socket s) { _targetSocket = s; }
     public void resetTimer() { _lastActivity = System.currentTimeMillis();  }
     public void setInactivityTimeout(long timeout) { _inactivityDelay = timeout; }
+
     public void setTotalTimeoutPeriod(long timeoutPeriod) { 
         if (timeoutPeriod > 0)
             _totalTimeoutTime = _startTime + timeoutPeriod;
         else
             _totalTimeoutTime = -1;
     }
+
     public void setTimeoutCommand(Runnable job) { _command = job; }
     
     private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS");
     private static String ts(long when) { synchronized (_fmt) { return _fmt.format(new Date(when)); } }
+
     @Override
     public String toString() {
         StringBuilder buf = new StringBuilder();
-        buf.append("started on ");
+        buf.append("SocketTimeout started on ");
         buf.append(ts(_startTime));
-        buf.append("idle for ");
+        buf.append(" idle for ");
         buf.append(System.currentTimeMillis() - _lastActivity);
         buf.append("ms ");
         if (_totalTimeoutTime > 0)
diff --git a/history.txt b/history.txt
index 070d8aeb5e1f59dd23616ca6ae919636640bdd6c..bf03074db7dec032ef70754f695c5b01e3355306 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,15 @@
+2012-10-07 zzz
+ * I2PAppContext: Improved synching in constructor
+ * i2ptunnel:
+   - Set default read timeout in standard server
+   - Reduce header timeout, enforce total header timeout
+     in IRC and HTTP servers (ticket #723)
+ * Logs:
+   - Flush buffers in logs.jsp
+   - Add dup message to buffers, was in file only
+ * Streaming: Don't ignore option or force connect timeout to 5 minutes
+ * UPnP: Workaround NPE (ticket #728)
+
 2012-10-06 zzz
  * configlogging.jsp: Fix IAE
  * error500.jsp: Fix whitespace
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 7ca1a3ec2c4e6b0a58a1f89cf73e6cb3036dcf84..5a58b19bc22ea9b489b5610aade18177efc4505e 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 10;
+    public final static long BUILD = 11;
 
     /** for example "-test" */
     public final static String EXTRA = "";