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/lib/errAbort.c src/lib/errAbort.c
index 19e64660806..d63b2944691 100644
--- src/lib/errAbort.c
+++ src/lib/errAbort.c
@@ -15,30 +15,31 @@
* This file is copyright 2002 Jim Kent, but license is hereby
* granted for all use - public, private or commercial. */
// developer: this include is for an occasionally useful means of getting stack info without
// crashing
// however, it is not supported on cygwin. Conditionally compile this in when desired.
//#define BACKTRACE_EXISTS
#ifdef BACKTRACE_EXISTS
#include <execinfo.h>
#endif///def BACKTRACE_EXISTS
#include <pthread.h>
#include "common.h"
#include "hash.h"
#include "dystring.h"
#include "errAbort.h"
+#include "cheapcgi.h"
// errAbort can optionally print a Content-type line and copy errors to stdout, so
// error messages don't lead to a 500 error but are shown in the web browser
// directly.
static boolean doContentType = FALSE;
#define maxWarnHandlers 20
#define maxAbortHandlers 12
struct perThreadAbortVars
/* per thread variables for abort and warn */
{
boolean debugPushPopErr; // generate stack dump on push/pop error
boolean errAbortInProgress; /* Flag to indicate that an error abort is in progress.
* Needed so that a warn handler can tell if it's really
* being called because of a warning or an error. */
@@ -46,31 +47,31 @@
int warnIx;
AbortHandler abortArray[maxAbortHandlers];
int abortIx;
};
static struct perThreadAbortVars *getThreadVars(); // forward declaration
static void defaultVaWarn(char *format, va_list args)
/* Default error message handler. */
{
if (format == NULL)
return;
if (doContentType)
{
- puts("Content-type: text/html\n");
+ cgiPrintContentType("text/html");
puts("Error: ");
// Need to destroy < and > in format AND args, to make XSS impossible.
va_list args_copy;
// first output message to stderr, as before
va_copy(args_copy, args); // vfprintf() & co cannot be called twice in a row without a va_copy
vfprintf(stderr, format, args);
va_end(args_copy);
va_copy(args_copy, args);
int needed = vsnprintf(NULL, 0, format, args_copy); // get size of buffer
va_end(args_copy);
if (needed < 0)
{