diff --git a/src/gnuwin32/embeddedR.c b/src/gnuwin32/embeddedR.c index b76aaaf5d78..08ef3b7d3df 100644 --- a/src/gnuwin32/embeddedR.c +++ b/src/gnuwin32/embeddedR.c @@ -141,6 +141,7 @@ void Rf_endEmbeddedR(int fatal) { R_RunExitFinalizers(); CleanEd(); + CurlCleanup(); R_CleanTempDir(); if(!fatal){ Rf_KillAllDevices(); diff --git a/src/gnuwin32/system.c b/src/gnuwin32/system.c index a3592a01a37..fc0256ab475 100644 --- a/src/gnuwin32/system.c +++ b/src/gnuwin32/system.c @@ -603,6 +603,7 @@ static void Rstd_CleanUp(SA_TYPE saveact, int status, int runLast) CleanEd(); KillAllDevices(); /* Unix does not do this under SA_SUICIDE */ AllDevicesKilled = TRUE; /* used in devWindows.c to inhibit callbacks */ + CurlCleanup(); R_CleanTempDir(); /* changes directory */ if (R_Interactive && CharacterMode == RTerm) SetConsoleTitle(oldtitle); diff --git a/src/include/Defn.h b/src/include/Defn.h index 21daaf4ae05..b6874f654d4 100644 --- a/src/include/Defn.h +++ b/src/include/Defn.h @@ -2294,6 +2294,7 @@ SEXP fixup_NaRm(SEXP args); /* summary.c */ void invalidate_cached_recodings(void); /* from sysutils.c */ void resetICUcollator(Rboolean disable); /* from util.c */ void dt_invalidate_locale(void); /* from Rstrptime.h */ +extern void CurlCleanup(void); /* from internet.c */ extern int R_OutputCon; /* from connections.c */ extern int R_InitReadItemDepth, R_ReadItemDepth; /* from serialize.c */ void get_current_mem(size_t *,size_t *,size_t *); /* from memory.c */ diff --git a/src/include/Rmodules/Rinternet.h b/src/include/Rmodules/Rinternet.h index 481541739f5..58652818f1b 100644 --- a/src/include/Rmodules/Rinternet.h +++ b/src/include/Rmodules/Rinternet.h @@ -45,6 +45,7 @@ typedef int (*R_HTTPDCreateRoutine)(const char *ip, int port); typedef void (*R_HTTPDStopRoutine)(void); typedef SEXP (*R_CurlRoutine)(SEXP call, SEXP op, SEXP args, SEXP rho); +typedef void (*R_CurlCleanupRoutine)(void); typedef struct { R_DownloadRoutine download; @@ -67,6 +68,7 @@ typedef struct { R_CurlRoutine curlVersion; R_CurlRoutine curlGetHeaders; R_CurlRoutine curlDownload; + R_CurlCleanupRoutine curlCleanup; R_NewUrlRoutine newcurlurl; } R_InternetRoutines; diff --git a/src/main/internet.c b/src/main/internet.c index 05058d56f47..83641db6567 100644 --- a/src/main/internet.c +++ b/src/main/internet.c @@ -285,6 +285,15 @@ attribute_hidden SEXP do_curlDownload(SEXP call, SEXP op, SEXP args, SEXP rho) } } +void CurlCleanup(void) +{ + if(!initialized) internet_Init(); + if(initialized > 0) + (*ptr->curlCleanup)(); + else + error(_("internet routines cannot be loaded")); +} + Rconnection attribute_hidden R_newCurlUrl(const char *description, const char * const mode, SEXP headers, int type) { diff --git a/src/modules/internet/internet.c b/src/modules/internet/internet.c index 3404cda92ad..eec274c10bf 100644 --- a/src/modules/internet/internet.c +++ b/src/modules/internet/internet.c @@ -39,6 +39,7 @@ typedef int_fast64_t DLsize_t; // used for download lengths and sizes SEXP in_do_curlVersion(SEXP call, SEXP op, SEXP args, SEXP rho); SEXP in_do_curlGetHeaders(SEXP call, SEXP op, SEXP args, SEXP rho); SEXP in_do_curlDownload(SEXP call, SEXP op, SEXP args, SEXP rho); +void in_curlCleanup(void); Rconnection in_newCurlUrl(const char *description, const char * const mode, SEXP headers, int type); @@ -729,6 +730,7 @@ R_init_internet(DllInfo *info) tmp->curlVersion = in_do_curlVersion; tmp->curlGetHeaders = in_do_curlGetHeaders; tmp->curlDownload = in_do_curlDownload; + tmp->curlCleanup = in_curlCleanup; tmp->newcurlurl = in_newCurlUrl; R_setInternetRoutines(tmp); diff --git a/src/modules/internet/libcurl.c b/src/modules/internet/libcurl.c index e84b9ccd39a..cddb9fcf8e9 100644 --- a/src/modules/internet/libcurl.c +++ b/src/modules/internet/libcurl.c @@ -55,13 +55,31 @@ extern void Rsleep(double timeint); static int current_timeout = 0; +// The multi-handle is shared between downloads for reusing connections +static CURLM *shared_mhnd = NULL; + +static CURLM attribute_hidden *get_mhnd(void) +{ + if(!shared_mhnd) + shared_mhnd = curl_multi_init(); + return shared_mhnd; +} + +void attribute_hidden in_curlCleanup(void) +{ + if(shared_mhnd){ + curl_multi_cleanup(shared_mhnd); + shared_mhnd = NULL; + } + curl_global_cleanup(); +} + # if LIBCURL_VERSION_MAJOR < 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 28) // curl/curl.h includes and headers it requires. #define curl_multi_wait R_curl_multi_wait - static CURLMcode R_curl_multi_wait(CURLM *multi_handle, /* IGNORED */ void *unused, @@ -565,8 +583,6 @@ static void download_cleanup(void *data) if (c->hnd && c->hnd[i]) curl_easy_cleanup(c->hnd[i]); } - if (c->mhnd) - curl_multi_cleanup(c->mhnd); if (c->headers) curl_slist_free_all(c->headers); @@ -668,7 +684,8 @@ in_do_curlDownload(SEXP call, SEXP op, SEXP args, SEXP rho) c.headers = headers = tmp; } - CURLM *mhnd = curl_multi_init(); + CURLM *mhnd = get_mhnd(); + if (!mhnd) error(_("could not create curl handle")); c.mhnd = mhnd; diff --git a/src/unix/Rembedded.c b/src/unix/Rembedded.c index 4fa114ad2e5..9c4ed374661 100644 --- a/src/unix/Rembedded.c +++ b/src/unix/Rembedded.c @@ -70,6 +70,7 @@ void Rf_endEmbeddedR(int fatal) R_RunExitFinalizers(); CleanEd(); if(!fatal) KillAllDevices(); + CurlCleanup(); R_CleanTempDir(); if(!fatal && R_CollectWarnings) PrintWarnings(); /* from device close and .Last */ diff --git a/src/unix/sys-std.c b/src/unix/sys-std.c index 3d355238e76..5cb251a3227 100644 --- a/src/unix/sys-std.c +++ b/src/unix/sys-std.c @@ -1242,6 +1242,7 @@ void Rstd_CleanUp(SA_TYPE saveact, int status, int runLast) R_RunExitFinalizers(); CleanEd(); if(saveact != SA_SUICIDE) KillAllDevices(); + CurlCleanup(); R_CleanTempDir(); if(saveact != SA_SUICIDE && R_CollectWarnings) PrintWarnings(); /* from device close and (if run) .Last */