cf9f4cb7f55c7beb8ad5f11118656a60770a71a5
max
  Thu Sep 10 01:02:36 2026 -0700
Move the extra-HTTP-header list into cheapcgi, and write the header only once

Follow-on to the cgiPrintContentType() refactor.

cart.c owned the mechanism for adding headers ahead of the content type: a
global slPair list plus addHttpHeaders() to print it.  That put it in hg/lib,
out of reach of the CGIs and library code that do not use a cart, even though
nothing about it is cart-specific.  It now lives next to cgiPrintContentType()
in lib/cheapcgi.c, behind cgiAddHttpHeader(name, value) instead of a bare
global, and cgiPrintContentType() writes the queued headers itself.  The one
caller, hgTracks/mainMain.c, reads the same but no longer reaches into cart.h
for it.  cspWriteResponseHeader() stays in hg/lib where it belongs, since it
needs hg.conf; cartWriteHeaderAndCont() calls it directly now, the way the
other ten callers already do.

cgiPrintContentType() also writes at most once per process now.  A second
content type cannot reach the browser as a header - it lands in the page body
as text - so the later caller is always the mistaken one.  cart.c had a private
cartDidContentType flag for exactly this, covering only the flows that went
through the cart; the guard is now in the one function every flow shares, and
cartDidContentType is gone.  Its public equivalent, cgiDidContentType(), is
what cartWriteHeaderAndCont() checks so it does not write a second cookie.

Verified: make libs, make cgi and the lib test suite are clean, hgTracks still
emits Cache-Control: no-store, and hgTracks, hgc and hgTables each emit exactly
one Content-Type on both their html and their text paths.

diff --git src/hg/hgTracks/mainMain.c src/hg/hgTracks/mainMain.c
index e827e3191a6..1be6a35cc7a 100644
--- src/hg/hgTracks/mainMain.c
+++ src/hg/hgTracks/mainMain.c
@@ -65,31 +65,31 @@
 if (cgiVarExists("hgt.redirectTool"))
     {
     // user has selected one of the tools in View > In external tools: Do not plot, just redirect.
     cgiPrintContentType("text/html");
     errAbortSetDoContentType(FALSE);
     cart = cartForSession(hUserCookie(), NULL, NULL);
     extToolRedirect(cart, cgiString("hgt.redirectTool"));
     }
 else if (cfgOptionBooleanDefault("doMyVariants", FALSE) && cgiVarExists("myVarShareCmd"))
     {
     cart = cartForSession(hUserCookie(), NULL, NULL);
     myVariantsShareApiHandler(cgiString("myVarShareCmd"));
     }
 else
     {
-    httpHeaders = slPairNew("Cache-Control", "no-store");
+    cgiAddHttpHeader("Cache-Control", "no-store");
     cartHtmlShell("UCSC Genome Browser v"CGI_VERSION, doMiddle, hUserCookie(), excludeVars, oldVars);
     }
 
 // TODO: better place for this ?
 webIncludeResourceFile("font-awesome.min.css");
 
 if (measureTiming)
     measureTime("Time to write and close cart");
 if (measureTiming)
     {
     fprintf(stdout, "<span class='timing'>Overall total time: %ld millis<br /></span>\n",
             clock1000() - enteredMainTime);
     }
 cgiExitTime("hgTracks", enteredMainTime);