Threaded HTTP downloader

Composite of the work of:
- Hacinef (SRB2 repository, https://git.do.srb2.org/STJr/SRB2/-/merge_requests/2322 )
- alufolie91 (SRB2Kart-Saturn repository, 84bfb3991e )
This advancement made possible by Indev in the KKD discord

Untested because I don't know how to set up a HTTP download server
This commit is contained in:
toaster 2025-08-24 18:55:02 +01:00
parent 10fdba373d
commit 8bd29a1011
4 changed files with 105 additions and 60 deletions

View file

@ -2067,8 +2067,10 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
break; break;
} }
#ifndef HAVE_THREADS
if (curl_running) if (curl_running)
CURLGetFile(); CURLGetFile();
#endif
if (waitmore) if (waitmore)
break; // exit the case break; // exit the case

View file

@ -1205,6 +1205,7 @@ void D_ClearState(void)
// okay, stop now // okay, stop now
// (otherwise the game still thinks we're playing!) // (otherwise the game still thinks we're playing!)
CURLAbortFile();
SV_StopServer(); SV_StopServer();
SV_ResetServer(); SV_ResetServer();
serverlistultimatecount = 0; serverlistultimatecount = 0;

View file

@ -101,6 +101,9 @@ static filetran_t transfer[MAXNETNODES];
INT32 fileneedednum; // Number of files needed to join the server INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
static tic_t lasttimeackpacketsent = 0; static tic_t lasttimeackpacketsent = 0;
#ifdef HAVE_THREADS
static I_mutex downloadmutex;
#endif
// For resuming failed downloads // For resuming failed downloads
typedef struct typedef struct
@ -1829,10 +1832,13 @@ void CURLPrepareFile(const char* url, int dfilenum)
I_Error("Attempted to download files in -nodownload mode"); I_Error("Attempted to download files in -nodownload mode");
#endif #endif
if (!multi_handle)
{
curl_global_init(CURL_GLOBAL_ALL); curl_global_init(CURL_GLOBAL_ALL);
multi_handle = curl_multi_init();
}
http_handle = curl_easy_init(); http_handle = curl_easy_init();
multi_handle = curl_multi_init();
if (http_handle && multi_handle) if (http_handle && multi_handle)
{ {
@ -1883,34 +1889,54 @@ void CURLPrepareFile(const char* url, int dfilenum)
curl_multi_add_handle(multi_handle, http_handle); curl_multi_add_handle(multi_handle, http_handle);
curl_multi_perform(multi_handle, &curl_runninghandles); curl_multi_perform(multi_handle, &curl_runninghandles);
curl_starttime = time(NULL); curl_starttime = time(NULL);
curl_running = true; curl_running = true;
#ifdef HAVE_THREADS
I_spawn_thread("http-download", (I_thread_fn)CURLGetFile, NULL);
#endif
} }
} }
void CURLAbortFile(void)
{
curl_running = false;
#ifdef HAVE_THREADS
// lock and unlock to wait for the download thread to exit
I_lock_mutex(&downloadmutex);
I_unlock_mutex(downloadmutex);
#endif
}
void CURLGetFile(void) void CURLGetFile(void)
{ {
CURLMcode mc; /* return code used by curl_multi_wait() */ CURLMcode mc; /* return code used by curl_multi_wait() */
CURLcode easyres; /* Return from easy interface */ CURLcode easyres; /* Return from easy interface */
int numfds;
CURLMsg *m; /* for picking up messages with the transfer status */ CURLMsg *m; /* for picking up messages with the transfer status */
CURL *e; CURL *e;
int msgs_left; /* how many messages are left */ int msgs_left; /* how many messages are left */
const char *easy_handle_error; const char *easy_handle_error;
long response_code = 0;
static char *filename;
#ifdef HAVE_THREADS
boolean running = true;
I_lock_mutex(&downloadmutex);
while (running && curl_running)
#endif
{
if (curl_runninghandles) if (curl_runninghandles)
{ {
curl_multi_perform(multi_handle, &curl_runninghandles); curl_multi_perform(multi_handle, &curl_runninghandles);
/* wait for activity, timeout or "nothing" */ /* wait for activity, timeout or "nothing" */
mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); mc = curl_multi_wait(multi_handle, NULL, 0, 1000, NULL);
if (mc != CURLM_OK) if (mc != CURLM_OK)
{ {
CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc); CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
return; continue;
} }
curl_curfile->currentsize = curl_dlnow; curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal; curl_curfile->totalsize = curl_dltotal;
@ -1921,16 +1947,23 @@ void CURLGetFile(void)
{ {
if (m && (m->msg == CURLMSG_DONE)) if (m && (m->msg == CURLMSG_DONE))
{ {
#ifdef HAVE_THREADS
running = false;
#endif
e = m->easy_handle; e = m->easy_handle;
easyres = m->data.result; easyres = m->data.result;
filename = Z_StrDup(curl_realname);
char *filename = Z_StrDup(curl_realname);
nameonly(filename); nameonly(filename);
if (easyres != CURLE_OK) if (easyres != CURLE_OK)
{ {
long response_code = 0;
if (easyres == CURLE_HTTP_RETURNED_ERROR) if (easyres == CURLE_HTTP_RETURNED_ERROR)
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code); curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
easy_handle_error = (response_code) ? va("HTTP reponse code %ld", response_code) : curl_easy_strerror(easyres); easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
curl_curfile->status = FS_FALLBACK; curl_curfile->status = FS_FALLBACK;
curl_curfile->currentsize = curl_origfilesize; curl_curfile->currentsize = curl_origfilesize;
curl_curfile->totalsize = curl_origtotalfilesize; curl_curfile->totalsize = curl_origtotalfilesize;
@ -1958,10 +1991,11 @@ void CURLGetFile(void)
} }
} }
Z_Free(filename); Z_Free(filename);
curl_curfile->file = NULL; curl_curfile->file = NULL;
#ifndef HAVE_THREADS
curl_running = false; curl_running = false;
#endif
curl_transfers--; curl_transfers--;
curl_multi_remove_handle(multi_handle, e); curl_multi_remove_handle(multi_handle, e);
curl_easy_cleanup(e); curl_easy_cleanup(e);
@ -1970,12 +2004,19 @@ void CURLGetFile(void)
break; break;
} }
} }
}
if (!curl_transfers) if (!curl_transfers || !curl_running)
{ {
curl_multi_cleanup(multi_handle); curl_multi_cleanup(multi_handle);
curl_global_cleanup(); curl_global_cleanup();
multi_handle = NULL;
} }
#ifdef HAVE_THREADS
curl_running = false;
I_unlock_mutex(downloadmutex);
#endif
} }
HTTP_login * HTTP_login *

View file

@ -166,6 +166,7 @@ size_t nameonlylength(const char *s);
#ifdef HAVE_CURL #ifdef HAVE_CURL
void CURLPrepareFile(const char* url, int dfilenum); void CURLPrepareFile(const char* url, int dfilenum);
void CURLAbortFile(void);
void CURLGetFile(void); void CURLGetFile(void);
HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next); HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);
#endif #endif