cb99f0b11bdeee5dfa76064d38b6410da0f4a709
max
Thu Sep 10 00:55:21 2026 -0700
Centralize CGI Content-Type printing in one cgiPrintContentType() helper
Around 90 places across the tree hand-rolled the CGI response header, each
with its own spelling: "Content-Type:" or "Content-type:", \n or \r\n, and
the terminating blank line written as part of the same string, as a separate
puts("\n") (which emits two newlines, so a stray blank line led the body) or
as printf("\r\n\r\n") (two blank lines). A handful forgot the terminator
entirely and relied on a following header to supply it.
cgiPrintContentType() in lib/cheapcgi.c now writes the Content-Type line and
the blank line that ends the header. Header lines are not ordered, so the
callers that also send Status, Set-Cookie, Content-Disposition, Content-Length
or X-Sendfile write those first and call this last to close the header; that
keeps it to a single helper rather than a print-the-line / end-the-header pair
that a caller can half-use. cart.c's existing httpHeaders list already worked
this way.
Only the CGI response path is touched. The dyStringPrintf("Content-type: ...")
calls that build outgoing HTTP *requests* (genomeSpace, oauthLogin, eapMetaSync,
edwWebAuthLogin, ga4ghToBed) are unrelated and left alone.
Also fills out the apiKey error message in botDelay.c to say where to create a
key and that keys are server-specific.
No behavior change on the wire beyond dropping those stray blank lines and
adding the missing newline after Retry-After.
diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 09bf1f9faa7..9c1893a16d8 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -6137,63 +6137,66 @@
hvGfxClose(&hvg);
if (measureTiming)
measureTime("Time completed writing trash hgt png image file");
#ifdef SUPPORT_CONTENT_TYPE
char *type = cartUsualString(cart, "hgt.contentType", "html");
if(sameString(type, "jsonp"))
{
struct jsonElement *json = newJsonObject(newHash(8));
char *jsonp = cartString(cart, "jsonp");
// This path only ever emits a wrapped response, so there is no bare form to
// fall back to: reject an invalid callback name outright.
if (!isValidJsonpCallback(jsonp))
errAbort("invalid callback");
- printf("Content-Type: application/json\n\n");
+ cgiPrintContentType("application/json");
errAbortSetDoContentType(FALSE);
jsonObjectAdd(json, "track", newJsonString(cartString(cart, "hgt.trackNameFilter")));
jsonObjectAdd(json, "height", newJsonNumber(pixHeight));
jsonObjectAdd(json, "width", newJsonNumber(pixWidth));
jsonObjectAdd(json, "img", newJsonString(pngTn.forHtml));
printf("%s(", jsonp);
hPrintEnable();
jsonPrint((struct jsonElement *) json, NULL, 0);
hPrintDisable();
printf(")\n");
return;
}
else if(sameString(type, "png") || sameString(type, "pdf") || sameString(type, "eps"))
{
// following code bypasses html and return png's directly - see redmine 4888
// NB: Pretty sure the pdf and eps options here are never invoked. I don't see any
// calls that would activate eps output, and pdf is locked behind an unused ifdef
char *file;
if(sameString(type, "pdf"))
{
- printf("Content-Disposition: filename=hgTracks.pdf\nContent-Type: application/pdf\n\n");
+ printf("Content-Disposition: filename=hgTracks.pdf\n");
+ cgiPrintContentType("application/pdf");
file = convertEpsToPdf(psOutput);
unlink(psOutput);
}
else if(sameString(type, "eps"))
{
- printf("Content-Disposition: filename=hgTracks.eps\nContent-Type: application/eps\n\n");
+ printf("Content-Disposition: filename=hgTracks.eps\n");
+ cgiPrintContentType("application/eps");
file = psOutput;
}
else
{
- printf("Content-Disposition: filename=hgTracks.png\nContent-Type: image/png\n\n");
+ printf("Content-Disposition: filename=hgTracks.png\n");
+ cgiPrintContentType("image/png");
file = pngTn.forCgi;
}
char buf[4096];
FILE *fd = fopen(file, "r");
if(fd == NULL)
// fail some other way (e.g. HTTP 500)?
errAbort("Couldn't open png for reading");
while (TRUE)
{
size_t n = fread(buf, 1, sizeof(buf), fd);
if(n)
fwrite(buf, 1, n, stdout);
else
break;
@@ -9306,31 +9309,31 @@
if (hTrackOnChrom(track->tdb, w->chromName))
hideIt = FALSE;
}
if (hideIt)
{
track->limitedVis = tvHide;
track->limitedVisSet = TRUE;
}
}
if (cartUsualBoolean(cart, "dumpTracks", FALSE))
{
struct dyString *dy = dyStringNew(1024);
logTrackList(dy, trackList);
- printf("Content-type: text/html\n\n");
+ cgiPrintContentType("text/html");
printf("%s\n", dy->string);
exit(0);
}
if (sameString(cfgOptionDefault("trackLog", "off"), "on"))
logTrackVisibilities(cartSessionId(cart), trackList, position);
struct track *visibleTracks = getVisibleTracks(trackList);
if (visibleTracks)
{
// add these tracks to the special 'visible' group
struct group *group, *visibleGroup = NULL;
for (group = groupList; group != NULL; group = group->next)
{
if (sameString(group->name, "visible"))