static int wswcurl_checkmsg( void ) { int cnt = 0; CURLMsg *msg; wswcurl_req *r; int ret = 0; char *info; do { msg = qcurl_multi_info_read( curlmulti, &cnt ); if( !msg || !msg->easy_handle ) { continue; } // Treat received message. qcurl_easy_getinfo( msg->easy_handle, CURLINFO_PRIVATE, &info ); r = ( wswcurl_req * )(( void * )info); if( !r ) { //Com_Printf("OOPS: Message from unknown source!\n"); continue; // Not for us - oops :) } if( msg->msg != CURLMSG_DONE ) { //Com_Printf("Other message for %s: %d\n", r->url, msg->msg); continue; } ret++; if( msg->data.result == CURLE_OK ) { // Done! r->status = WSTATUS_FINISHED; qcurl_easy_getinfo( r->curl, CURLINFO_RESPONSE_CODE, &(r->respcode) ); if( r->callback_done ) { r->callback_done( r, r->respcode, r->customp ); } } else { // failed, store and pass to callback negative status value r->status = -abs( msg->data.result ); r->respcode = -1; if( r->callback_done ) { r->callback_done( r, r->status, r->customp ); } } } while( cnt && msg ); return ret; }
void CL_cURL_PerformDownload(void) { CURLMcode res; CURLMsg *msg; int c; int i = 0; res = qcurl_multi_perform(clc.downloadCURLM, &c); while(res == CURLM_CALL_MULTI_PERFORM && i < 100) { res = qcurl_multi_perform(clc.downloadCURLM, &c); i++; } if(res == CURLM_CALL_MULTI_PERFORM) return; msg = qcurl_multi_info_read(clc.downloadCURLM, &c); if(msg == NULL) { return; } FS_FCloseFile(clc.download); if(msg->msg == CURLMSG_DONE && msg->data.result == CURLE_OK) { FS_SV_Rename(clc.downloadTempName, clc.downloadName, false); clc.downloadRestart = true; } else { long code; qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code); Com_Error(ERR_DROP, "Download Error: %s Code: %ld URL: %s", qcurl_easy_strerror(msg->data.result), code, clc.downloadURL); } CL_NextDownload(); }
/* ==================== Curl_GetDownloadSpeed returns the speed of the given download in bytes per second ==================== */ static double Curl_GetDownloadSpeed(downloadinfo *di) { if(!curl_dll) return -2; if(di->curle) { double speed; qcurl_easy_getinfo(di->curle, CURLINFO_SPEED_DOWNLOAD, &speed); return speed; } else return -1; }
// Some versions of CURL don't report the correct exepected size when following redirects // This manual interpretation of the expected size fixes this. static size_t wswcurl_readheader( void *ptr, size_t size, size_t nmemb, void *stream ) { char buf[1024], *str; int slen; wswcurl_req *req = (wswcurl_req*)stream; memset( buf, 0, sizeof( buf ) ); memcpy( buf, ptr, ( size * nmemb ) > ( sizeof( buf ) - 1 ) ? ( sizeof( buf ) - 1 ) : ( size * nmemb ) ); str = buf; while( *str ) { if( ( *str >= 'a' ) && ( *str <= 'z' ) ) { *str -= 'a' - 'A'; } str++; } if( ( str = (char*)strstr( buf, "CONTENT-LENGTH:" ) ) ) { int length; while( *str && ( *str != ':' ) ) { str++; } str++; while( *str && ( *str == ' ' ) ) { str++; } slen = strlen( str ) - 1; while( ( slen > 0 ) && ( ( str[slen] == '\n' ) || ( str[slen] == '\r' ) || ( str[slen] == ' ' ) ) ) { str[slen] = '\0'; slen = strlen( str ) - 1; } length = atoi( str ); if( length >= 0 ) { req->rx_expsize = length; } } else if( ( str = (char*)strstr( buf, "TRANSFER-ENCODING:" ) ) ) { req->rx_expsize = 0; } qcurl_easy_getinfo( req->curl, CURLINFO_RESPONSE_CODE, &( req->respcode ) ); // call header callback function if( req->callback_header ) { req->callback_header( req, buf, req->customp ); } req->last_action = wswcurl_now(); return size * nmemb; }
/* ==================== Curl_GetDownloadAmount returns a value from 0.0 to 1.0 which represents the downloaded amount of data for the given download. ==================== */ static double Curl_GetDownloadAmount(downloadinfo *di) { if(!curl_dll) return -2; if(di->curle) { double length; qcurl_easy_getinfo(di->curle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length); if(length > 0) return (di->startpos + di->bytes_received) / (di->startpos + length); else return 0; } else return -1; }
void CL_cURL_PerformDownload(void) { CURLMcode res; CURLMsg *msg; int c; int i = 0; res = qcurl_multi_perform(clc.downloadCURLM, &c); while(res == CURLM_CALL_MULTI_PERFORM && i < 100) { res = qcurl_multi_perform(clc.downloadCURLM, &c); i++; } if(res == CURLM_CALL_MULTI_PERFORM) return; msg = qcurl_multi_info_read(clc.downloadCURLM, &c); if(msg == NULL) { return; } FS_FCloseFile(clc.download); if(msg->msg == CURLMSG_DONE && msg->data.result == CURLE_OK) { FS_SV_Rename(clc.downloadTempName, clc.downloadName); } else { long code; qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code); Com_Error(ERR_DROP, "Download Error: %s Code: %d", qcurl_easy_strerror(msg->data.result), code); } *clc.downloadTempName = *clc.downloadName = 0; Cvar_Set( "cl_downloadName", "" ); CL_cURL_Cleanup(); FS_Restart(clc.checksumFeed); CL_Reconnect_f(); }
/* ==================== Curl_Run call this regularily as this will always download as much as possible without blocking. ==================== */ void Curl_Run(void) { double maxspeed; downloadinfo *di; noclear = FALSE; if(!cl_curl_enabled.integer) return; if(!curl_dll) return; Curl_CheckCommandWhenDone(); if(!downloads) return; if(realtime < curltime) // throttle return; { int remaining; CURLMcode mc; do { mc = qcurl_multi_perform(curlm, &remaining); } while(mc == CURLM_CALL_MULTI_PERFORM); for(di = downloads; di; di = di->next) { double b = 0; qcurl_easy_getinfo(di->curle, CURLINFO_SIZE_UPLOAD, &b); bytes_sent += (b - di->bytes_sent_curl); di->bytes_sent_curl = b; qcurl_easy_getinfo(di->curle, CURLINFO_SIZE_DOWNLOAD, &b); bytes_sent += (b - di->bytes_received_curl); di->bytes_received_curl = b; } for(;;) { CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining); if(!msg) break; if(msg->msg == CURLMSG_DONE) { CurlStatus failed = CURL_DOWNLOAD_SUCCESS; CURLcode result; qcurl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &di); result = msg->data.result; if(result) { failed = CURL_DOWNLOAD_FAILED; } else { long code; qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code); switch(code / 100) { case 4: // e.g. 404? case 5: // e.g. 500? failed = CURL_DOWNLOAD_SERVERERROR; result = (CURLcode) code; break; } } Curl_EndDownload(di, failed, result); } } } CheckPendingDownloads(); // when will we curl the next time? // we will wait a bit to ensure our download rate is kept. // we now know that realtime >= curltime... so set up a new curltime // use the slowest allowing download to derive the maxspeed... this CAN // be done better, but maybe later maxspeed = cl_curl_maxspeed.value; for(di = downloads; di; di = di->next) if(di->maxspeed > 0) if(di->maxspeed < maxspeed || maxspeed <= 0) maxspeed = di->maxspeed; if(maxspeed > 0) { double bytes = bytes_sent + bytes_received; // maybe smoothen a bit? curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0); bytes_sent = 0; bytes_received = 0; } else curltime = realtime; }
const char *wswcurl_get_effective_url( wswcurl_req *req ) { char *last_url = NULL; qcurl_easy_getinfo( req->curl, CURLINFO_EFFECTIVE_URL, &last_url ); return last_url; }
const char *wswcurl_getip( wswcurl_req *req ) { char *ipstr = NULL; qcurl_easy_getinfo( req->curl, CURLINFO_PRIMARY_IP, &ipstr ); return ipstr; }
const char *wswcurl_get_content_type( wswcurl_req *req ) { char *content_type = NULL; qcurl_easy_getinfo( req->curl, CURLINFO_CONTENT_TYPE, &content_type ); return content_type; }