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/inc/cart.h src/hg/inc/cart.h index 4400a6c77eb..72ff27cc422 100644 --- src/hg/inc/cart.h +++ src/hg/inc/cart.h @@ -12,33 +12,30 @@ #include "jksql.h" #include "errAbort.h" #include "dystring.h" #include "linefile.h" #include "trackDb.h" #define namedSessionDbTableConfVariable "namedSessionDbName" #define defaultNamedSessionDb "namedSessionDb" // If cgi set as CART_VAR_EMPTY, then removed from cart // If If cgi created new and oldVars are stored, then will be CART_VAR_EMPTY in old vars #define CART_VAR_EMPTY "[]" #define IS_CART_VAR_EMPTY(var) ((var) == NULL || sameString(var,CART_VAR_EMPTY)) -// A list of headers each CGI can use to control their own HTTP headers -extern struct slPair *httpHeaders; - typedef struct sqlConnection *(*DbConnector)(); /* funtion type used to get a connection to database */ typedef void (*DbDisconnect)(struct sqlConnection **pConn); /* function type used to cleanup a connection from database */ struct cart /* A cart of settings that persist. */ { struct cart *next; /* Next in list. */ char *userId; /* User ID in database. */ char *sessionId; /* Session ID in database. */ struct hash *hash; /* String valued hash. */ struct hash *exclude; /* Null valued hash of variables not to save. */