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/encode3/encodeDataWarehouse/edwScriptSubmitStatus/edwScriptSubmitStatus.c src/hg/encode3/encodeDataWarehouse/edwScriptSubmitStatus/edwScriptSubmitStatus.c index 79840497cd7..34e985c8c4a 100644 --- src/hg/encode3/encodeDataWarehouse/edwScriptSubmitStatus/edwScriptSubmitStatus.c +++ src/hg/encode3/encodeDataWarehouse/edwScriptSubmitStatus/edwScriptSubmitStatus.c @@ -1,129 +1,128 @@ /* edwScriptSubmitStatus - Programatically check status of submission.. */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "linefile.h" #include "hash.h" #include "cheapcgi.h" #include "htmshell.h" #include "dystring.h" #include "errAbort.h" #include "jksql.h" #include "encodeDataWarehouse.h" #include "edwLib.h" void usage() /* Explain usage and exit. */ { errAbort( "edwScriptSubmitStatus - Programatically check status of submission.\n" "This program is meant to be called as a CGI from a web server using HTTPS."); } void addErrFile(struct dyString *dy, int errCount, char *fileName, char *err) /* Add file and error message to JSON in dy */ { if (errCount > 0) dyStringPrintf(dy, ",\n"); dyStringPrintf(dy, " "); dyStringPrintf(dy, "{\"file\": \"%s\", \"error\": \"%s\"}", fileName, err); } void edwScriptSubmitStatus() /* edwScriptSubmitStatus - Programatically check status of submission.. */ { /* Pause a second - prevent inadvertent harsh denial of service from scripts. */ sleep(2); edwScriptRegistryFromCgi(); /* Get submission from url. */ struct sqlConnection *conn = edwConnect(); char query[512]; char *url = cgiString("url"); struct edwSubmit *sub = edwMostRecentSubmission(conn, url); char *status = NULL; if (sub == NULL) { int posInQueue = edwSubmitPositionInQueue(conn, url, NULL); if (posInQueue == -1) errAbort("%s has not been submitted", url); else status = "pending"; } else { time_t endUploadTime = sub->endUploadTime; if (!isEmpty(sub->errorMessage)) { status = "error"; } else if (endUploadTime == 0) { status = "uploading"; } else { sqlSafef(query, sizeof(query), "select count(*) from edwFile where submitId=%u and errorMessage != ''", sub->id); int errCount = sqlQuickNum(conn, query); int newValid = edwSubmitCountNewValid(sub, conn); if (newValid + errCount < sub->newFiles) status = "validating"; else if (errCount > 0) status = "error"; else status = "success"; } } /* Construct JSON result */ struct dyString *dy = dyStringNew(0); dyStringPrintf(dy, "{\n"); dyStringPrintf(dy, " \"status\": \"%s\"", status == NULL ? "" : status); if (sameString(status, "error")) { dyStringPrintf(dy, ",\n"); dyStringPrintf(dy, " \"errors\": [\n"); int errCount = 0; if (!isEmpty(sub->errorMessage)) { addErrFile(dy, errCount, sub->url, sub->errorMessage); ++errCount; } sqlSafef(query, sizeof(query), "select * from edwFile where submitId=%u and errorMessage != ''", sub->id); struct edwFile *file, *fileList = edwFileLoadByQuery(conn, query); for (file = fileList; file != NULL; file = file->next) { addErrFile(dy, errCount, file->submitFileName, file->errorMessage); ++errCount; } dyStringPrintf(dy, "\n ]\n"); dyStringPrintf(dy, "}\n"); } else { dyStringPrintf(dy, "\n}\n"); } /* Write out HTTP response */ printf("Content-Length: %ld\r\n", dy->stringSize); -puts("Content-Type: application/json; charset=UTF-8\r"); -puts("\r"); +cgiPrintContentType("application/json; charset=UTF-8"); printf("%s", dy->string); } int main(int argc, char *argv[]) /* Process command line. */ { if (!cgiIsOnWeb()) usage(); cgiSpoof(&argc, argv); pushWarnHandler(htmlVaBadRequestAbort); pushAbortHandler(htmlVaBadRequestAbort); edwScriptSubmitStatus(); return 0; }