8c172e6c4b985af79bf1a1f47c9db92ce0578999 max Tue Sep 15 05:47:36 2026 -0700 errCatch: put errAbort's doContentType back the way it found it, refs #38353 hVaUserAbort() turns doContentType on so that a user error reaches the browser when the CGI has not pushed a warn handler of its own. Inside an errCatch that is pointless -- the abort is caught and never reaches the default handler -- and the flag stayed on afterwards, so a later genuine abort on a page that had already written its header would print a second Content-Type line into the body. Everywhere else in the tree the flag is something a caller turns back off. errCatchPushHandlers() now records the flag and errCatchEnd() restores it, which covers every caller rather than just the one that set it. errAbortGetDoContentType() is the reader. diff --git src/lib/errCatch.c src/lib/errCatch.c index 6caf8ea5b70..c343ba5f2b8 100644 --- src/lib/errCatch.c +++ src/lib/errCatch.c @@ -75,40 +75,46 @@ longjmp(errCatchStack->jmpBuf, -1); } static void errCatchWarnHandler(char *format, va_list args) /* Write an error to top of errCatchStack. */ { struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack; errCatchStack->gotWarning = TRUE; dyStringVaPrintf(errCatchStack->message, format, args); dyStringAppendC(errCatchStack->message, '\n'); } boolean errCatchPushHandlers(struct errCatch *errCatch) /* Push error handlers. Not usually called directly. */ { +/* An abort caught here never reaches the default handler, so anything the aborting code set + * up for that handler - hVaUserAbort turning doContentType on so a user error reaches the + * browser - is not wanted and, worse, would still be set for the next abort, which may be a + * real one on a page that has already written its header. */ +errCatch->savedDoContentType = errAbortGetDoContentType(); pushAbortHandler(errCatchAbortHandler); pushWarnHandler(errCatchWarnHandler); struct errCatch **pErrCatchStack = getStack(); slAddHead(pErrCatchStack, errCatch); return TRUE; } void errCatchEnd(struct errCatch *errCatch) /* Restore error handlers and pop self off of catching stack. */ { +errAbortSetDoContentType(errCatch->savedDoContentType); popWarnHandler(); popAbortHandler(); struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack; if (errCatch != errCatchStack) errAbort("Mismatch between errCatch and errCatchStack"); *pErrCatchStack = errCatch->next; } void errCatchReWarn(struct errCatch *errCatch) /* Re-warn any warnings that happened even though no abort happened * to make them visible. */ { if (errCatch->gotWarning && !errCatch->gotError) { warn("%s", errCatch->message->string);