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() */