1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23/* <DESC> 24 * multi_socket API using libuv 25 * </DESC> 26 */ 27/* Example application code using the multi socket interface to download 28 multiple files at once, but instead of using curl_multi_perform and 29 curl_multi_wait, which uses select(), we use libuv. 30 It supports epoll, kqueue, etc. on unixes and fast IO completion ports on 31 Windows, which means, it should be very fast on all platforms.. 32 33 Written by Clemens Gruber, based on an outdated example from uvbook and 34 some tests from libuv. 35 36 Requires libuv and (of course) libcurl. 37 38 See http://nikhilm.github.com/uvbook/ for more information on libuv. 39*/ 40 41#include <stdio.h> 42#include <stdlib.h> 43#include <uv.h> 44#include <curl/curl.h> 45 46uv_loop_t *loop; 47CURLM *curl_handle; 48uv_timer_t timeout; 49 50typedef struct curl_context_s { 51 uv_poll_t poll_handle; 52 curl_socket_t sockfd; 53} curl_context_t; 54 55curl_context_t* create_curl_context(curl_socket_t sockfd) 56{ 57 curl_context_t *context; 58 59 context = (curl_context_t *) malloc(sizeof *context); 60 61 context->sockfd = sockfd; 62 63 uv_poll_init_socket(loop, &context->poll_handle, sockfd); 64 context->poll_handle.data = context; 65 66 return context; 67} 68 69void curl_close_cb(uv_handle_t *handle) 70{ 71 curl_context_t *context = (curl_context_t *) handle->data; 72 free(context); 73} 74 75void destroy_curl_context(curl_context_t *context) 76{ 77 uv_close((uv_handle_t *) &context->poll_handle, curl_close_cb); 78} 79 80 81void add_download(const char *url, int num) 82{ 83 char filename[50]; 84 FILE *file; 85 CURL *handle; 86 87 snprintf(filename, 50, "%d.download", num); 88 89 file = fopen(filename, "wb"); 90 if(!file) { 91 fprintf(stderr, "Error opening %s\n", filename); 92 return; 93 } 94 95 handle = curl_easy_init(); 96 curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); 97 curl_easy_setopt(handle, CURLOPT_PRIVATE, file); 98 curl_easy_setopt(handle, CURLOPT_URL, url); 99 curl_multi_add_handle(curl_handle, handle); 100 fprintf(stderr, "Added download %s -> %s\n", url, filename); 101} 102 103static void check_multi_info(void) 104{ 105 int running_handles; 106 char *done_url; 107 CURLMsg *message; 108 int pending; 109 FILE *file; 110 111 while((message = curl_multi_info_read(curl_handle, &pending))) { 112 switch(message->msg) { 113 case CURLMSG_DONE: 114 curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL, 115 &done_url); 116 curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, &file); 117 printf("%s DONE\n", done_url); 118 119 curl_multi_remove_handle(curl_handle, message->easy_handle); 120 curl_easy_cleanup(message->easy_handle); 121 if(file) { 122 fclose(file); 123 } 124 break; 125 126 default: 127 fprintf(stderr, "CURLMSG default\n"); 128 break; 129 } 130 } 131} 132 133void curl_perform(uv_poll_t *req, int status, int events) 134{ 135 int running_handles; 136 int flags = 0; 137 curl_context_t *context; 138 char *done_url; 139 CURLMsg *message; 140 int pending; 141 142 uv_timer_stop(&timeout); 143 144 if(events & UV_READABLE) 145 flags |= CURL_CSELECT_IN; 146 if(events & UV_WRITABLE) 147 flags |= CURL_CSELECT_OUT; 148 149 context = (curl_context_t *) req; 150 151 curl_multi_socket_action(curl_handle, context->sockfd, flags, 152 &running_handles); 153 154 check_multi_info(); 155} 156 157void on_timeout(uv_timer_t *req, int status) 158{ 159 int running_handles; 160 curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, 161 &running_handles); 162 check_multi_info(); 163} 164 165void start_timeout(CURLM *multi, long timeout_ms, void *userp) 166{ 167 if(timeout_ms <= 0) 168 timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in 169 a bit */ 170 uv_timer_start(&timeout, on_timeout, timeout_ms, 0); 171} 172 173int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, 174 void *socketp) 175{ 176 curl_context_t *curl_context; 177 if(action == CURL_POLL_IN || action == CURL_POLL_OUT) { 178 if(socketp) { 179 curl_context = (curl_context_t *) socketp; 180 } 181 else { 182 curl_context = create_curl_context(s); 183 } 184 curl_multi_assign(curl_handle, s, (void *) curl_context); 185 } 186 187 switch(action) { 188 case CURL_POLL_IN: 189 uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform); 190 break; 191 case CURL_POLL_OUT: 192 uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform); 193 break; 194 case CURL_POLL_REMOVE: 195 if(socketp) { 196 uv_poll_stop(&((curl_context_t*)socketp)->poll_handle); 197 destroy_curl_context((curl_context_t*) socketp); 198 curl_multi_assign(curl_handle, s, NULL); 199 } 200 break; 201 default: 202 abort(); 203 } 204 205 return 0; 206} 207 208int main(int argc, char **argv) 209{ 210 loop = uv_default_loop(); 211 212 if(argc <= 1) 213 return 0; 214 215 if(curl_global_init(CURL_GLOBAL_ALL)) { 216 fprintf(stderr, "Could not init cURL\n"); 217 return 1; 218 } 219 220 uv_timer_init(loop, &timeout); 221 222 curl_handle = curl_multi_init(); 223 curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); 224 curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); 225 226 while(argc-- > 1) { 227 add_download(argv[argc], argc); 228 } 229 230 uv_run(loop, UV_RUN_DEFAULT); 231 curl_multi_cleanup(curl_handle); 232 233 return 0; 234} 235