Created
March 7, 2018 15:24
-
-
Save cbay/c97334c6b202d27558ee70ccf2902614 to your computer and use it in GitHub Desktop.
curl multi-uv.c (+ slight modifications)
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
/*************************************************************************** | |
* _ _ ____ _ | |
* Project ___| | | | _ \| | | |
* / __| | | | |_) | | | |
* | (__| |_| | _ <| |___ | |
* \___|\___/|_| \_\_____| | |
* | |
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. | |
* | |
* This software is licensed as described in the file COPYING, which | |
* you should have received as part of this distribution. The terms | |
* are also available at https://curl.haxx.se/docs/copyright.html. | |
* | |
* You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
* copies of the Software, and permit persons to whom the Software is | |
* furnished to do so, under the terms of the COPYING file. | |
* | |
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
* KIND, either express or implied. | |
* | |
***************************************************************************/ | |
/* <DESC> | |
* multi_socket API using libuv | |
* </DESC> | |
*/ | |
/* Example application using the multi socket interface to download multiple | |
files in parallel, powered by libuv. | |
Requires libuv and (of course) libcurl. | |
See https://nikhilm.github.com/uvbook/ for more information on libuv. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <uv.h> | |
#include <curl/curl.h> | |
uv_loop_t *loop; | |
CURLM *curl_handle; | |
uv_timer_t timeout; | |
typedef struct curl_context_s { | |
uv_poll_t poll_handle; | |
curl_socket_t sockfd; | |
} curl_context_t; | |
static curl_context_t* create_curl_context(curl_socket_t sockfd) | |
{ | |
curl_context_t *context; | |
context = (curl_context_t *) malloc(sizeof *context); | |
context->sockfd = sockfd; | |
uv_poll_init_socket(loop, &context->poll_handle, sockfd); | |
context->poll_handle.data = context; | |
return context; | |
} | |
static void curl_close_cb(uv_handle_t *handle) | |
{ | |
curl_context_t *context = (curl_context_t *) handle->data; | |
free(context); | |
} | |
static void destroy_curl_context(curl_context_t *context) | |
{ | |
uv_close((uv_handle_t *) &context->poll_handle, curl_close_cb); | |
} | |
static size_t | |
do_nothing(void *contents, size_t size, size_t nmemb, void *userp) | |
{ | |
return size * nmemb; | |
} | |
static void add_download(const char *url, int num) | |
{ | |
CURL *handle; | |
handle = curl_easy_init(); | |
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, do_nothing); | |
curl_easy_setopt(handle, CURLOPT_URL, url); | |
curl_multi_add_handle(curl_handle, handle); | |
} | |
static void check_multi_info(void) | |
{ | |
CURLMsg *message; | |
int pending; | |
CURL *easy_handle; | |
while((message = curl_multi_info_read(curl_handle, &pending))) { | |
switch(message->msg) { | |
case CURLMSG_DONE: | |
/* Do not use message data after calling curl_multi_remove_handle() and | |
curl_easy_cleanup(). As per curl_multi_info_read() docs: | |
"WARNING: The data the returned pointer points to will not survive | |
calling curl_multi_cleanup, curl_multi_remove_handle or | |
curl_easy_cleanup." */ | |
easy_handle = message->easy_handle; | |
curl_multi_remove_handle(curl_handle, easy_handle); | |
curl_easy_cleanup(easy_handle); | |
break; | |
default: | |
fprintf(stderr, "CURLMSG default\n"); | |
break; | |
} | |
} | |
} | |
static void curl_perform(uv_poll_t *req, int status, int events) | |
{ | |
int running_handles; | |
int flags = 0; | |
curl_context_t *context; | |
if(events & UV_READABLE) | |
flags |= CURL_CSELECT_IN; | |
if(events & UV_WRITABLE) | |
flags |= CURL_CSELECT_OUT; | |
context = (curl_context_t *) req->data; | |
curl_multi_socket_action(curl_handle, context->sockfd, flags, | |
&running_handles); | |
check_multi_info(); | |
} | |
static void on_timeout(uv_timer_t *req) | |
{ | |
int running_handles; | |
curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, | |
&running_handles); | |
check_multi_info(); | |
} | |
static int start_timeout(CURLM *multi, long timeout_ms, void *userp) | |
{ | |
if(timeout_ms < 0) { | |
uv_timer_stop(&timeout); | |
} | |
else { | |
if(timeout_ms == 0) | |
timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it | |
in a bit */ | |
uv_timer_start(&timeout, on_timeout, timeout_ms, 0); | |
} | |
return 0; | |
} | |
static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, | |
void *socketp) | |
{ | |
curl_context_t *curl_context; | |
int events = 0; | |
switch(action) { | |
case CURL_POLL_IN: | |
case CURL_POLL_OUT: | |
case CURL_POLL_INOUT: | |
curl_context = socketp ? | |
(curl_context_t *) socketp : create_curl_context(s); | |
curl_multi_assign(curl_handle, s, (void *) curl_context); | |
if(action != CURL_POLL_IN) | |
events |= UV_WRITABLE; | |
if(action != CURL_POLL_OUT) | |
events |= UV_READABLE; | |
uv_poll_start(&curl_context->poll_handle, events, curl_perform); | |
break; | |
case CURL_POLL_REMOVE: | |
if(socketp) { | |
uv_poll_stop(&((curl_context_t*)socketp)->poll_handle); | |
destroy_curl_context((curl_context_t*) socketp); | |
curl_multi_assign(curl_handle, s, NULL); | |
} | |
break; | |
default: | |
abort(); | |
} | |
return 0; | |
} | |
int main(int argc, char **argv) | |
{ | |
loop = uv_default_loop(); | |
if(curl_global_init(CURL_GLOBAL_ALL)) { | |
fprintf(stderr, "Could not init curl\n"); | |
return 1; | |
} | |
uv_timer_init(loop, &timeout); | |
curl_handle = curl_multi_init(); | |
curl_multi_setopt(curl_handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, 10L); | |
curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); | |
curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); | |
for (int i=0; i < atoi(argv[1]); i++) { | |
add_download("127.0.0.1:8080/hello.html", i); | |
} | |
uv_run(loop, UV_RUN_DEFAULT); | |
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