21805a85e551d9688edb34683f955c8a41710e80
jcasper
  Wed Mar 18 22:17:44 2026 -0700
Added a #defined-out version of cgiDecode that's much faster for large cart variables.
Came up once years ago but was never put into practice.  refs #37262

diff --git src/lib/cheapcgi.c src/lib/cheapcgi.c
index 288b1c4c02e..798a498a25b 100644
--- src/lib/cheapcgi.c
+++ src/lib/cheapcgi.c
@@ -6,30 +6,33 @@
 
 #include "common.h"
 #include "hash.h"
 #include "cheapcgi.h"
 #include "portable.h"
 #include "linefile.h"
 #include "errAbort.h"
 #include "filePath.h"
 #include "htmshell.h"
 #include "dystring.h"
 #ifndef GBROWSE
 #include "mime.h"
 #endif /* GBROWSE */
 #include <signal.h>
 
+// FAST_CGI_DECODE can be defined in cheapcgi.h to try a faster decode process that
+// also limits variable/value lengths and applies encoding to variable names
+
 //============ javascript inline-separation routines ===============
 
 // One of the main services that CSP (Content Security Policy) provides
 // is protecting from reflected and stored XSS attacks by disabling all inline javacript,
 // both in script tags, and in inline event handlers.  The separated javascript 
 // can be either added back to the end of the html page with a nonce or sha hashid,
 // or it can be saved to a temp file in trash and then included as a non-inline, off-page .js.
 
 struct dyString *jsInlineLines = NULL;
 
 void jsInlineInit()
 /* init if needed */
 {
 if (!jsInlineLines) 
     {
@@ -743,35 +746,49 @@
 namePt = str;
 while (isNotEmpty(namePt))
     {
     dataPt = strchr(namePt, '=');
     if (dataPt == NULL)
 	errAbort("Mangled Cookie input string: no = in '%s' (offset %d in complete cookie string: '%s')",
 		 namePt, (int)(namePt - str), getenv("HTTP_COOKIE"));
     *dataPt++ = 0;
     nextNamePt = strchr(dataPt, ';');
     if (nextNamePt != NULL)
 	{
          *nextNamePt++ = 0;
 	 if (*nextNamePt == ' ')
 	     nextNamePt++;
 	}
+#ifndef FAST_CGI_DECODE
     cgiDecode(dataPt,dataPt,strlen(dataPt));
     AllocVar(el);
     el->val = dataPt;
     slAddHead(&list, el);
     hashAddSaveName(hash, namePt, el, &el->name);
+#else
+    int dataSize = strlen(dataPt);
+    int nameSize = strlen(namePt);
+    if ((dataSize <= CGI_VAR_SIZE_LIMIT) && (nameSize < CGI_VAR_NAME_LIMIT))
+    {
+        cgiDecode(namePt,namePt,nameSize);
+        cgiDecode(dataPt,dataPt,dataSize);
+        AllocVar(el);
+        el->val = dataPt;
+        slAddHead(&list, el);
+        hashAddSaveName(hash, namePt, el, &el->name);
+    }
+#endif // FAST_CGI_DECODE
     namePt = nextNamePt;
     }
 
 haveCookiesHash = TRUE;
 
 slReverse(&list);
 *retList = list;
 *retHash = hash;
 }
 
 char *findCookieData(char *varName)
 /* Get the string associated with varName from the cookie string. */
 {
 struct hashEl *hel;
 char *firstResult;
@@ -870,53 +887,92 @@
     next = el->next;
     cgiDictionaryFree(&el);
     }
 *pList = NULL;
 }
 
 boolean cgiParseNext(char **pInput, char **retVar, char **retVal)
 /* Parse out next var/val in a var=val&var=val... cgi formatted string 
  * This will insert zeroes and other things into string. 
  * Usage:
  *     char *pt = cgiStringStart;
  *     char *var, *val
  *     while (cgiParseNext(&pt, &var, &val))
  *          printf("%s\t%s\n", var, val); */
 {
+#ifndef FAST_CGI_DECODE
 char *var = *pInput;
 if (var == NULL || var[0] == 0)
     return FALSE;
 char *val = strchr(var, '=');
 if (val == NULL)
     errAbort("Mangled CGI input string %s", var);
 *val++ = 0;
 char *end = strchr(val, '&');
 if (end == NULL)
     end = strchr(val, ';');  // For DAS
 if (end == NULL)
     {
     end = val + strlen(val);
     *pInput = NULL;
     }
 else
     {
     *pInput = end+1;
     *end = 0;
     }
 *retVar = var;
 *retVal = val;
 cgiDecode(val,val,end-val);
+#else
+char *val = NULL;
+char *var = NULL;
+int varLength = 0;
+int valLength = 0;
+do
+    {
+    var = *pInput;
+    if (var == NULL || var[0] == 0)
+    {
+        *retVar = *retVal = NULL;
+        return FALSE;
+    }
+    val = strchr(var, '=');
+    if (val == NULL)
+        errAbort("Mangled CGI input string %s", var);
+    *val++ = 0;
+    char *end = strchr(val, '&');
+    if (end == NULL)
+        end = strchr(val, ';');  // For DAS
+    if (end == NULL)
+        {
+        end = val + strlen(val);
+        *pInput = NULL;
+        }
+    else
+        {
+        *pInput = end+1;
+        *end = 0;
+        }
+    *retVar = var;
+    *retVal = val;
+    valLength = end-val;
+    } while ((varLength > CGI_VAR_NAME_LIMIT) || (valLength > CGI_VAR_SIZE_LIMIT));
+            // skip variables that are too big
+cgiDecode(var,var,valLength);
+cgiDecode(val,val,valLength);
+#endif // FAST_CGI_DECODE
 return TRUE;
 }
 
 void cgiSetMaxLogLen(int l)
 /* set the size of variable values that are dumped to stderr. Default is 0, which means no logging */
 {
 logCgiVarMaxLen = l;
 }
 
 void cgiParseInputAbort(char *input, struct hash **retHash,
         struct cgiVar **retList)
 /* Parse cgi-style input into a hash table and list.  This will alter
  * the input data.  The hash table will contain references back
  * into input, so please don't free input until you're done with
  * the hash. Prints message aborts if there's an error.
@@ -940,36 +996,48 @@
     dataPt = strchr(namePt, '=');
     if (dataPt == NULL)
 	{
 	errAbort("Mangled CGI input string %s", namePt);
 	}
     *dataPt++ = 0;
     nextNamePt = strchr(dataPt, '&');
     if (nextNamePt == NULL)
 	nextNamePt = strchr(dataPt, ';');	/* Accomodate DAS. */
     if (nextNamePt != NULL)
          *nextNamePt++ = 0;
 
     if (logMsg && dataPt && strlen(dataPt) < logCgiVarMaxLen)
         dyStringPrintf(logMsg, "%s=%s ", namePt, dataPt); // if dataPt is empty string, still print it, could be important
 
+#ifndef FAST_CGI_DECODE
     cgiDecode(namePt,namePt,strlen(namePt));	/* for unusual ct names */
     cgiDecode(dataPt,dataPt,strlen(dataPt));
     AllocVar(el);
     el->val = dataPt;
     slAddHead(&list, el);
     hashAddSaveName(hash, namePt, el, &el->name);
+#else
+    if ((strlen(namePt) < CGI_VAR_NAME_LIMIT) && (strlen(dataPt) < CGI_VAR_SIZE_LIMIT))
+        {
+        cgiDecode(namePt,namePt,strlen(namePt));	/* for unusual ct names */
+        cgiDecode(dataPt,dataPt,strlen(dataPt));
+        AllocVar(el);
+        el->val = dataPt;
+        slAddHead(&list, el);
+        hashAddSaveName(hash, namePt, el, &el->name);
+        }
+#endif // FAST_CGI_DECODE
     namePt = nextNamePt;
 
     }
 
 if (logMsg)
     {
     char *logStr = dyStringCannibalize(&logMsg);
     fprintf(stderr, "CGIVARS %s\n", logStr);
     freez(&logStr);
     }
 
 slReverse(&list);
 *retList = list;
 *retHash = hash;