|
|
|
@@ -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<CURLSymbols> create()
|
|
|
|
{
|
|
|
|
std::unique_ptr<CURLSymbols> 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 <typename FuncPtr>
|
|
|
|
bool loadSymbol (FuncPtr& dst, const char* name)
|
|
|
|
{
|
|
|
|
dst = reinterpret_cast<FuncPtr> (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<long> (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<long> (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<curl_off_t> (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<curl_off_t> (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<int> (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<int64> (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<CURLSymbols> symbols { CURLSymbols::create() };
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
// curl stuff
|
|
|
|
|