Imported Upstream version 7.40.0
[platform/upstream/curl.git] / docs / examples / multi-uv.c
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
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
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
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
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
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
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
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
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
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
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 }