From baa8bbf300128c2e8e1ba1ff7d7e195d309bcb8f Mon Sep 17 00:00:00 2001 From: hogliux Date: Fri, 15 Jun 2018 17:03:32 +0100 Subject: [PATCH] Linux: Added an option to lazily load libcurl symbols only when they are needed --- .../ProjectSaving/jucer_ProjectExport_Make.h | 12 ++ modules/juce_core/juce_core.h | 18 ++- .../juce_core/native/juce_curl_Network.cpp | 139 +++++++++++++----- 3 files changed, 133 insertions(+), 36 deletions(-) diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h index 5b30f2ef4e..3cbc270360 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h @@ -468,6 +468,10 @@ private: packages.add ("gtk+-x11-3.0"); } + // don't add libcurl if curl symbols are loaded at runtime + if (! isLoadCurlSymbolsLazilyEnabled()) + packages.add ("libcurl"); + packages.removeDuplicates (false); return packages; @@ -608,6 +612,14 @@ private: && project.isConfigFlagEnabled ("JUCE_WEB_BROWSER", true)); } + bool isLoadCurlSymbolsLazilyEnabled() const + { + static String juceCoreModule ("juce_core"); + + return (project.getModules().isModuleEnabled (juceCoreModule) + && project.isConfigFlagEnabled ("JUCE_LOAD_CURL_SYMBOLS_LAZILY", false)); + } + //============================================================================== void writeDefineFlags (OutputStream& out, const MakeBuildConfiguration& config) const { diff --git a/modules/juce_core/juce_core.h b/modules/juce_core/juce_core.h index 0c4951088e..b95a958fec 100644 --- a/modules/juce_core/juce_core.h +++ b/modules/juce_core/juce_core.h @@ -42,7 +42,6 @@ OSXFrameworks: Cocoa IOKit iOSFrameworks: Foundation linuxLibs: rt dl pthread - linuxPackages: libcurl mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm END_JUCE_MODULE_DECLARATION @@ -138,7 +137,22 @@ If you disable this then https/ssl support will not be available on linux. */ #ifndef JUCE_USE_CURL - #define JUCE_USE_CURL 0 + #if JUCE_LINUX + #define JUCE_USE_CURL 1 + #else + #define JUCE_USE_CURL 0 + #endif +#endif + +/** Config: JUCE_LOAD_CURL_SYMBOLS_LAZILY + If enabled, JUCE will load libcurl lazily when required (for example, when WebInputStream + is used). Enabling this flag may also help with library dependency erros as linking + libcurl at compile-time may instruct the linker to hard depend on a specific version + of libcurl. It's also useful if you want to limit the amount of JUCE dependencies and + you are not using WebInputStream or the URL classes. +*/ +#ifndef JUCE_LOAD_CURL_SYMBOLS_LAZILY + #define JUCE_LOAD_CURL_SYMBOLS_LAZILY 0 #endif diff --git a/modules/juce_core/native/juce_curl_Network.cpp b/modules/juce_core/native/juce_curl_Network.cpp index 0e9927462c..be830086d3 100644 --- a/modules/juce_core/native/juce_curl_Network.cpp +++ b/modules/juce_core/native/juce_curl_Network.cpp @@ -23,6 +23,74 @@ namespace juce { +struct CURLSymbols +{ + CURL* (*curl_easy_init) (void); + CURLcode (*curl_easy_setopt) (CURL *curl, CURLoption option, ...); + void (*curl_easy_cleanup) (CURL *curl); + CURLcode (*curl_easy_getinfo) (CURL *curl, CURLINFO info, ...); + CURLMcode (*curl_multi_add_handle) (CURLM *multi_handle, CURL *curl_handle); + CURLMcode (*curl_multi_cleanup) (CURLM *multi_handle); + CURLMcode (*curl_multi_fdset) (CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd); + CURLMsg* (*curl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue); + CURLM* (*curl_multi_init) (void); + CURLMcode (*curl_multi_perform) (CURLM *multi_handle, int *running_handles); + CURLMcode (*curl_multi_remove_handle) (CURLM *multi_handle, CURL *curl_handle); + CURLMcode (*curl_multi_timeout) (CURLM *multi_handle, long *milliseconds); + struct curl_slist* (*curl_slist_append) (struct curl_slist *, const char *); + void (*curl_slist_free_all) (struct curl_slist *); + curl_version_info_data* (*curl_version_info) (CURLversion); + + static std::unique_ptr create() + { + std::unique_ptr symbols (new CURLSymbols); + + #if JUCE_LOAD_CURL_SYMBOLS_LAZILY + #define JUCE_INIT_CURL_SYMBOL(name) if (! symbols->loadSymbol (symbols->name, #name)) return nullptr; + #else + #define JUCE_INIT_CURL_SYMBOL(name) symbols->name = ::name; + #endif + + JUCE_INIT_CURL_SYMBOL (curl_easy_init) + JUCE_INIT_CURL_SYMBOL (curl_easy_setopt) + JUCE_INIT_CURL_SYMBOL (curl_easy_cleanup) + JUCE_INIT_CURL_SYMBOL (curl_easy_getinfo) + JUCE_INIT_CURL_SYMBOL (curl_multi_add_handle) + JUCE_INIT_CURL_SYMBOL (curl_multi_cleanup) + JUCE_INIT_CURL_SYMBOL (curl_multi_fdset) + JUCE_INIT_CURL_SYMBOL (curl_multi_info_read) + JUCE_INIT_CURL_SYMBOL (curl_multi_init) + JUCE_INIT_CURL_SYMBOL (curl_multi_perform) + JUCE_INIT_CURL_SYMBOL (curl_multi_remove_handle) + JUCE_INIT_CURL_SYMBOL (curl_multi_timeout) + JUCE_INIT_CURL_SYMBOL (curl_slist_append) + JUCE_INIT_CURL_SYMBOL (curl_slist_free_all) + JUCE_INIT_CURL_SYMBOL (curl_version_info) + + return symbols; + } + +private: + CURLSymbols() = default; + + #if JUCE_LOAD_CURL_SYMBOLS_LAZILY + static DynamicLibrary& getLibcurl() + { + static DynamicLibrary libcurl { "libcurl.so" }; + return libcurl; + } + + template + bool loadSymbol (FuncPtr& dst, const char* name) + { + dst = reinterpret_cast (getLibcurl().getFunction (name)); + return (dst != nullptr); + } + #endif +}; + + +//============================================================================== class WebInputStream::Pimpl { public: @@ -30,14 +98,16 @@ public: : owner (ownerStream), url (urlToCopy), isPost (shouldUsePost), httpRequest (isPost ? "POST" : "GET") { - multi = curl_multi_init(); + jassert (symbols); // Unable to load libcurl! + + multi = symbols->curl_multi_init(); if (multi != nullptr) { - curl = curl_easy_init(); + curl = symbols->curl_easy_init(); if (curl != nullptr) - if (curl_multi_add_handle (multi, curl) == CURLM_OK) + if (symbols->curl_multi_add_handle (multi, curl) == CURLM_OK) return; } @@ -103,21 +173,21 @@ public: if (curl != nullptr) { - curl_multi_remove_handle (multi, curl); + symbols->curl_multi_remove_handle (multi, curl); if (headerList != nullptr) { - curl_slist_free_all (headerList); + symbols->curl_slist_free_all (headerList); headerList = nullptr; } - curl_easy_cleanup (curl); + symbols->curl_easy_cleanup (curl); curl = nullptr; } if (multi != nullptr) { - curl_multi_cleanup (multi); + symbols->curl_multi_cleanup (multi); multi = nullptr; } } @@ -132,7 +202,7 @@ public: { auto address = url.toString (! isPost); - curl_version_info_data* data = curl_version_info (CURLVERSION_NOW); + curl_version_info_data* data = symbols->curl_version_info (CURLVERSION_NOW); jassert (data != nullptr); if (! requestHeaders.endsWithChar ('\n')) @@ -146,22 +216,22 @@ public: auto userAgent = String ("curl/") + data->version; - if (curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK - && curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK - && curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK - && curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK - && curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast (maxRedirects)) == CURLE_OK - && curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK - && curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK + && symbols->curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK + && symbols->curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK + && symbols->curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK + && symbols->curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast (maxRedirects)) == CURLE_OK + && symbols->curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK + && symbols->curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK) { if (isPost) { - if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK - || curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK + || symbols->curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK) return false; - if (curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK - || curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast (headersAndPostData.getSize())) != CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK + || symbols->curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast (headersAndPostData.getSize())) != CURLE_OK) return false; } @@ -169,20 +239,20 @@ public: bool hasSpecialRequestCmd = isPost ? (httpRequest != "POST") : (httpRequest != "GET"); if (hasSpecialRequestCmd) - if (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK) return false; - if (curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK - || curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK + || symbols->curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK) return false; if (timeOutMs > 0) { auto timeOutSecs = ((long) timeOutMs + 999) / 1000; - if (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK - || curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK - || curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK + || symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK + || symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK) return false; } @@ -212,10 +282,10 @@ public: // fromLines will always return at least one line if the string is not empty jassert (headerLines.size() > 0); - headerList = curl_slist_append (headerList, headerLines [0].toRawUTF8()); + headerList = symbols->curl_slist_append (headerList, headerLines [0].toRawUTF8()); for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i) - headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8()); + headerList = symbols->curl_slist_append (headerList, headerLines [i].toRawUTF8()); if (headerList == nullptr) { @@ -223,7 +293,7 @@ public: return false; } - if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK) + if (symbols->curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK) { cleanup(); return false; @@ -272,12 +342,12 @@ public: return false; long responseCode; - if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) + if (symbols->curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) statusCode = static_cast (responseCode); // get content length size double curlLength; - if (curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK) + if (symbols->curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK) contentLength = static_cast (curlLength); } @@ -295,7 +365,7 @@ public: { int cnt = 0; - if (CURLMsg* msg = curl_multi_info_read (multi, &cnt)) + if (CURLMsg* msg = symbols->curl_multi_info_read (multi, &cnt)) { if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl) { @@ -328,7 +398,7 @@ public: if (multi == nullptr) return; - if ((lastError = (int) curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK) + if ((lastError = (int) symbols->curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK) return; } @@ -350,7 +420,7 @@ public: if (multi == nullptr) return; - if ((lastError = (int) curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK) + if ((lastError = (int) symbols->curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK) return; } @@ -374,7 +444,7 @@ public: { const ScopedLock lock (cleanupLock); - while ((curlRet = (int) curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM) + while ((curlRet = (int) symbols->curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM) {} } @@ -510,6 +580,7 @@ public: //============================================================================== WebInputStream& owner; const URL url; + std::unique_ptr symbols { CURLSymbols::create() }; //============================================================================== // curl stuff