1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2014, 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
42 uv_loop_t *loop;
43 CURLM *curl_handle;
44 uv_timer_t timeout;
45
46 typedef struct curl_context_s {
47 uv_poll_t poll_handle;
48 curl_socket_t sockfd;
49 } curl_context_t;
50
create_curl_context(curl_socket_t sockfd)51 curl_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
curl_close_cb(uv_handle_t * handle)65 void curl_close_cb(uv_handle_t *handle)
66 {
67 curl_context_t *context = (curl_context_t *) handle->data;
68 free(context);
69 }
70
destroy_curl_context(curl_context_t * context)71 void destroy_curl_context(curl_context_t *context)
72 {
73 uv_close((uv_handle_t *) &context->poll_handle, curl_close_cb);
74 }
75
76
add_download(const char * url,int num)77 void 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) {
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_PRIVATE, file);
94 curl_easy_setopt(handle, CURLOPT_URL, url);
95 curl_multi_add_handle(curl_handle, handle);
96 fprintf(stderr, "Added download %s -> %s\n", url, filename);
97 }
98
check_multi_info(void)99 static void check_multi_info(void)
100 {
101 int running_handles;
102 char *done_url;
103 CURLMsg *message;
104 int pending;
105 FILE *file;
106
107 while((message = curl_multi_info_read(curl_handle, &pending))) {
108 switch(message->msg) {
109 case CURLMSG_DONE:
110 curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
111 &done_url);
112 curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, &file);
113 printf("%s DONE\n", done_url);
114
115 curl_multi_remove_handle(curl_handle, message->easy_handle);
116 curl_easy_cleanup(message->easy_handle);
117 if(file) {
118 fclose(file);
119 }
120 break;
121
122 default:
123 fprintf(stderr, "CURLMSG default\n");
124 break;
125 }
126 }
127 }
128
curl_perform(uv_poll_t * req,int status,int events)129 void curl_perform(uv_poll_t *req, int status, int events)
130 {
131 int running_handles;
132 int flags = 0;
133 curl_context_t *context;
134 char *done_url;
135 CURLMsg *message;
136 int pending;
137
138 uv_timer_stop(&timeout);
139
140 if(events & UV_READABLE)
141 flags |= CURL_CSELECT_IN;
142 if(events & UV_WRITABLE)
143 flags |= CURL_CSELECT_OUT;
144
145 context = (curl_context_t *) req;
146
147 curl_multi_socket_action(curl_handle, context->sockfd, flags,
148 &running_handles);
149
150 check_multi_info();
151 }
152
on_timeout(uv_timer_t * req,int status)153 void on_timeout(uv_timer_t *req, int status)
154 {
155 int running_handles;
156 curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
157 &running_handles);
158 check_multi_info();
159 }
160
start_timeout(CURLM * multi,long timeout_ms,void * userp)161 void start_timeout(CURLM *multi, long timeout_ms, void *userp)
162 {
163 if(timeout_ms <= 0)
164 timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in
165 a bit */
166 uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
167 }
168
handle_socket(CURL * easy,curl_socket_t s,int action,void * userp,void * socketp)169 int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp,
170 void *socketp)
171 {
172 curl_context_t *curl_context;
173 if(action == CURL_POLL_IN || action == CURL_POLL_OUT) {
174 if(socketp) {
175 curl_context = (curl_context_t *) socketp;
176 }
177 else {
178 curl_context = create_curl_context(s);
179 }
180 curl_multi_assign(curl_handle, s, (void *) curl_context);
181 }
182
183 switch(action) {
184 case CURL_POLL_IN:
185 uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform);
186 break;
187 case CURL_POLL_OUT:
188 uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform);
189 break;
190 case CURL_POLL_REMOVE:
191 if(socketp) {
192 uv_poll_stop(&((curl_context_t*)socketp)->poll_handle);
193 destroy_curl_context((curl_context_t*) socketp);
194 curl_multi_assign(curl_handle, s, NULL);
195 }
196 break;
197 default:
198 abort();
199 }
200
201 return 0;
202 }
203
main(int argc,char ** argv)204 int main(int argc, char **argv)
205 {
206 loop = uv_default_loop();
207
208 if(argc <= 1)
209 return 0;
210
211 if(curl_global_init(CURL_GLOBAL_ALL)) {
212 fprintf(stderr, "Could not init cURL\n");
213 return 1;
214 }
215
216 uv_timer_init(loop, &timeout);
217
218 curl_handle = curl_multi_init();
219 curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
220 curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
221
222 while(argc-- > 1) {
223 add_download(argv[argc], argc);
224 }
225
226 uv_run(loop, UV_RUN_DEFAULT);
227 curl_multi_cleanup(curl_handle);
228
229 return 0;
230 }
231