2197f6d5208aff4c48ccbe42e61a116d988ac392 max Tue May 19 08:23:54 2026 -0700 hubApi: add /blat endpoint with apiKey gating, format=hgblat, and known-agent bypass New src/hg/hubApi/blat.c implements /blat/<type> (dna, protein, transRna, transDna, guess) backed by the same gfServer logic as hgBlat. Key details: - Requires an apiKey for rate-limiting; botException() and botExceptionUserAgent() exempt IPs/user-agents in hg.conf (same policy as captcha bypass elsewhere in the browser stack). - Invalid apiKey returns a clean JSON 403 rather than an HTML 500 (pre-validated in hubApi.c main() before hgBotDelayTimeFrac runs). - Extra bot-delay fraction (default 0.3, 10x hubApi default) is configurable via hubApi.blatDelayFraction in hg.conf. - format=text/psl -> PSL text; format=hgblat -> byte-for-byte hgBlat?output=json shape; jsonOutputArrays=1 -> hubApi envelope with arrays (parallel to getData behaviour); default -> objects. - botExceptionUserAgent() carved out of cart.c's static isUserAgentException() into botDelay.c so non-cart callers can use it. - Cross-reference comments added in hgBlat.c and blat.c noting the shared logic so fixes get applied to both. refs #36315 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c index 2a02d921a3d..2c58ecc9795 100644 --- src/hg/hgBlat/hgBlat.c +++ src/hg/hgBlat/hgBlat.c @@ -1,16 +1,21 @@ -/* hgBlat - CGI-script to manage fast human genome sequence searching. */ +/* hgBlat - CGI-script to manage fast human genome sequence searching. + * + * NOTE: The hubApi /blat endpoint (src/hg/hubApi/blat.c) derives its + * alignment logic (server lookup, sequence filtering, gfAlign* calls, + * temp-file round-trip) from this file. If you fix a bug or change + * behaviour here, check whether hubApi/blat.c needs the same fix. */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include <pthread.h> #include "common.h" #include "errAbort.h" #include "hCommon.h" #include "jksql.h" #include "portable.h" #include "linefile.h" #include "dnautil.h" #include "fa.h" #include "psl.h" #include "genoFind.h" #include "cheapcgi.h" @@ -310,140 +315,68 @@ char *host; /* Name of machine hosting server. */ char *port; /* Port that hosts server. */ char *nibDir; /* Directory of sequence files. */ int tileSize; /* gfServer -tileSize */ int stepSize; /* gfServer -stepSize */ int minMatch; /* gfServer -minMatch */ boolean isDynamic; /* is a dynamic server */ char* genomeDataDir; /* genome name for dynamic gfServer */ }; char *typeList[] = {"BLAT's guess", "DNA", "protein", "translated RNA", "translated DNA"}; char *outputList[] = {"hyperlink", "psl", "psl no header", "JSON"}; int minMatchShown = 0; -static struct serverTable *databaseServerTable(char *db, boolean isTrans) -/* Load blat table for a database */ -{ -struct sqlConnection *conn = hConnectCentral(); -char query[512]; -struct sqlResult *sr; -char **row; -char dbActualName[32]; - -/* If necessary convert database description to name. */ -sqlSafef(query, sizeof(query), "select name from dbDb where name = '%s'", db); -if (!sqlExists(conn, query)) - { - sqlSafef(query, sizeof(query), "select name from dbDb where description = '%s'", db); - if (sqlQuickQuery(conn, query, dbActualName, sizeof(dbActualName)) != NULL) - db = dbActualName; - } - -struct serverTable *st; -AllocVar(st); - -/* Do a little join to get data to fit into the serverTable and grab - * dbDb.nibPath too. Check for newer dynamic flag and allow with or without - * it. - * For debugging, one set the variable blatServersTbl to some db.table to - * pick up settings from somewhere other than dbDb.blatServers. - */ -char *blatServersTbl = cfgOptionDefault("blatServersTbl", "blatServers"); -boolean haveDynamic = sqlColumnExists(conn, blatServersTbl, "dynamic"); -sqlSafef(query, sizeof(query), "select dbDb.name, dbDb.description, blatServers.isTrans," - "blatServers.host, blatServers.port, dbDb.nibPath, %s " - "from dbDb, %s blatServers where blatServers.isTrans = %d and " - "dbDb.name = '%s' and dbDb.name = blatServers.db", - (haveDynamic ? "blatServers.dynamic" : "0"), blatServersTbl, isTrans, db); -sr = sqlGetResult(conn, query); -if ((row = sqlNextRow(sr)) == NULL) +struct serverTable *findServer(char *db, boolean isTrans) +/* Return server for given database. Db can either be database name or + * description. Server lookup delegated to findBlatServer() in hg/lib/blatServers.c. */ { +struct blatServerParams *p = findBlatServer(db, isTrans); +if (p == NULL) errAbort("Can't find a server for %s database %s. Click " "<A HREF=\"/cgi-bin/hgBlat?%s&command=start&db=%s\">here</A> " "to reset to default database.", (isTrans ? "translated" : "DNA"), db, cartSidUrlString(cart), hDefaultDb()); - } -st->db = cloneString(row[0]); -st->genome = cloneString(row[1]); -st->isTrans = atoi(row[2]); -st->host = cloneString(row[3]); -st->port = cloneString(row[4]); -st->nibDir = hReplaceGbdbSeqDir(row[5], st->db); -if (atoi(row[6])) - { - st->isDynamic = TRUE; - st->genomeDataDir = cloneString(st->db); // directories by database name for database genomes - ++nonHubDynamicBlatServerCount; - } - -sqlFreeResult(&sr); -hDisconnectCentral(&conn); -return st; -} - -static struct serverTable *trackHubServerTable(char *db, boolean isTrans) -/* Load blat table for a hub */ -{ -char *host, *port; -char *genomeDataDir; - -if (!trackHubGetBlatParams(db, isTrans, &host, &port, &genomeDataDir)) - return NULL; - struct serverTable *st; AllocVar(st); - -st->db = cloneString(db); -st->genome = cloneString(hGenome(db)); -st->isTrans = isTrans; -st->host = host; -st->port = port; -struct trackHubGenome *genome = trackHubGetGenome(db); -st->nibDir = cloneString(genome->twoBitPath); -char *ptr = strrchr(st->nibDir, '/'); -// we only want the directory name -if (ptr != NULL) - *ptr = 0; -if (genomeDataDir != NULL) - { - st->isDynamic = TRUE; - st->genomeDataDir = cloneString(genomeDataDir); - ++hubDynamicBlatServerCount; - } -return st; -} - -struct serverTable *findServer(char *db, boolean isTrans) -/* Return server for given database. Db can either be - * database name or description. */ +st->db = p->db; +st->genome = p->genome; +st->isTrans = p->isTrans; +st->host = p->host; +st->port = p->port; +st->nibDir = p->nibDir; +st->isDynamic = p->isDynamic; +st->genomeDataDir = p->genomeDataDir; +if (p->isDynamic) { if (trackHubDatabase(db)) - return trackHubServerTable(db, isTrans); + ++hubDynamicBlatServerCount; else - return databaseServerTable(db, isTrans); + ++nonHubDynamicBlatServerCount; + } +return st; } void findClosestServer(char **pDb, char **pOrg) /* If db doesn't have a blat server, look for the closest db (or org) that has one, * as hgPcr does. */ { char *db = *pDb, *org = *pOrg; -if (trackHubDatabase(db) && (trackHubServerTable(db, FALSE) != NULL)) +if (trackHubDatabase(db) && (findBlatServer(db, FALSE) != NULL)) { *pDb = db; *pOrg = hGenome(db); return; } struct sqlConnection *conn = hConnectCentral(); char query[256]; sqlSafef(query, sizeof(query), "select db from blatServers where db = '%s'", db); if (!sqlExists(conn, query)) { sqlSafef(query, sizeof(query), "select blatServers.db from blatServers,dbDb " "where blatServers.db = dbDb.name and dbDb.genome = '%s'", org); char *db = sqlQuickString(conn, query); if (db == NULL)