• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
46 uv_loop_t *loop;
47 CURLM *curl_handle;
48 uv_timer_t timeout;
49 
50 typedef struct curl_context_s {
51   uv_poll_t poll_handle;
52   curl_socket_t sockfd;
53 } curl_context_t;
54 
create_curl_context(curl_socket_t sockfd)55 curl_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 
curl_close_cb(uv_handle_t * handle)69 void curl_close_cb(uv_handle_t *handle)
70 {
71   curl_context_t *context = (curl_context_t *) handle->data;
72   free(context);
73 }
74 
destroy_curl_context(curl_context_t * context)75 void destroy_curl_context(curl_context_t *context)
76 {
77   uv_close((uv_handle_t *) &context->poll_handle, curl_close_cb);
78 }
79 
80 
add_download(const char * url,int num)81 void 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 
check_multi_info(void)103 static 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 
curl_perform(uv_poll_t * req,int status,int events)133 void 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 
on_timeout(uv_timer_t * req,int status)157 void 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 
start_timeout(CURLM * multi,long timeout_ms,void * userp)165 void 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 
handle_socket(CURL * easy,curl_socket_t s,int action,void * userp,void * socketp)173 int 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 
main(int argc,char ** argv)208 int 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