/* =============== CL_FinishHTTPDownload A download finished, find out what it was, whether there were any errors and if so, how severe. If none, rename file and other such stuff. =============== */ static void CL_FinishHTTPDownload (void) { size_t i; int msgs_in_queue; CURLMsg *msg; CURLcode result; dlhandle_t *dl; CURL *curl; long responseCode; double timeTaken; double fileSize; char tempName[MAX_OSPATH]; qboolean isFile; do { msg = curl_multi_info_read (multi, &msgs_in_queue); if (!msg) { Com_Printf ("CL_FinishHTTPDownload: Odd, no message for us...\n"); return; } if (msg->msg != CURLMSG_DONE) { Com_Printf ("CL_FinishHTTPDownload: Got some weird message...\n"); continue; } curl = msg->easy_handle; // curl doesn't provide reverse-lookup of the void * ptr, so search for it for (i = 0; i < 4; i++) { if (cls.HTTPHandles[i].curl == curl) { dl = &cls.HTTPHandles[i]; break; } } if (i == 4) Com_Error (ERR_DROP, "CL_FinishHTTPDownload: Handle not found"); // we mark everything as done even if it errored to prevent multiple // attempts. dl->queueEntry->state = DLQ_STATE_DONE; //filelist processing is done on read if (dl->file) isFile = true; else isFile = false; if (isFile) { fclose (dl->file); dl->file = NULL; } //might be aborted if (pendingCount) pendingCount--; handleCount--; //Com_Printf ("finished dl: hc = %d\n", LOG_GENERAL, handleCount); cls.downloadname[0] = 0; cls.downloadposition = 0; result = msg->data.result; switch (result) { // for some reason curl returns CURLE_OK for a 404... case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { i = strlen (dl->queueEntry->quakePath); if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") ) downloading_pak = false; if (isFile) remove (dl->filePath); Com_Printf ("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->quakePath, pendingCount); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); if (fileSize > 512) { // ick isFile = false; result = CURLE_FILESIZE_EXCEEDED; Com_Printf ("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize); } else { curl_multi_remove_handle (multi, dl->curl); // Knightmare- fall back to UDP download for this map if failure on .bsp if ( !strncmp(dl->queueEntry->quakePath, "maps/", 5) && !strcmp(dl->queueEntry->quakePath + i - 4, ".bsp") ) { Com_Printf ("HTTP: failed to download %s, falling back to UDP until next map.\n", dl->queueEntry->quakePath); thisMapAbort = true; CL_CancelHTTPDownloads (false); CL_ResetPrecacheCheck (); } // end Knightmare continue; } } else if (responseCode == 200) { if (!isFile && !abortDownloads) CL_ParseFileList (dl); break; } //every other code is treated as fatal, fallthrough here Com_Printf ("Bad HTTP response code %d for %s, aborting HTTP downloading.\n", responseCode, dl->queueEntry->quakePath); //fatal error, disable http case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: if (isFile) remove (dl->filePath); // Com_Printf ("Fatal HTTP error: %s\n", curl_easy_strerror (result)); Com_Printf ("Fatal HTTP error: %s\n", CURL_ERROR(result)); curl_multi_remove_handle (multi, dl->curl); if (abortDownloads) continue; CL_CancelHTTPDownloads (true); continue; default: i = strlen (dl->queueEntry->quakePath); if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") ) downloading_pak = false; if (isFile) remove (dl->filePath); // Com_Printf ("HTTP download failed: %s\n", curl_easy_strerror (result)); Com_Printf ("HTTP download failed: %s\n", CURL_ERROR(result)); curl_multi_remove_handle (multi, dl->curl); continue; } if (isFile) { //rename the temp file Com_sprintf (tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->quakePath); if (rename (dl->filePath, tempName)) Com_Printf ("Failed to rename %s for some odd reason...", dl->filePath); //a pak file is very special... i = strlen (tempName); if ( !strcmp (tempName + i - 4, ".pak") || !strcmp (tempName + i - 4, ".pk3") ) { // FS_FlushCache (); // FS_ReloadPAKs (); // Knightmare- just add the pk3/ pak file if (!strcmp (tempName + i - 4, ".pk3")) FS_AddPK3File (tempName); else FS_AddPAKFile (tempName); CL_ReVerifyHTTPQueue (); downloading_pak = false; } } //show some stats curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &timeTaken); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); //FIXME: //technically i shouldn't need to do this as curl will auto reuse the //existing handle when you change the URL. however, the handleCount goes //all weird when reusing a download slot in this way. if you can figure //out why, please let me know. curl_multi_remove_handle (multi, dl->curl); Com_Printf ("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n", dl->queueEntry->quakePath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount); } while (msgs_in_queue > 0); // FS_FlushCache (); if (handleCount == 0) { if (abortDownloads == HTTPDL_ABORT_SOFT) abortDownloads = HTTPDL_ABORT_NONE; else if (abortDownloads == HTTPDL_ABORT_HARD) { // FS: Added because Whale's Weapons HTTP server rejects you after a lot of 404s. Then you lose HTTP until a hard reconnect. Q_strncpyz(cls.downloadServerRetry, cls.downloadServer, sizeof(cls.downloadServerRetry)); cls.downloadServer[0] = 0; } } // done current batch, see if we have more to dl - maybe a .bsp needs downloaded if (cls.state == ca_connected && !CL_PendingHTTPDownloads()) CL_RequestNextDownload (); }
/** * @brief A download finished, find out what it was, whether there were any errors and * if so, how severe. If none, rename file and other such stuff. */ static void CL_FinishHTTPDownload (void) { int messagesInQueue, i; CURLcode result; CURL *curl; long responseCode; double timeTaken, fileSize; char tempName[MAX_OSPATH]; bool isFile; do { CURLMsg *msg = curl_multi_info_read(multi, &messagesInQueue); dlhandle_t *dl = NULL; if (!msg) { Com_Printf("CL_FinishHTTPDownload: Odd, no message for us...\n"); return; } if (msg->msg != CURLMSG_DONE) { Com_Printf("CL_FinishHTTPDownload: Got some weird message...\n"); continue; } curl = msg->easy_handle; /* curl doesn't provide reverse-lookup of the void * ptr, so search for it */ for (i = 0; i < 4; i++) { if (cls.HTTPHandles[i].curl == curl) { dl = &cls.HTTPHandles[i]; break; } } if (!dl) Com_Error(ERR_DROP, "CL_FinishHTTPDownload: Handle not found"); /* we mark everything as done even if it errored to prevent multiple attempts. */ dl->queueEntry->state = DLQ_STATE_DONE; /* filelist processing is done on read */ if (dl->file) isFile = true; else isFile = false; if (isFile) { fclose(dl->file); dl->file = NULL; } /* might be aborted */ if (pendingCount) pendingCount--; handleCount--; /* Com_Printf("finished dl: hc = %d\n", handleCount); */ cls.downloadName[0] = 0; cls.downloadPosition = 0; result = msg->data.result; switch (result) { /* for some reason curl returns CURLE_OK for a 404... */ case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { const char *extension = Com_GetExtension(dl->queueEntry->ufoPath); if (extension != NULL && Q_streq(extension, "pk3")) downloadingPK3 = false; if (isFile) FS_RemoveFile(dl->filePath); Com_Printf("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->ufoPath, pendingCount); curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); if (fileSize > 512) { /* ick */ isFile = false; result = CURLE_FILESIZE_EXCEEDED; Com_Printf("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize); } else { curl_multi_remove_handle(multi, dl->curl); continue; } } else if (responseCode == 200) { if (!isFile && !abortDownloads) CL_ParseFileList(dl); break; } /* every other code is treated as fatal, fallthrough here */ /* fatal error, disable http */ case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: if (isFile) FS_RemoveFile(dl->filePath); Com_Printf("Fatal HTTP error: %s\n", curl_easy_strerror(result)); curl_multi_remove_handle(multi, dl->curl); if (abortDownloads) continue; CL_CancelHTTPDownloads(true); continue; default: i = strlen(dl->queueEntry->ufoPath); if (Q_streq(dl->queueEntry->ufoPath + i - 4, ".pk3")) downloadingPK3 = false; if (isFile) FS_RemoveFile(dl->filePath); Com_Printf("HTTP download failed: %s\n", curl_easy_strerror(result)); curl_multi_remove_handle(multi, dl->curl); continue; } if (isFile) { /* rename the temp file */ Com_sprintf(tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->ufoPath); if (!FS_RenameFile(dl->filePath, tempName, false)) Com_Printf("Failed to rename %s for some odd reason...", dl->filePath); /* a pk3 file is very special... */ i = strlen(tempName); if (Q_streq(tempName + i - 4, ".pk3")) { FS_RestartFilesystem(NULL); CL_ReVerifyHTTPQueue(); downloadingPK3 = false; } } /* show some stats */ curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &timeTaken); curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); /** @todo * technically i shouldn't need to do this as curl will auto reuse the * existing handle when you change the URL. however, the handleCount goes * all weird when reusing a download slot in this way. if you can figure * out why, please let me know. */ curl_multi_remove_handle(multi, dl->curl); Com_Printf("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n", dl->queueEntry->ufoPath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount); } while (messagesInQueue > 0); if (handleCount == 0) { if (abortDownloads == HTTPDL_ABORT_SOFT) abortDownloads = HTTPDL_ABORT_NONE; else if (abortDownloads == HTTPDL_ABORT_HARD) cls.downloadServer[0] = 0; } /* done current batch, see if we have more to dl - maybe a .bsp needs downloaded */ if (cls.state == ca_connected && !CL_PendingHTTPDownloads()) CL_RequestNextDownload(); }