diff --git a/apps/i2psnark/mime.properties b/apps/i2psnark/mime.properties
index 12eea9b1f8f85165e3538804df771b4d47a98d3c..4c0a7a2570df78d4e3b2153d0548afe6d827b00a 100644
--- a/apps/i2psnark/mime.properties
+++ b/apps/i2psnark/mime.properties
@@ -18,6 +18,7 @@ ogv	= video/ogg
 oga	= audio/ogg
 rar	= application/x-rar-compressed
 su2	= application/zip
+su3	= application/zip
 sud	= application/zip
 tbz	= application/x-bzip2
 txt	= text/plain
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
index 6f5704ce600e1a16cb53cfa15df4ffc000bb5cd9..17b33a0591c7c60c8c96bb855e86fe1977877a27 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
@@ -135,6 +135,11 @@ public class ConsoleUpdateManager implements UpdateManager {
         if (ConfigUpdateHandler.USE_SU3_UPDATE) {
             register(c, ROUTER_SIGNED_SU3, HTTP, 0);
             register(u, ROUTER_SIGNED_SU3, HTTP, 0);
+            // todo
+            //register(c, ROUTER_SIGNED_SU3, HTTPS_CLEARNET, 0);
+            //register(u, ROUTER_SIGNED_SU3, HTTPS_CLEARNET, -10);
+            //register(c, ROUTER_SIGNED_SU3, HTTP_CLEARNET, 0);
+            //register(u, ROUTER_SIGNED_SU3, HTTP_CLEARNET, -20);
         }
         // TODO see NewsFetcher
         //register(u, ROUTER_SIGNED, HTTPS_CLEARNET, -5);
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java
index e208d024d84a390214d2b3368fc43952cdd11c63..edd3ddbae0ae0a5033a7bfa8f27a2fc9541442ee 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java
@@ -114,6 +114,8 @@ class NewsFetcher extends UpdateRunner {
     private static final String SU3_KEY = "su3torrent";
     private static final String CLEARNET_SUD_KEY = "sudclearnet";
     private static final String CLEARNET_SU2_KEY = "su2clearnet";
+    private static final String CLEARNET_HTTP_SU3_KEY = "su3clearnet";
+    private static final String CLEARNET_HTTPS_SU3_KEY = "su3ssl";
     private static final String I2P_SUD_KEY = "sudi2p";
     private static final String I2P_SU2_KEY = "su2i2p";
 
@@ -150,14 +152,9 @@ class NewsFetcher extends UpdateRunner {
                             // Must do su3 first
                             if (ConfigUpdateHandler.USE_SU3_UPDATE) {
                                 sourceMap.put(HTTP, _mgr.getUpdateURLs(ROUTER_SIGNED_SU3, "", HTTP));
-                                String murl = args.get(SU3_KEY);
-                                if (murl != null) {
-                                    List<URI> uris = tokenize(murl);
-                                    if (!uris.isEmpty()) {
-                                        Collections.shuffle(uris, _context.random());
-                                        sourceMap.put(TORRENT, uris);
-                                    }
-                                }
+                                addMethod(TORRENT, args.get(SU3_KEY), sourceMap);
+                                addMethod(HTTP_CLEARNET, args.get(CLEARNET_HTTP_SU3_KEY), sourceMap);
+                                addMethod(HTTPS_CLEARNET, args.get(CLEARNET_HTTPS_SU3_KEY), sourceMap);
                                 // notify about all sources at once
                                 _mgr.notifyVersionAvailable(this, _currentURI, ROUTER_SIGNED_SU3,
                                                             "", sourceMap, ver, "");
@@ -166,14 +163,7 @@ class NewsFetcher extends UpdateRunner {
                             // now do sud/su2
                             sourceMap.put(HTTP, _mgr.getUpdateURLs(ROUTER_SIGNED, "", HTTP));
                             String key = FileUtil.isPack200Supported() ? SU2_KEY : SUD_KEY;
-                            String murl = args.get(key);
-                            if (murl != null) {
-                                List<URI> uris = tokenize(murl);
-                                if (!uris.isEmpty()) {
-                                    Collections.shuffle(uris, _context.random());
-                                    sourceMap.put(TORRENT, uris);
-                                }
-                            }
+                            addMethod(TORRENT, args.get(key), sourceMap);
                             // notify about all sources at once
                             _mgr.notifyVersionAvailable(this, _currentURI, ROUTER_SIGNED,
                                                         "", sourceMap, ver, "");
@@ -280,6 +270,21 @@ class NewsFetcher extends UpdateRunner {
         return rv;
     }
 
+    /**
+     *  Parse URLs and add to the map
+     *  @param urls may be null
+     *  @since 0.9.9
+     */
+    private void addMethod(UpdateMethod method, String urls, Map<UpdateMethod, List<URI>> map) {
+        if (urls != null) {
+            List<URI> uris = tokenize(urls);
+            if (!uris.isEmpty()) {
+                Collections.shuffle(uris, _context.random());
+                map.put(method, uris);
+            }
+        }
+    }
+
     /** override to prevent status update */
     @Override
     public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java
index af3f0e39ccd29b3936ce49e53116f9ea06984e4d..949fa372d89598094857e727c8ec41469002dca9 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/UpdateHandler.java
@@ -5,6 +5,8 @@ import java.util.List;
 
 import net.i2p.router.RouterContext;
 import net.i2p.update.*;
+import static net.i2p.update.UpdateType.*;
+import static net.i2p.update.UpdateMethod.*;
 
 /**
  * <p>Handles the request to update the router by firing one or more
@@ -38,10 +40,11 @@ class UpdateHandler implements Updater {
      */
     public UpdateTask update(UpdateType type, UpdateMethod method, List<URI> updateSources,
                              String id, String newVersion, long maxTime) {
-        if ((type != UpdateType.ROUTER_SIGNED && type != UpdateType.ROUTER_SIGNED_SU3) ||
-            method != UpdateMethod.HTTP || updateSources.isEmpty())
+        if ((type != ROUTER_SIGNED && type != ROUTER_SIGNED_SU3) ||
+           ( method != HTTP && method != HTTP_CLEARNET && method != HTTPS_CLEARNET) ||
+            updateSources.isEmpty())
             return null;
-        UpdateRunner update = new UpdateRunner(_context, _mgr, type, updateSources);
+        UpdateRunner update = new UpdateRunner(_context, _mgr, type, method, updateSources);
         // set status before thread to ensure UI feedback
         _mgr.notifyProgress(update, "<b>" + _mgr._("Updating") + "</b>");
         return update;
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java b/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java
index f048752d03fb7459a2ef5a924211fdc666806536..c3b388db2a47fb7552c5990a9f0fc61814bb7519 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/UpdateRunner.java
@@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.net.URI;
 import java.util.List;
+import java.util.Locale;
 import java.util.StringTokenizer;
 
 import net.i2p.crypto.TrustedUpdate;
@@ -13,10 +14,12 @@ import net.i2p.router.RouterContext;
 import net.i2p.router.RouterVersion;
 import net.i2p.router.web.ConfigUpdateHandler;
 import net.i2p.update.*;
+import static net.i2p.update.UpdateMethod.*;
 import net.i2p.util.EepGet;
 import net.i2p.util.I2PAppThread;
 import net.i2p.util.Log;
 import net.i2p.util.PartialEepGet;
+import net.i2p.util.SSLEepGet;
 import net.i2p.util.VersionComparator;
 
 /**
@@ -31,6 +34,7 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList
     protected final Log _log;
     protected final ConsoleUpdateManager _mgr;
     protected final UpdateType _type;
+    protected final UpdateMethod _method;
     protected final List<URI> _urls;
     protected final String _updateFile;
     protected volatile boolean _isRunning;
@@ -58,18 +62,38 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList
         this(ctx, mgr, type, uris, RouterVersion.VERSION);
     }
 
+    /**
+     *  Uses router version for partial checks
+     *  @since 0.9.9
+     */
+    public UpdateRunner(RouterContext ctx, ConsoleUpdateManager mgr, UpdateType type,
+                        UpdateMethod method, List<URI> uris) {
+        this(ctx, mgr, type, method, uris, RouterVersion.VERSION);
+    }
+
     /**
      *  @param currentVersion used for partial checks
      *  @since 0.9.7
      */
     public UpdateRunner(RouterContext ctx, ConsoleUpdateManager mgr, UpdateType type,
                         List<URI> uris, String currentVersion) { 
+        this(ctx, mgr, type, HTTP, uris, currentVersion);
+    }
+
+    /**
+     *  @param method HTTP, HTTP_CLEARNET, or HTTPS_CLEARNET
+     *  @param currentVersion used for partial checks
+     *  @since 0.9.9
+     */
+    public UpdateRunner(RouterContext ctx, ConsoleUpdateManager mgr, UpdateType type,
+                        UpdateMethod method, List<URI> uris, String currentVersion) { 
         super("Update Runner");
         setDaemon(true);
         _context = ctx;
         _log = ctx.logManager().getLog(getClass());
         _mgr = mgr;
         _type = type;
+        _method = method;
         _urls = uris;
         _baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES);
         _updateFile = (new File(ctx.getTempDir(), "update" + ctx.random().nextInt() + ".tmp")).getAbsolutePath();
@@ -87,7 +111,7 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList
 
     public UpdateType getType() { return _type; }
 
-    public UpdateMethod getMethod() { return UpdateMethod.HTTP; }
+    public UpdateMethod getMethod() { return _method; }
 
     public URI getURI() { return _currentURI; }
 
@@ -120,9 +144,26 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList
         // Alternative: In bytesTransferred(), Check the data in the output file after
         // we've received at least 56 bytes. Need a cancel() method in EepGet ?
 
-        boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
-        String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
-        int proxyPort = ConfigUpdateHandler.proxyPort(_context);
+        boolean shouldProxy;
+        String proxyHost;
+        int proxyPort;
+        boolean isSSL = false;
+        if (_method == HTTP) {
+            shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
+            proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
+            proxyPort = ConfigUpdateHandler.proxyPort(_context);
+        } else if (_method == HTTP_CLEARNET) {
+            shouldProxy = false;
+            proxyHost = null;
+            proxyPort = 0;
+        } else if (_method == HTTPS_CLEARNET) {
+            shouldProxy = false;
+            proxyHost = null;
+            proxyPort = 0;
+            isSSL = true;
+        } else {
+            throw new IllegalArgumentException();
+        }
 
         if (_urls.isEmpty()) {
             // not likely, don't bother translating
@@ -135,12 +176,23 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList
         for (URI uri : _urls) {
             _currentURI = uri;
             String updateURL = uri.toString();
+            if ((_method == HTTP && !"http".equals(uri.getScheme())) ||
+                (_method == HTTP_CLEARNET && !"http".equals(uri.getScheme())) ||
+                (_method == HTTPS_CLEARNET && !"https".equals(uri.getScheme())) ||
+                uri.getHost() == null ||
+                (_method != HTTP && uri.getHost().toLowerCase(Locale.US).endsWith(".i2p"))) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Bad update URI " + uri + " for method " + _method);
+                continue;
+            }
+
             updateStatus("<b>" + _("Updating from {0}", linkify(updateURL)) + "</b>");
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Selected update URL: " + updateURL);
 
             // Check the first 56 bytes for the version
-            if (shouldProxy) {
+            // PartialEepGet works with clearnet but not with SSL
+            if (!isSSL) {
                 _isPartial = true;
                 _baos.reset();
                 try {
@@ -160,6 +212,8 @@ class UpdateRunner extends I2PAppThread implements UpdateTask, EepGet.StatusList
                 if (shouldProxy)
                     // 40 retries!!
                     _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
+                else if (isSSL)
+                    _get = new SSLEepGet(_context, _updateFile, updateURL);
                 else
                     _get = new EepGet(_context, 1, _updateFile, updateURL, false);
                 _get.addStatusListener(UpdateRunner.this);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java
index 85cfd6066ed1cf352b3f457239e68dd9e6e5a1e4..5146d7b0d943ade43c0c55cde55da10068d58680 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHelper.java
@@ -14,8 +14,6 @@ public class ConfigTunnelsHelper extends HelperBase {
     private static final String HOPS = ngettext("1 hop", "{0} hops");
     private static final String TUNNELS = ngettext("1 tunnel", "{0} tunnels");
 
-    static final String PROP_ADVANCED = "routerconsole.advanced";
-
     public String getForm() {
         StringBuilder buf = new StringBuilder(1024);
         // HTML: <input> cannot be inside a <table>
@@ -69,7 +67,7 @@ public class ConfigTunnelsHelper extends HelperBase {
 
     private void renderForm(StringBuilder buf, int index, String prefix, String name, TunnelPoolSettings in, TunnelPoolSettings out) {
 
-        boolean advanced = _context.getBooleanProperty(PROP_ADVANCED);
+        boolean advanced = isAdvanced();
 
         buf.append("<tr><th colspan=\"3\"><a name=\"").append(prefix).append("\">");
         buf.append(name).append("</a></th></tr>\n");
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java b/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java
index 2bee07833bdc12b18c893aa782dd3aa6a2b3c565..ff0dc80592cf53b72661a16bf868901a2d15d8f5 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java
@@ -11,6 +11,8 @@ public abstract class HelperBase {
     protected RouterContext _context;
     protected Writer _out;
 
+    static final String PROP_ADVANCED = "routerconsole.advanced";
+
     /**
      * Configure this bean to query a particular router context
      *
@@ -25,6 +27,11 @@ public abstract class HelperBase {
         }
     }
 
+    /** @since 0.9.9 */
+    public boolean isAdvanced() {
+        return _context.getBooleanProperty(PROP_ADVANCED);
+    }
+
     /** might be useful in the jsp's */
     //public RouterContext getContext() { return _context; }
 
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java
index 6be7466a1ebe5d749d85d476ba919e924f396ab1..073ddad1b9ff49258200ca0a900c43e5e6afedca 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java
@@ -39,7 +39,7 @@ public class TunnelRenderer {
         Map<Hash, TunnelPool> clientInboundPools = _context.tunnelManager().getInboundClientPools();
         Map<Hash, TunnelPool> clientOutboundPools = _context.tunnelManager().getOutboundClientPools();
         destinations = new ArrayList(clientInboundPools.keySet());
-        boolean debug = _context.getBooleanProperty(ConfigTunnelsHelper.PROP_ADVANCED);
+        boolean debug = _context.getBooleanProperty(HelperBase.PROP_ADVANCED);
         for (int i = 0; i < destinations.size(); i++) {
             Hash client = destinations.get(i);
             boolean isLocal = _context.clientManager().isLocal(client);
diff --git a/apps/routerconsole/jsp/configupdate.jsp b/apps/routerconsole/jsp/configupdate.jsp
index 2d2e54d25782046cf2e580f6e137bc8727a277c4..2fd5e653607aa6e33d09353babd5ca56ac889676 100644
--- a/apps/routerconsole/jsp/configupdate.jsp
+++ b/apps/routerconsole/jsp/configupdate.jsp
@@ -49,17 +49,21 @@
           <td><jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /></td></tr>
     <% }   // if canInstall %>
         <tr><td class="mediumtags" align="right"><b><%=intl._("Update through the eepProxy?")%></b></td>
-          <td><jsp:getProperty name="updatehelper" property="updateThroughProxy" /></td>
-        </tr><tr><td class="mediumtags" align="right"><b><%=intl._("eepProxy host")%>:</b></td>
+          <td><jsp:getProperty name="updatehelper" property="updateThroughProxy" /></td></tr>
+      <% if (updatehelper.isAdvanced()) { %>
+        <tr><td class="mediumtags" align="right"><b><%=intl._("eepProxy host")%>:</b></td>
           <td><input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /></td>
         </tr><tr><td class="mediumtags" align="right"><b><%=intl._("eepProxy port")%>:</b></td>
           <td><input type="text" size="10" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /></td></tr>
+      <% }   // if isAdvanced %>
     <% if (updatehelper.canInstall()) { %>
+      <% if (updatehelper.isAdvanced()) { %>
         <tr><td class="mediumtags" align="right"><b><%=intl._("Update URLs")%>:</b></td>
           <td><textarea cols="60" rows="6" name="updateURL" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea></td>
         </tr><tr><td class="mediumtags" align="right"><b><%=intl._("Trusted keys")%>:</b></td>
-          <td><textarea cols="60" rows="6" name="trustedKeys" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></td>
-        </tr><tr><td id="unsignedbuild" class="mediumtags" align="right"><b><%=intl._("Update with unsigned development builds?")%></b></td>
+          <td><textarea cols="60" rows="6" name="trustedKeys" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></td></tr>
+      <% }   // if isAdvanced %>
+        <tr><td id="unsignedbuild" class="mediumtags" align="right"><b><%=intl._("Update with unsigned development builds?")%></b></td>
           <td><jsp:getProperty name="updatehelper" property="updateUnsigned" /></td>
         </tr><tr><td class="mediumtags" align="right"><b><%=intl._("Unsigned Build URL")%>:</b></td>
           <td><input type="text" size="60" name="zipURL" value="<jsp:getProperty name="updatehelper" property="zipURL" />"></td></tr>
diff --git a/core/java/src/net/i2p/util/PartialEepGet.java b/core/java/src/net/i2p/util/PartialEepGet.java
index d557137307865ad9c33d5fbd367189bb93c4ed79..7fe625b66c2e9b9c0ee221031d4a90f388186b88 100644
--- a/core/java/src/net/i2p/util/PartialEepGet.java
+++ b/core/java/src/net/i2p/util/PartialEepGet.java
@@ -11,6 +11,9 @@ import net.i2p.I2PAppContext;
  * Fetch exactly the first 'size' bytes into a stream
  * Anything less or more will throw an IOException
  * No retries, no min and max size options, no timeout option
+ * If the server does not return a Content-Length header of the correct size,
+ * the fetch will fail.
+ *
  * Useful for checking .sud versions
  *
  * @since 0.7.12
@@ -19,7 +22,13 @@ import net.i2p.I2PAppContext;
 public class PartialEepGet extends EepGet {
     long _fetchSize;
 
-    /** @param size fetch exactly this many bytes */
+    /**
+     * Instantiate an EepGet that will fetch exactly size bytes when fetch() is called.
+     *
+     * @param proxyHost use null or "" for no proxy
+     * @param proxyPort use 0 for no proxy
+     * @param size fetch exactly this many bytes
+     */
     public PartialEepGet(I2PAppContext ctx, String proxyHost, int proxyPort,
                          OutputStream outputStream,  String url, long size) {
         // we're using this constructor:
@@ -88,7 +97,8 @@ public class PartialEepGet extends EepGet {
     }
     
     private static void usage() {
-        System.err.println("PartialEepGet [-p 127.0.0.1:4444] [-l #bytes] url");
+        System.err.println("PartialEepGet [-p 127.0.0.1:4444] [-l #bytes] url\n" +
+                           "              (use -p :0 for no proxy)");
     }
     
     @Override
diff --git a/core/java/src/net/i2p/util/SSLEepGet.java b/core/java/src/net/i2p/util/SSLEepGet.java
index e7a060230fc78e3a09c6ef763c4f87f0af03850e..3866f3d1ab42124a16326cd13128155d264f3137 100644
--- a/core/java/src/net/i2p/util/SSLEepGet.java
+++ b/core/java/src/net/i2p/util/SSLEepGet.java
@@ -103,9 +103,39 @@ public class SSLEepGet extends EepGet {
      *  @since 0.8.2
      */
     public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url, SSLState state) {
+        this(ctx, null, outputStream, url, null);
+    }
+
+    /**
+     *  A new SSLEepGet with a new SSLState
+     *  @since 0.9.9
+     */
+    public SSLEepGet(I2PAppContext ctx, String outputFile, String url) {
+        this(ctx, outputFile, url, null);
+    }
+
+    /**
+     *  @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null.
+     *               This makes repeated fetches from the same host MUCH faster,
+     *               and prevents repeated key store loads even for different hosts.
+     *  @since 0.9.9
+     */
+    public SSLEepGet(I2PAppContext ctx, String outputFile, String url, SSLState state) {
+        this(ctx, outputFile, null, url, null);
+    }
+
+    /**
+     *  outputFile, outputStream: One null, one non-null
+     *
+     *  @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null.
+     *               This makes repeated fetches from the same host MUCH faster,
+     *               and prevents repeated key store loads even for different hosts.
+     *  @since 0.9.9
+     */
+    private SSLEepGet(I2PAppContext ctx, String outputFile, OutputStream outputStream, String url, SSLState state) {
         // we're using this constructor:
         // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
-        super(ctx, false, null, -1, 0, -1, -1, null, outputStream, url, true, null, null);
+        super(ctx, false, null, -1, 0, -1, -1, outputFile, outputStream, url, true, null, null);
         if (state != null && state.context != null)
             _sslContext = state.context;
         else