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