8aa6962feea3d82de4b697351040fadd50da2754
hiram
  Thu Aug 15 15:16:44 2019 -0700
begin to call bottleneck server at the very beginning of hgTracks and ready for other CGIs with library function refs #23217

diff --git src/hg/lib/botDelay.c src/hg/lib/botDelay.c
index 62e46b9..9a0418c 100644
--- src/hg/lib/botDelay.c
+++ src/hg/lib/botDelay.c
@@ -71,31 +71,31 @@
        , ip, asctime(localtime(&now)), millis);
 }
 
 static char *getCookieUser()
 /* get user from hguid cookie */
 {
 char *user = NULL;
 char *centralCookie = hUserCookie();
 
 if (centralCookie)
     user = findCookieData(centralCookie);
 
 return user;
 }
 
-static char *getBotCheckString(char *ip, double fraction)
+char *getBotCheckString(char *ip, double fraction)
 /* compose "user.ip fraction" string for bot check */
 {
 char *user = getCookieUser();
 char *botCheckString = needMem(256);
 if (user)
   safef(botCheckString, 256, "%s.%s %f", user, ip, fraction);
 else
   safef(botCheckString, 256, "%s %f", ip, fraction);
 return botCheckString;
 }
 
 void botDelayCgi(char *host, int port, boolean noWarn, double fraction)
 /* Connect with bottleneck server and sleep the
  * amount it suggests for IP address calling CGI script,
  * after imposing the specified fraction of the access penalty. */
@@ -201,15 +201,80 @@
 {
 char *ip = getenv("REMOTE_ADDR");
 char *host = cfgOption("bottleneck.host");
 char *port = cfgOption("bottleneck.port");
 
 int delay = 0;
 if (host != NULL && port != NULL && ip != NULL)
     {
     char *botCheckString = getBotCheckString(ip, fraction);
     delay = botDelayTime(host, atoi(port), botCheckString);
     freeMem(botCheckString);
     }
 return delay;
 }
 
+#define err429  429
+#define err429Msg       "Too Many Requests"
+int botDelayMillis = 0;
+
+static void hogExit(char *cgiName, long enteredMainTime)
+/* earlyBotCheck requests exit before CGI has done any output or
+ * setups of any kind.  HTML output has not yet started.
+ */
+{
+char *hogHost = getenv("REMOTE_ADDR");
+char cgiExitName[1024];
+safef(cgiExitName, ArraySize(cgiExitName), "%s hogExit", cgiName);
+
+puts("Content-Type:text/html");
+printf("Status: %d %s\n", err429, err429Msg);
+puts("Retry-After: 30");
+puts("\n");
+
+puts("<!DOCTYPE HTML 4.01 Transitional>\n");
+puts("<html lang='en'>");
+puts("<head>");
+puts("<meta charset=\"utf-8\">");
+printf("<title>Status %d: %s</title></head>\n", err429, err429Msg);
+
+printf("<body><h1>Status %d: %s</h1><p>\n", err429, err429Msg);
+time_t now = time(NULL);
+printf("There is an exceedingly high volume of traffic coming from your "
+       "site (IP address %s) as of %s (California time).  It looks like "
+       "a web robot is launching queries quickly, and not even waiting for "
+       "the results of one query to finish before launching another query. "
+       "/* We cannot service requests from your IP address under */ these "
+       "conditions.  (code %d) "
+       "To use the genome browser functionality from a Unix command line, "
+       "please read <a href='http://genome.ucsc.edu/FAQ/FAQdownloads.html#download36'>our FAQ</a> on this topic. "
+       "For further help on how to access our data from a command line, "
+       "or if "
+       "you think this delay is being imposed unfairly, please contact genome-www@soe.ucsc.edu."
+       ,hogHost, asctime(localtime(&now)), botDelayMillis);
+puts("</body></html>");
+cgiExitTime(cgiExitName, enteredMainTime);
+exit(0);
+}       /*      static void hogExit()   */
+
+boolean earlyBotCheck(long enteredMainTime, char *cgiName, double delayFrac, int warnMs, int exitMs)
+/* similar to botDelayCgi but for use before the CGI has started any
+ * output or setup the cart of done any MySQL operations.  The boolean
+ * return is used later in the CGI after it has done all its setups and
+ * started output so it can issue the warning.
+ */
+{
+boolean issueWarning = FALSE;
+botDelayMillis = hgBotDelayTimeFrac(delayFrac);
+if (botDelayMillis > 0)
+    {
+    sleep1000(botDelayMillis);
+    if (botDelayMillis > warnMs)
+	{
+	if (botDelayMillis > exitMs)
+	    hogExit(cgiName, enteredMainTime);
+	else
+	    issueWarning = TRUE;
+	}
+    }
+return issueWarning;
+}	/*	boolean earlyBotCheck()	*/