Files
wehub-resource-sync adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:17:32 +08:00

134 lines
3.2 KiB
JavaScript

/**
* Routing logic for the Gradio Node proxy.
*
* Extracted so it can be unit-tested independently of the SvelteKit handler.
*/
// Routes that must go to the Python server (FastAPI).
export const PYTHON_ROUTE_PREFIXES = [
"/gradio_api",
"/config",
"/login",
"/logout",
"/theme.css",
"/robots.txt",
"/pwa_icon",
"/manifest.json",
"/monitoring"
];
// Routes that can be offloaded to static workers.
// Workers serve both /upload and /gradio_api/upload (same handler).
// Checked BEFORE the /gradio_api Python catch-all.
export const STATIC_ROUTE_PREFIXES = [
"/gradio_api/upload",
"/gradio_api/upload_progress",
"/gradio_api/file=",
"/gradio_api/file/",
"/upload",
"/upload_progress",
"/file=",
"/file/",
"/static/",
"/assets/",
"/svelte/",
"/favicon.ico",
"/custom_component/"
];
// Upload routes that need affinity (upload + upload_progress for the same
// upload_id must land on the same worker).
const UPLOAD_AFFINITY_PREFIXES = [
"/gradio_api/upload",
"/gradio_api/upload_progress",
"/upload",
"/upload_progress"
];
export function matchesPrefix(path, prefixes) {
for (const prefix of prefixes) {
if (path === prefix || path.startsWith(prefix)) {
return true;
}
}
return false;
}
/**
* Simple string hash (djb2) that returns a non-negative integer.
* Used to map upload_id to a deterministic worker index.
*
* @param {string} str
* @returns {number}
*/
export function hashString(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash + str.charCodeAt(i)) >>> 0;
}
return hash;
}
/**
* Classify a request into a routing destination and optional worker index.
*
* @param {string} path - URL path (no query string)
* @param {object} options
* @param {boolean} options.hasWorkers - whether static workers are configured
* @param {boolean} options.serverModeEnabled - whether GRADIO_SERVER_MODE_ENABLED is set
* @param {number} options.numWorkers - number of static workers
* @param {string} [options.queryString] - raw query string (after ?)
* @returns {{route: "worker" | "python" | "sveltekit", workerIndex?: number}}
*/
export function classifyRoute(
path,
{
hasWorkers = false,
serverModeEnabled = false,
numWorkers = 0,
queryString = ""
} = {}
) {
if (matchesPrefix(path, STATIC_ROUTE_PREFIXES)) {
if (!hasWorkers) {
return { route: "python" };
}
// For upload routes with an upload_id, use affinity hashing
if (
numWorkers > 0 &&
queryString &&
matchesPrefix(path, UPLOAD_AFFINITY_PREFIXES)
) {
const uploadId = extractUploadId(queryString);
if (uploadId) {
return {
route: "worker",
workerIndex: hashString(uploadId) % numWorkers
};
}
}
return { route: "worker" };
}
if (matchesPrefix(path, PYTHON_ROUTE_PREFIXES) || serverModeEnabled) {
return { route: "python" };
}
return { route: "sveltekit" };
}
/**
* Extract upload_id from a query string.
*
* @param {string} qs - raw query string (without leading ?)
* @returns {string|null}
*/
function extractUploadId(qs) {
for (const part of qs.split("&")) {
const eq = part.indexOf("=");
if (eq === -1) continue;
if (part.substring(0, eq) === "upload_id") {
return decodeURIComponent(part.substring(eq + 1));
}
}
return null;
}