aeee0cca244f1a656d8f52ac1bec7922e136b9f0
max
  Fri Sep 18 07:35:05 2026 -0700
hgSession: a server that is not the node hg.conf names is still itself

#Preview2 week - bugs introduced now will need a build patch to fix
On hgwdev-max the new Sessions page called itself genome-test and listed
hgwdev-max among the servers to go and fetch sessions from, so the Server
column named the wrong machine and the page asked itself for its own list.

geoMirrorNodeList() was picking the gbNode row by browser.node, and a sandbox
inherits that setting from the shared hg.conf without being that node. It now
takes the row whose domain matches the host the visitor typed, and falls back
to browser.node only when the host is in no row at all - hgwdev.gi.ucsc.edu
rather than genome-test.gi.ucsc.edu, a bare IP, the command line.

refs #38157

diff --git src/hg/lib/geoMirror.c src/hg/lib/geoMirror.c
index 595a01b75ea..996dc803799 100644
--- src/hg/lib/geoMirror.c
+++ src/hg/lib/geoMirror.c
@@ -220,53 +220,120 @@
 
     if (
 	(ip6AddrCmpBits(&ipStart, &ip) <= 0)
 	&&
 	(ip6AddrCmpBits(&ipEnd  , &ip) >= 0)
        )
         {
         safef(response, sizeof response, "%s", row[2]);
         }
     }
 sqlFreeResult(&sr);
 
 return cloneString(response);
 }
 
+struct geoNode
+/* One row of hgcentral gbNode. */
+    {
+    struct geoNode *next;
+    char *node;         /* the browser.node number, as text */
+    char *domain;       /* e.g. genome-euro.ucsc.edu */
+    char *shortLabel;   /* e.g. European Server */
+    };
+
+static char *geoMirrorThisHost()
+/* The host name this request came in under, without any port, or NULL when there is none (the
+ * command line).  Not freed: it comes from the environment. */
+{
+static char host[256];
+char *httpHost = getenv("HTTP_HOST");
+if (isEmpty(httpHost))
+    return NULL;
+safecpy(host, sizeof host, httpHost);
+char *colon = strchr(host, ':');            // HTTP_HOST carries the port when it is not 80/443
+if (colon != NULL)
+    *colon = '\0';
+return host;
+}
+
+static struct geoNode *geoMirrorSelf(struct geoNode *nodeList)
+/* Which of the gbNode rows is the server answering this request?  The host the visitor typed
+ * decides it whenever that host is one of the nodes, so a machine serving a node other than the
+ * one browser.node names -- a sandbox, or two nodes behind one apache -- does not take itself for
+ * its own peer.  browser.node is the fallback, for a host that is in no gbNode row at all
+ * (hgwdev.gi.ucsc.edu rather than genome-test.gi.ucsc.edu, a bare IP, the command line). */
+{
+struct geoNode *node;
+char *myHost = geoMirrorThisHost();
+if (isNotEmpty(myHost))
+    for (node = nodeList; node != NULL; node = node->next)
+        if (sameWord(myHost, node->domain))
+            return node;
+char *myNode = geoMirrorNode();
+for (node = nodeList; node != NULL; node = node->next)
+    if (sameString(node->node, myNode))
+        return node;
+return NULL;
+}
+
+static void geoNodeFreeList(struct geoNode **pList)
+/* Free a list of geoNode. */
+{
+struct geoNode *node, *next;
+for (node = *pList; node != NULL; node = next)
+    {
+    next = node->next;
+    freeMem(node->node);
+    freeMem(node->domain);
+    freeMem(node->shortLabel);
+    freeMem(node);
+    }
+*pList = NULL;
+}
+
 static struct slPair *geoMirrorNodeList(boolean wantSelf)
 /* Return gbNode as pairs of name=shortLabel, val=domain, ordered by node: either every node but
  * this one (wantSelf FALSE) or only this one (wantSelf TRUE). */
 {
 if (!geoMirrorEnabled())
     return NULL;
-char *myNode = geoMirrorNode();
 char *geoSuffix = cfgOptionDefault("browser.geoSuffix","");
 char query[256];
 sqlSafef(query, sizeof query, "SELECT node, domain, shortLabel from gbNode%s order by node",
          geoSuffix);
 struct sqlConnection *conn = hConnectCentral();
 struct sqlResult *sr = sqlGetResult(conn, query);
-struct slPair *nodes = NULL;
+struct geoNode *nodeList = NULL, *node;
 char **row = NULL;
 while ((row = sqlNextRow(sr)) != NULL)
     {
-    if (sameString(row[0], myNode) != wantSelf)
-        continue;
-    slPairAdd(&nodes, row[2], cloneString(row[1]));
+    AllocVar(node);
+    node->node = cloneString(row[0]);
+    node->domain = cloneString(row[1]);
+    node->shortLabel = cloneString(row[2]);
+    slAddHead(&nodeList, node);
     }
 sqlFreeResult(&sr);
 hDisconnectCentral(&conn);
+slReverse(&nodeList);
+struct geoNode *self = geoMirrorSelf(nodeList);
+struct slPair *nodes = NULL;
+for (node = nodeList; node != NULL; node = node->next)
+    if ((node == self) == wantSelf)
+        slPairAdd(&nodes, node->shortLabel, cloneString(node->domain));
+geoNodeFreeList(&nodeList);
 slReverse(&nodes);
 return nodes;
 }
 
 struct slPair *geoMirrorThisNode()
 /* Return this node (browser.node) as a single pair of name=shortLabel, val=domain, or NULL when
  * geo mirroring is off or gbNode has no row for it.  slPairFreeValsAndList when done. */
 {
 return geoMirrorNodeList(TRUE);
 }
 
 struct slPair *geoMirrorOtherNodes()
 /* Return the other geo mirror nodes, as pairs of name=shortLabel, val=domain, ordered by node.
  * The node this CGI is running on (browser.node) is left out.  Returns NULL when geo mirroring
  * is off or this is the only node.  slPairFreeValsAndList when done. */