200字
基于边缘函数(edge one)加速docker镜像下载
2025-12-04
2025-12-09

该功能疑似被eo拦截,无法正常使用,但是部署在CF上可以正常使用





// Docker Hub 的官方 Registry 地址
const DOCKER_HUB_REGISTRY = "registry-1.docker.io";
const DOCKER_HUB_AUTH_SERVICE = "auth.docker.io";

// 带有重试机制的 fetch 函数
async function fetchWithRetry(request, retries = 3) {
  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 8000);

    const response = await fetch(request, {
      signal: controller.signal,
    });

    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    if (retries > 0 && (error.name === 'AbortError' || error.message.includes('timeout'))) {
      console.log(`Fetch failed, retrying... (${retries} attempts left)`);
      return fetchWithRetry(request, retries - 1);
    }
    throw error;
  }
}

// 处理请求的主函数
async function handleRequest(request) {
  const url = new URL(request.url);

  if (url.pathname.startsWith('/v2/')) {
    const newUrl = new URL(url.pathname + url.search, `https://${DOCKER_HUB_REGISTRY}`);
    
    const newRequest = new Request(newUrl, {
      method: request.method,
      headers: request.headers,
      body: request.body,
      redirect: 'manual',
    });
    newRequest.headers.delete('host');

    try {
      let response = await fetchWithRetry(newRequest);
      
      // 处理401未授权响应
      if (response.status === 401) {
        const authHeader = response.headers.get('Www-Authenticate');
        if (authHeader && authHeader.startsWith('Bearer')) {
          const authParams = parseAuthHeader(authHeader);
          const authUrl = new URL(`https://${DOCKER_HUB_AUTH_SERVICE}/token`);
          authUrl.searchParams.set('service', authParams.service || 'registry.docker.io');
          if (authParams.scope) {
            authUrl.searchParams.set('scope', authParams.scope);
          }
          
          console.log(`Requesting token from: ${authUrl.toString()}`);
          const authResponse = await fetchWithRetry(new Request(authUrl.toString()));

          if (!authResponse.ok) {
            const bodyText = await authResponse.text();
            console.error(`Auth failed with status ${authResponse.status}: ${bodyText}`);
            throw new Error(`Authentication failed: ${authResponse.status} ${authResponse.statusText}`);
          }
          
          // 安全地解析 JSON
          let tokenData;
          try {
            tokenData = await authResponse.json();
          } catch (e) {
            console.error(`Failed to parse auth response JSON: ${e.message}`);
            throw new Error('Invalid authentication response from server.');
          }

          // 兼容不同的 token 字段名
          const token = tokenData.token || tokenData.access_token;
          if (!token) {
            console.error('Token not found in auth response:', tokenData);
            throw new Error('Could not find token in authentication response.');
          }

          console.log('Successfully obtained token.');
          const authedRequest = new Request(newRequest);
          authedRequest.headers.set('Authorization', `Bearer ${token}`);
          response = await fetchWithRetry(authedRequest);
        }
      }
      
      // 清理响应头
      const newResponse = new Response(response.body, response);
      newResponse.headers.delete('set-cookie');
      newResponse.headers.delete('x-amz-cf-id');
      newResponse.headers.delete('x-cache-status');
      
      return newResponse;

    } catch (error) {
      // 捕获所有未处理的错误,并返回一个明确的 502 错误
      console.error(`Proxy error for ${url.pathname}: ${error.message}`);
      console.error(error.stack); // 打印详细的错误堆栈
      return new Response(`Proxy upstream error: ${error.message}`, { status: 502 });
    }
  }

  return new Response('Not Found. This is a Docker Registry proxy.', { status: 404 });
}

function parseAuthHeader(header) {
  const result = {};
  const parts = header.split(' ')[1] || '';
  parts.split(',').forEach(part => {
    const [key, value] = part.trim().split('=');
    if (key && value) {
      result[key] = value.replace(/"/g, '');
    }
  });
  return result;
}

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

评论