Skip to content

Instantly share code, notes, and snippets.

@jbandela
Last active December 25, 2015 15:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbandela/6997417 to your computer and use it in GitHub Desktop.
Save jbandela/6997417 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
#include <cppcomponents_libuv/cppcomponents_libuv.hpp>
#include<vector>
using namespace cppcomponents_libuv;
using namespace cppcomponents;
CURLM *curl_handle;
Timer timeout;
typedef struct curl_context_s {
use<IPoll> poll_handle;
std::vector<char> curl_error = std::vector<char>(CURL_ERROR_SIZE + 1 );
} curl_context_t;
curl_context_t* create_curl_context()
{
curl_context_t *context;
context = new curl_context_t;
//context->poll_handle = Poll{ sockfd, false };
return context;
}
//
//void curl_close_cb(uv_handle_t *handle)
//{
// curl_context_t* context = (curl_context_t*)handle->data;
// free(context);
//}
void destroy_curl_context(curl_context_t *context)
{
//uv_close((uv_handle_t*)&context->poll_handle, curl_close_cb);
context->poll_handle.Close().Then([context](Future<void> f){
delete context;
});
}
size_t callback(char *ptr, size_t size, size_t nmemb, void *userdata){
std::string s(ptr, ptr + size*nmemb);
return s.size();
}
void add_download(const char *url, int num)
{
char filename[50];
FILE *file;
CURL *handle;
sprintf(filename, "%d.download", num);
file = fopen(filename, "w");
if (file == NULL) {
fprintf(stderr, "Error opening %s\n", filename);
return;
}
auto pcontext = create_curl_context();
handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, callback);
curl_easy_setopt(handle, CURLOPT_URL, url);
//curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);
//curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, &(pcontext->curl_error[0]));
curl_easy_setopt(handle, CURLOPT_PRIVATE, reinterpret_cast<char*>(pcontext));
curl_multi_add_handle(curl_handle, handle);
fprintf(stderr, "Added download %s -> %s\n", url, filename);
}
void curl_perform(use<IPoll>, int status, int events, curl_socket_t sockfd)
{
int running_handles;
int flags = 0;
char *done_url;
CURLMsg *message;
int pending;
timeout.Stop();
if (events & Constants::PollEvent::Readable)
flags |= CURL_CSELECT_IN;
if (events & Constants::PollEvent::Writable)
flags |= CURL_CSELECT_OUT;
curl_multi_socket_action(curl_handle, sockfd, flags,
&running_handles);
while ((message = curl_multi_info_read(curl_handle, &pending))) {
switch (message->msg) {
case CURLMSG_DONE:
curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
&done_url);
printf("%s DONE\n", done_url);
{
auto easy = message->easy_handle;
char* charpcontext = 0;
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &charpcontext);
auto pcontext = reinterpret_cast<curl_context_t*>(charpcontext);
curl_multi_remove_handle(curl_handle, message->easy_handle);
curl_easy_cleanup(easy);
destroy_curl_context(pcontext);
}
break;
default:
fprintf(stderr, "CURLMSG default\n");
abort();
}
}
}
//void on_timeout(uv_timer_t *req, int status)
//{
// int running_handles;
// curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
// &running_handles);
//}
void start_timeout(CURLM *multi, long timeout_ms, void *userp)
{
if (timeout_ms <= 0)
timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in
a bit */
timeout.Start([multi](use<ITimer>, int status){
int running_handles;
curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
&running_handles);
}, std::chrono::milliseconds{ timeout_ms });
}
int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp,
void *)
{
char* charpcontext = 0;
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &charpcontext);
auto curl_context = reinterpret_cast<curl_context_t*>(charpcontext);
if (action == CURL_POLL_IN || action == CURL_POLL_OUT) {
if (!(curl_context->poll_handle)) {
curl_context->poll_handle = Poll{ s, false };
}
}
switch (action) {
case CURL_POLL_IN:
using namespace std::placeholders;
curl_context->poll_handle.Start(Constants::PollEvent::Readable, std::bind(curl_perform,_1, _2, _3, s));
break;
case CURL_POLL_OUT:
curl_context->poll_handle.Start(Constants::PollEvent::Writable, std::bind(curl_perform,_1, _2, _3, s));
break;
case CURL_POLL_REMOVE:
if (curl_context->poll_handle) {
curl_context->poll_handle.Stop();
curl_context->poll_handle = nullptr;
}
break;
default:
abort();
}
return 0;
}
int main(int argc, char **argv)
{
auto loop = Loop::DefaultLoop();
if (argc <= 1)
return 0;
if (curl_global_init(CURL_GLOBAL_ALL)) {
fprintf(stderr, "Could not init cURL\n");
return 1;
}
curl_handle = curl_multi_init();
curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
while (argc-- > 1) {
add_download(argv[argc], argc);
}
loop.Run();
curl_multi_cleanup(curl_handle);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment