Last active
May 20, 2020 07:44
-
-
Save gamapat/44f94ae56c80d27175da9f4f335af1f0 to your computer and use it in GitHub Desktop.
libcurl file download examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CurlGlobalStateGuard | |
{ | |
public: | |
CurlGlobalStateGuard() { curl_global_init(CURL_GLOBAL_DEFAULT); } | |
~CurlGlobalStateGuard() { curl_global_cleanup(); } | |
}; | |
static CurlGlobalStateGuard handle_curl_state; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using EasyHandle = std::unique_ptr<CURL, std::function<void(CURL*)>>; | |
EasyHandle CreateEasyHandle() | |
{ | |
auto curl = EasyHandle(curl_easy_init(), curl_easy_cleanup); | |
if (!curl) | |
{ | |
throw std::runtime_error("Failed creating CURL easy object"); | |
} | |
return curl; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
std::list<EasyHandle> handles(3); | |
/* init easy stacks */ | |
try | |
{ | |
std::for_each(handles.begin(), handles.end(), [](auto& handle) {handle = CreateEasyHandle(); }); | |
} | |
catch (const std::exception& ex) | |
{ | |
std::cerr << ex.what() << std::endl; | |
return -1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
for (auto& handle : handles) | |
{ | |
/* set options */ | |
curl_easy_setopt(handle.get(), CURLOPT_URL, "https://raw.githubusercontent.com/curl/curl/master/docs/examples/https.c"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void set_ssl(CURL* curl) | |
{ | |
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); | |
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); | |
} | |
/* ... */ | |
for (auto& handle : handles) | |
{ | |
set_ssl(handle.get()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace | |
{ | |
size_t write_to_file(void* contents, size_t size, size_t nmemb, void* userp) | |
{ | |
size_t realsize = size * nmemb; | |
auto file = reinterpret_cast<std::ofstream*>(userp); | |
file->write(reinterpret_cast<const char*>(contents), realsize); | |
return realsize; | |
} | |
} | |
void save_to_file(CURL* curl) | |
{ | |
static std::ofstream file("downloaded_data.txt", std::ios::binary); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_file); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast<void*>(&file)); | |
} | |
/* ... */ | |
for (auto& handle : handles) | |
{ | |
set_ssl(handle.get()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
for (auto& handle : handles) | |
{ | |
/* Perform the request, res will get the return code */ | |
auto res = curl_easy_perform(handle.get()); | |
/* Check for errors */ | |
if (res != CURLE_OK) | |
{ | |
std::cerr << "curl_easy_perform() failed:" << | |
curl_easy_strerror(res) << std::endl; | |
return -1; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int download_synchronous(void) | |
{ | |
std::list<EasyHandle> handles(3); | |
/* init easy stacks */ | |
try | |
{ | |
std::for_each(handles.begin(), handles.end(), [](auto& handle) {handle = CreateEasyHandle(); }); | |
} | |
catch (const std::exception& ex) | |
{ | |
std::cerr << ex.what() << std::endl; | |
return -1; | |
} | |
for (auto& handle : handles) | |
{ | |
/* set options */ | |
curl_easy_setopt(handle.get(), CURLOPT_URL, "https://raw.githubusercontent.com/curl/curl/master/docs/examples/https.c"); | |
set_ssl(handle.get()); | |
save_to_file(handle.get()); | |
/* Perform the request, res will get the return code */ | |
auto res = curl_easy_perform(handle.get()); | |
/* Check for errors */ | |
if (res != CURLE_OK) | |
{ | |
std::cerr << "curl_easy_perform() failed:" << | |
curl_easy_strerror(res) << std::endl; | |
return -1; | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MultiHandle = std::unique_ptr<CURLM, std::function<void(CURLM*)>>; | |
MultiHandle CreateMultiHandle() | |
{ | |
auto curl = MultiHandle(curl_multi_init(), curl_multi_cleanup); | |
if (!curl) | |
{ | |
throw std::runtime_error("Failed creating CURL multi object"); | |
} | |
return curl; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
std::list<EasyHandle> handles(3); | |
MultiHandle multi_handle; | |
/* init easy and multi stacks */ | |
try | |
{ | |
multi_handle = CreateMultiHandle(); | |
std::for_each(handles.begin(), handles.end(), [](auto& handle){handle = CreateEasyHandle(); }); | |
} | |
catch (const std::exception& ex) | |
{ | |
std::cerr << ex.what() << std::endl; | |
return -1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* set options */ | |
std::for_each(handles.begin(), handles.end(), [](auto& handle) { | |
curl_easy_setopt(handle.get(), CURLOPT_URL, "https://raw.githubusercontent.com/curl/curl/master/docs/examples/multi-double.c"); | |
set_ssl(handle.get()); | |
save_to_file(handle.get()); | |
}); | |
/* add the individual transfers */ | |
std::for_each(handles.begin(), handles.end(), [&multi_handle](auto& handle) {curl_multi_add_handle(multi_handle.get(), handle.get()); }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int still_running = 0; /* keep number of running handles */ | |
/* we start some action by calling perform right away */ | |
curl_multi_perform(multi_handle, &still_running); | |
while (still_running) { | |
/*...*/ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
timeval get_timeout(CURLM* multi_handle) | |
{ | |
long curl_timeo = -1; | |
/* set a suitable timeout to play around with */ | |
struct timeval timeout; | |
timeout.tv_sec = 1; | |
timeout.tv_usec = 0; | |
curl_multi_timeout(multi_handle, &curl_timeo); | |
if (curl_timeo >= 0) { | |
timeout.tv_sec = curl_timeo / 1000; | |
if (timeout.tv_sec > 1) | |
timeout.tv_sec = 1; | |
else | |
timeout.tv_usec = (curl_timeo % 1000) * 1000; | |
} | |
return timeout; | |
} | |
/*...*/ | |
while (still_running) { | |
struct timeval timeout = get_timeout(multi_handle); | |
/*...*/ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int wait_if_needed(CURLM* multi_handle, timeval& timeout) | |
{ | |
fd_set fdread; | |
fd_set fdwrite; | |
fd_set fdexcep; | |
FD_ZERO(&fdread); | |
FD_ZERO(&fdwrite); | |
FD_ZERO(&fdexcep); | |
int maxfd = -1; | |
/* get file descriptors from the transfers */ | |
auto mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); | |
if (mc != CURLM_OK) { | |
std::cerr << "curl_multi_fdset() failed, code " << mc << "." << std::endl; | |
} | |
/* On success the value of maxfd is guaranteed to be >= -1. We call | |
sleep for 100ms, which is the minimum suggested value in the | |
curl_multi_fdset() doc. */ | |
if (maxfd == -1) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(100)); | |
} | |
int rc = maxfd != -1 ? select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) : 0; | |
return rc; | |
} | |
/*...*/ | |
while (still_running) { | |
/*...*/ | |
auto rc = wait_if_needed(multi_handle, timeout); | |
/*...*/ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
while (still_running) { | |
/*...*/ | |
auto rc = wait_if_needed(multi_handle, timeout); | |
if (rc >= 0) | |
{ | |
/* timeout or readable/writable sockets */ | |
curl_multi_perform(multi_handle, &still_running); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace | |
{ | |
timeval get_timeout(CURLM* multi_handle) | |
{ | |
long curl_timeo = -1; | |
/* set a suitable timeout to play around with */ | |
struct timeval timeout; | |
timeout.tv_sec = 1; | |
timeout.tv_usec = 0; | |
curl_multi_timeout(multi_handle, &curl_timeo); | |
if (curl_timeo >= 0) { | |
timeout.tv_sec = curl_timeo / 1000; | |
if (timeout.tv_sec > 1) | |
timeout.tv_sec = 1; | |
else | |
timeout.tv_usec = (curl_timeo % 1000) * 1000; | |
} | |
return timeout; | |
} | |
int wait_if_needed(CURLM* multi_handle, timeval& timeout) | |
{ | |
fd_set fdread; | |
fd_set fdwrite; | |
fd_set fdexcep; | |
FD_ZERO(&fdread); | |
FD_ZERO(&fdwrite); | |
FD_ZERO(&fdexcep); | |
int maxfd = -1; | |
/* get file descriptors from the transfers */ | |
auto mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); | |
if (mc != CURLM_OK) { | |
std::cerr << "curl_multi_fdset() failed, code " << mc << "." << std::endl; | |
} | |
/* On success the value of maxfd is guaranteed to be >= -1. We call | |
sleep for 100ms, which is the minimum suggested value in the | |
curl_multi_fdset() doc. */ | |
if (maxfd == -1) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(100)); | |
} | |
int rc = maxfd != -1 ? select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) : 0; | |
return rc; | |
} | |
} | |
void multi_loop(CURLM* multi_handle) | |
{ | |
int still_running = 0; /* keep number of running handles */ | |
/* we start some action by calling perform right away */ | |
curl_multi_perform(multi_handle, &still_running); | |
while (still_running) { | |
struct timeval timeout = get_timeout(multi_handle); | |
auto rc = wait_if_needed(multi_handle, timeout); | |
if (rc >= 0) | |
{ | |
/* timeout or readable/writable sockets */ | |
curl_multi_perform(multi_handle, &still_running); | |
} | |
/* else select error */ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
multi_loop(multi_handle.get()); | |
std::for_each(handles.begin(), handles.end(), [&multi_handle](auto& handle) {curl_multi_remove_handle(multi_handle.get(), handle.get()); }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int download_asynchronous(void) | |
{ | |
std::list<EasyHandle> handles(3); | |
MultiHandle multi_handle; | |
/* init easy and multi stacks */ | |
try | |
{ | |
multi_handle = CreateMultiHandle(); | |
std::for_each(handles.begin(), handles.end(), [](auto& handle){handle = CreateEasyHandle(); }); | |
} | |
catch (const std::exception& ex) | |
{ | |
std::cerr << ex.what() << std::endl; | |
return -1; | |
} | |
/* set options */ | |
std::for_each(handles.begin(), handles.end(), [](auto& handle) { | |
curl_easy_setopt(handle.get(), CURLOPT_URL, "https://raw.githubusercontent.com/curl/curl/master/docs/examples/multi-double.c"); | |
set_ssl(handle.get()); | |
save_to_file(handle.get()); | |
}); | |
/* add the individual transfers */ | |
std::for_each(handles.begin(), handles.end(), [&multi_handle](auto& handle) {curl_multi_add_handle(multi_handle.get(), handle.get()); }); | |
multi_loop(multi_handle.get()); | |
std::for_each(handles.begin(), handles.end(), [&multi_handle](auto& handle) {curl_multi_remove_handle(multi_handle.get(), handle.get()); }); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*...*/ | |
for(auto& handle : handles) | |
{ | |
/* HTTP/2 please */ | |
curl_easy_setopt(handle.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); | |
/* wait for pipe connection to confirm */ | |
curl_easy_setopt(handle.get(), CURLOPT_PIPEWAIT, 1L); | |
} | |
curl_multi_setopt(multi_handle.get(), CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Download many transfers over HTTP/2, using the same connection! | |
*/ | |
int download_multiplexing(void) | |
{ | |
std::list<EasyHandle> handles(3); | |
MultiHandle multi_handle; | |
/* init easy and multi stacks */ | |
try | |
{ | |
multi_handle = CreateMultiHandle(); | |
for(auto& handle : handles) | |
{ | |
handle = CreateEasyHandle(); | |
/* HTTP/2 please */ | |
curl_easy_setopt(handle.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); | |
/* wait for pipe connection to confirm */ | |
curl_easy_setopt(handle.get(), CURLOPT_PIPEWAIT, 1L); | |
} | |
} | |
catch (const std::exception& ex) | |
{ | |
std::cerr << ex.what() << std::endl; | |
return -1; | |
} | |
for (auto& handle : handles) | |
{ | |
curl_easy_setopt(handle.get(), CURLOPT_URL, "https://raw.githubusercontent.com/curl/curl/master/docs/examples/http2-download.c"); | |
set_ssl(handle.get()); | |
save_to_file(handle.get()); | |
/* add the individual transfers */ | |
curl_multi_add_handle(multi_handle.get(), handle.get()); | |
} | |
curl_multi_setopt(multi_handle.get(), CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); | |
multi_loop(multi_handle.get()); | |
std::for_each(handles.begin(), handles.end(), [&multi_handle](auto& handle) {curl_multi_remove_handle(multi_handle.get(), handle.get()); }); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment