1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2013, 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 http://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/* Example application code using the multi socket interface to download 24 multiple files at once, but instead of using curl_multi_perform and 25 curl_multi_wait, which uses select(), we use libuv. 26 It supports epoll, kqueue, etc. on unixes and fast IO completion ports on 27 Windows, which means, it should be very fast on all platforms.. 28 29 Written by Clemens Gruber, based on an outdated example from uvbook and 30 some tests from libuv. 31 32 Requires libuv and (of course) libcurl. 33 34 See http://nikhilm.github.com/uvbook/ for more information on libuv. 35*/ 36 37#include <stdio.h> 38#include <stdlib.h> 39#include <uv.h> 40#include <curl/curl.h> 41 42uv_loop_t *loop; 43CURLM *curl_handle; 44uv_timer_t timeout; 45 46typedef struct curl_context_s { 47 uv_poll_t poll_handle; 48 curl_socket_t sockfd; 49} curl_context_t; 50 51curl_context_t* create_curl_context(curl_socket_t sockfd) 52{ 53 curl_context_t *context; 54 55 context = (curl_context_t *) malloc(sizeof *context); 56 57 context->sockfd = sockfd; 58 59 uv_poll_init_socket(loop, &context->poll_handle, sockfd); 60 context->poll_handle.data = context; 61 62 return context; 63} 64 65void curl_close_cb(uv_handle_t *handle) 66{ 67 curl_context_t* context = (curl_context_t*) handle->data; 68 free(context); 69} 70 71void destroy_curl_context(curl_context_t *context) 72{ 73 uv_close((uv_handle_t*) &context->poll_handle, curl_close_cb); 74} 75 76 77void add_download(const char *url, int num) 78{ 79 char filename[50]; 80 FILE *file; 81 CURL *handle; 82 83 sprintf(filename, "%d.download", num); 84 85 file = fopen(filename, "w"); 86 if (file == NULL) { 87 fprintf(stderr, "Error opening %s\n", filename); 88 return; 89 } 90 91 handle = curl_easy_init(); 92 curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); 93 curl_easy_setopt(handle, CURLOPT_URL, url); 94 curl_multi_add_handle(curl_handle, handle); 95 fprintf(stderr, "Added download %s -> %s\n", url, filename); 96} 97 98void curl_perform(uv_poll_t *req, int status, int events) 99{ 100 int running_handles; 101 int flags = 0; 102 curl_context_t *context; 103 char *done_url; 104 CURLMsg *message; 105 int pending; 106 107 uv_timer_stop(&timeout); 108 109 if (events & UV_READABLE) 110 flags |= CURL_CSELECT_IN; 111 if (events & UV_WRITABLE) 112 flags |= CURL_CSELECT_OUT; 113 114 context = (curl_context_t*)req; 115 116 curl_multi_socket_action(curl_handle, context->sockfd, flags, 117 &running_handles); 118 119 while ((message = curl_multi_info_read(curl_handle, &pending))) { 120 switch (message->msg) { 121 case CURLMSG_DONE: 122 curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL, 123 &done_url); 124 printf("%s DONE\n", done_url); 125 126 curl_multi_remove_handle(curl_handle, message->easy_handle); 127 curl_easy_cleanup(message->easy_handle); 128 129 break; 130 default: 131 fprintf(stderr, "CURLMSG default\n"); 132 abort(); 133 } 134 } 135} 136 137void on_timeout(uv_timer_t *req, int status) 138{ 139 int running_handles; 140 curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, 141 &running_handles); 142} 143 144void start_timeout(CURLM *multi, long timeout_ms, void *userp) 145{ 146 if (timeout_ms <= 0) 147 timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in 148 a bit */ 149 uv_timer_start(&timeout, on_timeout, timeout_ms, 0); 150} 151 152int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, 153 void *socketp) 154{ 155 curl_context_t *curl_context; 156 if (action == CURL_POLL_IN || action == CURL_POLL_OUT) { 157 if (socketp) { 158 curl_context = (curl_context_t*) socketp; 159 } 160 else { 161 curl_context = create_curl_context(s); 162 } 163 curl_multi_assign(curl_handle, s, (void *) curl_context); 164 } 165 166 switch (action) { 167 case CURL_POLL_IN: 168 uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform); 169 break; 170 case CURL_POLL_OUT: 171 uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform); 172 break; 173 case CURL_POLL_REMOVE: 174 if (socketp) { 175 uv_poll_stop(&((curl_context_t*)socketp)->poll_handle); 176 destroy_curl_context((curl_context_t*) socketp); 177 curl_multi_assign(curl_handle, s, NULL); 178 } 179 break; 180 default: 181 abort(); 182 } 183 184 return 0; 185} 186 187int main(int argc, char **argv) 188{ 189 loop = uv_default_loop(); 190 191 if (argc <= 1) 192 return 0; 193 194 if (curl_global_init(CURL_GLOBAL_ALL)) { 195 fprintf(stderr, "Could not init cURL\n"); 196 return 1; 197 } 198 199 uv_timer_init(loop, &timeout); 200 201 curl_handle = curl_multi_init(); 202 curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); 203 curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); 204 205 while (argc-- > 1) { 206 add_download(argv[argc], argc); 207 } 208 209 uv_run(loop, UV_RUN_DEFAULT); 210 curl_multi_cleanup(curl_handle); 211 return 0; 212} 213