void wswcurl_start(wswcurl_req *req) { CURLcode res; if (!req) return; if (req->status) return; // was already started if (!req) return; if (!req->curl) { wswcurl_delete(req); return; } if (req->txhead) { CURLSETOPT(req->curl, res, CURLOPT_HTTPHEADER, req->txhead); } if (req->post) { CURLSETOPT(req->curl, res, CURLOPT_HTTPPOST, req->post); req->post_last = NULL; } // Initialize multi handle if needed if (curlmulti == NULL) { curlmulti = curl_multi_init(); if (curlmulti == NULL) { CURLDBG(("OOPS: CURL MULTI NULL!!!")); } } if (curl_multi_add_handle(curlmulti, req->curl)) { CURLDBG(("OOPS: CURL MULTI ADD HANDLE FAIL!!!")); } req->status = 1; // Started }
int wswcurl_perform() { int ret = 0; wswcurl_req *r; if (!curlmulti) return 0; r = http_requests; while (r) { r->activity = 0; r = r->next; } //CURLDBG(("CURL BEFORE MULTI_PERFORM\n")); while ( curl_multi_perform(curlmulti, &ret) == CURLM_CALL_MULTI_PERFORM) { CURLDBG((" CURL MULTI LOOP\n")); } ret += wswcurl_checkmsg(); //CURLDBG(("CURL after checkmsg\n")); return ret; }
size_t wswcurl_getsize( wswcurl_req *req, size_t *rxreceived ) { #if 0 while( !req->headers_done && req->status >= 0 && req->status != WSTATUS_FINISHED/* && req->status != WSTATUS_QUEUED*/ ) { // blocking read until we finish reading all headers CURLDBG((" CURL BLOCKING GETSIZE LOOP\n")); wswcurl_perform_single( req ); } #endif if( rxreceived ) { *rxreceived = req->rxreceived; } if( req->status < 0 ) { return 0; } return req->rx_expsize; }
wswcurl_req *wswcurl_create( const char *furl, ... ) { wswcurl_req *retreq; CURL *curl; CURLcode res; char url[4 * 1024]; // 4kb url buffer? va_list arg; const char *proxy = http_proxy->string; const char *proxy_userpwd = http_proxyuserpwd->string; // Prepare url formatting with variable arguments va_start( arg, furl ); Q_vsnprintfz( url, sizeof( url ), furl, arg ); va_end( arg ); // Initialize structure if( !(curl = curl_easy_init()) ) { return NULL; } // allocate, copy retreq = ( wswcurl_req * )WMALLOC( sizeof( wswcurl_req ) ); memset( retreq, 0, sizeof( *retreq ) ); retreq->curl = curl; retreq->url = ( char* )WMALLOC( strlen( url ) + 1 ); memcpy( retreq->url, url, strlen( url ) + 1 ); CURLSETOPT( curl, res, CURLOPT_URL, retreq->url ); CURLSETOPT( curl, res, CURLOPT_WRITEFUNCTION, wswcurl_write ); CURLSETOPT( curl, res, CURLOPT_NOPROGRESS, 1 ); CURLSETOPT( curl, res, CURLOPT_FOLLOWLOCATION, 1 ); CURLSETOPT( curl, res, CURLOPT_HEADERFUNCTION, wswcurl_readheader ); CURLSETOPT( curl, res, CURLOPT_CONNECTTIMEOUT, WCONNECTTIMEOUT ); #if defined( APPLICATION ) && defined( APP_VERSION_STR ) && defined( OSNAME ) && defined( CPUSTRING ) CURLSETOPT( curl, res, CURLOPT_USERAGENT, APPLICATION"/"APP_VERSION_STR" (compatible; N; "OSNAME"; "CPUSTRING")" ); #endif CURLSETOPT( curl, res, CURLOPT_WRITEDATA, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_WRITEHEADER, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_PRIVATE, ( void * )retreq ); if( developer->integer ) { CURLSETOPT( curl, res, CURLOPT_DEBUGFUNCTION, &wswcurl_debug_callback ); CURLSETOPT( curl, res, CURLOPT_DEBUGDATA, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_VERBOSE, 1 ); } // HTTP proxy settings if( proxy && *proxy ) { CURLSETOPT( curl, res, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); CURLSETOPT( curl, res, CURLOPT_PROXY, proxy ); if( proxy_userpwd && *proxy_userpwd ) { CURLSETOPT( curl, res, CURLOPT_PROXYUSERPWD, proxy_userpwd ); } } wswcurl_set_timeout( retreq, WTIMEOUT ); // link retreq->prev = NULL; retreq->next = http_requests; if( retreq->next ) { retreq->next->prev = retreq; } else { http_requests_hnode = retreq; } http_requests = retreq; CURLDBG((va(" CURL CREATE %s\n", url))); return retreq; }
int wswcurl_perform() { int ret = 0; wswcurl_req *r, *next; if (!curlmulti) return 0; // process requests in FIFO manner // check for timed out requests and requests that need to be paused r = http_requests_hnode; while( r ) { next = r->prev; if (r->status == WSTATUS_QUEUED) { // queued if (curlmulti_num_handles < WMAXMULTIHANDLES) { if (curl_multi_add_handle(curlmulti, r->curl)) { CURLDBG(("OOPS: CURL MULTI ADD HANDLE FAIL!!!")); } r->status = WSTATUS_STARTED; r->last_action = wswcurl_now(); curlmulti_num_handles++; } else { // stay in queue } } // handle pauses for synchronous requests if( r->status == WSTATUS_STARTED && !r->callback_read ) { if( r->rxreceived >= r->rxreturned + WMAXBUFFERING ) { wswcurl_pause( r ); } } // handle timeouts if( r->status == WSTATUS_STARTED ) { time_t now = wswcurl_now(); if( r->paused ) { // paused r->last_action = now; } else if( r->timeout && ( r->last_action + r->timeout <= now ) ) { // timed out r->respcode = -1; r->status = -CURLE_OPERATION_TIMEDOUT; if( r->callback_done ) { r->callback_done( r, r->status, r->customp ); } } } r = next; } //CURLDBG(("CURL BEFORE MULTI_PERFORM\n")); while ( curl_multi_perform(curlmulti, &ret) == CURLM_CALL_MULTI_PERFORM) { CURLDBG((" CURL MULTI LOOP\n")); } ret += wswcurl_checkmsg(); //CURLDBG(("CURL after checkmsg\n")); return ret; }
size_t wswcurl_read(wswcurl_req *req, void *buffer, size_t size) { size_t written = 0; chained_buffer_t *cb; if( (req->rxreceived-req->rxreturned) < (size+WMINBUFFERING) && req->paused ) wswcurl_unpause(req); #if 0 // Make sure we have data in buffer while ( req->status >= 0 && req->status != WSTATUS_FINISHED && req->status != WSTATUS_QUEUED && (req->rxreceived-req->rxreturned) < size ) { CURLDBG((" CURL BLOCKING READ LOOP\n")); wswcurl_perform_single (req); } #endif // hmm, signal an error? if( req->status < 0 ) return 0; // go through the buffers in chain, dropping them if not needed // start from the beginning (chronological order) cb = req->bhead; while( cb && written < size ) { size_t numb = cb->rxsize - cb->rxoffset; if( numb + written > size ) numb = size - written; if( !numb ) break; if( req->ignore_bytes >= numb ) { req->ignore_bytes -= numb; cb->rxoffset += numb; continue; } else { cb->rxoffset += req->ignore_bytes; req->ignore_bytes = 0; } memcpy( ((char*)buffer)+written, cb->data+cb->rxoffset, numb ); written += numb; cb->rxoffset += numb; if( cb->rxoffset >= cb->rxsize ) { // flush the buffer away req->bhead = cb->next; if( req->btail == cb ) req->btail = NULL; WFREE( cb ); // advance to the next buffer in chain cb = req->bhead; } // else break; } if( written < size ) ((char*)buffer)[written] = '\0'; req->rxreturned += written; #if 0 if( req->paused) Com_Printf(S_COLOR_RED "%d - ", req->rxreturned - req->rxreceived); else Com_Printf(S_COLOR_CYAN "%d - ", req->rxreturned - req->rxreceived); #endif return written; }