0, 'cacheHit' => 0, 'apiCall' => 0, 'cacheCall' => 0, 'cacheReadError' => 0, 'fetchApiError' => 0, 'fetchApiWarning' => 0, ]; // Ensure cache directory exists if (!is_dir($cacheDir)) { mkdir($cacheDir, 0777, true); } Swoole\Coroutine\run(function () use ($port, $apiEndpoint, $cacheDir, &$pathIndex, &$viewsInfo) { $server = new Server('0.0.0.0', $port, false); $server->set([ 'daemonize' => true, ]); $server->handle('/', function ($request, $response) use ($apiEndpoint, $cacheDir, &$pathIndex, &$viewsInfo) { $viewsInfo['request']++; $uri = $request->server['request_uri']; $parsedUrl = parse_url($uri); parse_str($parsedUrl['query'] ?? '', $query); $reqPath = explode('/', trim($parsedUrl['path'], '/'))[0] ?? ''; $token = implode('/', array_slice(explode('/', trim($parsedUrl['path'], '/')), 1)); if ($reqPath === 'endpoint') { return sendJsonResponse($response, [ 'code' => 200, 'data' => [ 'api' => $apiEndpoint, 'port' => $port, 'cacheDir' => $cacheDir, 'pathIndexCount' => count($pathIndex), 'viewsInfo' => $viewsInfo, ], ]); } if (empty($token) || $token === 'undefined') { $token = $reqPath; $reqPath = 'go'; } if (!in_array($reqPath, ['avatar', 'go', 'bbs', 'www', 'url', 'thumb'])) { return sendErrorResponse($response, 404, 'Not Found'); } if (!$token) { return sendErrorResponse($response, 400, 'Bad Request: Missing Token or path'); } $uniqidhex = md5($reqPath . $token . ($query['sign'] ?? '')); if (isset($pathIndex[$uniqidhex]) && isCacheValid($cacheDir, $pathIndex[$uniqidhex])) { $viewsInfo['cacheHit']++; return serveFromCache($cacheDir, $pathIndex[$uniqidhex], $response); } $viewsInfo['apiCall']++; fetchApiData($apiEndpoint, $reqPath, $token, $query['sign'] ?? '', function ($apiData) use ($cacheDir, &$pathIndex, $uniqidhex, $response) { if ($apiData['code'] === 200 && isset($apiData['data']['url'])) { $pathIndex[$uniqidhex] = $uniqidhex; if (isCacheValid($cacheDir, $uniqidhex)) { serveFromCache($cacheDir, $uniqidhex, $response); return; } // 写入缓存 file_put_contents("$cacheDir/$uniqidhex.meta", json_encode($apiData['data'])); // 获取文件内容, curl $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $apiData['data']['url']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 50); $fileContent = curl_exec($ch); curl_close($ch); file_put_contents("$cacheDir/$uniqidhex.content", $fileContent); serveFromCache($cacheDir, $uniqidhex, $response); } else { sendErrorResponse($response, 502, 'Bad Gateway: ' . json_encode($apiData)); } }); }); $server->start(); }); function isCacheValid($cacheDir, $cacheFile) { return file_exists("$cacheDir/$cacheFile.meta") && file_exists("$cacheDir/$cacheFile.content"); } function serveFromCache($cacheDir, $cacheFile, $response) { $cacheData = json_decode(file_get_contents("$cacheDir/$cacheFile.meta"), true); $response->header('Content-Type', $cacheData['headers']['Content-Type'] ?? 'application/octet-stream'); $response->header('Cloud-Type', $cacheData['cloudtype']); $response->header('Cloud-Expiration', $cacheData['expiration']); $response->header('ETag', $cacheData['uniqid'] ?? ''); $response->header('Cache-Control', 'public, max-age=31536000'); $response->header('Expires', gmdate('D, d M Y H:i:s', time() + 31536000000) . ' GMT'); $response->header('Accept-Ranges', 'bytes'); $response->header('Connection', 'keep-alive'); $response->header('Date', gmdate('D, d M Y H:i:s') . ' GMT'); $response->header('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT'); // 高效的读取文件 $file = fopen("$cacheDir/$cacheFile.content", 'r'); $response->end(fread($file, filesize("$cacheDir/$cacheFile.content"))); fclose($file); } function fetchApiData($apiEndpoint, $reqPath, $token, $sign, $callback) { Coroutine::create(function () use ($apiEndpoint, $reqPath, $token, $sign, $callback) { $client = new Client(parse_url($apiEndpoint, PHP_URL_HOST), 443, true); $client->setHeaders([ 'Accept' => 'application/json', 'User-Agent' => 'Swoole-Client', 'token' => $token, ]); $client->get(parse_url($apiEndpoint, PHP_URL_PATH) . "?type=$reqPath&sign=$sign"); $callback(json_decode($client->body, true)); $client->close(); }); } function sendJsonResponse($response, $data) { $response->header('Content-Type', 'application/json'); $response->end(json_encode($data)); } function sendErrorResponse($response, $status, $message) { $response->status($status); $response->end($message); }