feat: add support for Hugging Face API operations
This change enables full support for Hugging Face API requests, including repository creation, by: 1. Identifying HF API requests via path patterns. 2. Allowing POST, PUT, PATCH, and DELETE methods for these requests. 3. Correctly forwarding request bodies and headers. 4. Skipping caching for API operations. Fixes: huggingface_hub.errors.HfHubHTTPError: Client error '405 Method Not Allowed'
This commit is contained in:
1 parent
da89e08f41
commit
893f976ab5
3 files changed
+87
-13
No files matched your search
+18
-9
@@ -11,6 +11,7 @@
|
||||
import { CONFIG, createConfig } from './config/index.js';
|
||||
import { SORTED_PLATFORMS, transformPath } from './config/platforms.js';
|
||||
import { configureAIHeaders, isAIInferenceRequest } from './protocols/ai.js';
|
||||
import { configureHuggingFaceHeaders, isHuggingFaceAPIRequest } from './protocols/huggingface.js';
|
||||
import {
|
||||
fetchToken,
|
||||
getScopeFromUrl,
|
||||
@@ -126,7 +127,10 @@ async function handleRequest(request, env, ctx) {
|
||||
// Check if this is an AI inference request
|
||||
const isAI = isAIInferenceRequest(request, url);
|
||||
|
||||
// Check cache first (skip cache for Git, Git LFS, Docker, and AI inference operations)
|
||||
// Check if this is a Hugging Face API request
|
||||
const isHF = isHuggingFaceAPIRequest(request, url);
|
||||
|
||||
// Check cache first (skip cache for Git, Git LFS, Docker, AI inference, and HF API operations)
|
||||
/** @type {Cache | null} */
|
||||
// @ts-ignore - Cloudflare Workers cache API
|
||||
const cache =
|
||||
@@ -134,7 +138,7 @@ async function handleRequest(request, env, ctx) {
|
||||
? /** @type {any} */ (caches).default // eslint-disable-line jsdoc/reject-any-type
|
||||
: null;
|
||||
|
||||
if (cache && !isGit && !isGitLFS && !isDocker && !isAI) {
|
||||
if (cache && !isGit && !isGitLFS && !isDocker && !isAI && !isHF) {
|
||||
try {
|
||||
// For Range requests, try cache match first
|
||||
const cacheKey = new Request(targetUrl, {
|
||||
@@ -177,10 +181,10 @@ async function handleRequest(request, env, ctx) {
|
||||
redirect: 'follow'
|
||||
};
|
||||
|
||||
// Add body for POST/PUT/PATCH requests (Git/Docker/AI inference operations)
|
||||
// Add body for POST/PUT/PATCH/DELETE requests (Git/Docker/AI/HF operations)
|
||||
if (
|
||||
['POST', 'PUT', 'PATCH'].includes(request.method) &&
|
||||
(isGit || isGitLFS || isDocker || isAI)
|
||||
['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method) &&
|
||||
(isGit || isGitLFS || isDocker || isAI || isHF)
|
||||
) {
|
||||
fetchOptions.body = request.body;
|
||||
}
|
||||
@@ -188,9 +192,9 @@ async function handleRequest(request, env, ctx) {
|
||||
// Cast headers to Headers for proper typing
|
||||
const requestHeaders = /** @type {Headers} */ (fetchOptions.headers);
|
||||
|
||||
// Set appropriate headers for Git/Docker/AI vs regular requests
|
||||
if (isGit || isGitLFS || isDocker || isAI) {
|
||||
// For Git/Docker/AI operations, copy all headers from the original request
|
||||
// Set appropriate headers for Git/Docker/AI/HF vs regular requests
|
||||
if (isGit || isGitLFS || isDocker || isAI || isHF) {
|
||||
// For Git/Docker/AI/HF operations, copy all headers from the original request
|
||||
// This ensures protocol compliance
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
// Skip headers that might cause issues with proxying
|
||||
@@ -209,6 +213,10 @@ async function handleRequest(request, env, ctx) {
|
||||
if (isAI) {
|
||||
configureAIHeaders(requestHeaders, request);
|
||||
}
|
||||
|
||||
if (isHF) {
|
||||
configureHuggingFaceHeaders(requestHeaders, request);
|
||||
}
|
||||
} else {
|
||||
// Regular file download headers
|
||||
Object.assign(fetchOptions, {
|
||||
@@ -617,8 +625,9 @@ async function handleRequest(request, env, ctx) {
|
||||
const isDocker = isDockerRequest(request, new URL(request.url));
|
||||
const isAI = isAIInferenceRequest(request, new URL(request.url));
|
||||
const isGitLFS = isGitLFSRequest(request, new URL(request.url));
|
||||
const isHF = isHuggingFaceAPIRequest(request, new URL(request.url));
|
||||
|
||||
return isGit || isGitLFS || isDocker || isAI
|
||||
return isGit || isGitLFS || isDocker || isAI || isHF
|
||||
? response
|
||||
: addPerformanceHeaders(response, monitor);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Xget - High-performance acceleration engine for developer resources
|
||||
* Copyright (C) 2025 Xi Xu
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hugging Face protocol handler for Xget
|
||||
*/
|
||||
|
||||
/**
|
||||
* Detects if a request is a Hugging Face API operation.
|
||||
*
|
||||
* Identifies Hugging Face API requests by checking for:
|
||||
* - Hugging Face platform prefix (/hf/)
|
||||
* - API path segment (/api/)
|
||||
*
|
||||
* @param {Request} request - The incoming request object
|
||||
* @param {URL} url - Parsed URL object
|
||||
* @returns {boolean} True if this is a Hugging Face API operation
|
||||
*/
|
||||
export function isHuggingFaceAPIRequest(request, url) {
|
||||
// Check for Hugging Face API endpoints
|
||||
if (url.pathname.startsWith('/hf/api/')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also check for token endpoint which is often used
|
||||
if (url.pathname.startsWith('/hf/token')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures headers for Hugging Face API requests.
|
||||
*
|
||||
* @param {Headers} headers - The headers object to modify
|
||||
* @param {Request} request - The original request
|
||||
*/
|
||||
export function configureHuggingFaceHeaders(headers, request) {
|
||||
// Pass through Authorization header if present
|
||||
if (request.headers.has('Authorization')) {
|
||||
headers.set('Authorization', request.headers.get('Authorization'));
|
||||
}
|
||||
|
||||
if (request.method === 'POST' && !headers.has('Content-Type')) {
|
||||
headers.set('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import { CONFIG } from '../config/index.js';
|
||||
// Imported protocol checks
|
||||
import { isAIInferenceRequest } from '../protocols/ai.js';
|
||||
import { isGitLFSRequest, isGitRequest } from '../protocols/git.js';
|
||||
import { isHuggingFaceAPIRequest } from '../protocols/huggingface.js';
|
||||
|
||||
/**
|
||||
* Detects if a request is a container registry operation (Docker/OCI).
|
||||
@@ -72,7 +73,7 @@ export function isDockerRequest(request, url) {
|
||||
}
|
||||
|
||||
// Re-export for standard usage
|
||||
export { isAIInferenceRequest, isGitLFSRequest, isGitRequest };
|
||||
export { isAIInferenceRequest, isGitLFSRequest, isGitRequest, isHuggingFaceAPIRequest };
|
||||
|
||||
/**
|
||||
* Validates incoming requests against security rules.
|
||||
@@ -90,15 +91,16 @@ export { isAIInferenceRequest, isGitLFSRequest, isGitRequest };
|
||||
* @returns {{valid: boolean, error?: string, status?: number}} Validation result object
|
||||
*/
|
||||
export function validateRequest(request, url, config = CONFIG) {
|
||||
// Allow POST method for Git, Git LFS, Docker, and AI inference operations
|
||||
// Allow POST method for Git, Git LFS, Docker, AI inference, and HF API operations
|
||||
const isGit = isGitRequest(request, url);
|
||||
const isGitLFS = isGitLFSRequest(request, url);
|
||||
const isDocker = isDockerRequest(request, url);
|
||||
const isAI = isAIInferenceRequest(request, url);
|
||||
const isHF = isHuggingFaceAPIRequest(request, url);
|
||||
|
||||
const allowedMethods =
|
||||
isGit || isGitLFS || isDocker || isAI
|
||||
? ['GET', 'HEAD', 'POST', 'PUT', 'PATCH']
|
||||
isGit || isGitLFS || isDocker || isAI || isHF
|
||||
? ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||
: config.SECURITY.ALLOWED_METHODS;
|
||||
|
||||
if (!allowedMethods.includes(request.method)) {
|
||||
|
||||
Reference in new issue
Block a user