89dfb5e8a0602139b83b4d6d1f981710e1a865b2
max
  Mon Sep 21 05:53:28 2026 -0700
geoMirror: notify peers over https, and drop the hardcoded genome-euro from the invalid-apiKey message, refs #38323

geoMirrorNotifyOtherNodes built its URL as http://.  genome-euro and
genome-asia redirect http to https, and netSlurpUrl does not follow
redirects, so the api key sync request never reached the peer CGI --
and the response is discarded, so nothing said so.  Confirmed with
lib/tests/fetchUrlTest, which uses the same call: http gives back a
bare 302, https gives back the cartJson reply.  https also keeps the
key off the wire in the clear on its way to Germany and Japan.

The geo mirror menu links now use https too.

botDelay's "Invalid apiKey" message pointed at genome-euro no matter
which server the user was on, which was wrong everywhere except euro.
Both branches now build the link from the current server.

diff --git src/hg/lib/geoMirror.c src/hg/lib/geoMirror.c
index 05c8e660463..2da6a2eb62f 100644
--- src/hg/lib/geoMirror.c
+++ src/hg/lib/geoMirror.c
@@ -345,31 +345,33 @@
 }
 
 void geoMirrorNotifyOtherNodes(char *cgiName, struct slPair *cgiVars)
 /* Best-effort: fire cgiVars (name=value) as a GET request at cgiName on every other geo mirror
  * node (per geoMirrorOtherNodes()).  No-ops if geo mirroring is off or this is the only node.
  * Adds no authentication of its own -- callers must put their own signed proof into cgiVars,
  * since the receiving CGI runs with no session/cart tying the request to a user.  A slow or
  * unreachable peer is logged with warn() and skipped; the caller's own action must already be
  * complete locally before this is called, since a peer being down must never fail the local
  * action. */
 {
 struct slPair *nodes = geoMirrorOtherNodes();
 struct slPair *node;
 for (node = nodes; node != NULL; node = node->next)
     {
-    struct dyString *url = dyStringCreate("http://%s/cgi-bin/%s?", (char *)node->val, cgiName);
+    // https, not http: the mirrors redirect http to https and netSlurpUrl does not follow
+    // redirects, so an http request never reaches the CGI at all
+    struct dyString *url = dyStringCreate("https://%s/cgi-bin/%s?", (char *)node->val, cgiName);
     struct slPair *var;
     for (var = cgiVars; var != NULL; var = var->next)
         dyStringPrintf(url, "%s%s=%s", (var == cgiVars) ? "" : "&", var->name,
                        cgiEncodeFull((char *)var->val));
     struct errCatch *errCatch = errCatchNew();
     if (errCatchStart(errCatch))
         {
         struct dyString *response = netSlurpUrl(url->string);
         dyStringFree(&response);
         }
     errCatchEnd(errCatch);
     if (errCatch->gotError)
         warn("geoMirrorNotifyOtherNodes: failed to reach %s (%s): %s",
              node->name, (char *)node->val, errCatch->message->string);
     errCatchFree(&errCatch);
@@ -400,27 +402,27 @@
     struct sqlResult *sr = sqlGetResult(conn, query);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	char *node = row[0];
 	char *domain = row[1];
 	char *shortLabel = row[2];
         dyStringPrintf(dy, "<li id=\"server%s\"", node);
         if (sameString(node, myNode))
             dyStringAppend(dy, " class=\"noHighlight\"");
         dyStringAppend(dy, ">\n");
         dyStringAppend(dy, "<img alt=\"X\" width=\"16\" height=\"16\" style=\"float:left;");
         if (!sameString(node, myNode))
             dyStringAppend(dy, "visibility:hidden;");
         dyStringAppend(dy, "\" src=\"../images/greenChecksmCtr.png\">\n");
         if (!sameString(node, myNode))
-            dyStringPrintf(dy, "<a href=\"http://%s/cgi-bin/hgGateway?redirect=manual\">", domain);
+            dyStringPrintf(dy, "<a href=\"https://%s/cgi-bin/hgGateway?redirect=manual\">", domain);
         dyStringPrintf(dy, "%s", shortLabel);
         if (!sameString(node, myNode))
             dyStringAppend(dy, "</a>");
         dyStringAppend(dy, "</li>\n");
 
 	}
     sqlFreeResult(&sr);
     hDisconnectCentral(&conn);
     }
 return dyStringCannibalize(&dy);
 }