diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
index 9d937019121154d45277beace5b3c017e7da7682..174704ba6774f976ec51b3b26d841c7c0fd60144 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
@@ -1,8 +1,11 @@
 package net.i2p.router.web;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.StringTokenizer;
 
@@ -15,10 +18,12 @@ import net.i2p.router.RouterVersion;
 import net.i2p.util.EepGet;
 import net.i2p.util.I2PAppThread;
 import net.i2p.util.Log;
+import net.i2p.util.PartialEepGet;
+import net.i2p.util.VersionComparator;
 
 /**
- * <p>Handles the request to update the router by firing off an
- * {@link net.i2p.util.EepGet} call to download the latest signed update file
+ * <p>Handles the request to update the router by firing one or more
+ * {@link net.i2p.util.EepGet} calls to download the latest signed update file
  * and displaying the status to anyone who asks.
  * </p>
  * <p>After the download completes the signed update file is verified with
@@ -125,6 +130,11 @@ public class UpdateHandler {
         protected boolean done;
         protected EepGet _get;
         protected final DecimalFormat _pct = new DecimalFormat("0.0%");
+        /** tells the listeners what mode we are in */
+        private boolean _isPartial;
+        /** set by the listeners on completion */
+        private boolean _isNewer;
+        private ByteArrayOutputStream _baos;
 
         public UpdateRunner() { 
             _isRunning = false;
@@ -141,39 +151,88 @@ public class UpdateHandler {
             System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
             _isRunning = false;
         }
+
+        /**
+         *  Loop through the entire list of update URLs.
+         *  For each one, first get the version from the first 56 bytes and see if
+         *  it is newer than what we are running now.
+         *  If it is, get the whole thing.
+         */
         protected void update() {
-            updateStatus("<b>" + _("Updating") + "</b>");
             // TODO:
             // Do a PartialEepGet on the selected URL, check for version we expect,
             // and loop if it isn't what we want.
             // This will allow us to do a release without waiting for the last host to install the update.
             // Alternative: In bytesTransferred(), Check the data in the output file after
             // we've received at least 56 bytes. Need a cancel() method in EepGet ?
-            String updateURL = selectUpdateURL();
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Selected update URL: " + updateURL);
+
             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 = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
-            try {
-                if (shouldProxy)
-                    // 40 retries!!
-                    _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
-                else
-                    _get = new EepGet(_context, 1, _updateFile, updateURL, false);
-                _get.addStatusListener(UpdateRunner.this);
-                _get.fetch();
-            } catch (Throwable t) {
-                _log.error("Error updating", t);
+
+            List<String> urls = getUpdateURLs();
+            if (urls.isEmpty()) {
+                // not likely, don't bother translating
+                updateStatus("<b>Update source list is empty, cannot download update</b>");
+                _log.log(Log.CRIT, "Update source list is empty - cannot download update");
+                return;
+            }
+
+            if (shouldProxy)
+                _baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES);
+            for (String updateURL : urls) {
+                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) {
+                    _isPartial = true;
+                    _isNewer = false;
+                    _baos.reset();
+                    try {
+                        // no retries
+                        _get = new PartialEepGet(_context, proxyHost, proxyPort, _baos, updateURL, TrustedUpdate.HEADER_BYTES);
+                        _get.addStatusListener(UpdateRunner.this);
+                        _get.fetch();
+                    } catch (Throwable t) {
+                        _isNewer = false;
+                    }
+                    _isPartial = false;
+                    if (!_isNewer)
+                        continue;
+                }
+
+                // Now get the whole thing
+                try {
+                    if (shouldProxy)
+                        // 40 retries!!
+                        _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
+                    else
+                        _get = new EepGet(_context, 1, _updateFile, updateURL, false);
+                    _get.addStatusListener(UpdateRunner.this);
+                    _get.fetch();
+                } catch (Throwable t) {
+                    _log.error("Error updating", t);
+                }
+                if (this.done)
+                    break;
             }
         }
         
+        // EepGet Listeners below.
+        // We use the same for both the partial and the full EepGet,
+        // with a couple of adjustments depending on which mode.
+
         public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
+            _isNewer = false;
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Attempt failed on " + url, cause);
             // ignored
         }
         public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
+            if (_isPartial)
+                return;
             StringBuilder buf = new StringBuilder(64);
             buf.append("<b>").append(_("Updating")).append("</b> ");
             double pct = ((double)alreadyTransferred + (double)currentWrite) /
@@ -186,6 +245,19 @@ public class UpdateHandler {
             updateStatus(buf.toString());
         }
         public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
+            if (_isPartial) {
+                // Compare version with what we have now
+                String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray()));
+                boolean newer = (new VersionComparator()).compare(newVersion, RouterVersion.VERSION) > 0;
+                if (!newer) {
+                    updateStatus("<b>" + _("No new version found at {0}", linkify(url)) + "</b>");
+                    if (_log.shouldLog(Log.WARN))
+                        _log.warn("Found old version \"" + newVersion + "\" at " + url);
+                }
+                _isNewer = newer;
+                return;
+            }
+            // Process the .sud/.su2 file
             updateStatus("<b>" + _("Update downloaded") + "</b>");
             TrustedUpdate up = new TrustedUpdate(_context);
             File f = new File(_updateFile);
@@ -223,15 +295,16 @@ public class UpdateHandler {
                 }
             } else {
                 _log.log(Log.CRIT, err + " from " + url);
-                updateStatus("<b>" + err + ' ' + _("from {0}", url) + " </b>");
+                updateStatus("<b>" + err + ' ' + _("from {0}", linkify(url)) + " </b>");
             }
         }
         public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
+            _isNewer = false;
             // don't display bytesTransferred as it is meaningless
-            _log.log(Log.CRIT, "Update from " + url + " did not download completely (" +
+            _log.error("Update from " + url + " did not download completely (" +
                                bytesRemaining + " remaining after " + currentAttempt + " tries)");
 
-            updateStatus("<b>" + _("Transfer failed") + "</b>");
+            updateStatus("<b>" + _("Transfer failed from {0}", linkify(url)) + "</b>");
         }
         public void headerReceived(String url, int attemptNum, String key, String val) {}
         public void attempting(String url) {}
@@ -242,27 +315,24 @@ public class UpdateHandler {
         _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
     }
 
-    private String selectUpdateURL() {
+    private List<String> getUpdateURLs() {
         String URLs = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
         StringTokenizer tok = new StringTokenizer(URLs, " ,\r\n");
-        List URLList = new ArrayList();
+        List<String> URLList = new ArrayList();
         while (tok.hasMoreTokens())
             URLList.add(tok.nextToken().trim());
-        int size = URLList.size();
-        //_log.log(Log.DEBUG, "Picking update source from " + size + " candidates.");
-        if (size <= 0) {
-            _log.log(Log.CRIT, "Update source list is empty - cannot download update");
-            return null;
-        }
-        int index = I2PAppContext.getGlobalContext().random().nextInt(size);
-        _log.log(Log.DEBUG, "Picked update source " + index + ".");
-        return (String) URLList.get(index);
+        Collections.shuffle(URLList, _context.random());
+        return URLList;
     }
     
     protected void updateStatus(String s) {
         _status = s;
     }
 
+    protected static String linkify(String url) {
+        return "<a target=\"_blank\" href=\"" + url + "\"/>" + url + "</a>";
+    }
+
     /** translate a string */
     protected String _(String s) {
         return Messages.getString(s, _context);